PageSourceSearch

https://cdn.optimizely.com/js/26942930964.js

js optimizely.com collected 2026-10-09 21:56:29 UTC 1,739,221 bytes, 23,102 lines download raw bytes

1/*! For license information please see client.min.js.LICENSE.txt */
2(function(){var __webpack_modules__={4254:function(t,n,e){var i=e(3766)(),r=e(2994);t.exports=function(t,n){function e(n){if(!(this instanceof e))return new e(n);try{throw new Error(n)}catch(n){n.name=t,this.stack=n.stack}i&&this.stack&&(this.stack=r(this.stack,t,n)),this.message=n||"",this.name=t}return e.prototype=new(n||Error),e.prototype.constructor=e,e.prototype.inspect=function(){return this.message?"["+t+": "+this.message+"]":"["+t+"]"},e.prototype.name=t,e}},2994:function(t){"use strict";t.exports=function(t,n,e){var i=n;return e&&(i+=": "+e),t=i+t.slice(t.indexOf("\n"))}},3766:function(t){"use strict";t.exports=function(){var t=new Error("yep");return!!t.stack&&"Error: yep\n"===t.stack.substr(0,11)}},8535:function(t,n,e){t.exports=function(){"use strict";function t(t){return"function"==typeof t||"object"==typeof t&&null!==t}function n(t){return"function"==typeof t}var i=Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)},r=0,o=void 0,a=void 0,u=function(t,n){_[r]=t,_[r+1]=n,2===(r+=2)&&(a?a(w):I())};function c(t){a=t}function s(t){u=t}var f="undefined"!=typeof window?window:void 0,l=f||{},d=l.MutationObserver||l.WebKitMutationObserver,v="undefined"==typeof self&&"undefined"!=typeof process&&"[object process]"==={}.toString.call(process),h="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel;function p(){return function(){return process.nextTick(w)}}function g(){return void 0!==o?function(){o(w)}:y()}function m(){var t=0,n=new d(w),e=document.createTextNode("");return n.observe(e,{characterData:!0}),function(){e.data=t=++t%2}}function b(){var t=new MessageChannel;return t.port1.onmessage=w,function(){return t.port2.postMessage(0)}}function y(){var t=setTimeout;return function(){return t(w,1)}}var _=new Array(1e3);function w(){for(var t=0;t<r;t+=2)(0,_[t])(_[t+1]),_[t]=void 0,_[t+1]=void 0;r=0}function E(){try{var t=e(6694);return o=t.runOnLoop||t.runOnContext,g()}catch(t){return y()}}var I=void 0;function A(t,n){var e=arguments,i=this,r=new this.constructor(O);void 0===r[S]&&J(r);var o,a=i._state;return a?(o=e[a-1],u((function(){return $(a,r,o,i._result)}))):G(i,r,t,n),r}function T(t){var n=this;if(t&&"object"==typeof t&&t.constructor===n)return t;var e=new n(O);return U(e,t),e}I=v?p():d?m():h?b():void 0===f?E():y();var S=Math.random().toString(36).substring(16);function O(){}var k=void 0,R=1,C=2,D=new H;function N(){return new TypeError("You cannot resolve a promise with itself")}function x(){return new TypeError("A promises callback cannot return that same promise.")}function j(t){try{return t.then}catch(t){return D.error=t,D}}function P(t,n,e,i){try{t.call(n,e,i)}catch(t){return t}}function M(t,n,e){u((function(t){var i=!1,r=P(e,n,(function(e){i||(i=!0,n!==e?U(t,e):B(t,e))}),(function(n){i||(i=!0,F(t,n))}),"Settle: "+(t._label||" unknown promise"));!i&&r&&(i=!0,F(t,r))}),t)}function L(t,n){n._state===R?B(t,n._result):n._state===C?F(t,n._result):G(n,void 0,(function(n){return U(t,n)}),(function(n){return F(t,n)}))}function V(t,e,i){e.constructor===t.constructor&&i===A&&e.constructor.resolve===T?L(t,e):i===D?(F(t,D.error),D.error=null):void 0===i?B(t,e):n(i)?M(t,e,i):B(t,e)}function U(n,e){n===e?F(n,N()):t(e)?V(n,e,j(e)):B(n,e)}function z(t){t._onerror&&t._onerror(t._result),q(t)}function B(t,n){t._state===k&&(t._result=n,t._state=R,0!==t._subscribers.length&&u(q,t))}function F(t,n){t._state===k&&(t._state=C,t._result=n,u(z,t))}function G(t,n,e,i){var r=t._subscribers,o=r.length;t._onerror=null,r[o]=n,r[o+R]=e,r[o+C]=i,0===o&&t._state&&u(q,t)}function q(t){var n=t._subscribers,e=t._state;if(0!==n.length){for(var i=void 0,r=void 0,o=t._result,a=0;a<n.length;a+=3)i=n[a],r=n[a+e],i?$(e,i,r,o):r(o);t._subscribers.length=0}}function H(){this.error=null}var Y=new H;function K(t,n){try{return t(n)}catch(t){return Y.error=t,Y}}function $(t,e,i,r){var o=n(i),a=void 0,u=void 0,c=void 0,s=void 0;if(o){if((a=K(i,r))===Y?(s=!0,u=a.error,a.error=null):c=!0,e===a)return void F(e,x())}else a=r,c=!0;e._state!==k||(o&&c?U(e,a):s?F(e,u):t===R?B(e,a):t===C&&F(e,a))}function W(t,n){try{n((function(n){U(t,n)}),(function(n){F(t,n)}))}catch(n){F(t,n)}}var X=0;function Q(){return X++}function J(t){t[S]=X++,t._state=void 0,t._result=void 0,t._subscribers=[]}function Z(t,n){this._instanceConstructor=t,this.promise=new t(O),this.promise[S]||J(this.promise),i(n)?(this._input=n,this.length=n.length,this._remaining=n.length,this._result=new Array(this.length),0===this.length?B(this.promise,this._result):(this.length=this.length||0,this._enumerate(),0===this._remaining&&B(this.promise,this._result))):F(this.promise,tt())}function tt(){return new Error("Array Methods must be provided an Array")}function nt(t){return new Z(this,t).promise}function et(t){var n=this;return i(t)?new n((function(e,i){for(var r=t.length,o=0;o<r;o++)n.resolve(t[o]).then(e,i)})):new n((function(t,n){return n(new TypeError("You must pass an array to race."))}))}function it(t){var n=new this(O);return F(n,t),n}function rt(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function ot(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object c
2onstructor cannot be called as a function.")}function at(t){this[S]=Q(),this._result=this._state=void 0,this._subscribers=[],O!==t&&("function"!=typeof t&&rt(),this instanceof at?W(this,t):ot())}function ut(){var t=void 0;if(void 0!==e.g)t=e.g;else if("undefined"!=typeof self)t=self;else try{t=Function("return this")()}catch(t){throw new Error("polyfill failed because global object is unavailable in this environment")}var n=t.Promise;if(n){var i=null;try{i=Object.prototype.toString.call(n.resolve())}catch(t){}if("[object Promise]"===i&&!n.cast)return}t.Promise=at}return Z.prototype._enumerate=function(){for(var t=this.length,n=this._input,e=0;this._state===k&&e<t;e++)this._eachEntry(n[e],e)},Z.prototype._eachEntry=function(t,n){var e=this._instanceConstructor,i=e.resolve;if(i===T){var r=j(t);if(r===A&&t._state!==k)this._settledAt(t._state,n,t._result);else if("function"!=typeof r)this._remaining--,this._result[n]=t;else if(e===at){var o=new e(O);V(o,t,r),this._willSettleAt(o,n)}else this._willSettleAt(new e((function(n){return n(t)})),n)}else this._willSettleAt(i(t),n)},Z.prototype._settledAt=function(t,n,e){var i=this.promise;i._state===k&&(this._remaining--,t===C?F(i,e):this._result[n]=e),0===this._remaining&&B(i,this._result)},Z.prototype._willSettleAt=function(t,n){var e=this;G(t,void 0,(function(t){return e._settledAt(R,n,t)}),(function(t){return e._settledAt(C,n,t)}))},at.all=nt,at.race=et,at.resolve=T,at.reject=it,at._setScheduler=c,at._setAsap=s,at._asap=u,at.prototype={constructor:at,then:A,catch:function(t){return this.then(null,t)}},at.polyfill=ut,at.Promise=at,at}()},762:function(t){"use strict";t.exports=function(t){var n,e={};if(!(t instanceof Object)||Array.isArray(t))throw new Error("keyMirror(...): Argument must be an object.");for(n in t)t.hasOwnProperty(n)&&(e[n]=n);return e}},2891:function(t){"use strict";t.exports=function(t,n,e,i){Object.defineProperty(t,n,{get:function(){var t=e.call(this);return Object.defineProperty(this,n,{value:t,enumerable:!!i,writable:!0}),t},set:function(t){return Object.defineProperty(this,n,{value:t,enumerable:!!i,writable:!0}),t},enumerable:!!i,configurable:!0})}},4827:function(t){"use strict";var n=Element.prototype,e=n.matches||n.matchesSelector||n.webkitMatchesSelector||n.mozMatchesSelector||n.msMatchesSelector||n.oMatchesSelector;t.exports=function(t,n){if(e)return e.call(t,n);for(var i=t.parentNode.querySelectorAll(n),r=0;r<i.length;r++)if(i[r]==t)return!0;return!1}},4188:function(t){!function(){function n(t,n){var e,i,r,o,a,u,c,s;for(e=3&t.length,i=t.length-e,r=n,a=3432918353,u=461845907,s=0;s<i;)c=255&t.charCodeAt(s)|(255&t.charCodeAt(++s))<<8|(255&t.charCodeAt(++s))<<16|(255&t.charCodeAt(++s))<<24,++s,r=27492+(65535&(o=5*(65535&(r=(r^=c=(65535&(c=(c=(65535&c)*a+(((c>>>16)*a&65535)<<16)&4294967295)<<15|c>>>17))*u+(((c>>>16)*u&65535)<<16)&4294967295)<<13|r>>>19))+((5*(r>>>16)&65535)<<16)&4294967295))+((58964+(o>>>16)&65535)<<16);switch(c=0,e){case 3:c^=(255&t.charCodeAt(s+2))<<16;case 2:c^=(255&t.charCodeAt(s+1))<<8;case 1:r^=c=(65535&(c=(c=(65535&(c^=255&t.charCodeAt(s)))*a+(((c>>>16)*a&65535)<<16)&4294967295)<<15|c>>>17))*u+(((c>>>16)*u&65535)<<16)&4294967295}return r^=t.length,r=2246822507*(65535&(r^=r>>>16))+((2246822507*(r>>>16)&65535)<<16)&4294967295,r=3266489909*(65535&(r^=r>>>13))+((3266489909*(r>>>16)&65535)<<16)&4294967295,(r^=r>>>16)>>>0}var e=n;e.v2=function(t,n){for(var e,i=t.length,r=n^i,o=0;i>=4;)e=1540483477*(65535&(e=255&t.charCodeAt(o)|(255&t.charCodeAt(++o))<<8|(255&t.charCodeAt(++o))<<16|(255&t.charCodeAt(++o))<<24))+((1540483477*(e>>>16)&65535)<<16),r=1540483477*(65535&r)+((1540483477*(r>>>16)&65535)<<16)^(e=1540483477*(65535&(e^=e>>>24))+((1540483477*(e>>>16)&65535)<<16)),i-=4,++o;switch(i){case 3:r^=(255&t.charCodeAt(o+2))<<16;case 2:r^=(255&t.charCodeAt(o+1))<<8;case 1:r=1540483477*(65535&(r^=255&t.charCodeAt(o)))+((1540483477*(r>>>16)&65535)<<16)}return r=1540483477*(65535&(r^=r>>>13))+((1540483477*(r>>>16)&65535)<<16),(r^=r>>>15)>>>0},e.v3=n,t.exports=e}()},243:function(t,n,e){var i=e(4188).v3,r=Math.pow(2,32),o=function(t,n){return(i(t,n)>>>0)/r};t.exports={Seed:{IGNORING:0,BUCKETING:1,FALLBACK:2,HOLDBACK:3,BEHAVIOR_EVENT:2716770798},hashToHex:function(t,n){var e=i(t,n);return(e>>>16).toString(16)+(65535&e).toString(16)},hashToInt:function(t,n,e){return Math.floor(o(t,n)*e)},hashToReal:o,toByteString:function(t){var n=String.fromCharCode;return t.replace(/[\S\s]/gi,(function(t){t=t.charCodeAt(0);var e=n(255&t);return t>255&&(e=n(t>>>8&255)+e),t>65535&&(e=n(t>>>16)+e),e}))}}},2030:function(t,n,e){var i=e(279).pick,r=e(3221).get("stores/audience_data"),o=e(6210),a=e(3659),u=e(5355),c=e(7702);function s(t){var n=["type","selector","attributes","value"],e=Object.assign({},t);return e.changeSet=t.changeSet.map((function(t){return i(c.dereferenceChangeId(t),n)})),e}n.emitLayerDecided=function(t){var n=(t.decisionTicket?t.decisionTicket.audienceIds:[]).map((function(t){return{id:t,name:r.get(t).name}})),e={type:a.TYPES.LIFECYCLE,name:"layerDecided",data:Object.assign(t,{audiences:n})},i=c.translateLayerEventToCampaignEvent(e);
2o.emit(e),o.emit(i)},n.emitViewActivated=function(t){var n={type:a.TYPES.LIFECYCLE,name:"viewActivated",data:t},e=c.translateViewActivatedToPageActivated(n);o.emit(n),o.emit(e)},n.emitViewsActivated=function(t){var n={type:a.TYPES.LIFECYCLE,name:"viewsActivated",data:t};o.emit(n)},n.emitPageDeactivated=function(t){var n={type:a.TYPES.LIFECYCLE,name:"pageDeactivated",data:t};o.emit(n)},n.emitActivateEvent=function(){o.emit({type:a.TYPES.LIFECYCLE,name:"activate"},!0)},n.emitActivatedEvent=function(){o.emit({type:a.TYPES.LIFECYCLE,name:"activated"})},n.emitInitializedEvent=function(){var t={type:a.TYPES.LIFECYCLE,name:"initialized"};window.optimizely&&(window.optimizely.initialized=!0),o.emit(t)},n.emitOriginsSyncedEvent=function(){var t={type:a.TYPES.LIFECYCLE,name:"originsSynced"};o.emit(t)},n.emitActionAppliedEvent=function(t){var n={type:t.type,campaignId:t.layerId,pageId:t.pageId,experimentId:t.experimentId,variationId:t.variationId};u.defineProperty(n,"changes",(function(){return s(t).changeSet}),"actionAppliedEvent");var e={type:a.TYPES.ACTION,name:"applied",data:n};o.emit(e)},n.emitActionsForDecisionAppliedEvent=function(t,n){var e={decision:t};u.defineProperty(e,"actions",(function(){return n.map(s)}),"appliedAllForDecisionEvent");var i={type:a.TYPES.ACTION,name:"appliedAllForDecision",data:e};o.emit(i)},n.emitSendEvents=function(){var t={type:a.TYPES.ANALYTICS,name:"sendEvents"};o.emit(t)},n.emitMaybeSendEvents=function(){var t={type:a.TYPES.ANALYTICS,name:"maybeSendEvents"};o.emit(t)},n.emitHoldEvents=function(){var t={type:a.TYPES.ANALYTICS,name:"holdEvents"};o.emit(t)}},6320:function(t,n,e){Object.defineProperty(n,"u",{value:!0});var i=e(279),r=i.cloneDeep,o=i.isEqual,a=i.mapValues,u=i.pick,c=i.pickBy,s=e(6555),f=e(5355),l=e(7702),d=e(1386),v=e(2060);function h(t,n,e,i){var r=t.getLayerState(i),o=n.get(i),a=e.get();if(!r||!o)return a?{layer:{name:a.layerName,id:a.layerId,policy:a.layerPolicy,integrationStringVersion:a.integrationStringVersion},experiment:{name:a.experimentName,id:a.experimentId},variation:{name:a.variationName,id:a.variationId},isLayerHoldback:!1}:null;if(d.isSingleExperimentPolicy(o.policy)&&r.decision.isLayerHoldback)return null;var u,c,s=r.decision.experimentId,f=r.decision.variationId;return s&&f&&(u=o.experiments.find((function(t){return t.id===s})))&&(c=u.variations.find((function(t){return t.id===f})))?{layer:{name:o.name,id:o.id,policy:o.policy,integrationStringVersion:o.integrationStringVersion},experiment:{name:u.name,id:u.id},variation:{name:c.name,id:c.id},isLayerHoldback:r.decision.isLayerHoldback}:null}function p(t,n,e,i,r,a,c){var s=[],f=t.getLayerStates();c.onlySingleExperiments&&(f=f.filter((function(t){var e=n.get(t.layerId);return e&&d.isSingleExperimentPolicy(e.policy)})));var l=e.getActiveViewIds(),h=f.map((function(t){var e,i=!!t.decision.variationId,o=t.decisionActivationId&&t.decisionActivationId===r.getActivationId(),a=v.getExperimentAndVariation(),u=a?a.variationId:null,c=i&&t.decision.variationId===u,s=((null==(e=n.get(null==t?void 0:t.layerId))?void 0:e.viewIds)||[]).every((function(t){return!l.includes(t)}));return Object.assign(t,{isActive:i&&o&&!s||c,visitorRedirected:c})}));return(a?"function"==typeof a?h.filter(a):h.filter((function(t){return Object.keys(a).every((function(n){return o(t[n],a[n])}))})):h).forEach((function(t){var e=function(t,n,e,i){var r,o,a=t.layerId,c=n.get(a)||{},s=(c.experiments||[]).map((function(t){return u(t,["id","name"])}));if(!i&&c.decisionMetadata&&c.decisionMetadata.offerConsistency)return;var f={id:a,campaignName:c.name||null,experiment:null,audiences:[],allExperiments:s,variation:null,reason:t.decision.reason,isActive:!!t.isActive,visitorRedirected:t.visitorRedirected,isInCampaignHoldback:t.decision.isLayerHoldback};t.decision&&t.decision.experimentId&&(r=(c.experiments||[]).find((function(n){return n.id===t.decision.experimentId}))),r&&(f.experiment=u(r,["id","name","campaignName"])),r&&t.decision.variationId&&(o=r.variations.find((function(n){return n.id===t.decision.variationId}))),o&&(f.variation=u(o,["id","name"]));var l=(t.decisionTicket.audienceIds||[]).map((function(t){return u(e.get(t),["id","name"])}));return f.audiences=l,c.decisionMetadata&&c.decisionMetadata.offerConsistency&&(f.pageId=t.pageId),f}(t,n,i,c.includeOfferConsistency);e&&s.push(e)})),s}
2n.data=["stores/audience_data","stores/client_metadata","stores/event_data","stores/layer_data","stores/view_data","stores/group_data","stores/interest_group","stores/tag_group","stores/global",function(t,n,i,r,o,a,u,c,s){e(6494).time("optimizelyGetData");var d={},v={},h={},p={audiences:t.getAudiencesMap(),events:i.getEventsMap(),campaigns:d,pages:o.getPagesMap(),experiments:v,variations:h,projectId:s.getProjectId(),snippetId:s.getSnippetId(),accountId:s.getAccountId(),dcpServiceId:s.getDCPServiceId(),revision:s.getRevision(),clientName:n.getClientName(),clientVersion:n.getClientVersion()},g=l.dereferenceChangeId;return r.getAll().forEach((function(t){f.defineProperty(d,t.id,(function(){var n=Object.assign({},t);return f.defineProperty(n,"changes",(function(){return(t.changes||[]).map(g)}),"campaign"),f.defineProperty(n,"experiments",(function(){return(t.experiments||[]).map((function(t){return v[t.id]}))}),"campaign"),n}),"campaignMap","byId"),(t.experiments||[]).forEach((function(t){f.defineProperty(v,t.id,(function(){var n=Object.assign({},t);return f.defineProperty(n,"changes",(function(){return(t.changes||[]).map(g)}),"experiment"),f.defineProperty(n,"variations",(function(){return(t.variations||[]).map((function(t){return h[t.id]}))}),"experiment"),n}),"experimentMap","byId"),(t.variations||[]).forEach((function(t){f.defineProperty(h,t.id,(function(){var n=Object.assign({},t);return f.defineProperty(n,"actions",(function(){return(t.actions||[]).map((function(t){return Object.assign({},t,{changes:(t.changes||[]).map(g)})}))}),"variation"),n}),"variationMap","byId")}))}))})),p.groups=a.getGroupsMap(),e(6494).timeEnd("optimizelyGetData"),p}],n.visitor=["stores/visitor",function(t){return r(t.getVisitorProfile())}],n.visitor_id=["stores/visitor_id",function(t){return{randomId:t.getRandomId()}}],n.state=["stores/audience_data","stores/layer_data","stores/layer","stores/view_data","stores/view","stores/global","stores/observed_redirect",function(t,n,i,r,o,f,l){return{getTrafficExclusionStats:function(){return e(5296).getStats()},getCampaignStates:function(e){var r={};return p(i,n,o,t,f,e,{includeOfferConsistency:!1}).forEach((function(t){r[t.id]=t})),r},getExperimentStates:function(e){var r=p(i,n,o,t,f,e,{includeOfferConsistency:!1,onlySingleExperiments:!0}),a=["audiences","variation","reason","visitorRedirected","isActive"];return r.reduce((function(t,n){var e=n.allExperiments[0];return t[e.id]=Object.assign({},u(n,a),{id:e.id,experimentName:e.name,isInExperimentHoldback:n.isInCampaignHoldback}),t}),{})},getCampaignStateLists:function(e){var r={};return p(i,n,o,t,f,e,{includeOfferConsistency:!0}).forEach((function(t){var n=t.id;r[n]||(r[n]=[]),r[n].push(t)})),r},getPageStates:function(t){var n=o.getAll(),e=Object.keys(n).reduce((function(t,e){var i=n[e],o=r.get(i.id);return t[i.id]=Object.assign({},u(o,["id","name","apiName","category","staticConditions","tags"]),u(i,["isActive","metadata"])),t[i.id].isActive=!!t[i.id].isActive,t}),{});return t?c(e,t):e},isGlobalHoldback:function(){return f.isGlobalHoldback()},getActivationId:function(){return f.getActivationId()},getVariationMap:function(){var t=i.getLayerStates(),e={};return t.forEach((function(t){var i=n.get(t.layerId);if(t.decision&&t.decision.experimentId&&(e[t.decision.experimentId]={id:t.decision.variationId,name:null,index:null},i)){var r=(i.experiments||[]).find((function(n){return n.id===t.decision.experimentId}));if(r&&t.decision.variationId)var o=r.variations.find((function(n){return n.id===t.decision.variationId})),a=r.variations.findIndex((function(n){return n.id===t.decision.variationId}));o&&(e[t.decision.experimentId]={id:t.decision.variationId,name:o.name,index:a})}})),e},getActiveExperimentIds:function(){var t={},n=this.getCampaignStateLists({isActive:!0});return Object.keys(n).forEach((function(e){n[e].forEach((function(n){t[n.experiment.id]=!0}))})),Object.keys(t)},getRedirectInfo:function(){var t=v.getExperimentAndVariation();return t&&(t.referrer=v.getReferrer()),t}
2,getDecisionString:function(t){if(!t)throw new Error("Must pass a config to getDecisionString");t=Object.assign({maxLength:255,shouldCleanString:!1},t);var e=h(i,n,l,t.campaignId);return e?s.generateAnalyticsString(e.layer,e.experiment,e.variation,e.isLayerHoldback,t.maxLength,t.shouldCleanString):null},getDecisionObject:function(t){if(!t)throw new Error("Must pass a config to getDecisionObject");t=Object.assign({maxLength:255,shouldCleanString:!1},t);var e=h(i,n,l,t.campaignId);if(!e)return null;var r=s.formatNamesAndIdsForAnalytics(e.layer,e.experiment,e.variation,t.shouldCleanString),o=a(r.names,(function(n,e){return s.combineAndTruncateIdAndName(n,r.idStrings[e],t.maxLength)})),u={experiment:o.experiment,variation:o.variation};return d.isSingleExperimentPolicy(e.layer.policy)||Object.assign(u,{campaign:o.layer,holdback:e.isLayerHoldback}),u}}}],n.utils=e(6139).create(),n.jquery=["env/jquery",function(t){return t}],n.event_emitter=e(879)},2945:function(t,n,e){var i=e(3496),r=e(3898),o="optimizelyDataApi";n.registerFunction=function(t,n){var e=r.getGlobal(o);e||(e={},r.setGlobal(o,e)),e[t]||(e[t]=n)},n.unregisterFunction=function(t){var n=r.getGlobal(o);n&&n[t]&&(n[t]=function(){i.log('Ignoring attempt to call "'+o+"."+t+'" which has been unregistered.')})},n.getFunction=function(t){return r.getGlobal(o)[t]}},5205:function(t,n,e){var i=e(279).isObject,r=e(3496),o=e(6320),a=e(3221),u=a.get("stores/plugins"),c=e(1756),s=e(2044),f=e(3941),l=[e(5502),e(8008),e(8457)],d=["clientMetadata","cookieDomain","disable","load","optOut","rum"];n.push=function(t,n){var e,o,a,u;if(!Array.isArray(t)&&i(t))u=void 0!==t.version?t.version:1,e=t.type,a=[t];else if(Array.isArray(t))u=0,e=t[0],a=t.slice(1);else{if("string"!=typeof t)return r.warn("API / Ignoring non-array/object/string argument:",t),!1;u=0,e=t,a=[]}if(l[u]&&(o=l[u][e]),n&&-1===d.indexOf(e))return r.debug("API / Ignoring non high priority function:",e,a),!1;if(!o)return r.warn('API / No function found for "'+e+'" (v'+u+") with arguments:",a),!1;r.log('API / Executing: "'+e,'" with arguments:',a);try{o.apply(null,a),f.dispatch(c.RECORD_API_USAGE,{methodName:u?"v"+u+"."+e:e})}catch(t){r.error(t)}return!0},n.get=function(t){r.log('API / Getting module: "'+t+'"');var n=o[t];if(n?Array.isArray(n)&&(n=a.evaluate(n)):n=u.getPlugin(s.PluginTypes.apiModules,t),n)return f.dispatch(c.RECORD_API_USAGE,{methodName:"get."+t}),n;r.warn('Module "'+t+'" not found.')}},5355:function(t,n,e){var i=e(2891),r=e(1756),o=e(3941),a=e(3496);n.defineProperty=function(t,n,e,u,c){i(t,n,(function(){var t=["prop",u,c||n].join(".");return a.debug('Evaluating getter: "'+t+'"'),o.dispatch(r.RECORD_API_USAGE,{methodName:t}),e()}),!0)}},5502:function(t,n,e){var i=e(8008);function r(t){var n,e={};if(t)if(o(t))n=Number(t);else{if("object"!=typeof t)throw new Error("tracker: Revenue argument "+t+"not a number.");if("revenue"in(e=Object.assign({},t))){if(!o(e.revenue))throw new Error("tracker: Revenue value "+e.revenue+"not a number.");n=Number(e.revenue),delete e.revenue}}return void 0!==n&&(e.revenue=n),e}function o(t){return"number"==typeof t||"string"==typeof t&&!isNaN(Number(t))&&Number(t).toString()==t}n.activateGeoDelayedExperiments=function(t,n){n||(n=t.lists?"odds":"cdn3"),i.dataFromSource({data:t,source:n})},n.activateSiteCatalyst=function(t){t&&t.sVariable&&i.integrationSettings({id:"adobe_analytics",settings:{sVariableReference:t.sVariable}})},n.bucketVisitor=function(t,n){if(t&&n){var e={experimentId:String(t)};n>256?e.variationId=String(n):e.variationIndex=String(n),i.bucketVisitor(e)}},n.bucketUser=n.bucketVisitor,n.disable=function(t){i.disable({scope:t})},n.log=function(t){void 0===t&&(t=!0),i.log({level:t?"INFO":"OFF"})},n.optOut=function(t){void 0===t&&(t=!0),i.optOut({isOptOut:t})},n.setCookieDomain=function(t){i.cookieDomain({cookieDomain:t})},n.setCookieExpiration=function(t){i.cookieExpiration({cookieExpirationDays:t})},n.setDimensionValue=function(t,n){var e={};e[t]=n,i.user({attributes:e})},n.setUserId=function(t){i.user({userId:t})},n.setGA4UserId=function(t){i.ga4User({ga4UserId:t})},n.storeThirdPartyData=function(t,n){i.dataFromSource({source:t,data:n})},n.trackEvent=function(t,n){i.event({eventName:t,tags:r(n)})}}
2,7702:function(t,n,e){var i=e(279),r=i.cloneDeep,o=i.omit,a=e(3221),u=e(1423),c=e(5355),s=a.get("stores/change_data");n.translateDecisionToCampaignDecision=function(t){return f(r(t),{layerId:"campaignId",isLayerHoldback:"isCampaignHoldback"})},n.translateLayerEventToCampaignEvent=function(t){var e={};return c.defineProperty(e,"campaign",(function(){return function(t){var e=r(t);return e.changes&&(e.changes=e.changes.map(n.dereferenceChangeId)),e.experiments&&e.experiments.forEach((function(t){t.changes&&(t.changes=t.changes.map(n.dereferenceChangeId)),t.variations&&t.variations.forEach((function(t){t.actions&&t.actions.forEach((function(t){t.changes&&(t.changes=t.changes.map(n.dereferenceChangeId))}))}))})),e}(t.data.layer)}),"campaignEvent"),e.decisionTicket=t.data.decisionTicket,e.decision=this.translateDecisionToCampaignDecision(t.data.decision),e.audiences=t.data.audiences,{type:"lifecycle",name:"campaignDecided",data:e}},n.translateViewActivatedToPageActivated=function(t){return{type:"lifecycle",name:"pageActivated",data:{page:t.data.view}}},n.dereferenceChangeId=function(t){var n=s.getChange(t);return n?u.safeReference(n):t};var f=function(t,n){var e=o(t,Object.keys(n));return Object.keys(n).forEach((function(i){var r=n[i];e[r]=t[i]})),e}},6139:function(t,n,e){var i=e(3341).Promise,r=e(2760).o,o=e(1906).F,a=e(2884).M,u=e(4541).poll,c=e(2322).x,s=e(4258).a;n.create=function(){return{observeSelector:r,onUrlChange:o,persistHtml:a,poll:u,Promise:i,waitForElement:c,waitUntil:s}}},1415:function(t){t.exports={DEFAULT_INTERVAL:20}},2760:function(t,n,e){var i=e(279),r=i.isEmpty,o=i.isObject,a=e(1756),u=e(3221),c=u.get("stores/directive"),s=e(9751),f=e(4462),l=e(2044),d=e(3941),v=e(167).generate,h=e(4541),p=e(3898),g=u.get("stores/rum"),m={once:!1,onTimeout:null,timeout:null},b={},y=function(t){y=function(){if(!c.shouldObserveChangesIndefinitely())return function(t){var n=h.poll(w.bind(null,t));b[t].cancelObservation=function(){n(),delete b[t]}};var t,n=function(){t.disconnect(),Object.keys(b).forEach(w),t.observe(i,e),g.getSampleRum()&&d.dispatch(a.RECORD_DOM_OBSERVATION_OCCURENCE,{counterName:l.PerformanceCounters.mutation_observer_invocation})},e={attributes:!0,childList:!0,subtree:!0},i=s.getDocumentElement();return t=f.create(n),function(n){var o=b[n];t.observe(i,e),o.cancelObservation=function(){delete b[n],r(b)&&t.disconnect()}}}(),y(t)};function _(t){var n=b[t];n&&n.cancelObservation&&n.cancelObservation()}function w(t){if(b[t]){if(function(t){var n=t.options.timeout;if(null!==n)if("function"==typeof n)try{return n()}catch(t){}else if(Date.now()-t.startTime>n)return!0;return!1}(b[t]))return 0===b[t].matchedCount&&"function"==typeof b[t].options.onT
2imeout&&b[t].options.onTimeout(),void _(t);var n=s.querySelectorAll(b[t].selector);n.length&&(n.forEach((function(n){n.l&&n.l[t]||b[t].callbackQueue.push(n)})),function(t){for(;b[t]&&b[t].callbackQueue.length;){var n=b[t].callbackQueue.shift();if(E(n,t),b[t].matchedCount=b[t].matchedCount+1,b[t].callback(n),b[t]&&b[t].options.once)return void _(t)}}(t)),g.getSampleRum()&&d.dispatch(a.RECORD_DOM_OBSERVATION_OCCURENCE,{counterName:l.PerformanceCounters.match_selector_invocation})}}function E(t,n){t.l||(t.l={}),t.l[n]=!0}n.o=function(t,n,e){if(!function(t){try{s.querySelector(t)}catch(t){return!1}return!0}(t))throw new Error("observeSelector expects a valid css selector as its first argument");if("function"!=typeof n)throw new Error("observeSelector expects a function as its second argument");if(e&&(!o(e)||"function"==typeof e))throw new Error("observeSelector expects an object as its third argument");var i=v();return e=Object.assign({},m,e||{}),b[i]={callback:n,callbackQueue:[],matchedCount:0,options:e,selector:t,startTime:Date.now()},y(i),p.setTimeout(w.bind(null,i),0),_.bind(null,i)}},1906:function(t,n,e){var i=e(167).generate,r=e(3898),o={},a=!1,u=null,c=null,s=null,f=null;function l(){a&&(window.history.pushState===s&&(window.history.pushState=u),window.history.replaceState===f&&(window.history.replaceState=c),u=null,c=null,s=null,f=null,a=!1)}function d(){var t=r.getHref();Object.keys(o).forEach((function(n){var e=o[n];e&&e(t)}))}n.F=function(t){var n=i(),e=0===Object.keys(o).length;return o[n]=t,e&&(r.addEventListener("popstate",d),r.addEventListener("hashchange",d),a||(u=window.history.pushState,c=window.history.replaceState,window.history.pushState=function(t,n,e){u.apply(window.history,arguments),d()},s=window.history.pushState,window.history.replaceState=function(t,n,e){c.apply(window.history,arguments),d()},f=window.history.replaceState,a=!0)),function(){!function(t){delete o[t],0===Object.keys(o).length&&(r.removeEventListener("popstate",d),r.removeEventListener("hashchange",d),l())}(n)}}},2884:function(t,n,e){var i=e(9751),r=e(167).generate,o=e(4462),a={before:"beforebegin",after:"afterend",first_child:"afterbegin",last_child:"beforeend"},u=Object.keys(a),c=[];function s(t,n,e){var i,r,o=[];if("beforebegin"===n)for(i=t.previousSibling,r=0;r<e&&i;r++)o.unshift(i),i=i.previousSibling;else if("afterend"===n)for(i=t.nextSibling,r=0;r<e&&i;r++)o.push(i),i=i.nextSibling;else if("afterbegin"===n)for(i=t.firstChild,r=0;r<e&&i;r++)o.push(i),i=i.nextSibling;else if("beforeend"===n)for(i=t.lastChild,r=0;r<e&&i;r++)o.unshift(i),i=i.previousSibling;return o}function f(t){return document.documentElement.contains(t)}n.M=function(t,n,e){if("string"!=typeof t||!t)throw new Error("persistHtml: html must be a non-empty string");if(!function(t){try{i.querySelector(t)}catch(t){return!1}return!0}(n))throw new Error("persistHtml: invalid CSS selector: "+n);if(e=null!=e?e:"before",-1===u.indexOf(e))throw new Error("persistHtml: placement must be one of: "+u.join(", "));var l=a[e],d=i.querySelector(n);if(!d)throw new Error('persistHtml: no element found for selector "'+n+'"');if(("beforebegin"===l||"afterend"===l)&&!d.parentElement)throw new Error('persistHtml: placement "'+e+'" requires the anchor element to have a parent');var v=function(t){var n=document.createElement("div");return n.innerHTML=t,n.childNodes.length}(t);if(0===v)throw new Error("persistHtml: html produces no DOM nodes");var h=function(t){var n=document.createElement("div");n.innerHTML=t;var e=[];return Array.prototype.forEach.call(n.childNodes,(function(t){if(t.nodeType===Node.ELEMENT_NODE){var n=r();e.push(n),t.setAttribute("data-optly-persist-id",n)}})),{taggedHtml:n.innerHTML,ids:e}}(t);d.insertAdjacentHTML(l,h.taggedHtml);var p,g=s(d,l,v),m=!1,b={childList:!0,subtree:!0},y=i.getDocumentElement();function _(){if(!m){m=!0,p.disconnect();var t=c.indexOf(_);-1!==t&&c.splice(t,1),g.forEach((function(t){f(t)&&t.parentNode&&t.parentNode.removeChild(t)}))}}return(p=o.create((function(){if(!m&&g.some((function(t){return!f(t)}))){var t=i.querySelector(n);!t||("beforebegin"===l||"afterend"===l)&&!t.parentElement||(p.disconnect(),g.forEach((function(t){f(t)&&t.parentNode&&t.parentNode.removeChild(t)})),m||(t.insertAdjacentHTML(l,h.taggedHtml),g=s(t,l,v)),m||p.observe(y,b))}}))).observe(y,b),c.push(_),_}},4541:function(t,n,e){var i=e(1756),r=e(3221),o=e(2044),a=e(3941),u=e(167).generate,c=e(3898),s=e(1415).DEFAULT_INTERVAL,f=r.get("stores/rum"),l={};
2function d(t){l[t]&&Object.keys(l[t].callbacks).forEach((function(n){l[t].callbacks[n].call(null)})),f.getSampleRum()&&a.dispatch(i.RECORD_DOM_OBSERVATION_OCCURENCE,{counterName:o.PerformanceCounters.polling_invocation})}function v(t,n){l[n]&&l[n].callbacks[t]&&(delete l[n].callbacks[t],0===Object.keys(l[n].callbacks).length&&(clearInterval(l[n].id),delete l[n]))}n.poll=function(t,n){"number"!=typeof n&&(n=s),l[n]||(l[n]={callbacks:{},id:c.setInterval(d.bind(null,n),n)});var e=u();return l[n].callbacks[e]=t,v.bind(null,e,n)},n.cancelAll=function(){Object.keys(l).forEach((function(t){var n=l[t];clearInterval(n.id),delete l[t]}))}},2322:function(t,n,e){var i=e(3341).Promise,r=e(2760).o;n.x=function(t){return new i((function(n,e){r(t,n,{once:!0})}))}},4258:function(t,n,e){var i=e(3341).Promise,r=e(4541).poll;n.a=function(t){return new i((function(n,e){if(t())n();else var i=r((function(){t()&&(i(),n())}))}))}},8008:function(t,n,e){var i=e(279).omit,r=e(1756),o=e(5205),a=e(6320),u=e(9663),c=e(2044),s=e(6967),f=e(2030),l=e(8548),d=e(9726).v,v=e(6310),h=e(7946),p=(e(8813),e(295)),g=e(6210),m=e(3941),b=e(6388),y=e(2902),_=e(4143),w=e(3496),E=e(5213),I=(e(5442),e(3432)),A=e(7649),T=e(3221),S=T.get("stores/dimension_data"),O=T.get("stores/view"),k=T.get("stores/view_data"),R=T.get("stores/visitor_id"),C=T.get("stores/layer_data"),D=T.get("stores/directive"),N=n.ApiListenerError=d("ApiListenerError");n.event=function(t){var n;switch(t.eventType){case"click":n=function(t){var n;return t.eventData&&(n=p.create(t.eventData.id,t.eventData.apiName,"click",t.eventData)),function(){var t=s.trackClickEvent(n);t?w.log("API / Tracking click event:",t):w.log("API / Not tracking click event:",t)}}(t);break;case"decision":n=function(t){var n=t.eventData,e=y.createLayerState(n.layerId,n.experimentId,n.variationId,n.isLayerHoldback,n.isLayerConcluded),i=y.createSingle(n.layerId,n.experimentId,n.variationId);return function(){y.recordLayerDecision(e.layerId,e.decisionTicket,e.decision),w.log("API / Tracking decision event:",e),s.trackDecisionEvent(e.decision,e.decisionTicket,i)}}(t);break;case"pageview":n=function(t){var n=I.create(t.eventData.id,t.eventData.apiName),e=I.createState(n.id);return function(){var t=s.trackViewActivation(n,e);t?w.log("API / Tracking pageview event:",t):w.log("API / Not tracking pageview event:",t)}}(t);break;default:n=function(t){var n;return t.eventId&&(n=p.create(t.eventId,t.eventName,"custom")),I.updateAllViewTags(),function(){s.trackCustomEvent(t.eventName,t.tags,n,t.properties)?w.log("API / Tracking custom event:",t.eventName,t.tags):w.log("API / Not tracking custom event:",t.eventName)}}(t)}R.getBucketingId()?n():m.dispatch(r.ADD_CLEANUP_FN,{lifecycle:c.Lifecycle.postActivate,cleanupFn:n})},n.clientMetadata=function(t){0},n.priorRedirectString=function(t){},n.microsnippetError=function(t){},n.rum=function(t){m.dispatch(r.SET_RUM_DATA,t.eventData)},n.initialViewStates=function(t){var n=Object.keys(t.states||{}).map((function(n){return{id:n,isActive:t.states[n]}}));I.registerViews(n)},n.page=function(t){var n=k.getByApiName(t.pageName);if(!n)throw new Error('Unknown page "'+t.pageName+'"');var e=!t.hasOwnProperty("isActive")||t.isActive,i=function(){e?I.activateViaAPI(n,t.tags):(I.deactivate(n),w.log("API / Deactivated Page",I.description(n)))};R.getBucketingId()?i():m.dispatch(r.ADD_CLEANUP_FN,{lifecycle:c.Lifecycle.postViewsActivated,cleanupFn:i})},n.tags=function(t){I.setGlobalTags(t.tags)},n.user=function(t){w.log("API / Setting visitor custom attributes:",t.attributes),Object.keys(t.attributes||{}).forEach((function(n){var e,i,r=t.attributes[n],o=n,a=S.getById(n)||S.getByApiName(n);a&&(o=a.id,e=a.apiName,i=a.segmentId||a.id);var u={id:i,value:r};e&&(u.name=e),x(o,u,!0)}))},n.ga4User=function(t){t.ga4UserId?(w.log("API / Setting ga4 user id in the global store:",t.ga4UserId),m.dispatch(r.SET_GA4_USER_ID,t)):w.error('API / Argument "ga4UserId" is missing')};var x=function(t,n,e){var i=[{key:e?["custom",t]:[t],value:n,metadata:{lastModified:v.now()}}],o=function(){m.dispatch(r.SET_VISITOR_ATTRIBUTES,{attributes:i})};R.getBucketingId()?o():m.dispatch(r.ADD_CLEANUP_FN,{lifecycle:c.Lifecycle.postVisitorProfileLoad,cleanupFn:o})};n.optOut=function(t){var n=!t.hasOwnProperty("isOptOut")||t.isOptOut;h.setOptOut(n)},n.cookieExpiration=function(t){var n=t.cookieExpirationDays;n<90&&(w.error('Argument "cookieExpirationDays"=',n,"less than minimum days:",90,", setting to minimum."),n=90),w.log("API / Setting cookie age to",n,"days."),m.dispatch(r.SET_COOKIE_AGE,86400*n)},n.extendCookieLifetime=function(t){t=Object.assign({isEnabled:!0},t),w.log("API / Setting cookie automatic lifetime extension to",t.isEnabled),m.dispatch(r.SET_COOKIE_AUTO_REFRESH,t.isEnabled)},n.cookieDomain=function(t){w.log("API / Setting cookie domain to",t.cookieDomain),m.dispatch(r.SET_COOKIE_DOMAIN,t.cookieDomain)},n.disable=function(t){if(t.scope){if("tracking"!==t.scope)throw new Error('Unknown "scope" for disable: '+t.scope);w.log("API / Disabling tracking"),m.dispatch(r.LOAD_DIRECTIVE,{trackingDisabled:!0})}else w.log("API / Disabling everything"),m.dispatch(r.LOAD_DIRECTIVE,{disabled:!0})},n.log=function(t){var n=t.level,e=t.match;void 0===n&&(n="INFO"),void 0===e&&(e=""),w.setLogMatcher(e),w.setLogLevel(n)},n.registerModule=function(t){var n="custom/"+t.moduleName;if(a[n]||o.get(n))throw new Error('Module name "'+n+'" is reserved. Will not be registered as plugin.');E.registerApiModule(n,t.module)},n.dataFromSource=function(t){var n=t.source;l.makeAsyncRequest(n),l.resolveRequest(n,t.data)},n.addListener=function(t){if("function"!=typeof t.handler)throw new Error("A handler function must be supplied");(t=i(t,["type"])).publicOnly=!0,t.emitErrors=!0;var n=t.handler;t.handler=function(t){try{return n(t)}catch(t){throw new N(t)}},g.on(t)}
2,n.removeListener=function(t){if(!t.token)throw new Error("Must supply a token to removeListener");g.off(t.token)},n.load=function(t){t.data=Object.assign({},t.data),u.normalizeClientData(t.data),m.dispatch(r.DATA_LOADED,{data:t.data})},n.integrationSettings=function(t){if(!t.id)throw new Error("id is required");if(!t.settings)throw new Error("settings is required");m.dispatch(r.SET_INTEGRATION_SETTINGS,Object.assign({},t.settings,{id:t.id}))},n.loadEmbeddedEditor=function(){document.getElementById("optimizely-visual-editor-root")?w.warn("API / Visual editor is already loaded."):_.loadEmbeddedEditor("https://app.optimizely.com/dist/visual-editor/main.js")},n.bucketVisitor=function(t){if(!t.variationId&&void 0===t.variationIndex||t.variationId&&t.variationIndex)throw new Error("One of a variationId or a variationIndex is required.");if(!t.experimentId)throw new Error("An experimentId is required.");var n,e,i=t.campaignId;if(i){if(!(n=C.get(i)))throw new Error("Could not find layer "+i)}else if(!(i=(n=C.getLayerByExperimentId(t.experimentId)).id))throw new Error("Could not find layer for experiment "+t.experimentId);if(!(e=n.experiments.find((function(n){return n.id===t.experimentId}))))throw new Error("Could not find experiment "+t.experimentId+" in layer "+i);var r=t.variationId;if(void 0!==t.variationIndex){if(!(r=e.variations[t.variationIndex].id))throw new Error("Could not find variation at index "+t.variationIndex+" in experiment "+t.experimentId)}else if(!e.variations.find((function(t){return t.id===r})))throw new Error("Cound not find variation "+r+" in experiment "+t.experimentId);A.updateVariationIdMap(i,t.experimentId,r),R.getBucketingId()&&A.persistVariationIdMap(),A.updateContextualMabMap(i,t.experimentId,{variationId:r}),R.getBucketingId()&&A.persistContextualMabMap()},n.waitForOriginSync=function(t){if(!Array.isArray(t.canonicalOrigins))throw new Error("canonicalOrigins must be an array. Got: "+b.stringify(t.canonicalOrigins));t.canonicalOrigins.forEach((function(t){if("string"!=typeof t)throw new Error("Each item in canonicalOrigins must be a string. Found type "+(void 0===t?"undefined":(n=t)&&"undefined"!=typeof Symbol&&n.constructor===Symbol?"symbol":typeof n));var n})),m.dispatch(r.XDOMAIN_SET_CANONICAL_ORIGINS,{canonicalOrigins:t.canonicalOrigins})},n.disableCrossOrigin=function(){w.log("API / cross origin tracking is DISABLED"),m.dispatch(r.XDOMAIN_SET_DISABLED,{disabled:!0})},n.activate=function(){D.shouldActivate()?(O.getActiveViewStates().forEac
2h((function(t){I.deactivate(k.get(t.id))})),m.dispatch(r.RESET_VIEW_STATES),f.emitActivateEvent()):w.debug("Not activating.")},n.sendEvents=function(){f.emitSendEvents()},n.holdEvents=function(){f.emitHoldEvents()}},8457:function(){},376:function(t,n,e){var i=e(279),r=i.cloneDeep,o=i.flattenDeep,a=i.isEmpty,u=i.isObject,c=e(9726).v,s=n.ActivationCodeError=c("ActivationCodeError"),f=n.ProjectJSError=c("ProjectJSError"),l=e(6183),d=e(1756),v=e(6967),h=e(2030),p=e(391),g=e(525),m=e(9663),b=e(6310),y=e(8079),_=e(3221),w=e(7946),E=e(9751),I=e(2044),A=e(6210),T=e(3659),S=e(3816),O=e(3941),k=e(5564),R=e(2902),C=e(7429).DT,D=e(3496),N=e(7108),x=e(6494),j=e(5213),P=e(8805),M=e(3341).Promise,L=e(8767),V=e(5442),U=e(8597),z=e(6903),B=e(3432),F=e(7649),G=e(3898),q=(_=e(3221)).get("stores/session"),H=_.get("stores/audience_data"),Y=_.get("stores/action_data"),K=_.get("stores/cleanup"),$=_.get("stores/directive"),W=_.get("stores/global"),X=_.get("stores/group_data"),Q=_.get("stores/layer_data"),J=_.get("stores/layer"),Z=_.get("stores/pending_events"),tt=_.get("stores/plugins"),nt=_.get("stores/rum"),et=_.get("stores/visitor"),it=_.get("stores/view_data"),rt=_.get("stores/view"),ot=_.get("stores/visitor_id"),at=_.get("stores/visitor_bucketing"),ut=_.get("stores/xdomain"),ct=e(5205),st=e(8813),ft=e(8942),lt=!1,dt=n;function vt(){x.timeEnd("xdomainSetup");h.emitActivateEvent()}function ht(t){st.handleError(t.data.error,t.data.metadata)}function pt(t,n){O.dispatch(d.SET_RUM_DATA,{data:{extras:{xdAttempt:t,xdError:n?n.toString():void 0}}})}function gt(t,n,e){x.time("getDecisionTicket"),t=t||[];var i=W.getGlobalHoldbackThreshold(),r=ot.getBucketingId();if(!r)throw new Error("bucketingId not set");var o,a=et.getVisitorProfile();n&&(o=at.getVariationIdMap()[n.id]);var u={bucketingId:r,visitorProfile:a,audiences:t,globalHoldback:i,preferredVariationMap:o,layer:n};return n&&e&&y.isPageIdRelevant(n)?e.map((function(t){return y.createTicket(Object.assign({},u,{pageId:t}))})):[y.createTicket(u)]}function mt(t,n){var e={};return t.forEach((function(t){Array.isArray(t)?Object.assign(e,mt(t,n)):u(t)&&n[t.type]&&(e[t.type]=!0)})),e}function bt(t){var n=[];return t.forEach((function(t){n.push(t.name,t)})),n}function yt(t){var n=K.getCleanupFns(t);if(n.length>0){for(;n.length>0;)n.shift()();O.dispatch(d.CLEAR_CLEANUP_FN,{lifecycle:t})}}n.initialize=function(t){return this.initGlobalStore(t.clientData),this.initializePlugins(t.plugins)},n.initGlobalStore=function(t){m.normalizeClientData(t),A.on({filter:{type:"error"},handler:ht}),O.dispatch(d.DATA_LOADED,{data:t}),D.log("Initialized with DATA:",t)},n.initializePlugins=function(t){if(x.timeEnd("preInitialize"),x.time("ClientInitialize"),Array.isArray(window.optimizely)&&(window.optimizely=window.optimizely.filter((function(t){var n=!0;return!ct.push(t,n)}))),w.setOptOut($.shouldOptOut()),!$.isDisabled()&&!$.shouldOptOut()){ft.queueBeacons(),E.isReady()?O.dispatch(d.SET_DOMCONTENTLOADED):E.addReadyHandler((function(){O.dispatch(d.SET_DOMCONTENTLOADED)})),x.time("projectJS");var e=W.getProjectJS();if("function"==typeof e)try{S.apply(e)}catch(t){D.error("Error while executing projectJS: ",t),g.emitError(new f(t))}x.timeEnd("projectJS"),x.time("pluginsRegister"),(t||[]).forEach((function(t){try{t(j)}catch(t){g.emitInternalError(t)}})),(W.getPlugins()||[]).forEach((function(t){try{S.apply(t,[j])}catch(t){g.emitError(t)}})),x.timeEnd("pluginsRegister"),V.load();var i=A.on({filter:{type:"lifecycle",name:"activated"},handler:function(){et.observe(F.persistVisitorProfile),J.observe(F.persistLayerStates),q.observe(F.persistSessionState),Z.observe(N.schedulePersistPendingEvents),at.observe(F.persistVisitorBucketingStore),A.off(i)}});A.on({filter:{type:"lifecycle",name:"viewsActivated"},handler:n.onViewsActivated}),A.on({filter:{type:"lifecycle",name:"pageDeactivated"},handler:n.onPageDeactivated}),n.initializeApi();var r=N.getPendingEvents();if(r&&(O.dispatch(d.LOAD_PENDING_EVENTS,{events:r}),N.retryPendingEvents(r)),A.on({filter:{type:"lifecycle",name:"activate"},handler:n.activate}),h.emitInitializedEvent(),!$.shouldActivate())return M.resolve();var o=[];if(ut.isDisabled())vt(),x.timeEnd("ClientInitialize");else{x.time("xdomainSetup");var a=n.initializeXDomainStorage();if(o.push(a),Boolean(ut.getCanonicalOrigins())){var u=U.makeTimeoutPromise(1e3);M.race([a,u]).catch((function(t){D.error("Failed to initialize xDomain storage: ",t)})).then(vt).catch(st.handleError)}else vt(),x.timeEnd("ClientInitialize")}return M.all(o)}D.log("Controller / Is disabled")},n.activate=function(){try{var t=[];D.log("Activated client"),yt(I.Lifecycle.preActivate);var n=!1;lt;var e=b.now();
2O.dispatch(d.ACTIVATE,{activationId:String(e),activationTimestamp:e});var i=it.getAll();B.registerViews(i,n),F.setId(F.getOrGenerateId()),t.push(v.trackPostRedirectDecisionEvent()),O.dispatch(d.MERGE_VARIATION_ID_MAP,{variationIdMap:F.getVariationIdMap()}),O.dispatch(d.MERGE_CONTEXTUAL_MAB_MAP,{contextualMabMap:F.getContextualMabMap()}),O.dispatch(d.SET_ODP_SEGMENTS,F.getOdpSegments()),O.dispatch(d.MERGE_PREFERRED_LAYER_MAP,{preferredLayerMap:F.getPreferredLayerMap()}),yt(I.Lifecycle.postVisitorProfileLoad),t.push((s=tt.getAllPlugins(I.PluginTypes.visitorProfileProviders),f=et.getVisitorProfile(),F.populateEagerVisitorData(s,f)).then((function(){D.log("Populated visitor profile")})));var r=gt(),o=y.decideGlobal(r);D.log("Made global decision",r,"->",o),O.dispatch(d.RECORD_GLOBAL_DECISION,o);var a=v.trackClientActivation();a?D.log("Tracked activation event",a):D.log("Not tracking activation event");var u=dt.setUpViewActivation(i),c=[];return u.forEach((function(t){c=c.concat(B.activateMultiple([t]))})),yt(I.Lifecycle.postViewsActivated),yt(I.Lifecycle.postActivate),h.emitActivatedEvent(),M.all(t).then((function(){A.emit({type:T.TYPES.LIFECYCLE,name:"activateDeferredDone"}),D.log("All immediate effects of activation resolved")}),g.emitError)}catch(t){return g.emitError(t),M.reject(t)}var s,f},dt.setUpViewActivation=function(t){var n=[];return t.forEach((function(t){"boolean"==typeof rt.getViewState(t.id).isActive&&B.isActivationTypeImmediate(t.activationType)?D.debug("Skipping page: already evaluated, presumably at the edge",B.description(t)):B.shouldTriggerImmediately(t.activationType)?n.push(t):t.activationType===I.ViewActivationTypes.callback?(D.debug("Setting up conditional activation for Page",B.description(t)),dt.activateViewOnCallback(t)):t.activationType===I.ViewActivationTypes.polling?(D.debug("Setting up polling activation for Page",B.description(t)),P.pollFor(S.apply.bind(null,t.activationCode),null,z.isTimedOut.bind(null,b.now())).then((function(){B.activateMultiple([t])})).catch((function(n){D.warn("Failed to activate view ",t,n)}))):t.activationType!==I.ViewActivationTypes.manual&&g.emitError(new Error("Unknown view activationType: "+t.activationType))})),n},dt.activateViewOnCallback=function(t){var n={pageId:t.id};Object.defineProperty(n,"isActive",{get:function(){return rt.isViewActive(t.id)}});try{S.apply(t.activationCode,[function(n){var e=Object.assign({},n,{pageName:t.apiName,type:"page"});ct.push(e)},n])}catch(n){var e=new s("("+n.toString()+") in activationCode for "+B.description(t));g.emitError(e,{originalError:n,userError:!0})}},n.onViewsActivated=function(t){var e,i=t.data.views,r=i.map((function(t){return t.id}));try{if(!ot.getBucketingId())throw new Error("View activated with no visitorId set");var o=function(t){var e=Q.getAllByPageIds(t),i=$.getForceVariationIds(),r=$.getForceAudienceIds(),o=!a(i);o&&D.log("Force variations are in use. Disabling mutual exclusivity.");var u=o?{individual:e}:e.reduce((function(t,n){return n.groupId?t.groups[n.groupId]||(t.groups[n.groupId]=X.get(n.groupId)):t.individual.push(n),t}),{groups:{},individual:[]});if(D.log("Deciding Campaigns/Experiments for Page(s)",t),u.groups){var c=Object.keys(u.groups).map((function(t){return k.description(u.groups[t])})).join(", ");D.log("Groups:",c)}var s=u.individual.map(R.description).join(", ");D.log("Campaigns/Experiments not in Groups (by Campaign id):",s);var f=Object.keys(u.groups||{}).map((function(e){return function(t,e,i,r){try{var o=function(t){return{bucketingId:ot.getBucketingId(),preferredLayerId:at.getPreferredLayerMap()[t.id]}}(r),u=y.decideGroup(r,o);if(u.reason)return D.debug("Not activating Group",k.description(r),"; reason:",u.reason),nt.getSampleRum()&&O.dispatch(d.RECORD_LAYER_FEATURE_USAGE,{feature:"mutex",entityId:r.id}),M.resolve();var c=Q.get(u.layerId);if(!c)return D.debug("Visitor was bucketed into a Campaign ("+u.layerId+") which is not in this snippet"),M.resolve();var s=(c.pageIds||[]).filter((function(t){return Array.isArray(i)&&i.includes(t)}));return a(s)?(D.debug("Not activating Group",k.description(r),"; reason: visitor was bucketed into a Campaign/Experiment not related to the currently-activating Page(s)"),M.resolve()):(nt.getSampleRum()&&O.dispatch(d.RECORD_LAYER_FEATURE_USAGE,{feature:"mutex",entityId:r.id}),n.decideAndExecuteLayerASAP(t,e,s,c))}catch(t){return D.error("Error getting decision for Group",k.description(r),"; ",t),M.reject(t)}}(i,r,t,u.groups[e])}))||[],l=u.individual.map((function(e){var o=(e.pageIds||[]).filter((function(n){return Array.isArray(t)&&t.includes(n)}));return n.decideAndExecuteLayerASAP(i,r,o,e)})),v=f.concat(l);return M.all(v).then((function(n){var e=n.filter((function(t){return!!t}));return D.log("All Campaigns/Experiments for Page(s) (by Campaign id)",t,"resolved:",e.map(R.description).join(", ")),e}))}(r).catch(g.emitError);return e=M.all(i.map((function(t){var n=function(){x.time("sendViewActivate"),B.parseViewTags(t);var n=v.trackViewActivation(t);n?D.log("Tracked activation for Page",B.description(t),n):D.log("Not Tracking activation for Page",B.description(t))};return E.isReady()?M.resolve(n()):P.pollFor(E.isReady,1e3).then(n)}))),M.all([o,e])}catch(t){g.emitError(t)}},n.onPageDeactivated=function(t){var n=t.data.page;
2(Y.getAllActionIdsByPageId(n.id)||[]).forEach((function(t){var e=Y.getActionState(t);e&&(Object.keys(e).forEach((function(t){var i=e[t];if(i.cancel)try{i.cancel(),D.debug("Controller / Canceled change",t,"observation due to deactivation of page:",n)}catch(n){D.error("Controller / Error canceling change",t,"observation upon deactivation of page.",n)}if(n.undoOnDeactivation&&i.undo)try{i.undo(),D.debug("Controller / Undid change",t,"due to deactivation of page:",n)}catch(t){D.error("Controller / Error undoing change upon deactivation of page.",t)}})),O.dispatch(d.REMOVE_ACTION_STATE,{actionId:t}),D.debug("Controller / Undid changes and/or canceled change observation due to deactivation of page:",n,t))}))},n.initializeApi=function(){var t={push:ct.push};t.get=ct.get;var n=window.optimizely;Array.isArray(n)&&n.forEach((function(n){t.push(n)})),t.data={note:"Obsolete, use optimizely.get('data') instead"},t.state={},window.optimizely=t},n.persistItemsWithId=function(t){return Object.keys(t).forEach((function(n){var e=t[n];F.checkKeyForVisitorId(n)&&C.setItem(n,e)})),t},n.initializeXDomainStorage=function(){var t=e(9232),i=!!F.getCurrentId(),r=!!i&&F.hasSomeData();i?r?D.log("xd / Existing visitor; has data on this origin"):D.log("xd / Existing visitor; new to this origin"):D.log("xd / New visitor");var o=W.getAccountId(),u="https://a26942930964.cdn.optimizely.com".replace("__SUBDOMAIN__","a"+o+"."),c="/client_storage/a"+o+".html";t.subscribe((function(t,n){F.checkKeyForVisitorId(t)&&C.setItem(t,n)}));var s=t.fetchAll().then((function(n){if(!ot.getVisitorIdLocator()){var e=ut.getCanonicalOrigins();if(e){var i=t.getXDomainUserId(n,e);i&&(D.log("Syncing cross-origin visitor randomId:",i),F.maybePersistVisitorId({randomId:i}))}}return F.deleteOldForeignData(),n})).then(n.persistItemsWithId).then((function(t){if(F.loadForeignData(),i&&!r){var n=!a(t);D.debug("xd / Loaded foreign data? ",n),pt(n)}D.log("Loaded visitor data from foreign origins"),h.emitOriginsSyncedEvent()}),(function(t){throw i&&!r&&(D.debug("xd / Failed to load foreign data:",t),pt(!1,t)),t}));return M.all([t.load(u,c).catch((function(t){throw D.debug("xd / Failed to load iframe:",t),i&&!r&&pt(!1,t),t})),s.catch((function(t){D.debug("xd / Ignored error syncing foreign data (expected if waitForOriginSync used):",t.message),D.debug("xd / Enqueuing sync to happen after visitorId set."),O.dispatch(d.ADD_CLEANUP_FN,{lifecycle:I.Lifecycle.postVisitorProfileLoad,cleanupFn:h.emitOriginsSyncedEvent})}))])},n.decideAndExecuteLayerASAP=function(t,e,i,u){return new M((function(c,s){x.time("decideAndExecuteLayer");try{!function(t,n,e,i,r){if(n.length||e.length)return void r(gt([],void 0,i));var u=R.relatedAudienceIds(t),c=u.reduce((function(t,n){var e=H.get(n);return e&&t.push(e),t}),[]),s=tt.getAllPlugins(I.PluginTypes.audienceMatchers);if(nt.getSampleRum()){var f={};if(c.forEach((function(t){Object.assign(f,mt(t.conditions,s))})),!a(f)){var l=Object.keys(f);O.dispatch(d.RECORD_AUDIENCE_USAGE,{audienceTypes:l,layerId:t.id})}}var v=function(t){D.debug("Audience list for extracting types",t);try{var n=new Set(o(t.map((function(t){return t.conditions}))).filter((function(t){return null!=t&&"string"!=typeof t})).map((function(t){return t.type})));return n.has("time_and_day")&&n.add("currentTimestamp"),D.debug("Extracted unique types from audiences",n),n}catch(t){return D.error("An error occurred while extracting types from audiences:",t),new Set}}(c),h=tt.getAllPlugins(I.PluginTypes.visitorProfileProviders),g=et.getVisitorProfile();F.populateLazyVisitorData(h,g,v),function(t,n,e,i){var r=t.reduce((function(t,e){return Object.assign(t,p.requiredAudienceFieldsForConditions(e.conditions,n))}),{}),o=Object.keys(r).reduce((function(t,n){var e=r[n];if(void 0===F.getAttribute(e)){var i=F.getPendingAttributeValue(e);void 0!==i&&t.push(i)}return t}),[]);if(0===o.length)return i();var u=[].concat(t),c=L.firstToResolve(o.map((function(t){return M.resolve(t).then((function(){var t=et.getVisitorProfile();if(u=u.filter((function(e){return void 0===p.isInAudience(t,e,n)})),!a(u))throw new Error("At least one audience is still pending")}))})));M.race([c,new M((function(t,n){G.setTimeout(n,e)}))]).then((function(){D.log("Activating Campaign after pending Audiences resolved",t),i()}),(function(){D.log("Activating Campaign after timeout on Audiences",t),i()}))}(c,s,R.getActivationTimeout(t),(function(){var n=gt(c,t,i);
2n.forEach((function(n){!function(t,n,e){var i=t.audienceIds.map(H.get.bind(H)).filter(Boolean),r=n.filter((function(n){return!t.audienceIds.includes(n.id)}));D.log("When deciding Campaign",R.description(e),"visitor is in audiences:",bt(i),"and not in audiences:",bt(r))}(n,c,t)})),r(n)}))}(u,t,e,i,(function(o){o.forEach((function(o){var c=o.pageId?[o.pageId]:i;D.debug("Deciding layer: ",u,"with decisionTicket: ",o,"and actionViewIds: ",c),function(t,e,i,o,u){var c=R.description(t);D.log("Activating Campaign",c,"on Page(s)",o),x.time("decideLayer"),i.length&&(D.log("Applying force audienceIds:",i,"to Campaign",c),(u=r(u)).audienceIds=i);var s=n.decideLayer(t,u,e),f=!(!e.length&&!i.length),p=n.getActionsForDecision(t,s,f),g=function(t,n){return t.filter((function(t){return void 0===t.pageId||n.includes(t.pageId)}))}(p.actions,o);if(p.maybeExecute&&function(t,n,e,i){var r=R.description(n);D.log("Preparing actions",t,"for Campaign",r,"on Page(s)",i),t.forEach(l.prepareAction)}(g,t,0,o),o.forEach((function(){v.trackDecisionEvent(s,u,t)})),h.emitLayerDecided({layer:t,decisionTicket:u,decision:s}),s.error)throw s.error;if(nt.getSampleRum()){O.dispatch(d.RECORD_LAYER_POLICY_USAGE,{policy:t.policy,layerId:t.id});var m=function(t){var n={};return t.forEach((function(t){t.changeSet.forEach((function(t){n[t.type]||(n[t.type]=!0)}))})),n}(p.actions);O.dispatch(d.RECORD_CHANGE_TYPE_USAGE,{changeTypes:Object.keys(m),layerId:t.id}),a(t.integrationSettings)||O.dispatch(d.RECORD_INTEGRATION_USAGE,{integrations:R.getIntegrationTypes(t),layerId:t.id})}if(x.timeEnd("decideLayer"),!y.isInCohort(s))return void D.log("Not activating Campaign: "+R.description(t)+"; not in the cohort because:",s.reason);x.time("executeLayer"),p.maybeExecute&&function(t,n,e,i){var r=R.description(n);D.log("Executing actions",t,"for Campaign",r,"on Page(s)",i),M.all(t.map((function(t){return l.executePreparedAction(t).then(h.emitActionAppliedEvent.bind(null,t))}))).then((function(){D.log("All page actions for",e,"applied:",t),h.emitActionsForDecisionAppliedEvent(e,t)})).catch((function(t){D.warn("Error evaluating page actions for decision",e,"because:",t)}))}(g,t,s,o),x.timeEnd("executeLayer")}(u,t,e,c,o)})),c(u)}))}catch(t){D.error("Error getting decision for Campaign: "+R.description(u),t),s(t)}x.timeEnd("decideAndExecuteLayer")}))},n.decideLayer=function(t,n,e){var i;x.time("sendDecision");var r=R.description(t);i=e.length?y.getDummyLayerDecision(t,e):y.decideLayer(t,n),D.log("Recording decision for Campaign",r,n,"->",i),R.recordLayerDecision(t.id,n,i),i.variationId&&i.experimentId&&F.updateVariationIdMap(t.id,i.experimentId,i.variationId),t.groupId&&F.updatePreferredLayerMap(t.groupId,t.id);return i},n.getActionsForDecision=function(t,n,e){var i=R.description(t),r="NOT applying changes for Campaign",o={actions:[],maybeExecute:!1};if(o.actions=[].concat(Y.getLayerActions(n.layerId)||[],Y.getExperimentActions(n.experimentId)||[],Y.getExperimentVariationActions(n.experimentId,n.variationId)||[]),!e&&W.isGlobalHoldback())return D.log(r,i,"(visitor is in global holdback)"),o;if(n.isLayerHoldback)return D.log(r,i,"(visitor is in layer holdback)"),x.timeEnd("layerHoldback"),o;if(!n.experimentId||!n.variationId)return D.log(r,i,"(visitor is not eligible for any Experiments)"),o;return o.maybeExecute=!0,D.log("Got Actions for Campaign:",i,o.actions),o}},9542:function(t,n,e){var i=e(3341).Promise,r=e(6310),o=e(3221).get("stores/plugins"),a=e(2044),u=e(3496);n.create=function(t,n,e){var c={identifier:t.id,action:n,startTime:e||r.now()};try{var s=o.getPlugin(a.PluginTypes.changeAppliers,t.type);if(!s)throw new Error("Unrecognized change type "+t.type);return new s(t,c)}catch(t){return u.error("Change applier was never properly constructed:",t),{apply:function(){return i.reject(t)}}}}},7042:function(t){t.exports={SELECTOR_POLLING_MAX_TIME:2e3,CHANGE_DATA_KEY:"optimizelyChangeData",CHANGE_ID_ATTRIBUTE_PREFIX:"data-optly-"}},1894:function(t,n,e){var i=e(279).isEmpty,r=e(7042).CHANGE_DATA_KEY;function o(t,n){return[t,n].join("_")}n.getData=function(t,n,e){var i=o(n,e);return t[r]&&t[r][i]?t[r][i]:null},n.hasData=function(t){return Boolean(t&&t[r]&&!i(t[r]))},n.removeData=function(t,n,e){t[r]&&delete t[r][o(n,e)]},n.setData=function(t,n,e,i){if("object"!=typeof i)throw new Error("setData expects an object");t[r]||(t[r]={}),t[r][o(n,e)]=i}},7065:function(t,n,e){var i=e(762);t.exports={changeType:{CUSTOM_CODE:"custom_code",ATTRIBUTE:"attribute",APPEND:"append",REARRANGE:"rearrange",REDIRECT:"redirect",WIDGET:"widget"},DOMInsertionType:{AFTER:"after",APPEND:"append",BEFORE:"before",PREPEND:"prepend"},insertAdjacentHTMLType:{AFTER_BEGIN:"afterbegin",AFTER_END:"afterend",BEFORE_BEGIN:"beforebegin",BEFORE_END:"beforeend"},selectorChangeType:{CLASS:"class",HTML:"html",HREF:"href",SRC:"src",SRCSET:"srcset",STYLE:"style",TEXT:"text",HIDE:"hide",REMOVE:"remove"},changeApplierState:i({APPLIED:null,APPLYING:null,UNAPPLIED:null,UNDOING:null}),changeState:i({BLOCKED:null,UNAPPLIED:null,APPLIED:null,APPLYING:null,UNDOING:null,TIMED_OUT:null,IGNORED:null,ERROR:null})}},6183:function(t,n,e){var i=e(1756),r=e(525),o=e(8548),a=e(9726).v,u=e(6310),c=e(3221),s=e(3941),f=e(3496),l=e(3341).Promise,d=e(7743),v=c.get("stores/global"),h=c.get("stores/action_data"),p=c.get("stores/change_data"),g=c.get("stores/session"),m=e(9542);e(6903).initialize();var b=a("ActionError");function y(t,n,e){if(h.getActionState(n.id))if(void 0===h.getChangeApplier(t.id,n.id)){var r={changeId:t.id,actionId:n.id,changeApplier:m.create(t,n,e)};s.dispatch(i.SET_CHANGE_APPLIER,r)}else f.warn("Action / Attempted to prepare a change which is already being applied: ",t);else f.warn("Action / Attempted to prepare change for inactive action: ",n)}function _(t,n,i,a){if(!a.includes(n)){if(!t[String(n)]){var c=p.getChange(n);if(!c){var s="Change with id "+n+" is absent";return a.length&&(s+=" but listed as a dependency for "+a[a.length-1]),void f.warn(s)}t[String(n)]=new l((function(s){var v=(c.dependencies||[]).map((function(e){return _(t,e,i,a.concat([n]))}));
2if(c.src){var g="change_"+c.src,m=o.makeAsyncRequest(g,(function(){return d.addScriptAsync("https://cdn.optimizely.com/public/26942930964/data"+c.src,(function(){o.resolveRequest(g)}))})).then((function(){var t=p.getChange(c.id);t||r.emitError(new b("Failed to load async change from src: "+c.src)),y(t,i,u.now())}));v.push(m)}l.all(v).then((function(){var t=u.now(),r=h.getChangeApplier(n,i.id);return r?(f.debug("Action / Applying change:",c),r.apply().then((function(n){n?f.log(n):f.debug("Action / Applied change for the first time in "+(u.now()-t)+"ms:",c),e(6494).timeEnd("flash"),s()}))):(f.debug("Action / Not applying change ",n," - No changeApplier found."),void s())})).catch((function(t){f.error("Action / Failed to apply change:",c,t),s()}))}))}return t[String(n)]}f.error("Change with id "+n+" has circular dependencies: "+a.concat(n))}n.prepareAction=function(t){f.debug("Action / Preparing:",t),s.dispatch(i.ACTION_EXECUTED,{actionId:t.id,sessionId:g.getSessionId(),layerId:t.layerId,pageId:t.pageId,timestamp:u.now(),activationId:v.getActivationId()});var n=u.now();t.changeSet.forEach((function(e){var r="object"==typeof e&&null!==e?e.id:e,o=p.getChange(r);o||(s.dispatch(i.ADD_CHANGE,e),o=p.getChange(r)),o.src||y(o,t,n)}))},n.executePreparedAction=function(t){f.debug("Action / Executing:",t);var n={},e=t.changeSet.map((function(e){var i="object"==typeof e&&null!==e?e.id:e;return _(n,i,t,[])}));return l.all(e).then((function(){f.debug("changes for action id="+t.id+" applied")}))}},6903:function(t,n,e){var i=e(7042),r=e(3221).get("stores/directive");function o(){("interactive"===document.readyState||"complete"===document.readyState)&&(n.domReadyTime=Date.now())}n.domReadyTime=null,n.initialize=function(){o(),document.addEventListener("readystatechange",o,!0)},n.isTimedOut=function(t){var e=Date.now();if(!n.domReadyTime||!t)return!1;var o=Math.max(t,n.domReadyTime);return r.isEditor()&&(o=n.domReadyTime),!(e-o<i.SELECTOR_POLLING_MAX_TIME)}},6967:function(t,n,e){var i=e(279),r=i.omit,o=i.pick,a=e(1756),u=e(525),c=e(2030),s=e(6027),f=e(6310),l=e(6387),d=e(3221),v=e(9751),h=e(2044),p=e(7065),g=e(8701),m=e(6210),b=e(3659),y=e(3786),_=e(3941),w=e(2902),E=e(3496),I=e(6494),A=e(3341).Promise,T=e(2060),S=e(5442),O=e(8597),k=e(3454),R=e(167),C=d.get("stores/client_metadata"),D=d.get("stores/global"),N=d.get("stores/session"),x=d.get("stores/view"),j=d.get("stores/plugins"),P=d.get("stores/layer"),M=d.get("stores/layer_data"),L=d.get("stores/observed_redirect"),V=d.get("stores/pending_redirect"),U=d.get("stores/visitor"),z=d.get("stores/directive"),B=d.get("stores/event_data"),F=d.get("stores/visitor_id");function G(t,n,e){return void 0===e&&(e=!1),Object.assign({},t,{isLayerHoldback:n,isLayerConcluded:e,isGlobalHoldback:!1,clientName:null!==t.clientName?t.clientName:l.NAME,integrationStringVersion:null!==t.integrationStringVersion?t.integrationStringVersion:1,anonymizeIP:null!==D.getAnonymizeIP()?D.getAnonymizeIP():void 0,activationId:D.getActivationId(),decisionTicketAudienceIds:[],sessionId:N.getSessionId(),activeViewStates:[],userFeatures:Y(U.getVisitorProfile()),layerStates:P.getLayerStatesForAnalytics()})}function q(t){var n=T.getReferrer()||v.getReferrer();return{eventId:R.generate(),timestamp:f.now(),revision:D.getRevision(),clientName:C.getClientName(),clientVersion:C.getClientVersion(),projectId:D.getProjectId(),accountId:D.getAccountId(),activationId:D.getActivationId(),sessionId:N.getSessionId(),isGlobalHoldback:D.isGlobalHoldback(),namespace:D.getNamespace(),referrer:n,visitorId:F.getRandomId(),activeViewStates:t.activeViewStates,layerStates:t.layerStates,userFeatures:Y(t.visitorProfile,!t.deferBehaviorFeatures)}}function H(t){var n,e;return t.pageId?(n=t.pageId,(e=x.getViewState(n))&&e.isActive?e.metadata:{}):{}}function Y(t,n){void 0===n&&(n=!0);var e=j.getAllPlugins(h.PluginTypes.visitorProfileProviders),i=Object.values(e).filter((function(t){return!0===t.shouldTrack})),r={id:null,type:null,name:"",value:null,shouldIndex:!0},a=i.reduce((function(n,e){try{var i=e.provides,a=t[i],u=[];if(void 0!==a){null!==a&&"object"==typeof a?u=Object.keys(a).map((function(t){var n=a[t],e=null!==n&&"object"==typeof n?n:{value:n};return Object.assign({},{type:i,name:t},e)})):u.push({type:i,value:a});var c=u.map((function(t){return o(Object.assign({},r,t),Object.keys(r))})).filter((function(t){return!!t.value}));n=n.concat(c)}}catch(t){E.warn("Error evaluating userFeature against visitorProfile:",t)}return n}),[]);return n?a.concat([]):a}function K(t){t&&t.userFeatures&&(t.userFeatures=t.userFeatures.concat([]))}function $(t,n,e){var i=function(t,n){var e=[],i=j.getAllPlugins(h.PluginTypes.analyticsTrackers);
2return Object.keys(i).forEach((function(r){var o=i[r];if(o[t]&&(!n||!o[n]))try{e.push({name:r,hookFn:d.evaluate(o[t])})}catch(t){E.error(t)}})),e}(t,e);E.debug("Found "+i.length+" analytics integrations defining a "+t+" hook"),E.debug("Calling each with data: ",n),i.forEach((function(t){try{E.debug("Calling plugin: "+t.name),t.hookFn(n),E.debug("Called plugin: "+t.name)}catch(t){E.error(t)}}))}function W(t,n,e){var i=function(t,n){0;var e=[],i=j.getAllPlugins(h.PluginTypes.analyticsTrackers);return Object.keys(i).forEach((function(r){var o=i[r];n.includes(o[t])&&e.push({name:r,hookFn:o.trackLayerDecision})})),e}(t,n);E.debug("Found "+i.length+" analytics integrations  defining a trackLayerDecision "+t+" timing of "+n.join("|")),E.debug("Calling each with data: ",e),i.forEach((function(t){try{E.debug("Calling plugin: "+t.name),t.hookFn(e),E.debug("Called plugin: "+t.name)}catch(t){E.error(t)}}))}n.trackClientActivation=function(){if(!e(5296).shouldDrop()&&z.shouldSendTrackingData()){var t,n=(t=q({activeViewStates:[],visitorProfile:U.getVisitorProfile(),layerStates:P.getLayerStatesForAnalytics()}),Object.assign(t,{eventTags:{}}));return I.timeEnd("sendActivate"),$("onClientActivation",n),n}},n.trackCustomEvent=function(t,n,i,o){if(!e(5296).shouldDrop()){n=n||{},o=o||{},i||(i=B.getByApiName(t));var a=function(t,n,e,i){var r=q({activeViewStates:x.getActiveViewStates(),visitorProfile:U.getVisitorProfile(),layerStates:P.getLayerStatesForAnalytics(),deferBehaviorFeatures:!0}),o=i&&i.pageId?H(i):x.getActiveViewTags(),a=Object.assign({},o,n),u=i&&i.category?i.category:g.OTHER;return Object.assign(r,{eventEntityId:i&&i.id,eventApiName:t,eventCategory:u,eventTags:a,eventProperties:e})}(t,n,o,i),c={name:t,type:y.CUSTOM,category:a.eventCategory,tags:r(a.eventTags,["revenue"])};if(void 0!==n.revenue&&(c.revenue=n.revenue),u.emitAnalyticsEvent({name:i?i.name||i.apiName:t,apiName:i?i.apiName:void 0,type:y.CUSTOM,properties:a.eventProperties,tags:r(a.eventTags,["revenue"]),category:a.eventCategory,metrics:c.revenue?{revenue:c.revenue}:{}},!z.shouldSendTrackingData()),z.shouldSendTrackingData())return s.addEvent(c),K(a),$("onCustomEvent",a),a}},n.trackDecisionEvent=function(t,n,i){if(!e(5296).shouldDrop()){I.time("trackDecisionEvent"),i||(i=M.get(t.layerId));var r=R.generate();_.dispatch(a.RECORD_LAYER_DECISION_EVENT_ID,{layerId:t.layerId,pageId:n.pageId,decisionId:r});var o=t.experimentId?i.experiments.find((function(n){return n.id===t.experimentId})):null,u=function(t,n,e,i,r){var o=null,a=null,u=null;if(r&&(o=r.name||null,u=r.integrationSettings,n.variationId)){var c=r.variations.find((function(t){return t.id===n.variationId}));c&&(a=c.name||null)}var s=T.getReferrer()||v.getReferrer(),l={sessionId:N.getSessionId(),decisionTicketAudienceIds:e.audienceIds,visitorId:F.getRandomId(),decisionId:t,activationId:D.getActivationId(),namespace:D.getNamespace(),timestamp:f.now(),pageId:e.pageId||null,variationId:n.variationId,variationName:a,experimentId:n.experimentId,experimentName:o,layerId:n.layerId,layerName:i.name,layerPolicy:i.policy,accountId:D.getAccountId(),projectId:D.getProjectId(),revision:String(D.getRevision()),clientName:C.getClientName(),clientVersion:C.getClientVersion(),referrer:s,integrationStringVersion:i.integrationStringVersion||1,integrationSettings:Object.assign({},i.integrationSettings,u)};return l}(r,t,n,i,o),s=V.isExpectingRedirect(),l=V.getLayerId(),d=w.description(i);if(s&&l===i.id&&(S.persist(u,"COOKIE"),E.log("Relaying decision for redirect Campaign",d)),z.shouldSendTrackingData()){var h=G(u,t.isLayerHoldback,t.isLayerConcluded);
2if(s&&l===i.id){var g=k.TrackLayerDecisionTimingFlags.preRedirectPolicy;h.timing=g,W(g,[k.PreRedirectPolicies.PERSIST_BEFORE_AND_TRACK_DURING_REDIRECT],h),E.log("Called trackLayerDecision for redirect Campaign",d,h)}else{g=k.TrackLayerDecisionTimingFlags.nonRedirectPolicy;h.timing=g,W(g,[k.NonRedirectPolicies.TRACK_IMMEDIATELY],h),E.log("Called trackLayerDecision for non-redirect Campaign",d,h)}(function(t){return!(!t||!t.variations)&&t.variations.some((function(t){return!!t.actions&&t.actions.some((function(t){return!(!t||!t.changes)&&t.changes.some((function(t){return t.type===p.changeType.REDIRECT}))}))}))})(o)&&(_.dispatch(a.SET_TRACKER_USE_BEACON,!0),c.emitMaybeSendEvents(),_.dispatch(a.SET_TRACKER_USE_BEACON,!1)),I.timeEnd("trackDecisionEvent")}else E.log("Analytics / Not tracking decision for Campaign",w.description(i))}},n.trackPostRedirectDecisionEvent=function(){if(e(5296).shouldDrop()||!z.shouldSendTrackingData()||L.hasTracked())return A.resolve();var t=L.get();if(!t)return A.resolve();var n=G(t,!1),i=k.TrackLayerDecisionTimingFlags.postRedirectPolicy;n.timing=i,W(i,[k.PostRedirectPolicies.TRACK_IMMEDIATELY],n);var r=new A((function(t){var n=m.on({filter:{type:b.TYPES.LIFECYCLE,name:"originsSynced"},handler:function(){t(),m.off(n)}})})),o=O.makeTimeoutPromise(1e3);return A.race([r,o]).then((function(){E.log("Calling trackers after successful sync")}),(function(t){E.warn("Calling trackers after failed sync:",t)})).then((function(){(n=G(t,!1)).timing=k.TrackLayerDecisionTimingFlags.postRedirectPolicy,W(k.TrackLayerDecisionTimingFlags.postRedirectPolicy,[k.PostRedirectPolicies.TRACK_AFTER_SYNC],n),_.dispatch(a.REGISTER_TRACKED_REDIRECT_DATA)})).catch((function(t){E.error("Error when calling trackers after sync:",t)}))},n.trackClickEvent=function(t){if(!e(5296).shouldDrop()){var n=function(t){var n=q({activeViewStates:x.getActiveViewStates(),visitorProfile:U.getVisitorProfile(),layerStates:P.getLayerStatesForAnalytics(),deferBehaviorFeatures:!0}),e=t.config&&t.config.selector?t.config.selector:t.eventFilter.selector,i=t.apiName,r=t.category||g.OTHER,o=t.id,a=H(t);return Object.assign(n,{eventApiName:i,eventCategory:r,eventEntityId:o,eventTags:a,pageId:t.pageId,selector:e})}(t),i={name:t.apiName,type:t.eventType,category:n.eventCategory,tags:n.eventTags};if(u.emitAnalyticsEvent({name:t.name||t.apiName,apiName:t?t.apiName:void 0,type:t.eventType,category:n.eventCategory,tags:n.eventTags,metrics:{}},!z.shouldSendTrackingData()),z.shouldSendTrackingData())return s.addEvent(i),K(n),$("onClickEvent",n),n}},n.trackViewActivation=function(t,n){if(!e(5296).shouldDrop())if(n||(n=x.getViewState(t.id)),n.isActive){var i=function(t,n){var e=q({activeViewStates:x.getActiveViewStates(),visitorProfile:U.getVisitorProfile(),layerStates:P.getLayerStatesForAnalytics(),deferBehaviorFeatures:!0});return Object.assign(e,{pageId:t.id,pageApiName:t.apiName,viewCategory:t.category,eventTags:n.metadata})}(t,n);if(u.emitAnalyticsEvent({name:t.name||i.pageApiName,apiName:i.pageApiName,type:y.PAGEVIEW,category:i.viewCategory,tags:i.eventTags,metrics:{}},!z.shouldSendTrackingData()),z.shouldSendTrackingData())return s.addEvent({name:i.pageApiName,type:y.PAGEVIEW,category:i.viewCategory,tags:i.eventTags}),K(i),_.dispatch(a.TRACK_VIEW_ACTIVATED_EVENT,{pageId:t.id,eventData:i}),I.timeEnd("sendViewActivate"),$("onPageActivated",i),i}else E.debug("Inactive view passed to `trackViewActivation`")}},5442:function(t,n,e){var i=e(1756),r=e(2658),o=e(525),a=e(3221),u=e(2044),c=e(3941),s=e(3496),f=e(3454),l=a.get("stores/plugins"),d=".",v=[{name:"sessionId",relayName:"s"},{name:"decisionTicketAudienceIds",relayName:"as",valueToValueString:function(t){return t.map(encodeURIComponent).join(",")},encodeValueString:function(t){return t},decodeValueString:function(t){return t},valueFromValueString:function(t){return t.split(",").map(r.safeDecodeURIComponent)}},{name:"decisionId",relayName:"d"},{name:"activationId",relayName:"aId"},{name:"pageId",relayName:"vId",isNullable:!0},{name:"variationId",relayName:"v",isNullable:!0},{name:"referrer",relayName:"r"},{name:"timestamp",relayName:"t",valueFromValueString:Number},{name:"visitorId",relayName:"i"},{name:"projectId",relayName:"p"},{name:"revision",relayName:"n"},{name:"clientName",relayName:"cN",isNullable:!0},{name:"clientVersion",relayName:"cV"},{name:"namespace",relayName:"ns"},{name:"accountId",relayName:"a"},{name:"layerId",relayName:"l"},{name:"layerName",relayName:"lN",isNullable:!0},{name:"layerPolicy",relayName:"lP"},{name:"experimentId",relayName:"x",isNullable:!0},{name:"experimentName",relayName:"xN",isNullable:!0},{name:"variationName",relayName:"vN",isNullable:!0},{name:"integrationStringVersion",relayName:"isv",valueFromValueString:Number,isNullable:!0},{name:"integrationSettings",relayName:"iS",isMulti:!0,valueToValueString:function(t,n){var e=null;if(t){var i=l.getPlugin(u.PluginTypes.analyticsTrackers,n);if(i&&"function"==typeof i.serializeSettings)try{e=i.serializeSettings(t)}catch(t){s.warn("Analytics / Failed to persist integrationSettings for plugin:",n,t)}}return e},valueFromValueString:function(t,n){var e=null,i=l.getPlugin(u.PluginTypes.analyticsTrackers,n);if(i&&"function"==typeof i.deserializeSettings)try{e=i.deserializeSettings(t)}catch(t){s.warn("Analytics / Failed to persist integrationSettings for plugin:",n,t)}return e},isNullable:!0}],h={},p={};v.forEach((function(t){h[t.name]=t,p[t.relayName]=t})),n.persist=function(t,n){n===f.RedirectRelayMedia.COOKIE?function(t){try{var n=function(t){var n=[],e=Object.keys(t).reduce((function(e,i){var r=t[i],o=h[i];return o?(o.isMulti?Object.keys(r).forEach((function(t){var n=r[t];null!==(n=o.valueToValueString?o.valueToValueString(n,t):String(n))&&(n=(o.encodeValueString||encodeURIComponent)(n),e.push(encodeURIComponent(o.relayName+d+t)+"="+n))})):null!==r&&(r=(o.valueToValueString||String)(r),r=(o.encodeValueString||encodeURIComponent)(r),e.push(o.relayName+"="+r)),e):(n.push(i),e)}),[]);if(n.length)throw new Error("Relay / Don't know how to relay some fields: "+n);return e.sort(),e.join("&")}(t)}
2catch(t){return s.error("Relay / Error computing redirect relay cookie: ",t),void o.emitError(t)}s.debug("Relay / Setting redirect relay cookie:",n);try{r.set(u.COOKIES.REDIRECT,n,{maxAge:5,encodeValue:!1})}catch(t){s.error("Relay / Failed to set redirect relay cookie",t),o.emitError(t)}}(t):s.error("Relay / Unsupported redirect relay medium: "+n)},n.load=function(t){if(t||(t=function(){var t=r.get(u.COOKIES.REDIRECT,!1);if(t)return s.log("Relay / Found redirect cookie:",t),t}()),t){var n=function(t){var n={};return t.split("&").forEach((function(t){var e=t.split("=");if(2===e.length){var i=r.safeDecodeURIComponent(e[0]),a=p[i];if(a||(a=v.find((function(t){return t.isMulti&&0===i.indexOf(t.relayName+d)})))){var u=e[1];try{if(a.isMulti){n[a.name]=n[a.name]||{};var c=i.substring(a.relayName.length+1);u=(a.decodeValueString||r.safeDecodeURIComponent)(u),u=(a.valueFromValueString||function(t){return t})(u,c),n[a.name][c]=u}else u=(a.decodeValueString||r.safeDecodeURIComponent)(u),u=(a.valueFromValueString||function(t){return t})(u),n[a.name]=u}catch(n){return s.warn("Relay / Skipping segment due to decode or parse error:",t,n),void o.emitError(n)}}else s.warn("Relay / Skipping segment with unknown field identifier:",t,i)}else s.warn("Relay / Skipping invalid segment:",t)})),n}(t);if(n){var e=[];if(v.forEach((function(t){null!==n[t.name]&&void 0!==n[t.name]||(t.isNullable?n[t.name]=null:(delete n[t.name],e.push(t.name)))})),!e.length)return c.dispatch(i.LOAD_REDIRECT_DATA,n),c.dispatch(i.ADD_CLEANUP_FN,{lifecycle:u.Lifecycle.postVisitorProfileLoad,cleanupFn:function(){var t,e;e=(t=n).pageId||void 0,c.dispatch(i.RECORD_LAYER_DECISION,{layerId:t.layerId,decision:{layerId:t.layerId,experimentId:t.experimentId,variationId:t.variationId,isLayerHoldback:!1},decisionTicket:{audienceIds:t.decisionTicketAudienceIds,bucketingId:t.visitorId,globalHoldback:0,preferredVariationMap:void 0,pageId:e,activationId:t.activationId},sessionId:t.sessionId,activationId:t.activationId,timestamp:t.timestamp,revision:t.revision,namespace:t.namespace,pageId:e}),c.dispatch(i.RECORD_LAYER_DECISION_EVENT_ID,{layerId:t.layerId,pageId:e,decisionId:t.decisionId}),c.dispatch(i.ACTION_EXECUTED,{sessionId:t.sessionId,layerId:t.layerId,pageId:t.pageId,timestamp:t.timestamp,activationId:t.activationId})}}),n;s.error("Relay / Observed redirect data with missing fields:",e)}}}},525:function(t,n,e){var i=e(6210);n.emitError=function(t,n,e){i.emit({type:"error",name:t.name||"Error",data:{error:t,metadata:n}},e||!1,!0)},n.emitInternalError=function(t,e){n.emitError(t,e,!0)},n.emitAnalyticsEvent=function(t,n){var e={type:"analytics",name:"trackEvent",data:t};i.emit(e,n)}},391:function(t,n,e){var i=e(4681),r=e(2103),o=e(3496),a=e(7649);function u(t,n){var e="function"==typeof t?t(n):t;return"string"==typeof e&&(e=[e]),Array.isArray(e)?e:(o.warn("Couldn't determine fieldsNeeded for matcher; assuming []"),[])}function c(t){return t.name?t.name+" ("+t.id+")":t.id}n.isInAudience=function(t,n,e){var a,s=function(t,n){return function(e){var i=e.type,a=n[i];if(!a)throw new Error("Audience / No matcher found for type="+i);if(a.fieldsNeeded)for(var c=u(a.fieldsNeeded,e),s=0;s<c.length;s++){var f=c[s];if(void 0===r.getFieldValue(t,f))return void o.debug("Audience / Required field",f,"for type",i,"has no value")}o.debug("Matching condition:",e,"to values:",t);var l=a.match(t,e);if(void 0!==l)return!!l}}(t,e);o.groupCollapsed("Checking audience",n.name,n.id,n),o.debug("Visitor Profile:",t);try{var f=i.evaluate(n.conditions,s)}catch(t){a=t,f=!1}return o.groupEnd(),a&&o.error("Audience / Error evaluating audience",c(n),":",a),o.log("Is "+(f?"in":"NOT in")+" audience:",c(n)),f},n.requiredAudienceFieldsForConditions=function t(n,e){var i={};return n.forEach((function(n){if(Array.isArray(n))Object.assign(i,t(n,e));else if(null!==n&&"object"==typeof n){var r=e[n.type];
2if(r)u(r.fieldsNeeded,n).forEach((function(t){i[a.serializeFieldKey(t)]=t}))}})),i}},6085:function(t,n){n.FIELDS={NAME:"n",TIME:"t",TYPE:"y",CATEGORY:"c",REVENUE:"r",SESSION_ID:"s",OPTIONS:"o",SESSION_INDEX:"si"},n.FIELDS_V0_2={name:n.FIELDS.NAME,time:n.FIELDS.TIME,type:n.FIELDS.TYPE,category:n.FIELDS.CATEGORY,tags:n.FIELDS.OPTIONS,session_index:n.FIELDS.SESSION_INDEX}},5507:function(t,n,e){var i=e(6085),r=e(2103).getFieldValue,o=e(243);function a(t,n,e,r,o){this[i.FIELDS.NAME]=t,this[i.FIELDS.TYPE]=n,"string"==typeof e&&e.trim().length>0&&(this[i.FIELDS.CATEGORY]=e),r&&Object.keys(r).length>0&&(this[i.FIELDS.OPTIONS]=r),void 0!==o&&(this[i.FIELDS.REVENUE]=o)}function u(t,n,e,r){this.eventBase=t,this[i.FIELDS.TIME]=n,void 0!==e&&(this[i.FIELDS.SESSION_ID]=e),void 0!==r&&(this[i.FIELDS.SESSION_INDEX]=r)}n._=a,a.prototype.digest=function(){var t=function(t,n){return encodeURIComponent(t)+"="+encodeURIComponent(n)},n=[];if(n.push(t(i.FIELDS.NAME,this[i.FIELDS.NAME])),n.push(t(i.FIELDS.TYPE,this[i.FIELDS.TYPE])),this[i.FIELDS.CATEGORY]&&n.push(t(i.FIELDS.CATEGORY,this[i.FIELDS.CATEGORY])),this[i.FIELDS.REVENUE]&&n.push(t(i.FIELDS.REVENUE,this[i.FIELDS.REVENUE])),!this[i.FIELDS.OPTIONS])return n.join("&");var e=this[i.FIELDS.OPTIONS]||{},r=Object.keys(e).filter((function(t){return e.hasOwnProperty(t)}));r=r.sort();for(var o=0;o<r.length;o++)n.push(t(r[o],e[r[o]]));return n.join("&")},a.prototype.hash=function(){return this.hash_||(this.hash_=o.hashToHex(o.toByteString(this.digest()),o.Seed.BEHAVIOR_EVENT)),this.hash_},a.prototype.setHash=function(t){this.hash_=t},a.prototype.reHash=function(){this.hash_=null,this.hash()},a.prototype.equals=function(t){if(this.hash()!==t.hash()||this[i.FIELDS.NAME]!==t[i.FIELDS.NAME]||this[i.FIELDS.TYPE]!==t[i.FIELDS.TYPE]||this[i.FIELDS.CATEGORY]!==t[i.FIELDS.CATEGORY]||this[i.FIELDS.REVENUE]!==t[i.FIELDS.REVENUE])return!1;var n=this[i.FIELDS.OPTIONS]||{},e=t[i.FIELDS.OPTIONS]||{},r=Object.keys(n).filter((function(t){return n.hasOwnProperty(t)})),o=Object.keys(e).filter((function(t){return e.hasOwnProperty(t)}));if(r.length!==o.length)return!1;for(var a=0;a<r.length;a++){var u=r[a];if(!e.hasOwnProperty(u)||n[u]!==e[u])return!1}return!0},a.prototype.getValueOrDefault=function(t,n){var e=r(this,t);return void 0!==e?e:n},a.prototype.setFieldValue=function(t,n){t!==i.FIELDS.NAME&&t!==i.FIELDS.TYPE&&t!==i.FIELDS.CATEGORY&&t!==i.FIELDS.REVENUE&&t!==i.FIELDS.OPTIONS||(this[t]=n,this.reHash())},n.J=u,u.prototype.getValueOrDefault=function(t,n){if(0===t.length)return this;var e={};e[i.FIELDS.TIME]=this[i.FIELDS.TIME],e[i.FIELDS.SESSION_ID]=this[i.FIELDS.SESSION_ID],e[i.FIELDS.SESSION_INDEX]=this[i.FIELDS.SESSION_INDEX];var o=r(e,t);return void 0!==o?o:this.eventBase.getValueOrDefault(t,n)},u.prototype.setFieldValue=function(t,n){t===i.FIELDS.TIME||t===i.FIELDS.SESSION_ID||t===i.FIELDS.SESSION_INDEX?this[t]=n:this.eventBase.setFieldValue(t,n)};var c={n:"name",y:"type",c:"category",r:"revenue",s:"session_id",o:"tags",si:"session_index"};u.prototype.readableEvent=function(){var t,n,e=function(t){return"string"==typeof t?'"'+t+'"':t},r=this,o=[];[i.FIELDS.NAME,i.FIELDS.TYPE,i.FIELDS.CATEGORY,i.FIELDS.REVENUE,i.FIELDS.SESSION_ID].forEach((function(i){t=c[i],void 0!==(n=r.getValueOrDefault([i]))&&o.push(t+": "+e(n))}));var a=[];if(t=c[i.FIELDS.OPTIONS],void 0!==(n=r.getValueOrDefault([i.FIELDS.OPTIONS]))&&(Object.keys(n).forEach((function(t){var i=n[t];a.push(t+": "+String(e(i)))})),o.push(t+": {\n\t\t"+a.join(",\n\t\t")+"\n\t}")),"number"==typeof(n=r.getValueOrDefault([i.FIELDS.TIME]))&&(n=e(new Date(n).toString())),void 0!==n){o.push("timestamp: "+n)}return"{\n\t"+o.join(",\n\t")+"\n}"},u.prototype.toObject=function(t){var n,e,r={},o=this;[i.FIELDS.NAME,i.FIELDS.TYPE,i.FIELDS.CATEGORY,i.FIELDS.REVENUE,i.FIELDS.OPTIONS,i.FIELDS.SESSION_INDEX].forEach((function(t){n=c[t],void 0!==(e=o.getValueOrDefault([t],t===i.FIELDS.OPTIONS?{}:void 0))&&(r[n]=e)}));var a=c[i.FIELDS.OPTIONS],u=c[i.FIELDS.REVENUE];if(t&&t.revenueAsTag&&r[u]&&(r[a]=r[a]||{},r[a][u]=r[u],delete r[u]),"number"==typeof(e=o.getValueOrDefault([i.FIELDS.TIME])))if(t&&t.timeAsTimestamp){r.timestamp=new Date(e)}else{r.time=e}return r}},6027:function(t,n,e){var i=e(9990);n.initializeStore=i.initialize,n.addEvent=i.addEvent,n.getEvents=i.getEvents,n.getEventC
2ount=i.getEventCount},8129:function(t,n,e){var i=e(5507).J,r=e(6310),o=e(6085),a=e(5507)._;n.CURRENT_SESSION_INDEX=0;var u=18e5;function c(t,e){var i;i=n.isInSameSession(t,e)?t.getValueOrDefault([o.FIELDS.SESSION_ID]):e.getValueOrDefault([o.FIELDS.TIME]),e.setFieldValue(o.FIELDS.SESSION_ID,i)}function s(t,e,i){var r,a=t.getValueOrDefault([o.FIELDS.SESSION_INDEX]);r=n.isInSameSession(e,t)?a:i?a+1:a-1,e.setFieldValue(o.FIELDS.SESSION_INDEX,r)}n.isInSameSession=function(t,n){var e=t.getValueOrDefault([o.FIELDS.TIME],0),i=n.getValueOrDefault([o.FIELDS.TIME],0);return this.isTimestampInSameSession(e,i)},n.isTimestampInSameSession=function(t,n){return Math.abs(t-n)<u},n.updateSessionId=function(t,n){if(t){var e=t.getValueOrDefault([o.FIELDS.TIME]),r=t.getValueOrDefault([o.FIELDS.SESSION_ID]),u=n.getValueOrDefault([o.FIELDS.TIME]);e="number"!=typeof e?u-36e5:e,r="number"!=typeof r?e:r,c(t=new i(new a("",""),e,r),n)}else n.setFieldValue(o.FIELDS.SESSION_ID,n.getValueOrDefault([o.FIELDS.TIME]))},n.updateSessionIndex=function(t,n){if(t){var e=t.getValueOrDefault([o.FIELDS.TIME]),r=t.getValueOrDefault([o.FIELDS.SESSION_INDEX]),u=n.getValueOrDefault([o.FIELDS.TIME]),c=t.getValueOrDefault([o.FIELDS.SESSION_ID]);e="number"!=typeof e?u-36e5:e,r="number"!=typeof r?0:r,c="number"!=typeof c?e:c,s(t=new i(new a("",""),e,c,r),n,!1)}else n.setFieldValue(o.FIELDS.SESSION_INDEX,0)},n.sessionize=function(t){var e=t.length;if(0!==e){t[0].setFieldValue(o.FIELDS.SESSION_ID,t[0].getValueOrDefault([o.FIELDS.TIME]));for(var i=1;i<e;i++)c(t[i-1],t[i]);var a=n.CURRENT_SESSION_INDEX,f=t[e-1].getValueOrDefault([o.FIELDS.TIME]);r.now()-f>u&&(a+=1),t[e-1].setFieldValue(o.FIELDS.SESSION_INDEX,a);for(i=e-1;i>0;i--)s(t[i],t[i-1],!0)}},n.reindexIfNecessary=function(t,n,e){var i=function(t){for(var n=0;n<t.length;n++){var e=t[n].getValueOrDefault([o.FIELDS.SESSION_INDEX]);t[n].setFieldValue(o.FIELDS.SESSION_INDEX,e+1)}};-1===t.getValueOrDefault([o.FIELDS.SESSION_INDEX])&&(i(n),i(e))},n.sessionSortPredicate=function(t,n){return t[o.FIELDS.TIME]-n[o.FIELDS.TIME]},n.applyMigrations=function(t){return!1}},9990:function(t,n,e){var i=e(1756),r=e(2658),o=e(6310),a=e(8701),u=e(3941),c=e(3496),s=e(8129),f=e(7649),l=n,d=e(5507).J,v=e(6085),h=e(2044),p=e(5507)._,g=(s=e(8129),e(3221)),m=g.get("stores/visitor_events"),b=g.get("stores/visitor_events_manager"),y={EVENTBASE:"eb",HASH:"h",TIMEBASE:"tb",TIMESTAMPS:"ts",DELTA:"d",INDEX:"i"},_=1e3;function w(t){u.dispatch(i.SET_VISITOR_EVENTS,t)}function E(t){u.dispatch(i.UPDATE_BEHAVIOR_STORE,{key:"baseMap",value:t})}function I(t){u.dispatch(i.UPDATE_BEHAVIOR_STORE,{key:"eventQueue",value:t})}function A(t){u.dispatch(i.UPDATE_BEHAVIOR_STORE,{key:"lastEvent",value:t})}function T(){return m.getEvents()}function S(){return b.getBaseMap()}function O(){return b.getEventQueue()}function k(){return b.getLastEvent()}function R(){var t=T().concat(O()),n=!1;return t.length>_&&(t=t.slice(-1e3),n=!0),w(t),I([]),n}n.initialize=function(t,n){if(!b.getInitialized()){l.I(t,n);var e=T();e.length>0&&A(e[e.length-1]);var r=O();r.length>0&&A(r[r.length-1]),u.dispatch(i.UPDATE_BEHAVIOR_STORE,{key:"initialized",value:!0})}},n.addEvent=function(t){c.debug("Behavior store: adding event",t);var n=l.A(t);A(n),I(O().concat(n)),s.reindexIfNecessary(k(),T(),O()),l.T(O()),n.t&&function(t){try{var n=Number(r.get(h.COOKIES.OPTIMIZELY_SESSION,!1));if(isNaN(n)||0!==n){var e=n?s.isTimestampInSameSession(t,n)?t:0:t;r.set(h.COOKIES.OPTIMIZELY_SESSION,e.toString(),{path:"/",encodeValue:!1},!0)}}catch(t){c.error("Error setting session cookie",t)}}(n.t)},n.getEvents=function(){return O().length>0&&(R()&&s.sessionize(T()),l.S(T()),l.T(O())),T()},n.getEventCount=function(){return O().length+T().length},l.I=function(t,n){e(6494).time("populatePersistedEvents"),l.O(t,n)&&(l.S(T()),l.T(O())),s.sessionize(T()),e(6494).timeEnd("populatePersistedEvents")},l.O=function(t,n){if(0===t.length&&0===n.length)return w([]),I([]),!1;var e=!1,i=t[0]||n[0];return y.EVENTBASE in i?(w(l.k(t)),I(l.k(n))):(e=!0,w(l.R(t)),I(l.R(n))),O().length>0&&(R(),e=!0),w(l._updateBaseMapAndMaybeDedupe(T())),l._migrateEventBasesAndUpdateStore(
2)&&(e=!0),e},l.R=function(t){for(var n=[],e=0;e<t.length;e++){var i=t[e],r=l.D(i);n[e]=new d(r,i[v.FIELDS.TIME])}return n},l._migrateEventBasesAndUpdateStore=function(){var t=!1,n=l.N();return s.applyMigrations(n)&&(t=!0,E({}),w(l._updateBaseMapAndMaybeDedupe(T())),I(l._updateBaseMapAndMaybeDedupe(O()))),t},l.j=function(){return o.now()},l.A=function(t){var n,e=t.name,i=t.type||"default",r=t.category||a.OTHER,o=t.tags||{};t.revenue&&(n=t.revenue);var u=new p(e,i,r,o,n);u=l.P(u);var c=l.j(),f=new d(u,c,-1);return s.updateSessionId(k(),f),s.updateSessionIndex(k(),f),f},l._updateBaseMapAndMaybeDedupe=function(t){for(var n=0;n<t.length;n++)t[n].eventBase=l.P(t[n].eventBase);return t},l.S=function(t){var n=l.L(t);f.persistBehaviorEvents(n)},l.T=function(t){var n=l.L(t);f.persistBehaviorEventQueue(n)},l.V=function(){var t;b.getCleared()||(w([]),I([]),l.S(T()),l.T(O()),E({}),A(null),t=!0,u.dispatch(i.UPDATE_BEHAVIOR_STORE,{key:"cleared",value:t}))},l.P=function(t){var n=t.hash(),e=S(),i=e[n];if(void 0===i)return e[n]=[t],E(e),t;for(var r=0;r<i.length;r++)if(t.equals(i[r]))return i[r];return i.push(t),E(e),t},l.N=function(){var t=[],n=S();for(var e in n)!n.hasOwnProperty(e)||(t=t.concat(n[e]));return t},l.L=function(t){for(var n=function(t){var n={};n[v.FIELDS.NAME]=t.getValueOrDefault([v.FIELDS.NAME]),n[v.FIELDS.TYPE]=t.getValueOrDefault([v.FIELDS.TYPE]);var e=t.getValueOrDefault([v.FIELDS.CATEGORY]);void 0!==e&&(n[v.FIELDS.CATEGORY]=e);var i=t.getValueOrDefault([v.FIELDS.REVENUE]);void 0!==i&&(n[v.FIELDS.REVENUE]=i);var r=t.getValueOrDefault([v.FIELDS.OPTIONS]);return void 0!==r&&(n[v.FIELDS.OPTIONS]=r),n},e=y,i=[],r="_idx_",o=0;o<t.length;o++){var a,u,c=t[o],s=c.eventBase;if(s.hasOwnProperty(r)){a=i[s[r]];var f=c[v.FIELDS.TIME]-(a[e.TIMEBASE]||0);(u={})[e.DELTA]=f,u[e.INDEX]=o,a[e.TIMESTAMPS].push(u)}else(a={})[e.EVENTBASE]=n(c),a[e.HASH]=s.hash(),a[e.TIMEBASE]=c[v.FIELDS.TIME],(u={})[e.DELTA]=0,u[e.INDEX]=o,a[e.TIMESTAMPS]=[u],i.push(a),s[r]=i.length-1}for(o=0;o<t.length;o++)delete t[o].eventBase[r];return i},l.D=function(t,n){var e=new p(t[v.FIELDS.NAME],t[v.FIELDS.TYPE],t[v.FIELDS.CATEGORY],t[v.FIELDS.OPTIONS],t[v.FIELDS.REVENUE]);return void 0!==n&&e.setHash(n),e},l.k=function(t){for(var n=y,e=[],i=0;i<t.length;i++)for(var r=t[i],o=l.D(r[n.EVENTBASE],r[n.HASH]),a=r[n.TIMEBASE],u=r[n.TIMESTAMPS],c=0;c<u.length;c++){var s=u[c],f=new d(o,a+s[n.DELTA]);e[s[n.INDEX]]=f}return e},n.deserialize=function(t){return l.k(t)},n.mergeAllEvents=function(t){var n=[].concat.apply([],t);return n.sort(s.sessionSortPredicate),s.sessionize(n),n},n.MAX_EVENTS=_},1756:function(t,n,e){var i=e(762);t.exports=i({LOG:null,SET_LOGLEVEL:null,INITIALIZE_STATE:null,SET_DOMCONTENTLOADED:null,ACTIVATE:null,UPDATE_BEHAVIOR_STORE:null,DATA_LOADED:null,SET_CLIENT_NAME:null,SET_CLIENT_VERSION:null,LOAD_PERSISTED_LAYER_STATES:null,RECORD_GLOBAL_DECISION:null,RECORD_LAYER_DECISION:null,ENSURE_ORIGINAL_PUSHSTATE:null,ENSURE_ORIGINAL_REPLACESTATE:null,SET_VISITOR_ATTRIBUTES:null,SET_VISITOR_ATTRIBUTE_PENDING:null,LOAD_EXISTING_VISITOR_PROFILE:null,SET_VISITOR_EVENTS:null,SET_FOREIGN_VISITOR_EVENTS:null,SET_FOREIGN_VISITOR_EVENT_QUEUE:null,SET_VISITOR_ID:null,SET_VISITOR_ID_VIA_API:null,SET_GA4_USER_ID:null,REFRESH_SESSION:null,LOAD_SESSION_STATE:null,UPDATE_VARIATION_ID_MAP:null,MERGE_VARIATION_ID_MAP:null,UPDATE_PREFERRED_LAYER_MAP:null,MERGE_PREFERRED_LAYER_MAP:null,UPDATE_CONTEXTUAL_MAB_MAP:null,MERGE_CONTEXTUAL_MAB_MAP:null,SET_ODP_SEGMENTS:null,RECORD_LAYER_DECISION_EVENT_ID:null,TRACK_VIEW_ACTIVATED_EVENT:null,REGISTER_ASYNC_DEFERRED:null,RESOLVE_DEFERRED:null,REJECT_DEFERRED:null,REGISTER_PLUGIN:null,ADD_CLEANUP_FN:null,CLEAR_CLEANUP_FN:null,ACTION_EXECUTED:null,REGISTER_ACTION:null,SET_VIEW_ACTIVE_STATE:null,UPDATE_PARSED_VIEW_METADATA:null,UPDATE_USER_SUPPLIED_METADATA:null,REGISTER_VIEWS:null,SET_GLOBAL_TAGS:null,SET_VIEW_BATCHING:null,RESET_VIEW_STATES:null,ATTACH_EVENT_STREAM_PUBLISHERS:null,DETACH_EVENT_STREAM_PUBLISHERS:null,LOAD_DIRECTIVE:null,SET_COOKIE_AGE:null,SET_COOKIE_DOMAIN:null,SET_COOKIE_AUTO_REFRESH:null,XDOMAIN_SET_DEFAULT_FRAME:null,XDOMAIN_ADD_FRAME:null,XDOMAIN_SET_MESSAGE:null,XDOMAIN_ADD_SUBSCRIBER:null,XDOMAIN_SET_CANONICAL_ORIGINS:null,XDOMAIN_SET_DISABLED:null,ADD_EMITTER_HANDLER:null,REMOVE_
2EMITTER_HANDLER:null,SET_INTEGRATION_SETTINGS:null,ADD_CHANGE:null,SET_CHANGE_APPLIER:null,REMOVE_ACTION_STATE:null,ANNOUNCE_PENDING_REDIRECT:null,LOAD_REDIRECT_DATA:null,REGISTER_TRACKED_REDIRECT_DATA:null,SET_PENDING_EVENT:null,REMOVE_PENDING_EVENT:null,LOAD_PENDING_EVENTS:null,SANDBOXED_FUNCTIONS_ADDED:null,SET_RUM_DATA:null,RECORD_API_USAGE:null,INITIALIZE_CHANGE_METRICS:null,RECORD_ACTIVATION_TYPE_USAGE:null,RECORD_AUDIENCE_USAGE:null,RECORD_CHANGE_MACROTASK_RATE:null,RECORD_CHANGE_OVERHEATED:null,RECORD_CHANGE_TYPE_USAGE:null,RECORD_DOM_OBSERVATION_OCCURENCE:null,RECORD_INTEGRATION_USAGE:null,RECORD_LAYER_FEATURE_USAGE:null,RECORD_LAYER_POLICY_USAGE:null,RECORD_RECOMMENDATIONS_USAGE:null,RECORD_VIEW_FEATURE_USAGE:null,RECORD_VIEWS_INITIALLY_ACTIVATED_COUNT:null,RECORD_VISITOR_ID_LOCATOR_USAGE:null,RECORD_VISITOR_ID_ERROR:null,RECORD_STICKY_BUCKETING_FEATURE:null,SET_PERFORMANCE_MARKS_DATA:null,FINALIZE_BATCH_SNAPSHOT:null,REGISTER_PREVIOUS_BATCH:null,REGISTER_TRACKER_VISITOR:null,REGISTER_TRACKER_EVENT:null,REGISTER_TRACKER_DECISION:null,RESET_TRACKER_EVENTS:null,RESET_TRACKER_PREVIOUS_BATCHES:null,RESET_TRACKER_STORE:null,SET_TRACKER_POLLING:null,SET_TRACKER_BATCHING:null,SET_TRACKER_USE_BEACON:null,SET_TRACKER_SEND_EVENTS:null,SET_TRACKER_PERSISTABLE_STATE:null,SET_TRACKER_DIRTY:null,UPDATE_TRACKER_VISITOR_ATTRIBUTES:null,SET_UA_DATA:null})},2044:function(t,n,e){var i=e(762);n.COOKIES={OPT_OUT:"optimizelyOptOut",PREVIEW:"optimizelyPreview",REDIRECT:"optimizelyRedirectData",SESSION_STATE:"optimizelySessionState",OPTIMIZELY_SESSION:"optimizelySession",TOKEN:"optimizelyToken",VISITOR_ID:"optimizelyEndUserId",VISITOR_UUID:"optimizelyPPID"},n.LayerActivationTypes={CONDITIONAL:"conditional",IMMEDIATE:"immediate",MANUAL:"manual",READY:"ready",TIMEOUT:"timeout"},n.LogLevel={OFF:0,ERROR:1,WARN:2,INFO:3,DEBUG:4},n.Lifecycle=i({preActivate:null,postVisitorProfileLoad:null,postViewsActivated:null,postActivate:null}),n.ViewActivationTypes={immediate:"immediate",manual:"manual",callback:"callback",polling:"polling",URLChanged:"url_changed",DOMChanged:"dom_changed"},n.StorageKeys={PENDING_EVENTS:"pending_events",RELAYED_EVENTS:"relayed_events"},n.PluginTypes=i({visitorProfileProviders:null,viewProviders:null,audienceMatchers:null,viewMatchers:null,analyticsTrackers:null,viewTagLocators:null,userFeatureDefs:null,apiModules:null,changeAppliers:null,deciders:null,eventImplementations:null,viewTriggers:null}),n.ResourceTimingAttributes=i({connectStart:null,connectEnd:null,decodedBodySize:null,domainLookupStart:null,domainLookupEnd:null,duration:null,encodedBodySize:null,fetchStart:null,requestStart:null,responseStart:null,responseEnd:null,secureConnectionStart:null,startTime:null,transferSize:null,serverTiming:null}),n.RUMPerformanceTimingAttributes=i({blockTime:null}),n.AttributionTypes=i({FIRST_TOUCH:null,LAST_TOUCH:null}),n.SandboxedFunctions=i({XMLHttpRequest:null}),n.PerformanceData=i({performance_marks:null,resource_timing:null,performance_timing:null}),n.PerformanceCounters=i({mutation_observer_invocation:null,polling_invocation:null,match_selector_invocation:null}),n.VisitorStorageKeys={EVENTS:"events",EVENT_QUEUE:"event_queue",LAYER_MAP:"layer_map",LAYER_STATES:"layer_states",SESSION_STATE:"session_state",VISITOR_PROFILE:"visitor_profile",VARIATION_MAP:"variation_map",TRACKER_OPTIMIZELY:"tracker_optimizely",CONTEXTUAL_MAB:"contextual_mab",ODP_SEGMENTS:"odp_segments"},n.AllStorageKeys=Object.assign({},n.StorageKeys,n.VisitorStorageKeys),n.ListTargetingKeyTypes={COOKIE:"c",QUERY:"q",JS_VARIABLE:"j"},n.VisitorIdLocatorType={COOKIE:"cookie",JS_VARIABLE:"js",LOCALSTORAGE:"localStorage",QUERY:"query"},n.AllocationPolicyEnum={CMAB:"contextual-multi-arm-bandit",MIN_REGRET:"min-regret",MIN_TIME:"min-time",MANUAL:"manual"}},8701:function(t){t.exports={OTHER:"other"}},3659:function(t,n){n.TYPES={ACTION:"action",ANALYTICS:"analytics",EDITOR:"editor",LIFECYCLE:"lifecycle"}},3786:function(t){t.exports={CLICK:"click",CUSTOM:"custom",ENGAGEMENT:"engagement",PAGEVIEW:"pageview"}},3454:function(t,n,e){var i=e(762);n.TrackLayerDecisionTimingFlags=i({preRedirectPolicy:null,postRedirectPolicy:null,nonRedirectPolicy:null}),n.PreRedirectPolicies=i({PERSIST_BEFORE_AND_TRACK_DURING_REDIRECT:null,PERSIST_BEFORE_REDIRECT:null}
2),n.PostRedirectPolicies=i({TRACK_IMMEDIATELY:null,TRACK_AFTER_SYNC:null}),n.NonRedirectPolicies=i({TRACK_IMMEDIATELY:null}),n.RedirectRelayMedia=i({COOKIE:null})},8917:function(t,n,e){var i=e(243),r=n.TOTAL_POINTS=1e4;n.bucketingNumber=function(t,n,e){return i.hashToInt(t+n,e,r)},n.isHoldback=function(t,e){return n.bucketingNumber(t,e.id,i.Seed.IGNORING)<(e.holdback||0)},n.chooseWeightedCandidate=function(t,e,r){for(var o=n.bucketingNumber(t,e,i.Seed.BUCKETING),a=0;a<r.length;a++)if(r[a].endOfRange>o)return r[a].entityId;throw new Error("Unable to choose candidate")}},6271:function(t,n,e){var i=e(279).isEmpty,r=e(1756),o=e(8917),a=e(4681),u=e(229).b,c=e(3941),s=e(3496),f=e(9819);n.isValidExperiment=function(t,n){var e;return s.groupCollapsed("Decision / Evaluating audiences for experiment:",n,t),e=!n.audienceIds||a.evaluate(n.audienceIds,(function(n){return t.includes(n)})),s.groupEnd(),s.debug("Decision / Experiment",n,"is valid?",e),e},n.selectVariation=function(t,n,e,i,a){if(s.debug("Decision / Selecting variation for experiment:",t.id),!t.variations||0===t.variations.length)throw new u('No variations in selected experiment "'+t.id+'"');if(!t.weightDistributions&&t.variations.length>1)throw new u('On selected experiment "'+t.id+'", weightDistributions must be defined if # variations > 1');var l;if(t.bucketingStrategy&&"impression"===t.bucketingStrategy)if(1===t.variations.length)l=t.variations[0].id;else{var d=i;l=o.chooseWeightedCandidate(e+d,t.id,t.weightDistributions)}else if(l=1===t.variations.length?t.variations[0].id:o.chooseWeightedCandidate(e,t.id,t.weightDistributions),null==t?void 0:t.deployed)s.debug("Decision / Experiment is deployed. Skipping bucketing.");else if(a&&a[t.id]){s.debug("Decision / Using preferredVariationMap to select variation for experiment:",t.id);var v=a[t.id];if(!t.variations.find((function(t){return t.id===v})))return c.dispatch(r.RECORD_STICKY_BUCKETING_FEATURE,{feature:"stoppedVariation",id:t.id}),s.debug("Decision / Preferred variation:",v,"not found on experiment:",t.id,". Visitor not bucketed."),null;v!==l&&(c.dispatch(r.RECORD_STICKY_BUCKETING_FEATURE,{feature:"preferredVariation",id:t.id}),l=v)}var h=f.processCmabExperiment(t,l);h&&(l=h);var p=t.variations.find((function(t){return t.id===l}));if(p)return s.debug("Decision / Selected variation:",p),p;throw new u('Unable to find selected variation: "'+l+'".')},n.getExperimentById=function(t,n){var e=t.experiments.find((function(t){return t.id===n}));if(e)return e;throw new u("Unable to find selected experiment.")},n.hasVariationActionsOnView=function(t,n){return s.debug("Decision / Checking variation:",t,"for actions on pageId:",n),!!(t.actions||[]).find((function(t){return t.pageId===n&&!i(t.changes)}))}},229:function(t,n){function e(t){this.message=t}e.prototype=new Error,n.b=e},8079:function(t,n,e){var i=e(279),r=i.isEmpty,o=i.pick,a=e(1756),u=e(525),c=e(391),s=e(8917),f=e(6271),l=e(229).b,d=e(3221),v=e(2044),h=e(3941),p=e(5564),g=e(2902),m=e(3496),b=e(6494),y=e(1386),_=d.get("stores/plugins"),w=d.get("stores/global"),E=d.get("stores/layer_data");function I(t){var n=_.getPlugin(v.PluginTypes.deciders,t);if(r(n))throw new Error("No deciders found for policy: "+t);return n}n.isPageIdRelevant=function(t){if(!t)return!1;var n=I(t.policy);return"function"==typeof n.includePageIdInDecisionTicket?n.includePageIdInDecisionTicket(t):!0===n.includePageIdInDecisionTicket},n.createTicket=function(t){var n,i,r,a=o(t,["bucketingId","globalHoldback","preferredVariationMap","pageId"]);return Object.assign(a,{audienceIds:(n=t.visitorProfile,i=t.audiences,r=_.getAllPlugins(v.PluginTypes.audienceMatchers),i.reduce((function(t,e){return c.isInAudience(n,e,r)&&t.push(e.id),t}),[])),activationId:w.getActivationId()}),e(6494).timeEnd("getDecisionTicket"),a},n.decideGlobal=function(t){return{isGlobalHoldback:s.isHoldback(t.bucketingId,{id:null,holdback:t.globalHoldback})}},n.decideGroup=function(t,n){m.debug("Decision / Deciding layer for group: ",p.description(t));var e,i,r=n.preferredLayerId,o=!!r;try{(e=s.chooseWeightedCandidate(n.bucketingId,t.id,t.weightDistributions))&&"None"!==e||(i='Group traffic allocation. Visitor maps to a "hole" in the bucket space left by an experiment or campaign that\'s since been removed from the group')}catch(t){i="Group traffic allocation. Visitor maps to a point in the bucket space which has never been covered by any experiment or campaign."}if(o&&(m.debug("Decision / Using preferredLayerMap to select layer for group:",p.description(t)),r!==e&&(h.dispatch(a.RECORD_STICKY_BUCKETING_FEATURE,{feature:"preferredLayer",id:t.id}),e=r)),i)return{layerId:null,reason:i};if(!t.weightDistributions.find((function(t){return t.entityId===e}))){var u="Visitor was"+(o?" sticky-":" non-sticky ")+"bucketed into a campaign ("+e+") which is not in the group";if(!o)throw new l(u);return{layerId:null,reason:u}}return{layerId:e}},n.decideLayer=function(t,n){var e,i;m.debug("Deciding: ",t,n);var o=I(t.policy);b.time("layerHoldback");var a,c,d={layerId:t.id,experimentId:null,variationId:null,isLayerHoldback:s.isHoldback(n.bucketingId,t),isLayerConcluded:!!t.concluded};if(r(t.experiments))throw new l("No experiments in layer.");try{if(o.decideLayer){m.debug("Decision / Using decider's custom decideLayer.");var v=o.decideLayer(t,n);e=v.experiment,i=v.variation}else m.debug("Decision / Using default decideLayer behavior."),e=o.selectExperiment(t,n.audienceIds,n.bucketingId),i=f.selectVariation(e,n.audienceIds,n.bucketingId,n.activationId,n.preferredVariationMap)}catch(t){a=t,(null!=(c=l)&&"undefined"!=typeof Symbol&&c[Symbol.hasInstance]?c[Symbol.hasInstance](a):a instanceof c)?d.reason=t.message:d.error=t}return d.experimentId=e?e.id:null,d.variationId=i?i.id:null,d.error&&(d.error.
2name="DecisionEngineError",u.emitError(d.error)),d},n.getDummyLayerDecision=function(t,n){var e,i=function(t,n){for(var e=0;e<t.experiments.length;e++)for(var i=0;i<t.experiments[e].variations.length;i++)if(n.indexOf(t.experiments[e].variations[i].id)>-1)return{experimentId:t.experiments[e].id,variationId:t.experiments[e].variations[i].id};return null}(t,n);return i?(m.log("Decision / Applying force variation:",i.variationId,"to Campaign",g.description(t)),e={layerId:t.id,variationId:i.variationId,experimentId:i.experimentId,isLayerHoldback:!1,isLayerConcluded:!1,reason:"force"}):(m.log("No variation matches ids:",n,"in Campaign",g.description(t)),e={layerId:t.id,variationId:null,experimentId:null,isLayerHoldback:!1,isLayerConcluded:!1,reason:"force"}),e},n.isInCohort=function(t){if(!t.experimentId||!t.variationId)return!1;var n=E.get(t.layerId);return!(y.isSingleExperimentPolicy(n.policy)&&t.isLayerHoldback)}},3221:function(t,n,e){var i=e(9716),r=e(3941),o=e(2682),a=i.create(),u={action_data:e(608),async_request:e(4033),audience_data:e(9624),change_data:e(7542),cleanup:e(2735),client_metadata:e(3970),cookie_options:e(7032),event_data:e(5846),event_emitter:e(2910),dimension_data:e(890),directive:e(3650),global:e(8808),history:e(9607),integration_settings:e(6585),layer:e(6946),layer_data:e(3077),log:e(9325),observed_redirect:e(3958),pending_events:e(2446),performance:e(9969),plugins:e(7631),provider_status:e(6631),pending_redirect:e(1663),rum:e(5787),sandbox:e(9688),session:e(8147),tracker_optimizely:e(9794),ua_data:e(5756),view:e(6430),view_data:e(2161),visitor:e(1445),visitor_attribute_entity:e(7918),visitor_events:e(7577),visitor_events_manager:e(4173),visitor_id:e(9221),visitor_bucketing:e(6916),xdomain:e(1943)};u.group_data=e(2091),r.registerStores(u),Object.keys(u).forEach((function(t){a.register("stores/"+t,r.getStore(t))})),a.register("core/plugins/matchers/key_value",o),t.exports=a},7946:function(t,n,e){var i=e(1756),r=e(525),o=e(2658),a=e(9726).v,u=e(2044),c=e(3941),s=e(3496),f=n.JSONParseError=a("JSONParseError"),l=e(2122),d=e(6388),v=e(3898),h="optimizely_show_preview",p="optimizely_disable",g="optimizely_editor",m="optimizely_p13n",b="optimizely_x_audiences",y="optimizely_x",_="optimizely_show_preview",w="optimizely_opt_out",E="optimizely_token",I="optimizely_force_tracking",A="optimizely_embed_editor";n.populateDirectiveData=function(){var t,n,e,a,T,S,O,k,R;!function(){var t="OFF",n=l.getQueryParamValue("optimizely_log");if(n){var e=n.split(":");""!==e[0]&&(t=String(e[0]).toUpperCase()),void 0!==e[1]&&s.setLogMatch(e[1])}s.setLogLevel(t)}(),function(){var t=v.getUserAgent()||"";if("string"!=typeof t)return void s.warn("Directive / userAgent not a string");t=t.toLowerCase(),["googlebot","yahoo! slurp","bingbot","bingpreview","msnbot","keynote","ktxn","khte","gomezagent","alertsite","yottaamonitor","pingdom.com_bot","aihitbot","baiduspider","adsbot-google","mediapartners-google","applebot","catchpoint","phantomjs","moatbot","facebookexternalhit"].some((function(n){if(t.includes(n))return s.warn("Directive / Matches bot:",n),!0}))&&(s.log("Directive / Disabling tracking"),c.dispatch(i.LOAD_DIRECTIVE,{trackingDisabled:!0}))}(),t=window.optimizely?window.optimizely.initialized:void 0,n=Boolean("function"==typeof t?t():t),c.dispatch(i.LOAD_DIRECTIVE,{alreadyInitialized:n}),c.dispatch(i.LOAD_DIRECTIVE,{mutationObserverAPISupported:v.isMutationObserverAPISupported()}),function(){var t=o.get(u.COOKIES.OPT_OUT),n=l.getQueryParamValue(w),e="You have successfully opted out of Optimizely for this domain.",r="You are NOT opted out of Optimizely for this domain.";if("true"===n||"false"===n){var a="true"===n;c.dispatch(i.LOAD_DIRECTIVE,{shouldOptOut:a}),v.alert(a?e:r)}else t&&c.dispatch(i.LOAD_DIRECTIVE,{shouldOptOut:"true"===t})}(),function(){var t=!1,n=[h,p];n.push(g);for(var e=0;e<n.length;e++)if("true"===l.getQueryParamValue(n[e])){s.warn("Directive / Not activating because "+n[e]+" is set."),t=!0;break}c.dispatch(i.LOAD_DIRECTIVE,{disabled:t})}(),c.dispatch(i.LOAD_DIRECTIVE,{isEditor:!1}),c.dispatch(i.LOAD_DIRECTIVE,{isPreview:!1}),(e=l.getQueryParamValue(_))&&s.log("Directive / Is legacy preview mode"),c.dispatch(i.LOAD_DIRECTIVE,{isLegacyPreview:!!e}),c.dispatch(i.LOAD_DIRECTIVE,{isSlave:!1}),a=v.getGlobal("optlyDesktop"),(T=!(!a||void 0===a.p13nInner))&&s.log("Directive / Is running in desktop app editor"),c.dispatch(i.LOAD_DIRECTIVE,{isRunningInDesktopApp:T}),(S="true"===l.getQueryParamValue(m))&&s.log("Directive / Is running in editor"),c.dispatch(i.LOAD_DIRECTIVE,{isRunningInV2Editor:S}),function(){var t=!1,n=v.getGlobal("sessionStorage"),e=l.getQueryParamValue(A);t="true"===e,e&&n&&("true"===e?n.setItem(A,"true"):n.removeItem(A)),n&&"true"===n.getItem(A)&&(t=!0,s.log("Directive / Should mount embedded editor"));c.dispatch(i.LOAD_DIRECTIVE,{requestedEmbeddedEditor:t})}(),O=o.get(u.COOKIES.TOKEN)||null,k=l.getQueryParamValue(E)||O,c.dispatch(i.LOAD_DIRECTIVE,{projectToken:k}),function(){var t=o.get(u.COOKIES.PREVIEW),n=[],e=l.getQueryParamValue(b);if(e)n=o.safeDecodeURIComponent(e).split(",");else if(t)try{n=d.parse(t).forceAudienceIds}catch(n){var i=new f("Failed to parse previewCookie in registerForceAudienceIds: "+t),a={originalMessage:n.message,userError:!0};r.emitError(i,a)}n.length&&s.warn("Directive / Force parameters disabled - ignoring force audience IDs.")}(),function(){var t=o.get(u.COOKIES.PREVIEW),n=[],e=l.getQueryParamValue(y);if(e)n=o.safeDecodeURIComponent(e).split(",");else if(t)try{n=d.parse(t).forceVariationIds}catch(n){var i=new f("Failed to parse previewCookie in registerForceVariationIds: "+t),a={originalMessage:n.message,userError:!0};r.emitError(i,a)}n.length&&s.warn("Directive / Force parameters disabled - ignoring force variation IDs.")}(),(R=l.getQueryParamValue(I))&&c.dispatch(i.LOAD_DIRECTIVE,{forceTracking:R})};n.setOptOut=function(t){t?(s.warn("Directive / Opting out"),o.set(u.COOKIES.OPT_OUT,"true",{maxAge:31536e4},!0)):o.remove(u.COOKIES.OPT_OUT),c.dispatch(i.LOAD_DIRECTIVE,{shouldOptOut:t})}},6310:function(t,n){n.now=function(){return+new Date}},6387:function(t,n){n.VERSION="0.258.0",n.NAME="js"},9751:function(t,n,e){var i=e(3221).get("stores/global");n.getDocumentElement=function(){return document.documentElement},n.getCookieString=function(){return document.cookie||""},n.setCookie=function(t){document.cookie=t},n.querySelector=function(t){return document.querySelector(t)},n.querySelectorAll=function(t){return document.querySelectorAll(t)},n.parseUri=function(t){var e=n.createElement("a");return e.href=t,e},n.childrenOf=function(t){return Array.prototype.slice.call(t.querySelectorAll("*"))},n.createElement=function(t){return document.createElement(t)},n.isReady=function(){return i.domContentLoadedHasFired()||"interactive"===document.readyState||"complete"===document.readyState},n.isLoaded=function(){return"complete"===document.readyState},n.addReadyHandler=function(t){return document.addEventListener("DOMContentLoaded",t),function(){n.removeReadyHandler(t)}},n.removeReadyHandler=function(t){return function(){document.removeEventListener("DOMContentLoaded",t)}},n.getReferrer=function(){return document.referrer},n.getReadyState=function(){return document.readyState},n.write=function(t){if("loading"!==n.getReadyState())throw new Error("Aborting attempt to write to already-loaded document");document.write(t)},n.appendToHead=function(t){return n.appendTo(document.head,t)},n.appendTo=function(t,n){t.appendChild(n)},n.addEventListener=function(t,n,e){return document.addEventListener(t,n,e),function(){document.removeEventListener(t,n,e)}},n.getCurrentScript=function(){if(document.currentScript)return document.currentScript},n.parentElement=function(t){for(var n=t.parentNode;n.nodeType!==Node.ELEMENT_NODE;)n=n.parentNode;return n}},7429:function(t,n,e){var i,r,o="optimizely_data",a=e(9726).v,u=e(688),c=e(3898),s=a("StorageError");try{r=c.getGlobal("localStorage")}catch(t){throw new s("Unable to read localStorage: "+t.toString())}if(!r)throw new s("localStorage is undefined");i=u.create(r,o),n.DT=i},4462:function(t,n,e){var i=e(9682);n.create=function(t){return new(i.get("MutationObserver",MutationObserver))(t)},n.observe=function(t,n,e){t.observe(n,e)}},4645:function(t,n,e){var i=e(3341).Promise,r=e(3898);n.estimateStorage=function(){var t=r.getGlobal("navigator");try{return t.storage.estimate()}catch(t){return i.resolve({usage:null,quota:null})}}},6494:function(t,n,e){var i=e(1756),r=e(9726).v,o=e(6310),a=e(3941),u=e(3898),c=e(3221).get("stores/rum"),s="optimizely:",f=n.Error=r("PerformanceError");function l(){return u.getGlobal("performance")}
2n.time=function(t){if(c.getSampleRum()){var n=l();if(n&&n.mark){var e=s+t;n.clearMarks(e+"Begin"),n.mark(e+"Begin")}}},n.timeEnd=function(t){if(c.getSampleRum()){var n=l();if(n&&n.mark){var e=s+t,r=n.getEntriesByName(e+"Begin");if(0===r.length)throw new f("Called timeEnd without matching time: "+t);n.clearMarks(e+"End"),n.mark(e+"End");var o=t+"Time",u=n.getEntriesByName(e+"End")[0].startTime-r[0].startTime;a.dispatch(i.SET_PERFORMANCE_MARKS_DATA,{name:o,data:{startTime:Math.round(1e3*r[0].startTime)/1e3,duration:Math.round(1e3*u)/1e3}})}}},n.setMark=function(t,n,e){if(c.getSampleRum()){var r=l();if(r&&r.mark){var o=s+t;r.mark(o),a.dispatch(i.SET_PERFORMANCE_MARKS_DATA,{name:t,data:{startTime:Math.round(1e3*n)/1e3,duration:Math.round(1e3*e)/1e3}})}}},n.now=function(){var t=l();return t?t.now():o.now()}},9682:function(t,n){n.get=function(t,n){try{var e=window.Zone;if(e&&"function"==typeof e.U){var i=window[e.U(t)];if("function"==typeof i)return i}}catch(t){}return n}},3898:function(t,n,e){var i=e(3496);n.getUserAgent=function(){return window.navigator.userAgent},n.getLocationSearch=function(){return window.location.search},n.getNavigatorLanguage=function(){return window.navigator.language||window.navigator.userLanguage},n.getHref=function(){return window.location.href},n.getLocation=function(){return window.location},n.setLocation=function(t){window.location.replace(t)},n.setGlobal=function(t,n){window[t]=n},n.getGlobal=function(t){return window[t]},n.getGlobalByPath=function(t){for(var n=t.split("."),e=window;n.length;)try{e=e[n.shift()]}catch(n){throw i.error("Attempted to access nonexistent property. Path ",t),new Error("Attempted to access nonexistent property. Path "+t)}return e},n.addEventListener=function(){return window.addEventListener.apply(window,arguments)},n.removeEventListener=function(){return window.removeEventListener.apply(window,arguments)},n.isMutationObserverAPISupported=function(){return void 0!==window.MutationObserver},n.alert=function(t){alert(t)},n.setTimeout=function(t,n){return setTimeout((function(){try{t()}catch(t){i.warn("Deferred function threw error:",t)}}),n)},n.setInterval=function(t,n){return setInterval((function(){try{t()}catch(t){i.warn("Polling function threw error:",t)}}),n)}},295:function(t,n){n.create=function(t,n,e,i){return Object.assign({category:"other"},i,{id:t,apiName:n,eventType:e})}},6210:function(t,n,e){var i=e(167),r=e(1756),o=e(525),a=e(3221),u=e(3941),c=e(3496),s=a.get("stores/event_emitter");n.on=function(t){return t.token||(t.token=i.generate()),u.dispatch(r.ADD_EMITTER_HANDLER,t),t.token},n.off=function(t){u.dispatch(r.REMOVE_EMITTER_HANDLER,{token:t})},n.emit=function(t,n,e){s.getHandlers(t,n).forEach((function(i){try{i.handler.call({$di:a},t)}catch(r){!e&&i.emitErrors?(c.error("Error in handler for event:",t,r),o.emitError(r,null,n)):c.warn("Suppressed error in handler for event:",t,r)}}))}},879:function(t,n,e){var i=e(6210);n.on=function(t){return t.publicOnly=!0,i.on(t)},n.off=i.off,n.emit=function(t){i.emit(t)}},3816:function(__unused_webpack_module,exports,__webpack_require__){var createError=__webpack_require__(4254),di=__webpack_require__(3221),Logger=__webpack_require__(3496),CSP_MODE=!1,EXEC_WITH_JQUERY=!0,ExecError=exports.Error=createError("ExecError");exports.apply=function(t,n){n=(n=n||[]).concat(di.get("env/jquery"));try{return t.apply(void 0,n)}catch(e){throw Logger.warn("Error applying function",t,"with args:",n,e),new ExecError(e)}},exports.eval=function(str){try{return str="var $ = optimizely.get('jquery');"+str,eval(str)}catch(t){throw Logger.warn("Error executing JS:",str,t),new ExecError(t)}}},3941:function(t,n,e){var i=e(7283);t.exports=i.create()},5564:function(t,n){n.description=function(t){var n=!!t.name?'"'+t.name+'" ':"",e=function(t){return(t.weightDistributions||[]).map((function(t){return t.entityId}))}(t).join(", ");return n+"(id "+t.id+", campaigns: "+e+")"}},2902:function(t,n,e){var i=e(279).flattenDeep,r=e(1756),o=e(6310),a=e(3221),u=e(3941),c=a.get("stores/global"),s=a.get("stores/session");n.recordLayerDecision=function(t,n,e){return u.dispatch(r.RECORD_LAYER_DECISION,{layerId:t,decision:e,decisionTicket:n,sessionId:s.getSessionId(),activationId:c.getActivationId(),timestamp:o.now(),revision:c.getRevision(),namespace:c.getNamespace(),pageId:n.pageId}),e},n.relatedAudienceIds=function(t){var n={},e=["and","or","not"];
2return(t.experiments||[]).forEach((function(t){i(t.audienceIds).forEach((function(t){e.includes(t)||(n[t]=!0)}))})),Object.keys(n)},n.getActivationTimeout=function(t){var n=t.activation;return n&&null!==n.timeout&&void 0!==n.timeout?n.timeout:2e3},n.description=function(t){return(t.name?'"'+t.name+'" ':"")+"("+t.id+")"},n.createSingle=function(t,n,e){return{id:t,policy:"single_experiment",holdback:0,experiments:[{id:n||"",variations:[{id:e||"",actions:[]}],audienceIds:[]}],integrationStringVersion:1}},n.createLayerState=function(t,n,e,i,r){return void 0===i&&(i=!1),void 0===r&&(r=!1),{layerId:t,decision:{layerId:t,experimentId:n,variationId:e,isLayerHoldback:i||!1,isLayerConcluded:r||!1},decisionTicket:{audienceIds:[]}}},n.getIntegrationTypes=function(t){return Object.keys(Object.keys(t.integrationSettings).reduce((function(t,n){return Number.isNaN(Number(n))||(n="custom"),t[n]=1,t}),{}))}},6036:function(t,n){n.G=function(t,n){if(!n)return 0;for(var e=n.toString().split("."),i=t.toString().split("."),r=0;r<e.length;r++){if(void 0===i[r])return-1;if(isNaN(Number(i[r]))){if(i[r]!==e[r])return-1}else{if(Number(i[r])<Number(e[r]))return-1;if(Number(i[r])>Number(e[r]))return 1}}return 0}},4681:function(t,n,e){var i=e(6388),r=e(3496),o="or",a="not";var u={};function c(t,n){var e;if(Array.isArray(t)){var a,c;"string"==typeof t[0]&&t[0]in u?(a=t[0],c=t.slice(1)):(a=o,c=t),r.groupCollapsed('Condition / Applying operator "'+a+'" with args',i.stringify(c));try{e=u[a](c,n),r.debug("Condition / Result:",e)}finally{r.groupEnd()}return e}return e=n(t),r.debug("Condition / Evaluated:",i.stringify(t),":",e),e}u["and"]=function(t,n){for(var e,i,r=0;r<t.length;r++){if(!1===(e=c(t[r],n)))return!1;void 0===e&&(i=!0)}if(!i)return!0},u[o]=function(t,n){for(var e,i=!1,r=0;r<t.length;r++){if(!0===(e=c(t[r],n)))return!0;void 0===e&&(i=!0)}if(!i)return!1},u[a]=function(t,n){if(1!==t.length)return!1;var e=c(t[0],n);return void 0!==e?!e:void 0},t.exports={evaluate:c}},8409:function(t,n,e){var i=e(4779);function r(t){return(t=(t||"").toLowerCase())in u?t:Object.keys(u).find((function(n){return u[n].includes(t)}))||"unknown"}function o(t,n,e){return n||("unknown"===t?"unknown":e?"mobile":"desktop_laptop")}n.parseUA=function(t){var n=new i(t),e=n.getBrowser(),u=n.getOS(),s=n.getDevice(),f=(u.name||"unknown").toLowerCase(),l=(e.name||"unknown").toLowerCase(),d=function(t){var n=/\b(iPad|iPhone)\d+,\d+/i.exec(t);return n?n[1].toLowerCase():null}(t),v="ipad"===d?"tablet":s.type,h=a(v,l,f);return{browser:{id:r(e.name),version:e.version},platform:{name:f,version:u.version},device:{model:c[s.model]||d||"unknown",type:o(l,v,h),isMobile:h}}};var a=function(t,n,e){if(["mobile","tablet"].includes(t)||["opera mini"].includes(n))return!0;return!!["android","blackberry","ios","windows phone"].includes(e)},u={gc:["chrome","chromium","silk","yandex","maxthon","chrome webview"],edge:["edge"],ie:["internet explorer","iemobile"],ff:["firefox","iceweasel"],opera:["opera","opera mini","opera tablet"],safari:["safari","mobile safari","webkit"],ucbrowser:["uc browser"]},c={iPhone:"iphone",iPad:"ipad"}},9716:function(t){function n(){this.B={}}n.prototype.register=function(t,n){if(1!==arguments.length){if(this.B[t])throw new Error("Module already registered for: "+t);this.B[t]=n}else{var e=this;Object.keys(t).forEach((function(n){var i=t[n];e.register(n,i)}))}},n.prototype.get=function(t){return this.B[t]},n.prototype.getModuleKeys=function(){var t=this.B;return Object.keys(t)},n.prototype.evaluate=function(t){var n=t.length,e=t.slice(0,n-1),i=t[n-1];if("function"!=typeof i)throw new Error("Evaluate must take a function as last element in array");var r=e.map(this.get,this);return i.apply(null,r)},n.prototype.reset=function(){this.B={}},t.exports={create:function(){return new n}}},8564:function(t,n,e){var i=e(279).cloneDeep;function r(t,n,e){this.q=t,this.H=n,this.Y=0,this.K=!1,this.$={},Object.assign(this,e),this.W={},this.initialize&&this.initialize()}r.prototype.X=function(t,n){var e=this.$[t];e&&"function"==typeof e&&e.call(this,n,t)},r.prototype.Z=function(){return i(this.W)},r.prototype.on=function(t,n){this.$[t]=n.bind(this)},r.prototype.observe=function(t){return this.H.tt(this.q,t)},r.prototype.emitChange=function(){this.K=!0,this.Y++},r.prototype.hasChanges=function(){return this.K},r.prototype.resetChange=function(){this.K=!1},r.prototype.getStateId=function(){return this.Y},r.prototype.nt=function(){this.reset&&"function"==typeof this.reset&&this.reset(),this.initialize()},t.exports=r},7283:function(t,n,e){var i=e(8564);function r(t){t=t||{},this.et={},this.it={},this.rt=0,this.ot=[],this.ut=[]}function o(t,n){return function(){var e=t.indexOf(n);-1!==e&&t.splice(e,1)}}r.prototype.registerStores=function(t){Object.keys(t).forEach((function(n){this.et[n]=new i(n,this,t[n])}),this)},r.prototype.getStore=function(t){return this.et[t]},r.prototype.dispatch=function(t,n){this.dispatchId++;var e=this;this.ot.forEach((function(i){i.call(e,t,n)}));var i=this.et;
2Object.keys(i).forEach((function(e){i[e].X(t,n)})),this.ut.forEach((function(i){i.call(e,t,n)})),Object.keys(i).forEach((function(t){var n=i[t];n.hasChanges()&&this.it[t]&&(n.resetChange(),this.it[t].forEach((function(t){t(n)})))}),this)},r.prototype.reset=function(){this.it={};var t=this.et;Object.keys(t).forEach((function(n){t[n].nt()}))},r.prototype.getState=function(){var t={},n=this.et;return Object.keys(n).forEach((function(e){t[e]=n[e].Z()})),t},r.prototype.onPreAction=function(t){var n=this.ot;return n.push(t),o(n,t)},r.prototype.onPostAction=function(t){var n=this.ut;return n.push(t),o(n,t)},r.prototype.tt=function(t,n){return this.it[t]||(this.it[t]=[]),this.it[t].push(n),o(this.it[t],n)},t.exports={create:function(t){return new r(t)}}},6388:function(t,n){n.stringify=function(){var t=arguments;return function(t){var n,e,i=[Array.prototype],r=[];i.forEach((function(t){void 0!==t.toJSON&&(r.push(t.toJSON),delete t.toJSON)}));try{n=t()}catch(t){e=t}finally{r.forEach((function(t,n){i[n].toJSON=t}))}if(e)throw e;return n}((function(){return JSON.stringify.apply(null,t)}))},n.parse=JSON.parse},8681:function(t,n){n.hasMatch=function(t,n,e){var i=null!=e,r=null!=t;switch(n||(r?"exact":"exists")){case"exists":return i;case"exact":return i&&String(e)===t;case"substring":return i&&String(e).indexOf(t)>-1;case"regex":try{return!(!r||!i)&&new RegExp(t).test(String(e))}catch(t){}return!1;case"range":var o=t.split(":"),a=parseFloat(o[0]),u=parseFloat(o[1]),c=parseFloat(e);return c>=a&&c<=u;default:return!1}}},3341:function(t,n,e){t.exports=e(8535)},688:function(t,n,e){var i=e(3496);function r(t,n){this.ct=t,this.st=n}r.prototype.ft=function(t){return[this.st,t].join("$$")},r.prototype.dt=function(t){return t.replace(this.st+"$$","")},r.prototype.setItem=function(t,n){e(6494).time("localstorageSetItem");try{this.ct.setItem(this.ft(t),n)}catch(n){i.warn("Failed to save",t,"to localStorage:",n)}e(6494).timeEnd("localstorageSetItem")},r.prototype.removeItem=function(t){this.ct.removeItem(this.ft(t))},r.prototype.getItem=function(t){e(6494).time("localstorageGetItem");var n=null;try{n=this.ct.getItem(this.ft(t))}catch(t){}return e(6494).timeEnd("localstorageGetItem"),n},r.prototype.keys=function(){var t=this;return Object.keys(this.ct).filter((function(n){return n.includes(t.st)})).map((function(n){return t.dt(n)}))},r.prototype.allKeys=function(){return Object.keys(this.ct)},r.prototype.allValues=function(){return Object.values(this.ct)},t.exports={create:function(t,n){return new r(t,n)},mockStorage:{keys:function(){},getItem:function(t){},removeItem:function(t){},setItem:function(t,n){}}}},7067:function(t,n,e){var i=e(9751),r=e(6210),o=e(4462);n.createDOMChangedObserver=function(){var t=i.getDocumentElement(),n={type:"viewTrigger",name:"DOMChanged"},e=o.create((function(){r.emit(n,!0)}));o.observe(e,t,{attributes:!0,childList:!0,subtree:!0,characterData:!0})}},7108:function(t,n,e){var i=e(279).isEmpty,r=e(525),o=e(2044),a=e(6388),u=e(7429).DT,c=e(3496),s=e(3341),f=e(4464),l=e(3221).get("stores/pending_events"),d=o.StorageKeys.PENDING_EVENTS,v=!1;n.persistPendingEvents=function(){try{var t=l.getEventsString();u.setItem(d,t),e(9232).setItem(d,t)}catch(t){c.warn("PendingEvents / Unable to set localStorage key, error was: ",t),r.emitInternalError(t)}},n.flushPersistPendingEvents=function(){v&&(v=!1,n.persistPendingEvents())},n.schedulePersistPendingEvents=function(){v||(v=!0,s.resolve().then(n.flushPersistPendingEvents).catch((function(t){c.warn("PendingEvents / Unable to persist pending events, error was: ",t)})))},n.getPendingEvents=function(){try{return a.parse(u.getItem(d))}catch(t){return null}},n.retryPendingEvents=function(t){Object.keys(t).forEach((function(n){var e=t[n];f.retryableRequest(e.data,n,e.retryCount)})),i(t)||c.log("Retried pending events: ",t)}},5213:function(t,n,e){var i=e(279).cloneDeep,r=e(1756),o=e(3221),a=e(2044),u=e(6210),c=e(3941);n.registerApiModule=function(t,n){Array.isArray(n)&&(n=o.evaluate(n)),c.dispatch(r.REGISTER_PLUGIN,{type:a.PluginTypes.apiModules,name:t,plugin:n})},n.registerDependency=function(t,n){o.get(t)||o.register(t,n)},n.registerVisitorProfileProvider=function(t){c.dispatch(r.REGIST
2ER_PLUGIN,{type:a.PluginTypes.visitorProfileProviders,name:t.provides,plugin:t})},n.registerViewProvider=function(t){c.dispatch(r.REGISTER_PLUGIN,{type:a.PluginTypes.viewProviders,name:t.provides,plugin:t})},n.registerAudienceMatcher=function(t,n){c.dispatch(r.REGISTER_PLUGIN,{type:a.PluginTypes.audienceMatchers,name:t,plugin:n})},n.registerViewMatcher=function(t,n){c.dispatch(r.REGISTER_PLUGIN,{type:a.PluginTypes.viewMatchers,name:t,plugin:n})},n.registerAnalyticsTracker=function(t,n){c.dispatch(r.REGISTER_PLUGIN,{type:a.PluginTypes.analyticsTrackers,name:t,plugin:n})},n.registerViewTagLocator=function(t,n){c.dispatch(r.REGISTER_PLUGIN,{type:a.PluginTypes.viewTagLocators,name:t,plugin:n})},n.registerAudiencePlugin=function(t){t.dependencies&&Object.keys(t.dependencies).forEach((function(e){var i=t.dependencies[e];n.registerDependency(e,i)}));var e,r,a="vendor."+t.vendor;e="string"==typeof t.provider?o.get(t.provider)(t.vendor):"function"==typeof t.provider?t.provider(t.vendor):i(t.provider),n.registerVisitorProfileProvider(Object.assign(e,{provides:a})),r="string"==typeof t.matcher?o.get(t.matcher):t.matcher;var u={fieldsNeeded:[a],match:function(t,n){return r(t[a],n)}};n.registerAudienceMatcher(a,u)};var s={};n.registerWidget=function(t){Array.isArray(t)&&(t=o.evaluate(t));var n=t.widgetId,e=s[n];e&&(u.off(e.showToken),u.off(e.hideToken));var i=u.on({filter:{type:"showWidget",name:n},handler:t.showFn}),r=u.on({filter:{type:"hideWidget",name:n},handler:t.hideFn});return s[n]={showToken:i,hideToken:r},s[n]},n.hasWidget=function(t){return Object.prototype.hasOwnProperty.call(s,t)},n.registerChangeApplier=function(t,n){c.dispatch(r.REGISTER_PLUGIN,{type:a.PluginTypes.changeAppliers,name:t,plugin:n})},n.registerDecider=function(t,n){c.dispatch(r.REGISTER_PLUGIN,{type:a.PluginTypes.deciders,name:t,plugin:n})},n.registerEventImplementation=function(t,n){c.dispatch(r.REGISTER_PLUGIN,{type:a.PluginTypes.eventImplementations,name:t,plugin:n})},n.registerViewTrigger=function(t,n){c.dispatch(r.REGISTER_PLUGIN,{type:a.PluginTypes.viewTriggers,name:t,plugin:n})}},6555:function(t,n,e){var i=e(279),r=i.isEmpty,o=i.mapValues,a=e(3221),u=":",c="holdback",s="treatment",f="",l=e(3496),d=e(1386);function v(t){return t.replace(/[^a-zA-Z0-9\.\~\!\*\(\)\']+/g,"_")}n.formatNamesAndIdsForAnalytics=function(t,n,e,i){var u={layer:t.name||f,experiment:n.name||f,variation:e.name||f};i&&(u=o(u,v));var c,s=!t.integrationStringVersion||1===t.integrationStringVersion;if(u.experiment===f&&s)if(c=n.audienceIds,r(c)||-1===["and","or","not"].indexOf(c[0])){var l=a.get("stores/audience_data");u.experiment=function(t,n){return r(n)?"everyone_else":n.reduce((function(n,e){var i=t.get(e);return i?n+v(i.name?i.name:i.id)+",":n}),"").slice(0,-1)}(l,n.audienceIds)}else u.experiment="Exp";return{names:u,idStrings:{layer:"("+v(t.id)+")",experiment:"("+v(n.id)+")",variation:"("+v(e.id)+")"}}},n.combineAndTruncateIdAndName=function(t,n,e){var i=e-n.length;if(i<0&&(l.warn("maxLength must be at least long enough to fit the entity ID, which is length"+n.length+". Defaulting to only use entity ID as name."),t=f),t===f)return n;if(t.length>i){var r=Math.min(t.length,i);return(t=t.substring(0,r))+n}return t+" "+n},n.generateAnalyticsString=function(t,e,i,r,o,a){return t.integrationStringVersion&&2===t.integrationStringVersion?function(t,e,i,r,o,a){if(d.isSingleExperimentPolicy(t.policy)&&r)return;var s=!d.isSingleExperimentPolicy(t.policy)&&r,f=n.formatNamesAndIdsForAnalytics(t,e,i,a),l=[f.names.experiment,f.names.variation],v=[f.idStrings.experiment,f.idStrings.variation];d.isSingleExperimentPolicy(t.policy)||(l.unshift(f.names.layer),v.unshift(f.idStrings.layer));var h=v.reduce((function(t,n){return t+n.length}),0),p=l.length-1+(s?1:0),g=h+1*p;if(s&&(g+=8),g>o)throw new Error("The analytics string size is too low to send the entity IDs.");for(var m=o-g,b=l.length,y=[],_=l.length-1;_>=0;_--){var w=l[_],E=Math.min(w.length,Math.floor(m/b));m-=E,b--,y.unshift(w.substring(0,E))}var I=y.map((function(t,n){return t+v[n]}));return s&&I.push(c),I.join(u)}
2(t,e,i,r,o,a):function(t,e,i,r,o,a){var f=r?c:s,l=3,v=n.formatNamesAndIdsForAnalytics(t,e,i,a),h=v.names,p=v.idStrings,g=Object.values(p).reduce((function(t,n){return t+n.length}),0);if(g+l+f.length>o)throw new Error("The analytics string size is too low to send the campaign, experiment, and variation IDs.");var m=o-g-l-f.length,b={};b.variation=Math.min(h.variation.length,Math.floor(m/3)),m-=b.variation,b.experiment=Math.min(h.experiment.length,Math.floor(m/2)),m-=b.experiment,b.layer=m;var y={};Object.keys(h).forEach((function(t){var n=h[t];y[t]=n.substring(0,b[t])}));var _=[];return d.isSingleExperimentPolicy(t.policy)||_.push(y.layer+p.layer),(_=_.concat([y.experiment+p.experiment,y.variation+p.variation,f])).join(u)}(t,e,i,r,o,a)}},2034:function(t){t.exports=function(t,n){t=function(t){var n=t.split("?");if(n[1]){var i=n[1].split("#"),r=i[0],o=i.slice(1).join("#"),a=r.split("&"),u=[];return a.forEach((function(t){0!==t.indexOf(e)&&u.push(t)})),n[1]="",u.length>0&&(n[1]="?"+u.join("&")),o&&(n[1]+="#"+o),n.join("")}return t}(t);var i=n.value;switch(n.match){case"exact":return(t=o(t))===o(i);case"regex":try{return Boolean(t.match(i))}catch(t){}return!1;case"simple":return(t=r(t))===(i=r(i));case"substring":return t=o(t,!0),i=o(i,!0),-1!==t.indexOf(i);default:return!1}};var n=["www."],e="optimizely_",i=["https?://.*?.?optimizelyedit.(com|test)/","https?://.*.?optimizelypreview.(com|test)/","https?://(edit|preview)(-hrd|-devel)?.optimizely.(com|test)/","https?://.*?.?optimizelyedit(-hrd)?.appspot.com/","https?://"];function r(t){return o(function(t){var n=t.indexOf("?");return-1!==n&&(t=t.substring(0,n)),-1!==(n=t.indexOf("#"))&&(t=t.substring(0,n)),t}(t))}function o(t,e){t=(t=t.replace("/?","?")).toLowerCase().replace(/[/&?]+$/,"");var r=i.slice(0);e||(r=r.concat(n));for(var o=r.length,a=0;a<o;a++){var u=r[a],c=new RegExp("^"+u);t=t.replace(c,"")}return t}},2682:function(t,n,e){var i=e(2103).getFieldValue,r=e(8681);t.exports=function(t,n){var e=i(t,n.name.split("."));return Array.isArray(e)?e.some((function(t){return r.hasMatch(n.value,n.match,t)})):r.hasMatch(n.value,n.match,e)}},815:function(t,n,e){var i=e(8548),r=e(7743),o="cdn3";n.getIP=function(){return i.makeAsyncRequest(o,u).then((function(t){return t.ip}))},n.getIPDerivedGeolocation=function(){return i.makeAsyncRequest(o,u).then((function(t){return t.location}))};var a="//cdn3.optimizely.com/js/geo4.js";function u(){r.addScriptAsync(a,void 0,"script")}},608:function(t,n,e){var i=e(1756),r=e(1423),o=e(3496),a=e(279).isEmpty;t.exports={initialize:function(){this.W={actions:{},actionState:{}},this.on(i.DATA_LOADED,this.vt),this.on(i.ACTION_EXECUTED,this.ht),this.on(i.SET_CHANGE_APPLIER,this.bt),this.on(i.REMOVE_ACTION_STATE,this.yt)},vt:function(t){var n=this;a(t.data.layers)||(t.data.layers.forEach((function(t){var e;if(t.changes){var i="layerId:"+t.id;e={id:i,layerId:t.id,changeSet:t.changes,type:"layer"},r.deepFreeze(e),n.W.actions[i]=e}(t.experiments||[]).forEach((function(i){if(i.changes){var o="experimentId:"+i.id;e={id:o,layerId:t.id,experimentId:i.id,changeSet:i.changes,type:"experiment"},r.deepFreeze(e),n.W.actions[o]=e}(i.variations||[]).forEach((function(o){(o.actions||[]).forEach((function(a){var u=a.pageId||a.viewId,c=i.id+":"+o.id+":"+u;e={id:c,layerId:t.id,experimentId:i.id,variationId:o.id,pageId:u,changeSet:a.changes,type:"variation"},r.deepFreeze(e),n.W.actions[c]=e}))}))}))})),this.emitChange())},ht:function(t){var n=t.actionId;void 0===n||this.W.actionState[n]||(this.W.actionState[n]={})},bt:function(t){var n=t.actionId,e=t.changeId;this.W.actionState[n]?this.W.actionState[n][e]=t.changeApplier:o.warn("Action Data / Attempted to set changeApplier for inactive action: ",n)},yt:function(t){delete this.W.actionState[t.actionId]},get:function(t){return r.safeReference(this.W.actions[t])},getActionState:function(t){return r.safeReference(this.W.actionState[t])},getByChangeId:function(t){return Object.values(this.W.actions).find((function(n){return n.changeSet&&n.changeSet.some((function(n){return n.id===t}))}))},getAllActionIdsByPageId:function(t){return Object.values(this.W.actions).filter((function(n){return n.pageId===t})).map((function(t){return t.id}))},getChangeApplier:function(t,n){var e=this.W.actionState[n];if(e)return e[t]}
2,getExperimentVariationActions:function(t,n){return r.safeReference(Object.values(this.W.actions).filter((function(e){return e.experimentId===t&&e.variationId===n})))},getLayerActions:function(t){return r.safeReference(Object.values(this.W.actions).filter((function(n){return n.id==="layerId:"+t})))},getExperimentActions:function(t){return r.safeReference(Object.values(this.W.actions).filter((function(n){return n.id==="experimentId:"+t})))},getAll:function(){return r.safeReference(Object.values(this.W.actions))}}},4033:function(t,n,e){var i=e(1756);t.exports={initialize:function(){this.W={},this.on(i.REGISTER_ASYNC_DEFERRED,this._t),this.on(i.RESOLVE_DEFERRED,this.wt),this.on(i.REJECT_DEFERRED,this.Et)},getRequest:function(t){return this.W[t]},getPromise:function(t){var n=this.getRequest(t);if(n)return n.promise},_t:function(t){this.W[t.source]={promise:t.promise,resolver:t.resolver,rejecter:t.rejecter}},wt:function(t){var n=this.getRequest(t.source);if(!n)throw new Error("No request registered for source: "+t.source);n.resolver(t.resolveWith)},Et:function(t){var n=this.getRequest(t.source);if(!n)throw new Error("No request registered for source: "+t.source);if(!n.rejecter)throw new Error("No rejecter registered for source: "+t.source);n.rejecter(t.rejectWith)}}},9624:function(t,n,e){var i=e(279),r=i.isEmpty,o=i.merge,a=e(1756),u=e(1423);function c(t,n){return n||(n={}),t?(t.forEach((function(t){if("string"!=typeof t){if("object"==typeof t&&null!==t&&!Array.isArray(t)){var e=t.type,i=t.name||"_";n[e]||(n[e]={}),n[e][i]=!0}Array.isArray(t)&&c(t,n)}})),n):n}t.exports={initialize:function(){this.W={audiences:{},featuresNeeded:{}},this.on(a.DATA_LOADED,this.vt)},vt:function(t){if(!r(t.data.audiences)){var n=this;t.data.audiences.forEach((function(t){u.deepFreeze(t),o(n.W.featuresNeeded,c(t.conditions)),n.W.audiences[t.id]=t})),this.emitChange()}},getAll:function(){return u.safeReference(Object.values(this.W.audiences))},getFeaturesNeeded:function(t){return u.safeReference(this.W.featuresNeeded[t]||{})},getAudiencesMap:function(){return u.safeReference(this.W.audiences)},get:function(t){return u.safeReference(this.W.audiences[t])},getAudienceName:function(t){return Object.values(this.W.audiences).find((function(n){return n.id===t})).name||"Aud "+t}}},7542:function(t,n,e){var i=e(279).isEmpty,r=e(1756),o=e(1423);t.exports={initialize:function(){this.W={},this.on(r.ADD_CHANGE,this.It),this.on(r.DATA_LOADED,this.vt)},getChange:function(t){return this.W[t]},vt:function(t){if(!i(t.data.changes)){var n=this;
2t.data.changes.forEach((function(t){n.It(t)}))}},It:function(t){o.deepFreeze(t),this.W[t.id]=t,this.emitChange()}}},2735:function(t,n,e){var i=e(279).cloneDeep,r=e(1756),o=e(2044);t.exports={initialize:function(){this.W={};var t=this;Object.keys(o.Lifecycle).forEach((function(n){var e=o.Lifecycle[n];t.W[e]=[]})),this.on(r.ADD_CLEANUP_FN,this.At),this.on(r.CLEAR_CLEANUP_FN,this.Tt)},getCleanupFns:function(t){return i(this.W[t])},At:function(t){this.W[t.lifecycle].push(t.cleanupFn),this.emitChange()},Tt:function(t){var n=this.W[t.lifecycle];if(t.cleanupFn){var e=n.indexOf(t.cleanupFn);e>-1&&(n.splice(e,1),this.emitChange())}else this.W[t.lifecycle]=[],this.emitChange()}}},3970:function(t,n,e){var i=e(1756),r=e(6387);t.exports={initialize:function(){this.W={name:r.NAME,version:r.VERSION},this.on(i.SET_CLIENT_NAME,this.St),this.on(i.SET_CLIENT_VERSION,this.Ot)},getClientName:function(){return this.W.name},getClientVersion:function(){return this.W.version},St:function(t){t&&(this.W.name=t),this.emitChange()},Ot:function(t){t&&(this.W.version=t),this.emitChange()}}},7032:function(t,n,e){var i=e(1756);t.exports={initialize:function(){this.W={currentDomain:null,defaultAgeSeconds:15552e3,autoRefresh:true},this.on(i.SET_COOKIE_DOMAIN,this.kt),this.on(i.SET_COOKIE_AGE,this.Rt),this.on(i.SET_COOKIE_AUTO_REFRESH,this.Ct)},getCurrentDomain:function(){return this.W.currentDomain},getDefaultAgeInSeconds:function(){return this.W.defaultAgeSeconds},getAutoRefresh:function(){return this.W.autoRefresh},kt:function(t){this.W.currentDomain=t,this.emitChange()},Rt:function(t){this.W.defaultAgeSeconds=t,this.emitChange()},Ct:function(t){this.W.autoRefresh=t,this.emitChange()}}},890:function(t,n,e){var i=e(1756),r=e(1423),o=e(279).isEmpty;t.exports={initialize:function(){this.W={},this.on(i.DATA_LOADED,this.vt)},vt:function(t){if(!o(t.data.dimensions)){var n=this;t.data.dimensions.forEach((function(t){r.deepFreeze(t),n.W[t.id]=t})),this.emitChange()}}
2,getAll:function(){return r.safeReference(Object.values(this.W))},getById:function(t){return r.safeReference(this.W[t])},getByApiName:function(t){var n=Object.values(this.W);return r.safeReference(n.find((function(n){return n.apiName===t})))}}},3650:function(t,n,e){var i=e(279),r=i.cloneDeep,o=i.isEmpty,a=e(1756);t.exports={initialize:function(){this.W={disabled:!1,forceAudienceIds:[],forceVariationIds:[],alreadyInitialized:!1,mutationObserverAPISupported:!1,isEditor:!1,isPreview:!1,isLegacyPreview:!1,isSlave:!1,previewLayerIds:[],projectToken:null,shouldOptOut:!1,trackingDisabled:!1,isRunningInV2Editor:!1,isRunningInDesktopApp:!1,forceTracking:!1,requestedEmbeddedEditor:!1},this.on(a.LOAD_DIRECTIVE,this.Dt)},getAll:function(){return r(this.W)},conflictInObservingChanges:function(){return!this.W.mutationObserverAPISupported},isDisabled:function(){return this.W.disabled},isEditor:function(){return this.W.isEditor},clientHasAlreadyInitialized:function(){return this.W.alreadyInitialized},getForceAudienceIds:function(){return this.W.forceAudienceIds},getForceVariationIds:function(){return this.W.forceVariationIds},getPreviewLayerIds:function(){return this.W.previewLayerIds},getProjectToken:function(){return this.W.projectToken},getForceTracking:function(){return this.W.forceTracking},shouldActivate:function(){return!this.W.isEditor&&!this.isDisabled()},shouldBootstrapDataForPreview:function(){return this.W.isPreview},shouldBootstrapDataForEditor:function(){return this.W.isEditor},shouldInitialize:function(){return!(this.shouldLoadPreview()||this.isDisabled()||this.getProjectToken())},shouldLoadPreview:function(){return!(this.W.isPreview||this.W.isLegacyPreview||!this.getProjectToken()||this.W.isEditor)},shouldLoadEmbeddedEditor:function(){return this.W.requestedEmbeddedEditor&&!this.W.isEditor},shouldBailForDesktopApp:function(){return!this.W.isEditor&&this.W.isRunningInDesktopApp},shouldLoadInnie:function(){return!this.W.isSlave&&!this.W.isEditor&&this.W.isRunningInV2Editor},shouldObserveChangesIndefinitely:function(){return this.W.mutationObserverAPISupported},shouldObserveChangesUntilTimeout:function(){return!this.shouldObserveChangesIndefinitely()},shouldOptOut:function(){return this.W.shouldOptOut},shouldSendTrackingData:function(){return!this.W.trackingDisabled&&(!!this.W.forceTracking||!this.W.isPreview&&o(this.getForceVariationIds())&&o(this.getForceAudienceIds()))},isSlave:function(){return this.W.isSlave},isRunningInDesktopApp:function(){return this.W.isRunningInDesktopApp},isRunningInV2Editor:function(){return this.W.isRunningInV2Editor},Dt:function(t){Object.assign(this.W,t),this.emitChange()}}},5846:function(t,n,e){var i=e(1756),r=e(1423),o=e(279).isEmpty;t.exports={initialize:function(){this.W={},this.on(i.DATA_LOADED,this.vt)}
2,getAll:function(){return r.safeReference(Object.values(this.W))},getEventsMap:function(){return r.safeReference(this.W)},get:function(t){return r.safeReference(this.W[t])},getByApiName:function(t){var n=Object.values(this.W);return r.safeReference(n.find((function(n){return n.apiName===t})))},getByPageId:function(t){var n=Object.values(this.W);return r.safeReference(n.filter((function(n){return n.pageId===t})))},vt:function(t){if(!o(t.data.events)){var n=this;t.data.events.forEach((function(t){t.pageId||(t.pageId=t.viewId),r.deepFreeze(t),n.W[t.id]=t})),this.emitChange()}}}},2910:function(t,n,e){var i=e(1756);function r(t){var n=[];return t&&"object"==typeof t&&null!==t?(t.type&&n.push(t.type),n.push("|"),t.type&&t.name&&n.push(t.name),n.join("")):"|"}t.exports={initialize:function(){this.W={handlers:{}},this.on(i.ADD_EMITTER_HANDLER,this.Nt),this.on(i.REMOVE_EMITTER_HANDLER,this.xt)},getHandlers:function(t,n){var e=[null,{type:t.type},{type:t.type,name:t.name}],i=[],o=this;return e.forEach((function(t){var n=r(t),e=o.W.handlers[n];e&&(i=i.concat(e))})),n&&(i=i.filter((function(t){return!t.publicOnly}))),i},Nt:function(t){var n=r(t.filter);this.W.handlers[n]||(this.W.handlers[n]=[]),this.W.handlers[n].push({handler:t.handler,token:t.token,publicOnly:!!t.publicOnly,emitErrors:!!t.emitErrors}),this.emitChange()},xt:function(t){var n=!1,e=t.token;Object.keys(this.W.handlers).forEach(function(t){var i=this.W.handlers[t],r=i.filter((function(t){return t.token!==e}));r.length!==i.length&&(n=!0,this.W.handlers[t]=r)}.bind(this)),n&&this.emitChange()}}},8808:function(t,n,e){var i=e(1756),r=e(1423);t.exports={initialize:function(){this.W={holdback:0,isGlobalHoldback:null,listTargetingKeys:[],revision:null,projectId:null,accountId:null,namespace:null,activationId:null,activationTimestamp:null,dcpServiceId:null,dcpKeyfieldLocators:[],recommenderServices:[],anonymizeIP:null,projectJS:null,snippetId:null,plugins:[],domContentLoaded:!1,experimental:{},ga4UserId:null,trafficExclusion:null},this.on(i.DATA_LOADED,this.jt),this.on(i.ACTIVATE,this.Pt),this.on(i.RECORD_GLOBAL_DECISION,this.Mt),this.on(i.SET_DOMCONTENTLOADED,this.Lt),this.on(i.SET_GA4_USER_ID,this.Vt)},getRevision:function(){return this.W.revision},getGlobalHoldbackThreshold:function(){return this.W.holdback},getProjectId:function(){return this.W.projectId},getSnippetId:function(){return this.W.snippetId},getAccountId:function(){return this.W.accountId},getNamespace:function(){return this.W.namespace},getActivationId:function(){return this.W.activationId},getActivationTimestamp:function(){return this.W.activationTimestamp},getAnonymizeIP:function(){return this.W.anonymizeIP},getTrafficExclusion:function(){return this.W.trafficExclusion},isGlobalHoldback:function(){return!!this.W.isGlobalHoldback},getListTargetingKeys:function(){return this.W.listTargetingKeys.slice()},getDCPServiceId:function(){return this.W.dcpServiceId},getDCPKeyfieldLocators:function(){return this.W.dcpKeyfieldLocators},getRecommenderServices:function(){return this.W.recommenderServices},getProjectJS:function(){return this.W.projectJS},getPlugins:function(){return this.W.plugins}
2,getExperimental:function(){return r.safeReference(this.W.experimental)},getGA4UserId:function(){return this.W.ga4UserId},domContentLoadedHasFired:function(){return this.W.domContentLoaded},Pt:function(t){this.W.activationId=t.activationId,this.W.activationTimestamp=t.activationTimestamp,this.W.isGlobalHoldback=null},Mt:function(t){var n=t.isGlobalHoldback;if(null!==this.W.isGlobalHoldback&&this.W.isGlobalHoldback!==n)throw new Error("Attempted to change already set global holdback!");this.W.isGlobalHoldback=n,this.emitChange()},Vt:function(t){var n=t.ga4UserId;if(!n)throw new Error('Argument "payload.ga4UserId" is missing');this.W.ga4UserId=n,this.emitChange()},jt:function(t){var n=t.data,e={};if(["holdback","accountId","projectId","snippetId","namespace","revision","listTargetingKeys","dcpServiceId","dcpKeyfieldLocators","recommenderServices","anonymizeIP","plugins","projectJS","experimental","ga4UserId","trafficExclusion"].forEach((function(t){t in n&&(e[t]=n[t])})),0!==Object.keys(e).length){Object.assign(this.W,{listTargetingKeys:[],dcpServiceId:null,dcpKeyfieldLocators:[]},e),this.emitChange()}},Lt:function(){this.W.domContentLoaded=!0,this.emitChange()}}},2091:function(t,n,e){var i=e(1756),r=e(1423),o=e(279).isEmpty;t.exports={initialize:function(){this.W={},this.on(i.DATA_LOADED,this.vt)},vt:function(t){if(!o(t.data.groups)){var n=this;t.data.groups.forEach((function(t){r.deepFreeze(t),n.W[t.id]=t})),this.emitChange()}}
2,getAll:function(){return r.safeReference(Object.values(this.W))},getGroupsMap:function(){return r.safeReference(this.W)},get:function(t){return r.safeReference(this.W[t])}}},9607:function(t,n,e){var i=e(1756),r=e(3898);t.exports={initialize:function(){this.W={originalPushState:null,originalReplaceState:null},this.on(i.ENSURE_ORIGINAL_PUSHSTATE,this.Ut),this.on(i.ENSURE_ORIGINAL_REPLACESTATE,this.zt)},getOriginalPushState:function(){return this.W.originalPushState},getOriginalReplaceState:function(){return this.W.originalReplaceState},Ut:function(){this.W.originalPushState||(this.W.originalPushState=r.getGlobal("history").pushState.bind(r.getGlobal("history")))},zt:function(){this.W.originalReplaceState||(this.W.originalReplaceState=r.getGlobal("history").replaceState.bind(r.getGlobal("history")))}}},6585:function(t,n,e){var i=e(279),r=i.cloneDeep,o=i.isEmpty,a=e(1756);t.exports={initialize:function(){this.W={},this.on(a.DATA_LOADED,this.vt),this.on(a.SET_INTEGRATION_SETTINGS,this.Bt)},vt:function(t){if(!o(t.data.integrationSettings)){var n=this;t.data.integrationSettings.forEach((function(t){n.W[t.id]=t})),this.emitChange()}},Bt:function(t){var n=this.W[t.id];n?Object.assign(n,t):this.W[t.id]=t},getAll:function(){return r(Object.values(this.W))},get:function(t){return r(this.W[t])},getReference:function(t){return this.W[t]}}},6946:function(t,n,e){var i=e(279).cloneDeep,r=e(1756),o=e(3496),a="*";t.exports={initialize:function(){this.W={},this.on(r.LOAD_PERSISTED_LAYER_STATES,this.Ft),this.on(r.RECORD_LAYER_DECISION,this.Gt),this.on(r.RECORD_LAYER_DECISION_EVENT_ID,this.qt)},getLayerState:function(t,n){if(this.W[t]){var e=this.W[t];if(Object.keys(e).length>1&&!n)throw new Error("View Id must be specified when more than one layerState for layer.");return i(n?e[n]:e[a])}},getLayerStates:function(t){var n=this,e=function(e){var o=n.W[e];Object.keys(o).forEach((function(n){var e=o[n];(void 0===t||e.namespace===t)&&r.push(i(e))}))},r=[];for(var o in this.W)e(o);return r},getLayerStatesForAnalytics:function(){var t=this,n=function(n){var i=t.W[n];
2Object.keys(i).forEach((function(t){var n=i[t];e.push({layerId:n.layerId,decision:n.decision,decisionEventId:n.decisionEventId})}))},e=[];for(var i in this.W)n(i);return e},Ft:function(t){t.merge||(this.W={});var n=this;t.layerStates.forEach((function(t){var e=t.layerId;t.pageId||(t.pageId=t.viewId);var i=t.pageId||a,r=n.W[e];if(void 0===r)n.W[e]={},n.W[e][i]=t;else{var o=r[i];(!o||t.decisionTimestamp>(o.decisionTimestamp||0))&&(n.W[e][i]=t)}})),this.emitChange()},Gt:function(t){var n={layerId:t.layerId,revision:t.revision,namespace:t.namespace,pageId:t.pageId,decisionTicket:t.decisionTicket,decision:t.decision,decisionActivationId:t.activationId,decisionTimestamp:t.timestamp,decisionEventId:null},e=this.W[t.layerId]||{};t.pageId?(delete e[a],e[t.pageId]=n):(e={})[a]=n,this.W[t.layerId]=e,this.emitChange()},qt:function(t){var n=t.layerId,e=t.pageId||a;this.W[n]?this.W[n][e]?(this.W[n][e].decisionEventId=t.decisionId,this.emitChange()):o.warn("Not recording decision event: Layer state not found for view",e):o.warn("Not recording decision event: Campaign not registered",n)}}},3077:function(t,n,e){var i=e(1756),r=e(1423),o=e(1386),a=e(279).isEmpty;t.exports={initialize:function(){this.W={layers:{},experiments:{},variations:{}},this.on(i.DATA_LOADED,this.vt)},vt:function(t){if(!a(t.data.layers)){var n=this;(t.data.layers||[]).forEach((function(t){(t.experiments||[]).forEach((function(e){t.pageIds||(t.pageIds=t.viewIds),e.campaignName||o.isSingleExperimentPolicy(t.policy)?o.isSingleExperimentPolicy(t.policy)&&t.groupId&&(e.groupId=t.groupId):e.campaignName=t.name,(e.variations||[]).forEach((function(t){(t.actions||[]).forEach((function(t){t.pageId||(t.pageId=t.viewId)})),n.W.variations[t.id]=t})),n.W.experiments[e.id]=e})),r.deepFreeze(t),n.W.layers[t.id]=t})),this.emitChange()}}
2,getAll:function(){return r.safeReference(Object.values(this.W.layers))},getCampaignsMap:function(){return r.safeReference(this.W.layers)},getExperimentsMap:function(){return r.safeReference(this.W.experiments)},getVariationsMap:function(){return r.safeReference(this.W.variations)},getCount:function(){return Object.keys(this.W.layers).length},getAllByPageIds:function(t){var n=this.W.layers;return r.safeReference(Object.keys(n).reduce((function(e,i){var r=n[i];return t.some((function(t){return(r.pageIds||[]).includes(t)}))&&e.push(r),e}),[]))},get:function(t){return r.safeReference(this.W.layers[t])},getLayerByExperimentId:function(t){var n,e=this.W.layers;return Object.keys(e).some((function(i){var r=e[i];return!!(r.experiments||[]).find((function(n){return n.id===t}))&&(n=r,!0)})),r.safeReference(n)},getExperimentByVariationId:function(t){var n,e=this.W.layers;return Object.keys(e).some((function(i){return(e[i].experiments||[]).some((function(e){return(e.variations||[]).find((function(n){return n.id===t}))&&(n=e),!!n}))})),r.safeReference(n)}}},9325:function(t,n,e){var i=e(1756);t.exports={initialize:function(){this.W={logs:[]},this.on(i.LOG,this.Ht)},getLogs:function(){return this.W.logs},Ht:function(t){this.W.logs.push(t),this.emitChange()},Z:function(){return this.W.logs.slice()}}},3958:function(t,n,e){var i=e(1756),r=e(1423);t.exports={initialize:function(){this.W={data:null,hasTracked:null},this.on(i.LOAD_REDIRECT_DATA,this.Yt),this.on(i.REGISTER_TRACKED_REDIRECT_DATA,this.Kt)}
2,get:function(){return r.safeReference(this.W.data)},hasTracked:function(){return this.W.hasTracked},Yt:function(t){r.deepFreeze(t),this.W.data=t,this.W.hasTracked=!1,this.emitChange()},Kt:function(){this.W.hasTracked=!0}}},2446:function(t,n,e){var i=e(1756),r=e(6388);t.exports={initialize:function(){this.W={},this.on(i.SET_PENDING_EVENT,this.$t),this.on(i.REMOVE_PENDING_EVENT,this.Wt),this.on(i.LOAD_PENDING_EVENTS,this.Xt)},getEvents:function(){return this.W},getEventsString:function(){return r.stringify(this.W)},$t:function(t){Object.keys(this.W).length>=1e3&&this.Qt();var n=t.id,e=t.retryCount;this.W[n]&&this.W[n].retryCount===e||(this.W[n]={id:n,timeStamp:t.timeStamp,data:t.data,retryCount:e},this.emitChange())},Wt:function(t){delete this.W[t.id],this.emitChange()},Xt:function(t){this.W=t.events,this.Qt(),this.emitChange()},Qt:function(){for(var t=Object.values(this.W).sort((function(t,n){return t.timeStamp-n.timeStamp})),n=0;n<=t.length-1e3;n++)delete this.W[t[n].id];this.emitChange()}}},1663:function(t,n,e){var i=e(1756);t.exports={initialize:function(){this.W={layerId:null},this.on(i.ANNOUNCE_PENDING_REDIRECT,this.Yt)},isExpectingRedirect:function(){return"string"==typeof this.W.layerId},getLayerId:function(){return this.W.layerId},Yt:function(t){this.isExpectingRedirect()||(this.W.layerId=t.layerId,this.emitChange())}}},9969:function(t,n,e){var i=e(279).mapValues,r=e(1756),o=e(2044);t.exports={initialize:function(){this.W={},this.W[o.PerformanceData.performance_marks]={},this.on(r.SET_PERFORMANCE_MARKS_DATA,this.Jt)},Jt:function(t){void 0===this.W[o.PerformanceData.performance_marks][t.name]&&(this.W[o.PerformanceData.performance_marks][t.name]=[]),this.W[o.PerformanceData.performance_marks][t.name].push(t.data),this.emitChange()},getMarks:function(){return i(this.W[o.PerformanceData.performance_marks],(function(t){return t.map((function(t){return[t.startTime,t.duration]}))}))},getDurationsFor:function(t){var n=this;return t.reduce((function(t,e){var i=n.W[o.PerformanceData.performance_marks][e];return i&&(t[e]=Math.round(i.reduce((function(t,n){return t+n.duration}),0))),t}),{})}}},7631:function(t,n,e){var i=e(279).mapValues,r=e(1756),o=e(2044),a=e(3496);t.exports={initialize:function(){this.W=i(o.PluginTypes,(function(){return{}})),this.on(r.REGISTER_PLUGIN,this.Zt)},Zt:function(t){var n=t.type,e=t.name,i=t.plugin;if(!n||!e)throw new Error("Missing information needed to register plugins: "+n+":"+e);if(!this.W[n])throw new Error("Invalid plugin type specified: "+n);this.W[n][e]=i,a.debug("Plugin Store: Registering Plugin :",t)},getAllPlugins:function(t){if(!t)return this.W;if(this.W[t])return this.W[t];throw new Error("Invalid plugin type: "+t)},getPlugin:function(t,n){if(!n||!t)throw new Error("Missing plugin parameters");return this.getAllPlugins(t)[n]||null}}},6631:function(t,n,e){var i=e(1756),r=e(2103);t.exports={initialize:function(){this.W={},this.on(i.SET_VISITOR_ATTRIBUTE_PENDING,this.tn)},getPendingAttributeValue:function(t){return t=Array.isArray(t)?t.concat("pending"):[t,"pending"],r.getFieldValue(this.W,t)},tn:function(t){r.setFieldValue(this.W,t.key,{pending:t.pending}),this.emitChange()}}},5787:function(t,n,e){var i=e(279),r=i.cloneDeep,o=i.isEmpty,a=i.merge,u=e(1756);t.exports={initialize:function(){this.W={inRumSample:!1,id:null,src:null,RumHost:null,data:{extras:{}},apis:{},DOMObservation:{},featuresNeeded:{}},this.on(u.SET_RUM_DATA,this.nn),this.on(u.RECORD_API_USAGE,this.en),this.on(u.INITIALIZE_CHANGE_METRICS,this.rn),this.on(u.RECORD_ACTIVATION_TYPE_USAGE,this.an),this.on(u.RECORD_AUDIENCE_USAGE,this.un),this.on(u.RECORD_CHANGE_MACROTASK_RATE,this.cn),this.on(u.RECORD_CHANGE_OVERHEATED,this.sn),this.on(u.RECORD_CHANGE_TYPE_USAGE,this.fn),this.on(u.RECORD_DOM_OBSERVATION_OCCURENCE,this.ln),this.on(u.RECORD_INTEGRATION_USAGE,this.dn),this.on(u.RECORD_LAYER_FEATURE_USAGE,this.vn),this.on(u.RECORD_LAYER_POLICY_USAGE,this.hn),this.on(u.RECORD_VIEW_FEATURE_USAGE,this.pn),this.on(u.RECORD_VIEWS_INITIALLY_ACTIVATED_COUNT,this.gn),this.on(u.RECORD_VISITOR_ID_LOCATOR_USAGE,this.mn),this.on(u.RECORD_VISITOR_ID_ERROR,this.bn),this.on(u.RECORD_STICKY_BUCKETING_FEATURE,this.yn)},nn:function(t){a(this.W,t),this.emitChange()},en:function(t){this.W.apis[t.methodName]||(this.W.apis[t.methodName]=0),this.W.apis[t.methodName]++,this.emitChange()},rn:function(){void 0===this.W.data.extras.changeMacrotaskRate&&(this.W.data.extras.changeMacrotaskRate=0),void 0===this.W.data.extras.numOverheatedChanges&&(this.W.data.extras.numOverheatedChanges=0)},cn:function(t){void 0===this.W.data.extras.changeMacrotaskRate&&(this.W.data.extras.changeMacrotaskRate=0),t.changeMacrotaskRate>this.W.data.extras.changeMacrotaskRate&&(this.W.data.extras.changeMacrotaskRate=t.changeMacrotaskRate),this.emitChange()},sn:function(){void 0===this.W.data.extras.numOverheatedChanges&&(this.W.data.extras.numOverheatedChanges=0),this.W.data.extras.numOverheatedChanges++,this.emitChange()},ln:function(t){this.W.DOMObservation[t.counterName]||(this.W.DOMObservation[t.counterName]=0),this.W.DOMObservation[t.counterName]++,this.emitChange()},_n:function(t,n,e){void 0===this.W.featuresNeeded[t]&&(this.W.featuresNeeded[t]={});var i=this.W.featuresNeeded[t];
2n.forEach((function(t){i[t]||(i[t]={}),i[t][e]||(i[t][e]=!0)}))},dn:function(t){this._n("integrations",t.integrations,t.layerId)},fn:function(t){this._n("changeTypes",t.changeTypes,t.layerId)},an:function(t){this._n("activationTypes",[t.activationType],t.entityId),this.emitChange()},pn:function(t){this._n("viewFeatures",t.featuresUsed,t.entityId),this.emitChange()},vn:function(t){this._n("layerFeatures",[t.feature],t.entityId),this.emitChange()},hn:function(t){this._n("policy",[t.policy],t.layerId),this.emitChange()},un:function(t){this._n("audiences",t.audienceTypes,t.layerId),this.emitChange()},gn:function(t){this.W.data.extras.viewsInitiallyActivatedCount=t.viewsInitiallyActivatedCount,this.emitChange()},mn:function(t){this._n("visitorIdLocatorType",[t.visitorIdLocatorType],t.entityId),this.emitChange()},bn:function(t){this.W.data.extras.errorCustomVisitorId=t.isError,this.emitChange()},yn:function(t){this._n("stickyBucketing",[t.feature],t.id)},getSampleRum:function(){return this.W.inRumSample},getRumId:function(){return this.W.id},getRumHost:function(){return this.W.RumHost},getApiData:function(){return this.W.apis},getDOMObservationData:function(){return this.W.DOMObservation},getRumData:function(){return r(this.W.data)},getScriptSrc:function(){return this.W.src},getFeaturesNeededData:function(){var t=this.W.featuresNeeded,n={};return Object.keys(t).forEach((function(e){var i=t[e],r=Object.keys(i);o(r)||(n[e]={}),r.forEach((function(t){n[e][t]=Object.keys(i[t]).length}))})),n}}},9688:function(t,n,e){var i=e(1756);t.exports={initialize:function(){this.W={initialized:!1,natives:{}},this.on(i.SANDBOXED_FUNCTIONS_ADDED,this.wn)},wn:function(t){if(!t.sandboxedFunctions)throw new Error("No sandboxedFunctions found in payload");this.W.natives=t.sandboxedFunctions,this.W.initialized=!0,this.emitChange()},getAll:function(){return this.W.natives},get:function(t){if(!t)throw new Error("Missing name parameter");return this.W.natives[t]||null},isInitialized:function(){return this.W.initialized}}},8147:function(t,n,e){var i=e(279).cloneDeep,r=e(1756),o=e(6310),a=e(167);t.exports={initialize:function(){this.W={lastSessionTimestamp:0,sessionId:null},this.on(r.REFRESH_SESSION,this.En),this.on(r.LOAD_SESSION_STATE,this.In)},getState:function(){return i(this.W)},getSessionId:function(){return this.W.sessionId},In:function(t){this.W.sessionId=t.sessionId,this.W.lastSessionTimestamp=t.lastSessionTimestamp,this.emitChange()},En:function(){var t=o.now(),n=this.W.lastSessionTimestamp;(!this.W.sessionId||t-n>18e5)&&(this.W.sessionId=a.generate()),this.W.lastSessionTimestamp=t,this.emitChange()}}},9794:function(t,n,e){var i=e(279),r=i.cloneDeep,o=i.isEmpty,a=e(1756);t.exports={initialize:function(){this.An(),this.on(a.FINALIZE_BATCH_SNAPSHOT,this.Tn),this.on(a.REGISTER_PREVIOUS_BATCH,this.Sn),this.on(a.REGISTER_TRACKER_VISITOR,this.On),this.on(a.REGISTER_TRACKER_EVENT,this.kn),this.on(a.REGISTER_TRACKER_DECISION,this.Rn),this.on(a.RESET_TRACKER_EVENTS,this.Cn),this.on(a.RESET_TRACKER_STORE,this.An),this.on(a.RESET_TRACKER_PREVIOUS_BATCHES,this.Dn),this.on(a.SET_TRACKER_POLLING,this.Nn),this.on(a.SET_TRACKER_BATCHING,this.xn),this.on(a.SET_TRACKER_USE_BEACON,this.jn),this.on(a.SET_TRACKER_SEND_EVENTS,this.Pn),this.on(a.SET_TRACKER_PERSISTABLE_STATE,this.Mn),this.on(a.SET_TRACKER_DIRTY,this.Ln),this.on(a.UPDATE_TRACKER_VISITOR_ATTRIBUTES,this.Vn)},getPersistableState:function(){return this.W.isDirty?this.hasEventsToSend()||this.hasPreviousBatchesToSend()?{data:this.W.data,decisions:this.W.decisions,decisionEvents:this.W.decisionEvents,previousBatches:this.W.previousBatches}:{}:null},Mn:function(t){o(this.W.data)||o(t.data)||(this.Tn(),this.W.previousBatches.push(this.getEventBatch())),this.W.data=t.data||{},this.W.decisions=t.decisions||[],this.W.decisionEvents=t.decisionEvents||[],o(this.W.previousBatches)||o(t.previousBatches)?this.W.previousBatches=t.previousBatches||[]:this.W.previousBatches=this.W.previousBatches.concat(t.previousBatches),this.emitChange()},Ln:function(t){this.W.isDirty=t,this.emitChange()},kn:function(t){var n=this.Un();(o(n.snapshots)||!o(this.W.decisionEvents))&&this.zn(),this.Bn().events.push(t.event),this.W.decisions=t.decisions,this.Ln(!0)},Rn:function(t){this.W.decisionEvents.push(t.decisionEvent),this.W.decisions=t.decisions,this.Ln(!0)},On:function(t){o(this.W.data)?this.W.data=t.data:this.Tn(),this.W.data.visitors.push(t.visitor),this.W.decisions=t.decisions,this.W.decisionEvents=[],this.Ln(!0)},Sn:function(t){this.W.previousBatches.push(t),this.Ln(!0)},An:function(){this.W={polling:!1,shouldBatch:!0,data:{},decisions:[],decisionEvents:[],canSend:!1,isDirty:!1,previousBatches:[]},this.emitChange()},Cn:function(){var t=this.Un();this.W.data.visitors=[t],t.snapshots=[],this.Ln(!0)},Dn:function(){this.W.previousBatches=[],this.Ln(!0)},Nn:function(t){this.W.polling=t,this.emitChange()},xn:function(t){this.W.shouldBatch=t,this.emitChange()},jn:function(t){this.W.shouldUseBeacon=t,this.emitChange()},Pn:function(t){this.W.canSend=t,this.emitChange()},getEventBatch:function(){return r(this.W.data)},getPreviousBatches:function(){return r(this.W.previousBatches)},Fn:function(){return this.W.decisionEvents.slice()},Gn:function(){this.W.decisionEvents=[]},qn:function(){return this.W.decisions.slice()},isPolling:function(){return this.W.polling},shouldBatch:function(){return this.W.shouldBatch},shouldUseBeacon:function(){return this.W.shouldUseBeacon},Bn:function(){var t=this.Un(),n=t&&t.snapshots;return n&&n[n.length-1]},Un:function(){var t=this.W.data.visitors;return t&&t[t.length-1]},zn:function(){var t=this.Fn();this.Un().snapshots.push({decisions:this.qn(),events:t}),this.Gn(),this.Ln(!0)},Tn:function(){this.W.decisionEvents.length>0&&this.zn()},hasEventsToSend:function(){if(!o(this.W.decisionEvents))return!0;
2if(!o(this.W.data)&&(this.W.data.visitors||[]).some((function(t){return t.snapshots.length>0})))return!0;return!1},hasPreviousBatchesToSend:function(){return!o(this.W.previousBatches)},canSend:function(){return this.W.canSend},Vn:function(t){var n=this.Un();n&&(n.attributes=t.attributes)}}},5756:function(t,n,e){var i=e(279),r=i.cloneDeep,o=i.isEmpty,a=e(1756);t.exports={initialize:function(){this.W={},this.on(a.SET_UA_DATA,this.vt)},vt:function(t){o(this.W)&&(this.W=t.data)},get:function(){return r(this.W)}}},6430:function(t,n,e){var i=e(279).cloneDeep,r=e(1756),o=e(3496),a={globalTags:{},viewStates:{},shouldBatch:!1};t.exports={initialize:function(){this.W=i(a),this.on(r.REGISTER_VIEWS,this.Hn),this.on(r.SET_VIEW_ACTIVE_STATE,this.Yn),this.on(r.UPDATE_PARSED_VIEW_METADATA,this.Kn),this.on(r.UPDATE_USER_SUPPLIED_METADATA,this.$n),this.on(r.TRACK_VIEW_ACTIVATED_EVENT,this.Wn),this.on(r.SET_GLOBAL_TAGS,this.Xn),this.on(r.RESET_VIEW_STATES,this.Qn),this.on(r.SET_VIEW_BATCHING,this.xn)},getAll:function(){var t={};for(var n in this.W.viewStates)t[n]=this.getViewState(n);return t},getActiveViewIds:function(){var t=this,n=this.W.viewStates;return Object.keys(n).reduce((function(n,e){return t.isViewActive(e)&&n.push(e),n}),[])},shouldBatch:function(){return this.W.shouldBatch},getViewState:function(t){var n=i(this.W.viewStates[t]),e=this.W.globalTags;return n.metadata=Object.assign({},n.parsedMetadata,e,n.userSuppliedMetadata),n},getActiveViewTags:function(){var t=this.getActiveViewStates().map((function(t){return t.metadata})),n=[{}].concat(t);return Object.assign.apply(Object,n)},getActivationEventId:function(t){return this.W.viewStates[t]?this.W.viewStates[t].activationEventId:null},getActiveViewStates:function(){var t=this,n=this.W.viewStates;return Object.keys(n).reduce((function(n,e){return t.isViewActive(e)&&n.push(t.getViewState(e)),n}),[])},isViewActive:function(t){var n=this.W.viewStates[t];return n||o.warn("No Page registered with id",t),!!n.isActive},getGlobalTags:function(){return i(this.W.globalTags)},Qn:function(){this.W.viewStates={},this.emitChange()},Hn:function(t){var n=t.forceClean||!1,e=this;t.views.forEach((function(t){var i=t.id;e.W.viewStates[i]={id:i,isActive:"boolean"!=typeof t.isActive||n?null:t.isActive,activatedTimestamp:null,activationEventId:null,parsedMetadata:{},userSuppliedMetadata:{}}})),this.emitChange()},Yn:function(t){var n=t.view.id;if(!this.W.viewStates[n])throw new Error("No view exists with id "+n);this.W.viewStates[n].isActive=t.isActive,t.isActive?this.W.viewStates[n].activatedTimestamp=t.timestamp:(this.W.viewStates[n].parsedMetadata={},this.W.viewStates[n].userSuppliedMetadata={}),this.emitChange()},Kn:function(t){var n=t.pageId;if(!this.W.viewStates[n])throw new Error("No view exists with id "+n);Object.assign(this.W.viewStates[n].parsedMetadata,t.metadata),this.emitChange()},$n:function(t){var n=t.pageId;if(!this.W.viewStates[n])throw new Error("No view exists with id "+n);Object.assign(this.W.viewStates[n].userSuppliedMetadata,t.metadata),this.emitChange()},Wn:function(t){var n=t.pageId;this.W.viewStates[n]&&(this.W.viewStates[n].activationEventId=t.eventData.eventId,this.emitChange())},Xn:function(t){Object.assign(this.W.globalTags,t),this.emitChange()},xn:function(t){this.W.shouldBatch=t,this.emitChange()}}},2161:function(t,n,e){var i=e(1756),r=e(1423),o=e(279).isEmpty;t.exports={initialize:function(){this.W={views:{},apiNamesToViews:{}},this.on(i.DATA_LOADED,this.vt)}
2,getAll:function(){return r.safeReference(Object.values(this.W.views))},getPagesMap:function(){return r.safeReference(this.W.views)},get:function(t){return r.safeReference(this.W.views[t])},getByApiName:function(t){return r.safeReference(this.W.apiNamesToViews[t])},apiNameToId:function(t){var n=this.W.apiNamesToViews[t];if(n)return n.id},idToApiName:function(t){var n=this.W.views[t];if(n)return n.apiName},getNumberOfPages:function(){return Object.keys(this.W.views).length},getAllViewsForActivationType:function(t){var n=this.W.views;return Object.keys(n).map((function(t){return n[t]})).filter((function(n){return n.activationType===t}))},vt:function(t){if(!o(t.data.views)){var n=this;t.data.views.forEach((function(t){r.deepFreeze(t),n.W.views[t.id]=t,n.W.apiNamesToViews[t.apiName]=t})),this.emitChange()}}}},1445:function(t,n,e){var i=e(279).cloneDeep,r=e(1756),o=e(2103);t.exports={initialize:function(){this.W={profile:{},metadata:{},visitorId:null},this.on(r.SET_VISITOR_ID_VIA_API,this.Jn),this.on(r.SET_VISITOR_ATTRIBUTES,this.Zn),this.on(r.LOAD_EXISTING_VISITOR_PROFILE,this.te)},getVisitorProfile:function(){return this.W.profile},getVisitorProfileMetadata:function(){return this.W.metadata},getAttribute:function(t){var n=this.W.profile;return i(o.getFieldValue(n,t))},getAttributeMetadata:function(t){return i(this.W.metadata[t])},getVisitorIdFromAPI:function(){return this.W.visitorId},te:function(t){this.W.profile=t.profile,this.W.metadata=t.metadata,this.emitChange()},Zn:function(t){var n=this;t.attributes.forEach((function(t){var e=t.key;o.setFieldValue(n.W.profile,e,t.value),t.metadata&&Object.keys(t.metadata).forEach((function(i){var r=t.metadata[i];o.setFieldValue(n.W.metadata,e.concat(i),r)}))})),this.emitChange()},Jn:function(t){this.W.visitorId=t,this.emitChange()}}},7918:function(t,n,e){var i=e(1756),r=e(279).isEmpty;t.exports={initialize:function(){this.W={},this.on(i.DATA_LOADED,this.ne)},getCustomBehavioralAttributes:function(){var t=this.W;return Object.keys(t).reduce((function(n,e){var i=t[e];return i.rule_json&&n.push(i),n}),[])},getVisitorAttribute:function(t){var n=Object.values(this.W);if(t.datasourceId&&(n=n.filter((function(n){return n.dcp_datasource_id===String(t.datasourceId)}
2))),t.attributeName&&t.attributeId)throw new Error("Must not specify both attribute name and attribute ID");if(t.attributeId){var e=this.W[t.attributeId];if(!e)throw new Error("Unrecognized attribute ID: "+t.attributeId);return e}if(t.attributeName){var i=n.filter((function(n){return n.name===t.attributeName}));if(!i.length)throw new Error("Unrecognized attribute name: "+t.attributeName);if(i.length>1)throw new Error("Too many attributes with name: "+t.attributeName);return i[0]}throw new Error("Must specify attribute name or attribute ID")},ne:function(t){if(!r(t.data.visitorAttributes)){var n=this;t.data.visitorAttributes.forEach((function(t){n.W[t.id]=t})),this.emitChange()}}}},6916:function(t,n,e){Object.defineProperty(n,"u",{value:!0});var i=e(279),r=i.cloneDeep,o=i.isEqual,a=e(1756),u=e(6388);t.exports={initialize:function(){this.W={variationIdMap:{},preferredLayerMap:{},contextualMabMap:{},odpSegments:{}},this.on(a.UPDATE_VARIATION_ID_MAP,this.ee),this.on(a.MERGE_VARIATION_ID_MAP,this.re),this.on(a.UPDATE_PREFERRED_LAYER_MAP,this.oe),this.on(a.MERGE_PREFERRED_LAYER_MAP,this.ae),this.on(a.UPDATE_CONTEXTUAL_MAB_MAP,this.ue),this.on(a.MERGE_CONTEXTUAL_MAB_MAP,this.ce),this.on(a.SET_ODP_SEGMENTS,this.se)},se:function(t){this.W.odpSegments=t,this.emitChange()},getOdpSegments:function(){return this.W.odpSegments},getContextualMabMap:function(){return r(this.W.contextualMabMap)},getContextualMabMapString:function(){return u.stringify(this.W.contextualMabMap)},ue:function(t){var n=this.W.contextualMabMap[t.layerId]||{};o(n[t.experimentId],t.variationData)||(n[t.experimentId]=t.variationData,this.W.contextualMabMap[t.layerId]=n,this.emitChange())},ce:function(t){var n=this.W.contextualMabMap;Object.keys(t.contextualMabMap).forEach((function(e){var i=t.contextualMabMap[e];n[e]||(n[e]={}
2),Object.keys(i).forEach((function(t){var r=i[t];n[e][t]||(n[e][t]=r)}))})),this.W.contextualMabMap=n,this.emitChange()},getVariationIdMap:function(){return r(this.W.variationIdMap)},getVariationIdMapString:function(){return u.stringify(this.W.variationIdMap)},ee:function(t){var n=this.W.variationIdMap[t.layerId]||{};n[t.experimentId]!==t.variationId&&(n[t.experimentId]=t.variationId,this.W.variationIdMap[t.layerId]=n,this.emitChange())},re:function(t){var n=this.getVariationIdMap(),e=t.variationIdMap;Object.keys(n||{}).forEach((function(t){var i=(n||{})[t];e[t]?Object.assign(e[t],i):e[t]=i})),this.W.variationIdMap=e,this.emitChange()},getPreferredLayerMap:function(){return r(this.W.preferredLayerMap)},getPreferredLayerMapString:function(){return u.stringify(this.W.preferredLayerMap)},getPreferredLayerId:function(t){return this.W.preferredLayerMap[t]},oe:function(t){this.W.preferredLayerMap[t.groupId]!==t.layerId&&(this.W.preferredLayerMap[t.groupId]=t.layerId,this.emitChange())},ae:function(t){var n=this.getPreferredLayerMap(),e=t.preferredLayerMap;Object.assign(e,n),this.W.preferredLayerMap=e,this.emitChange()}}},7577:function(t,n,e){var i=e(1756);e(5507).J;t.exports={initialize:function(){this.W={events:[],foreignEvents:{},foreignEventQueues:{}},this.on(i.SET_VISITOR_EVENTS,this.vt),this.on(i.SET_FOREIGN_VISITOR_EVENTS,this.fe),this.on(i.SET_FOREIGN_VISITOR_EVENT_QUEUE,this.le)},getEvents:function(){return this.W.events},getForeignEvents:function(){return this.W.foreignEvents},getForeignEventQueues:function(){return this.W.foreignEventQueues},vt:function(t){this.W.events=t,this.emitChange()},fe:function(t){this.W.foreignEvents[t.key]=t.value},le:function(t){this.W.foreignEventQueues[t.key]=t.value}}},4173:function(t,n,e){var i=e(1756);t.exports={initialize:function(){this.W={baseMap:{},eventQueue:[],lastEvent:null,initialized:!1,cleared:!1},this.on(i.UPDATE_BEHAVIOR_STORE,this.de)},getBaseMap:function(){return this.W.baseMap},getEventQueue:function(){return this.W.eventQueue},getLastEvent:function(){return this.W.lastEvent},getCleared:function(){return this.W.cleared},getInitialized:function(){return this.W.initialized},de:function(t){this.W[t.key]=t.value}}},9221:function(t,n,e){var i=e(279).isEmpty,r=e(1756);t.exports={initialize:function(){this.W={randomId:null,visitorIdLocator:null},this.on(r.SET_VISITOR_ID,this.vt),this.on(r.DATA_LOADED,this.ve)},getBucketingId:function(){return this.getRandomId()},getRandomId:function(){return this.W.randomId},getVisitorIdLocator:function(){return this.W.visitorIdLocator},vt:function(t){Object.assign(this.W,t),this.emitChange()},ve:function(t){i(t.data.visitorIdLocator)||(this.W.visitorIdLocator=t.data.visitorIdLocator,this.emitChange())}}},1943:function(t,n,e){var i=e(279).cloneDeep,r=e(3496),o=e(1756);t.exports={initialize:function(){this.W={frames:[],defaultFrame:null,messages:[],subscribers:[],canonicalOrigins:null,disabled:!1},this.on(o.XDOMAIN_SET_DEFAULT_FRAME,this.he),this.on(o.XDOMAIN_ADD_FRAME,this.pe),this.on(o.XDOMAIN_SET_MESSAGE,this.ge),this.on(o.XDOMAIN_ADD_SUBSCRIBER,this.me),this.on(o.XDOMAIN_SET_CANONICAL_ORIGINS,this.be),this.on(o.XDOMAIN_SET_DISABLED,this.ye)},getMessages:function(){return i(this.W.messages)},getOffset:function(){return 0===this.W.messages.length?0:this.W.messages[0].data.id},getNextMessageId:function(){return this.W.messages.length+this.getOffset()},getMessageById:function(t){return this.W.messages[t-this.getOffset()]},getSubscribers:function(){return this.W.subscribers},getFrames:function(){return this.W.frames},getNextFrameId:function(){return this.W.frames.length},getDefaultFrame:function(){return this.W.defaultFrame},getCanonicalOrigins:function(){return i(this.W.canonicalOrigins)},isDisabled:function(){return this.W.disabled},he:function(t){this.W.defaultFrame=t},pe:function(t){this.W.frames.push(t)},ge:function(t){for(this.W.messages[t.messageId-this.getOffset()]=t.message;this.W.messages.length>1e3;){var n=this.W.messages.shift();r.debug("XDomainStorage: Cleared old message: "+n.data.id)}},me:function(t){this.W.subscribers.push(t.subscriber)},be:function(t){this.W.canonicalOrigins=t.canonicalOrigins},ye:function(t){this.W.disabled=t.disabled}}},2858:function(t,n){n.locatorType={CSS_SELECTOR:"css_selector",JAVASCRIPT:"javascript",URL_REGEX:"url_regex"},n.valueType={STRING:"string",NUMBER:"number",CURRENCY:"currency"},n.nodeNames={INPUT:"INPUT",SELECT:"SELECT"}},4634:function(t,n,e){var i=e(9726).v;n.Error=i("TagError")}
2,6952:function(t,n,e){var i=e(2044).PluginTypes,r=e(3221).get("stores/plugins");n.getTagValue=function(t){var e=r.getPlugin(i.viewTagLocators,t.locatorType);if(!e)throw new n.Error("No locator registered for tag locatorType: "+t.locatorType);return e(t)},n.enums=e(2858),n.Error=e(4634).Error},6652:function(t,n,e){var i=e(2858),r=e(4634).Error;function o(t,n,e){var i=new RegExp("^\\s*([+-]?)\\s*((\\d+)((?:\\"+n+"\\d{3})+)?)(?:\\"+e+"(\\d+))?\\s*$"),r=t.match(i);if(!r)return NaN;var o=r[3],a=r[4],u=r[5];if(a&&!u&&o.length>3&&4===a.length||!a&&o.length<=3&&u&&3===u.length)return NaN;var c=r[1],s=r[2].replace(new RegExp("\\"+n,"g"),"");return Number(c+s+(u?"."+u:""))}function a(t){var n=o(t=String(t),",",".");return isNaN(n)&&(n=o(t,".",",")),n}t.exports=function(t,n){var e;switch(t){case i.valueType.STRING:return n?String(n):"";case i.valueType.NUMBER:if(e=a(n),isNaN(e))throw new r('"'+n+'" cannot be parsed as a number');return e;case i.valueType.CURRENCY:if(e=a(e=String(n).replace(/^[^\d\,\.\-]*/g,"").replace(/[^\d\,\.\-]*$/g,"")),isNaN(e))throw new r('"'+n+'" cannot be parsed as currency');return Math.round(100*e)}throw new r("Unknown ViewTag type: "+t)}},8548:function(t,n,e){var i=e(1756),r=e(3941),o=e(3341).Promise,a=e(3221).get("stores/async_request");n.makeAsyncRequest=function(t,n){var e,u,c=a.getPromise(t);if(c)return c;var s=new o((function(t,n){e=t,u=n}));return r.dispatch(i.REGISTER_ASYNC_DEFERRED,{source:t,promise:s,resolver:e,rejecter:u}),n&&n(),s},n.resolveRequest=function(t,n){r.dispatch(i.RESOLVE_DEFERRED,{source:t,resolveWith:n})},n.rejectRequest=function(t,n){r.dispatch(i.REJECT_DEFERRED,{source:t,rejectWith:n})}},2122:function(t,n,e){var i=e(950),r=e(3898);n.getLanguage=function(){return r.getNavigatorLanguage()},n.getQueryParams=i.getQueryParams,n.getQueryParamValue=i.getQueryParamValue,n.getUrl=function(){return r.getHref()}},2658:function(t,n,e){var i=e(9726).v,r=e(6310),o=e(9751),a=e(3898),u=e(1756),c=e(3221),s=e(3941),f=c.get("stores/cookie_options"),l=n.SetError=i("CookieSetError"),d=n.MismatchError=i("CookieMismatchError"),v=["co","com","net","org","edu","gov","mil","int","ac","com","net","org"];n.getAll=function(t){var e,i,r,a,u;void 0===t&&(t=!0),e=o.getCookieString().split(/\s*;\s*/);var c={};for(r=0;r<e.length;r++)if((a=(i=e[r]).indexOf("="))>0&&void 0===c[u=n.safeDecodeURIComponent(i.substring(0,a))]){var s=i.substring(a+1);t&&(s=n.safeDecodeURIComponent(s)),c[u]=s}return c},n.safeDecodeURIComponent=function(t){try{return decodeURIComponent(t)}catch(n){return t}},n.get=function(t,e){return n.getAll(e)[t]},n.set=function(t,e,i,c){!1!==c&&(c=!0);var g=[];if(void 0===(i=Object.assign({encodeValue:!0},i)).domain){var m=f.getCurrentDomain();m||(m=function(t,e){!1!==e&&(e=!0);for(var i,r,o=t.hostname.split("."),a=[],c=null,f=o.length-1;f>=0;f--)if(a.unshift(o[f]),i=a.join("."),!(o.length>1&&(1===a.length||2===a.length&&v.includes(a[0]))||h.includes(i))){r={domain:e?"."+i:i};try{n.set(p,Math.random().toString(),r),n.remove(p,r),c=r.domain;break}catch(t){}}return s.dispatch(u.SET_COOKIE_DOMAIN,c),c}(a.getLocation(),!0)),i.domain=m}if(i.domain&&g.push("domain="+i.domain),void 0===i.path&&(i.path="/"),i.path&&g.push("path="+i.path),void 0===i.expires){var b=void 0===i.maxAge?f.getDefaultAgeInSeconds():i.maxAge;i.expires=new Date(r.now()+1e3*b)}if(void 0!==i.expires&&g.push("expires="+i.expires.toUTCString()),i.secure&&g.push("secure"),o.setCookie(t+"="+(i.encodeValue?encodeURIComponent(e):e)+";"+g.join(";")),c){var y=i.encodeValue,_=n.get(t,y);if(_!==e){if(!_)throw new l('Failed to set cookie "'+t+'"');throw new d('Expected "'+e+'" for "'+t+'", got "'+_+'"')}}},n.remove=function(t,e){for(var i=a.getLocation().hostname.split("."),r=1===i.length?0:1;i.length>r&&(2!==i.length||!v.includes(i[0]));)n.set(t,null,Object.assign({},e,{domain:"."+i.join("."),expires:new Date(0)}),!1),i.shift()};var h=["optimizely.test"],p="optimizelyDomainTestCookie"},9726:function(t,n,e){var i=e(4254),r=i("InternalError");n.C=r,n.v=function(t){return i(t,r)}},1623:function(t,n,e){var i=e(3341).Promise;t.exports=function(){var t,n,e=new i((function(e,i){t=e,n=i}));return e.resolve=function(){return t.apply(null,Array.from(arguments)),e},e.reject=function(){return n.apply(null,Array.from(arguments)),e},e}},4143:function(t,n,e){Object.defineProperty(n,"u",{value:!0}),n.getEditorConfig=f,n.getEditorScriptUrl=l,n.persistEditorParams=v,n.loadEmbeddedEditor=function(t){"function"!=typeof(null==window.optimizely?void 0:window.optimizely.push)&&(o.warn("EditorLoader / window.optimizely does not have a push method; it may have been overridden by page code. Resetting to ensure embedded editor compatibility."),window.optimizely=[]);v(),r.setOptOut(!1);var n=f(t),e=l(n);n.pullRequestId&&!isNaN(Number(n.pullRequestId))&&o.debug("Main / Loading editor pull request ID: "+n.pullRequestId),a.addScriptAsync(e)};var i=e(2658),r=e(7946),o=e(3496),a=e(7743),u=e(3898),c=["optimizely_project_id","optimizely_experiment_id","optimizely_section_id","optimizely_variation_id","optimizely_embed_editor","optimizely_use_shadow_dom","optimizely_page_id","optimizely_device_mode","optimizely_events_mode"],s=["optimizely_oauth_token","optimizely_token_expires_in"];function f(t){var n,e={true:"http://localhost:9888/main.js",http:"http://localhost:9888/main.js",https:"https://localhost:9888/main.js"},r=null==(n=i.get("useLocalBundle"))?void 0:n.toLowerCase(),o=i.get("optimizelyVePr");return{useLocalBundle:!!e[r],pullRequestId:o,mfeEntrypoint:e[r]?e[r]:t}}function l(t){var n=t.pullRequestId;return n&&!isNaN(Number(n))?"https://develrc-app.optimizely.com/dist/visual-editor/pull-request/"+n+"/main.js":t.mfeEntrypoint}function d(t){var n={};if(!t)return n;for(var e=t.split("&"),i=0;i<e.length;i++){var r=e[i].indexOf("=");if(-1!==r){var o=e[i].substring(0,r),a=e[i].substring(r+1);try{n[o]=decodeURIComponent(a)}catch(t){n[o]=a}}}return n}function v(){var t=u.getGlobal("sessionStorage");if(t){var n=u.getHref()||"",e=n.indexOf("?"),i=n.indexOf("#"),r=-1!==e?n.substring(e+1,-1!==i?i:void 0):"",a=-1!==i?n.substring(i+1):"";a=a.replace(/^#?\/*/,"");var f=d(r),l=d(a);
2c.forEach((function(n){f[n]&&t.setItem(n,f[n])})),s.forEach((function(n){l[n]&&t.setItem(n,l[n])})),o.log("EditorLoader / Persisted visual editor params to sessionStorage")}}},1423:function(t,n,e){var i=e(279).cloneDeep;n.deepFreeze=function(t){},n.safeReference=function(t){return i(t)}},279:function(t,n){function e(t,n){return null!=n&&"undefined"!=typeof Symbol&&n[Symbol.hasInstance]?!!n[Symbol.hasInstance](t):t instanceof n}Object.defineProperty(n,"u",{value:!0}),n.cloneDeep=function t(n,r,o){if(void 0===r&&(r=0),null==n)return n;if("object"!==(void 0===n?"undefined":i(n)))return n;if(r>100)throw new Error("cloneDeep: Maximum recursion depth exceeded");if(o||(o=new Map),o.has(n))return o.get(n);var a=Date,u=RegExp,c=Array;if(e(n,a)){return new a(n.getTime())}if(e(n,u)){var s=n,f="";return s.global&&(f+="g"),s.ignoreCase&&(f+="i"),s.multiline&&(f+="m"),s.unicode&&(f+="u"),s.sticky&&(f+="y"),new u(s.source,f)}if(c.isArray(n)){var l=n,d=[];o.set(n,d);for(var v=l.length,h=0;h<v;h++)d[h]=t(l[h],r+1,o);return d}var p=Object.getPrototypeOf(n),g=p&&p!==Object.prototype?Object.create(p):{};for(var m in o.set(n,g),n)Object.prototype.hasOwnProperty.call(n,m)&&(g[m]=t(n[m],r+1,o));return g},n.isEmpty=function(t){return null==t||("string"==typeof t||Array.isArray(t)?0===t.length:"object"==typeof t&&0===Object.keys(t).length)},n.isEqual=function t(n,r){if(n===r)return!0;if(null==n||null==r)return n===r;if((void 0===n?"undefined":i(n))!==(void 0===r?"undefined":i(r)))return!1;if("object"!=typeof n)return n===r;if(Array.isArray(n)&&Array.isArray(r)){if(n.length!==r.length)return!1;for(var o=0;o<n.length;o++)if(!t(n[o],r[o]))return!1;return!0}if(Array.isArray(n)!==Array.isArray(r))return!1;if(e(n,Date)&&e(r,Date))return n.getTime()===r.getTime();if(e(n,RegExp)&&e(r,RegExp))return n.source===r.source&&n.global===r.global&&n.ignoreCase===r.ignoreCase&&n.multiline===r.multiline&&n.sticky===r.sticky&&n.unicode===r.unicode;var a=Object.keys(n),u=Object.keys(r);if(a.length!==u.length)return!1;for(var c=0;c<a.length;c++){var s=a[c];if(!Object.prototype.hasOwnProperty.call(r,s)||!t(n[s],r[s]))return!1}return!0},n.isObject=r,n.pick=function(t,n){var e={};if(null==t)return e;for(var i=0;i<n.length;i++){var r=n[i];Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])}return e},n.omit=function(t,n){var e={};if(null==t)return e;var i=new Set(n);for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&!i.has(r)&&(e[r]=t[r]);return e},n.merge=function t(n){for(var e=arguments.length,i=new Array(e>1?e-1:0),o=1;o<e;o++)i[o-1]=arguments[o];if(!r(n))throw new TypeError("Target must be an object");for(var a=0;a<i.length;a++){var u=i[a];if(r(u))for(var c in u)if(Object.prototype.hasOwnProperty.call(u,c)){var s=u[c],f=n[c];if(void 0===s)continue;r(s)&&r(f)?n[c]=t({},f,s):n[c]=s}}return n},n.mapValues=function(t,n){var e={};for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=n(t[i],i,t));return e},n.flatten=function(t){for(var n=[],e=0;e<t.length;e++){var i=t[e];if(Array.isArray(i))for(var r=0;r<i.length;r++)n.push(i[r]);else n.push(i)}return n},n.flattenDeep=function t(n){var e=[];if(null==n)return e;for(var i=0;i<n.length;i++){var r=n[i];if(Array.isArray(r))for(var o=t(r),a=0;a<o.length;a++)e.push(o[a]);else e.push(r)}return e},n.pickBy=o,n.omitBy=function(t,n){var e="function"==typeof n;return o(t,(function(t,i){return!(e?n(t,i):r(t)&&Object.keys(n).every((function(e){return t[e]===n[e]})))}))};var i=function(t){return t&&"undefined"!=typeof Symbol&&t.constructor===Symbol?"symbol":typeof t};function r(t){var n=void 0===t?"undefined":i(t);return null!=t&&("object"===n||"function"===n)}function o(t,n){var e=function(e){if(!Object.prototype.hasOwnProperty.call(t,e))return"continue";var a=t[e];(i?n(a,e):r(a)&&Object.keys(n).every((function(t){return a[t]===n[t]})))&&(o[e]=a)},i="function"==typeof n,o={};for(var a in t)e(a);return o}},2103:function(t,n,e){var i=e(279),r=i.isEmpty,o=i.isObject;n.getFieldValue=function(t,n){Array.isArray(n)||(n=[n]);for(var e=t,i=0;i<n.length;i++){var r=n[i];if(!o(e)||!e.hasOwnProperty(r))return;e=e[r]}return e},n.setFieldValue=function(t,n,e){if(!Array.isArray(n)||r(n))throw new Error("Attempted to set an invali
2d key path: "+n);for(var i=t,a=0;a<n.length-1;a++){var u=n[a];o(i[u])||(i[u]={}),i=i[u]}i[n[n.length-1]]=e}},123:function(t,n,e){var i=e(279).isEmpty;n.create=function(t){if(i(t))return null;var n=document.createElement("a");return n.href=t,n}},3496:function(t,n,e){var i=e(1756),r=e(6310),o=e(2044),a=e(3941),u=e(6388);function c(){this.logLevel=null,this.logMatch=null,this.logs=[],this.timebase=r.now()}c.prototype._e=function(){return null!==this.logLevel},c.prototype.setLogLevel=function(t){var n=this.we(t);null===n?console.error("Unknown log level: "+t):this.logLevel!==n&&(this.log("Setting log level to "+n),this.logLevel=n,this.flush())},c.prototype.setLogMatcher=function(t){this.logMatcher="string"==typeof t?t:"",this.logGroup=0},c.prototype.shouldLog=function(t){return this._e()&&this.logLevel>=t},c.prototype.matchesLogMessage=function(t,n){var e=this.logMatcher;if(!this.logMatcher)return!0;if(this.logGroup)return"GROUPSTART"===t?this.logGroup++:"GROUPEND"===t&&this.logGroup--,!0;var i=n.some((function(t){if("string"!=typeof t)try{t=u.stringify(t)}catch(t){}return"string"==typeof t&&t.includes(e)}));return i&&"GROUPSTART"===t&&this.logGroup++,i},c.prototype.storeLog=function(t,n){var e={logLevel:t,logMessage:n};a.dispatch(i.LOG,e)},c.prototype.flush=function(){var t=e(3221).get("stores/log");this.logGroup=0;var n=t.getLogs(),i=this;n.forEach((function(t){i.Ee(t.logLevel,t.logMessage,!0)}))},c.prototype.Ee=function(t,n,e){var i,r=t;if(console)switch(t){case"GROUPSTART":i=console.groupCollapsed,r=o.LogLevel.DEBUG;break;case"GROUPEND":i=console.groupEnd,r=o.LogLevel.DEBUG;break;case o.LogLevel.ERROR:i=console.error;break;case o.LogLevel.WARN:i=console.warn;break;case o.LogLevel.DEBUG:i=console.debug;break;default:i=console.log}try{e||this._e()&&!this.shouldLog(r)||(Array.isArray(n)&&"string"==typeof n[0]&&(n=this.Ie(n)),this.storeLog(t,n)),i&&this.shouldLog(r)&&this.matchesLogMessage(t,n)&&i.apply(console,n)}catch(t){console&&(console.error?console.error(t):console.log(t))}},c.prototype.debug=function(){this.Ee(o.LogLevel.DEBUG,[].slice.call(arguments))},c.prototype.log=function(){this.Ee(o.LogLevel.INFO,[].slice.call(arguments))},c.prototype.logAlways=function(){var t=this.Ie([].slice.call(arguments));console&&console.log&&console.log.apply&&console.log.apply(console,t),this.storeLog(o.LogLevel.INFO,t)},c.prototype.warn=function(){this.Ee(o.LogLevel.WARN,[].slice.call(arguments))},c.prototype.error=function(t){var n=[].slice.call(arguments);1===n.length&&t.stack?(this.Ee(o.LogLevel.ERROR,[this.Ae(),t]),this.Ee(o.LogLevel.INFO,[t.stack])):this.Ee(o.LogLevel.ERROR,n)},c.prototype.groupCollapsed=function(){this.Ee("GROUPSTART",[].slice.call(arguments))},c.prototype.groupEnd=function(){this.Ee("GROUPEND",[].slice.call(arguments))},c.prototype.Ie=function(t){var n=this.Ae().toString();return n.length<6&&(n=("     "+n).slice(-6)),[n+"| Optly / "+t[0]].concat(t.slice(1))},c.prototype.Ae=function(){return this.timebase?r.now()-this.timebase:0},c.prototype.we=function(t){return t&&("TRUE"===(t=t.toUpperCase())&&(t="INFO"),"FALSE"===t&&(t="OFF"),"ALL"===t&&(t="DEBUG"),void 0!==o.LogLevel[t])?o.LogLevel[t]:null},t.exports=new c},9655:function(t,n,e){var i;i=e(4827),t.exports=i},8671:function(t,n,e){Object.defineProperty(n,"u",{value:!0});var i=e(243),r=e(6388),o=(0,e(9726).v)("ODPError");n.getHashForOdpSegments=function(t){if(!Array.isArray(t))throw new o("Segments must be an array");return t=t.slice().sort(),i.hashToHex(r.stringify({segments:t}),i.Seed.BEHAVIOR_EVENT)}},1386:function(t,n){n.isSingleExperimentPolicy=function(t){return"single_experiment"===t||"multivariate"===t}},8805:function(t,n,e){var i=e(3341).Promise,r=e(3898);n.pollFor=function(t,n,e){var o,a;return"function"==typeof e?a=e:(o=e||100,a=function(){return--o<-1}),n=n||50,new i((function(e,i){!function o(){var u;if(!a()){try{var c=t();if(c)return e(c)}catch(t){u=t}return r.setTimeout(o,n)}i(u||new Error("Poll timed out"))}()}))}},8767:function(t,n,e){var i=e(3341).Promise;n.firstToResolve=function(t){return new i((function(n){t.forEach((function(t){i.resolve(t).then(n,(function(){}))}))}))}},950:function(t,n,e){var i=e(3898);
2n.getQueryParams=function(){var t=i.getLocationSearch()||"";if(0===t.indexOf("?")&&(t=t.substring(1)),0===t.length)return[];for(var n=t.split("&"),e=[],r=0;r<n.length;r++){var o="",a="",u=n[r].split("=");u.length>0&&(o=u[0]),u.length>1&&(a=u[1]),e.push([o,a])}return e},n.getQueryParamValue=function(t){for(var e=n.getQueryParams(),i=0;i<e.length;i++)if(e[i][0]===t)return e[i][1]},n.queryStringFromMap=function(t){return Object.keys(t).map((function(n){return n+"="+t[n]})).join("&")}},1148:function(t,n,e){var i=e(3496),r=e(3898);function o(t){this.windowLength=t,this.count=0,this.listeners={},this.isIncrementingTick=!1}o.prototype.countCurrentTick=function(){this.isIncrementingTick||(this.isIncrementingTick=!0,this.increment(),r.setTimeout(function(){this.isIncrementingTick=!1}.bind(this),0))},o.prototype.increment=function(){this.count+=1,(this.listeners[String(this.count)]||[]).forEach((function(t){t()})),r.setTimeout(this.decrement.bind(this),this.windowLength)},o.prototype.decrement=function(){this.count-=1,this.count<0&&(i.warn("Decremented down to negative count: ",this.count),this.count=0)},o.prototype.addListener=function(t,n){this.listeners[t]||(this.listeners[t]=[]),this.listeners[t].push(n)},t.exports=o},2060:function(t,n,e){var i=e(279).pick,r=e(3221).get("stores/observed_redirect");n.getReferrer=function(){var t=r.get();return t?t.referrer:null},n.getExperimentAndVariation=function(){var t=r.get();return t&&"string"==typeof t.variationId?i(t,["experimentId","variationId"]):null}},2530:function(t,n,e){var i=e(279).mapValues,r=e(1756),o=e(6310),a=e(3221),u=e(9751),c=e(2044),s=e(3941),f=e(3496),l=a.get("stores/sandbox"),d=e(3898);n.shouldSandbox=function(){return!1},n.get=function(t){if(!t)throw new Error("Name is required");if(n.shouldSandbox()){l.isInitialized()||v();var e=l.get(t);if(e)return e}return d.getGlobal(t)};var v=function(){try{var t="optimizely_"+o.now(),n=u.createElement("iframe");n.name=t,n.style.display="none",u.appendToHead(n);var e=n.contentWindow,a=n.contentDocument;a.write("<scr"),a.write("ipt></scr"),a.write("ipt>"),a.close();var l=i(c.SandboxedFunctions,(function(t){return e[t]}));s.dispatch(r.SANDBOXED_FUNCTIONS_ADDED,{sandboxedFunctions:l})}catch(t){f.warn("Unable to create a sandbox: ",t)}}},7743:function(t,n,e){var i=e(9751),r=e(3496),o=e(4464);n.addScriptAsync=function(t,n,e){var o,a=i.querySelector("head"),u=e?i.querySelector(e):null,c=i.createElement("script");(c.type="text/javascript",c.async=!0,c.src=t,n&&(c.onload=n),u)?(r.debug("Target element found for selector: "+e+", adding script before it."),null==(o=u.parentNode)||o.insertBefore(c,u)):(r.debug("Target element not found for selector: "+e+", adding script to HEAD instead."),a.insertBefore(c,a.firstChild));r.debug("Asynchronously requesting "+t)},n.addScriptSync=function(t,e){try{var a="optimizely_synchronous_script_"+Math.floor(1e5*Math.random());if(-1!==t.indexOf('"'))return void r.error("Blocked attempt to load unsafe script: "+t);i.write("<scr"),i.write('ipt id="'+a+'" src="'+t+'"></scr'),i.write("ipt>");var u=i.querySelector("#"+a);if(!u)throw new Error("DocumentEnv.write failed to append script");u.onload=e,u.onerror=function(i){r.warn("Failed to load script ("+t+") synchronously:",i),n.addScriptAsync(t,e)}}catch(i){r.debug("DocumentEnv.write failed for "+t+": "+i.message);return o.request({url:t,async:!1,contentType:"text/plain",success:function(t){new Function(t.responseText)(),e&&e()}}).catch((function(i){r.error("Failed to load "+t+" via synchronous XHR: "+i.message),n.addScriptAsync(t,e)}))}}},8597:function(t,n,e){var i=e(3341).Promise,r=e(3898);n.makeTimeoutPromise=function(t){return new i((function(n,e){r.setTimeout((function(){e(new Error("Timed out after "+t+" ms"))}),t)}))}},7566:function(t,n,e){var i=e(279).isEmpty,r=e(8409),o=e(3898),a=e(1756),u=e(3221),c=e(3941),s=u.get("stores/ua_data");n.get=function(){var t=s.get();return i(t)&&(t=r.parseUA(o.getUserAgent()),c.dispatch(a.SET_UA_DATA,{data:t})),t}},2291:function(t,n){n.guessDomain=function(t,n){if(!t)return"";try{return n?t.match(/:\/\/(.[^/]+)/)[1]:t.match(/:\/\/(?:www[0-9]?\.)?(.[^/:]+)/)[1]}catch(t){return""}}},167:function(t,n){n.generate=function(){var t=new Uint8Array(16);crypto.getRandomValues(t),t[6]=15&t[6]|64,t[8]=63&t[8]|128;var n=Array.prototype.map.call(t,(function(t){return("0"+t.toString(16)).slice(-2)})).join("");return n.slice(0,8)+"-"+n.slice(8,12)+"-"+n.slice(12,16)+"-"+n.slice(16,20)+"-"+n.slice(20)}},9232:function(t,n,e){var i=e(3341).Promise,r=e(1756),o=e(3221),a=e(3941),u=e(525),c=e(9726).v,s=e(6310),f=e(9751),l=e(6388),d=e(8805),v=e(7649),h=e(3898),p=e(2044).VisitorStorageKeys,g=e(3496),m=o.get("stores/xdomain"),b=n.Error=c("XDomainStorageError");function y(t){var n;if(m.getFrames().find((function(n){return n.origin===t.origin}))){try{n=l.parse(t.data)}catch(n){return void g.debug("XDomain","Ignoring malformed message event:",t)}if("ERROR"===n.type)a.dispatch(r.XDOMAIN_SET_DISABLED,{disabled:!0}),u.emitInternalError(new b("Xdomain Error: "+n.response));else if("SYNC"===n.type)e(6494).time("xdomainStorageSync"),m.getSubscribers().forEac
2h((function(t){t(n.response.key,n.response.value)})),e(6494).timeEnd("xdomainStorageSync");else{var i=m.getMessageById(n.id);if(!i){if(g.warn("XDomain","No stored message found for ID",n.id),"number"!=typeof n.id)u.emitInternalError(new b("Message ID is not a number: "+n.id));else{var o=m.getNextMessageId();n.id>=o?u.emitInternalError(new b("Message ID is greater than expected maximum ID ("+n.id+">"+o+")")):n.id<0?u.emitInternalError(new b("Message ID is < 0: "+n.id)):u.emitInternalError(new b("No stored message found for message ID: "+n.id))}return}if(!i.resolver)return void g.warn("XDomain","Message already resolved, ignoring:",n.id);i.resolver(n.response),a.dispatch(r.XDOMAIN_SET_MESSAGE,{messageId:n.id,message:{data:{id:n.id,type:i.data.type,key:i.data.key},startTime:i.startTime,endTime:s.now()}})}}else g.debug("XDomain","No frame found for origin: "+t.origin)}function _(t){return!(["GET","PUT"].includes(t.type)&&!t.key.includes(p.SESSION_STATE))}function w(t,n){return n||(n=m.getDefaultFrame()),new i((function(e){var i={data:Object.assign({},t,{id:m.getNextMessageId(),use_cookie:_(t)}),resolver:e};n?m.isDisabled()||E(i,n):a.dispatch(r.XDOMAIN_SET_MESSAGE,{messageId:i.data.id,message:i})}))}function E(t,n){var e=t.data;a.dispatch(r.XDOMAIN_SET_MESSAGE,{messageId:t.data.id,message:Object.assign({},t,{startTime:s.now()})}),n.target.postMessage(l.stringify(e),n.origin)}n.setItem=function(t,n,e){return w({type:"PUT",key:t,value:n},e)},n.getItem=function(t,n){return w({type:"GET",key:t},n)},n.fetchAll=function(t){return w({type:"GETALL"},t)},n.deleteData=function(t,n){return w({type:"DELETE",visitorId:t},n)},n.subscribe=function(t){a.dispatch(r.XDOMAIN_ADD_SUBSCRIBER,{subscriber:t})},n.loadIframe=function(t,n){return new i((function(i){e(6494).time("xdomainIframeLoad");var o=f.createElement("iframe");o.src=t+n,o.hidden=!0,o.setAttribute("tabindex","-1"),o.setAttribute("title","Optimizely Internal Frame"),o.style.display="none",o.height=0,o.width=0,o.onload=function(){e(6494).timeEnd("xdomainIframeLoad");var u={id:m.getNextFrameId(),target:o.contentWindow,origin:t,path:n};a.dispatch(r.XDOMAIN_ADD_FRAME,u),i(u)},f.appendTo(f.querySelector("body"),o)}))},n.getXDomainUserId=function(t,n){var e,i={},r=Object.keys(t);return n.forEach((function(t){i[t]=[],r.forEach((function(n){var r=v.getUserIdFromKey(n,t);!e&&r&&(e=r),r&&!i[t].includes(r)&&i[t].push(r)}))})),g.debug("XDomain: Found userIds:",i),e},n.load=function(t,e){h.addEventListener("message",y);return d.pollFor((function(){return!!f.querySelector("body")})).then((function(){return n.loadIframe(t,e)})).then((function(t){a.dispatch(r.XDOMAIN_SET_DEFAULT_FRAME,t),m.isDisabled()||m.getMessages().forEac
2h((function(n){n.startTime||E(n,t)}))}))}},4464:function(t,n,e){var i=e(279).isObject,r=e(1756),o=e(6310),a=e(3941),u=e(6388),c=e(3496),s=e(3341).Promise,f=e(2530);n.isCORSSupported=function(){return"withCredentials"in new(f.get("XMLHttpRequest"))},n.request=function(t){return t=Object.assign({method:"GET",async:!0,contentType:"text/plain;charset=UTF-8"},t),new s((function(e,r){if(!n.isCORSSupported())return r("CORS is not supported");var o=new(f.get("XMLHttpRequest"));o.onload=function(){t.success&&t.success(o),e(o)},o.onerror=function(){t.error&&t.error(o),r(o)},i(t.data)&&(t.data=u.stringify(t.data)),o.open(t.method,t.url,t.async),t.withCredentials&&(o.withCredentials=t.withCredentials),o.setRequestHeader("Content-Type",t.contentType),i(t.headers)&&Object.keys(t.headers).forEach((function(n){o.setRequestHeader(n,t.headers[n])})),o.send(t.data)}))},n.retryableRequest=function(t,e,i,u){if(!e)return s.reject(new Error("No id specified for request."));if(!n.isCORSSupported())return s.reject(new Error("CORS is not supported."));void 0===u&&(u=3),void 0===i&&(i=0);var f={id:e,timeStamp:o.now(),data:t,retryCount:i};return a.dispatch(r.SET_PENDING_EVENT,f),c.debug("Sending event ",e),n.request(t).then((function(t){return a.dispatch(r.REMOVE_PENDING_EVENT,{id:e}),t}),(function(t){throw f.retryCount>=u?(a.dispatch(r.REMOVE_PENDING_EVENT,{id:e}),c.warn("Event ",f," could not be sent after ",u," attempts.")):(f.retryCount++,a.dispatch(r.SET_PENDING_EVENT,f),c.debug("Event ",f," failed to send, with error ",t," It will be retried ",u-i," times.")),t}))},n.sendBeacon=function(t,e){if(!t.url||!t.data)return s.reject(new Error("Mu
2st provide required options"));if(navigator&&navigator.sendBeacon)try{var r=i(t.data)?u.stringify(t.data):t.data;if(navigator.sendBeacon(t.url,r))return s.resolve();throw new Error("Unable to sendBeacon")}catch(t){c.warn("sendBeacon failed: ",t)}return t=Object.assign({method:"POST"},t),n.request(t,e)}},3432:function(t,n,e){var i=e(279).isEmpty,r=e(1756),o=e(2030),a=e(4681),u=e(6310),c=e(3221),s=e(2044),f=e(3786),l=e(3941),d=e(3496),v=e(6952),h=c.get("stores/event_data"),p=c.get("stores/plugins"),g=c.get("stores/rum"),m=c.get("stores/view"),b=c.get("stores/view_data");n.parseViewTags=function(t){var e=n.evaluateViewTags(t);n.setParsedViewTags(t.id,e)},n.updateAllViewTags=function(){m.getActiveViewStates().forEach((function(t){var e=b.get(t.id);n.parseViewTags(e)}))},n.evaluateViewTags=function(t){return t.tags?t.tags.reduce((function(t,n){try{t[n.apiName]=v.getTagValue(n)}catch(t){e=t,(null!=(i=v.Error)&&"undefined"!=typeof Symbol&&i[Symbol.hasInstance]?i[Symbol.hasInstance](e):e instanceof i)?d.warn("Page / Ignoring unparseable tag",n,t):d.error(t)}var e,i;return t}),{}):{}},n.createViewTicket=function(){var t={},n=p.getAllPlugins(s.PluginTypes.viewProviders);return Object.keys(n).forEach((function(e){var i=n[e];t[i.provides]=c.evaluate(i.getter)})),t},n.registerViews=function(t,n){l.dispatch(r.REGISTER_VIEWS,{views:t,forceClean:n})},n.activateViaAPI=function(t,e){e&&n.setUserSuppliedViewTags(t.id,e),n.activateMultiple([t],e)},n.getViewsAndActivate=function(t){var e=b.getAllViewsForActivationType(t);n.activateMultiple(e)},n.activateMultiple=function(t,a){var u=[];return t.forEach((function(t){var i=m.getViewState(t.id),c=n.createViewTicket();if(e(6494).time("setupViewActivation"),i.isActive)if(t.deactivationEnabled)try{n.hasValidStaticConditions(t,c)||n.deactivate(t)}catch(e){d.error("Page / Error evaluating whether to deactivate page ",n.description(t),e)}else d.log("Not activating Page, already active ",n.description(t));else{try{if(!n.hasValidStaticConditions(t,c))return"boolean"!=typeof i.isActive&&n.setViewActiveState(t,!1),void d.log("Page / Failed to match page conditions for "+n.description(t),t.staticConditions)}catch(e){return"boolean"!=typeof i.isActive&&n.setViewActiveState(t,!1),void d.error("Page / Error evaluating whether to activate page ",n.description(t),e)}if(u.push(t),n.setViewActiveState(t,!0),d.log("Activated Page",n.description(t)),o.emitViewActivated({view:t,metadata:a}),g.getSampleRum()){var f=t.activationType||s.ViewActivationTypes.immediate;l.dispatch(r.RECORD_ACTIVATION_TYPE_USAGE,{activationType:f,entityId:t.id})}var v=h.getByPageId(t.id);n.toggleEventImplementations(v,!0),e(6494).timeEnd("setupViewActivation")}})),i(u)||o.emitViewsActivated({views:u}),u},n.deactivate=function(t){if(m.getViewState(t.id).isActive){n.setViewActiveState(t,!1),d.log("Deactivated Page",n.description(t)),o.emitPageDeactivated({page:t});var e=h.getByPageId(t.id);n.toggleEventImplementations(e,!1)}else d.log("Not deactivating Page, already inactive ",n.description(t))},n.setViewActiveState=function(t,n){l.dispatch(r.SET_VIEW_ACTIVE_STATE,{view:t,timestamp:u.now(),isActive:n})},n.setGlobalTags=function(t){l.dispatch(r.SET_GLOBAL_TAGS,t)},n.setParsedViewTags=function(t,n){l.dispatch(r.UPDATE_PARSED_VIEW_METADATA,{pageId:t,metadata:n})},n.setUserSuppliedViewTags=function(t,n){l.dispatch(r.UPDATE_USER_SUPPLIED_METADATA,{pageId:t,metadata:n})},n.hasValidStaticConditions=function(t,n){var e={};if(i(t.staticConditions))return!0;var o=p.getAllPlugins(s.PluginTypes.viewMatchers);d.groupCollapsed("Page / Evaluating staticConditions:",t.staticConditions),d.debug("Matching to current value:",n);var u=a.evaluate(t.staticConditions,(function(t){var i=t.type,r=o[i];if(!r)throw new Error("Page / No matcher found for type="+i);return r&&(e[t.type]||(e[t.type]=!0)),r.match(n,t)}));return d.groupEnd(),g.getSampleRum()&&u&&l.dispatch(r.RECORD_VIEW_FEATURE_USAGE,{featuresUsed:Object.keys(e),entity
2Id:t.id}),u},n.description=function(t){return'"'+t.name+'" ('+t.id+")"},n.toggleEventImplementations=function(t,n){t.forEach((function(t){if(t.eventType!==f.CUSTOM){var e=p.getPlugin(s.PluginTypes.eventImplementations,t.eventType);e?n?e.attach(t):e.detach(t):d.warn("No implementation found for event type:",t.eventType,"needed for event:",t)}}))},n.isActivationTypeImmediate=function(t){return t===s.ViewActivationTypes.immediate||!t},n.shouldTriggerImmediately=function(t){return t===s.ViewActivationTypes.DOMChanged||t===s.ViewActivationTypes.URLChanged||t===s.ViewActivationTypes.immediate||!t},n.create=function(t,n){return{id:t,apiName:n,category:"other"}},n.createState=function(t){return{id:t,isActive:!0,metadata:{},parsedMetadata:{},userSuppliedMetadata:{}}}},7649:function(t,n,e){Object.defineProperty(n,"u",{value:!0});var i,r=e(279),o=(r.flattenDeep,r.isEmpty),a=r.omit,u=r.omitBy,c=e(3941),s=e(1756),f=e(6027),l=e(9990),d=e(2658),v=e(6310),h=e(3221),p=e(2044),g=e(6388),m=e(2103),b=e(7429).DT,y=e(3496),_=e(3341).Promise,w=e(950),E=e(2044).VisitorStorageKeys,I=e(3898);i=e(9232);var A=h.get("stores/cookie_options"),T=h.get("stores/global"),S=h.get("stores/layer"),O=h.get("stores/plugins"),k=h.get("stores/rum"),R=h.get("stores/session"),C=h.get("stores/visitor_id"),D=h.get("stores/visitor_bucketing"),N=h.get("stores/visitor"),x=h.get("stores/provider_status");function j(t){e(6494).time("visitorData");var n=L(t),i=b.getItem(n);return e(6494).timeEnd("visitorData"),"string"==typeof i&&(i=z(i)),i}function P(t){var n;if(t.layerId)n=t;else{var e=t;n={layerId:e.i,pageId:e.p,decisionTimestamp:e.t,decisionTicket:{audienceIds:e.a||[]},decision:{layerId:e.i,experimentId:e.x||null,variationId:e.v||null,isLayerHoldback:e.h||!1}}}return n}function M(t,n,e){try{var r=L(t);e||(n=g.stringify(n));try{b.setItem(r,n)}catch(t){throw y.warn("Visitor / Unable to set localStorage key, error was:",t),new Error("Unable to set localStorage")}i.setItem(r,n)}catch(t){y.warn("Unable to persist visitor data:",t.message)}}function L(t){var e=C.getBucketingId();if(!e)throw new Error("Visitor bucketingId not set");var i=n.getNamespace();if(!i)throw new Error("Namespace is not set");return[e,i,t].join("$$")}function V(t,e){if(!C.getBucketingId())throw new Error("Cannot update local store because bucketingId not set");if(U(t)){var i=n.getStorageKeyFromKey(t);if(Object.keys(E).some((function(t){return E[t]===i}))){var r=L(i);if(!(t.indexOf(r)<=0)&&(e=z(e)))if(i===E.EVENT_QUEUE)c.dispatch(s.SET_FOREIGN_VISITOR_EVENT_QUEUE,{key:t,value:l.deserialize(e)});else if(i===E.EVENTS)c.dispatch(s.SET_FOREIGN_VISITOR_EVENTS,{key:t,value:l.deserialize(e)});else if(i===E.LAYER_STATES)c.dispatch(s.LOAD_PERSISTED_LAYER_STATES,{layerStates:e.map(P),merge:!0});else if(i===E.VARIATION_MAP)c.dispatch(s.MERGE_VARIATION_ID_MAP,{variationIdMap:e});else if(i===E.CONTEXTUAL_MAB)c.dispatch(s.MERGE_CONTEXTUAL_MAB_MAP,{contextualMabMap:e});else if(i===E.VISITOR_PROFILE){var a=e;["custom"].forEach((function(t){var n=O.getPlugin(p.PluginTypes.visitorProfileProviders,t);if(n){if(a.profile&&a.metadata){var e=function(t,n,e){var i=N.getAttribute(n),r=N.getAttributeMetadata(n),a=t.profile[n],u=t.metadata[n];if(o(i))return{data:a,metadata:u};var c={};return Object.keys(a).forEach((function(t){var n,i,o=a[t];r&&r[t]&&(n=r[t].lastModified),u&&u[t]&&(i=u[t].lastModified),(e===p.AttributionTypes.FIRST_TOUCH&&n>=i||e===p.AttributionTypes.LAST_TOUCH&&i>=n||void 0===n&&i)&&(c.data=c.data||{},c.data[t]=o,i&&(c.metadata=c.metadata||{},c.metadata[t]=c.metadata[t]||{},c.metadata[t].lastModified=i))})),c}(a,t,n.attributionType);if(!o(e)){var i=[];Object.keys(e.data||{}).forEach((function(n){var r=e.data[n],o=e.metadata[n],a={key:[t,n],value:r,metadata:o};i.push(a)})),c.dispatch(s.SET_VISITOR_ATTRIBUTES,{attributes:i})}}}else y.debug("Attribute type",t,"not used by any audiences")}))}}}}function U(t){return t.split("$$")[0].indexOf("://")>0}function z(t){try{return g.parse(t)}catch(n){return y.debug("Failed to parse: ",t,n),null}}n.getOrGenerateId=function(){return{randomId:n.getCurrentId()||"oeu"+v.now()+"r"+Math.random()}},n.getCurrentId=function(){var t=C.getVisitorIdLocator();return N.getVisitorIdFromAPI()||(t?function(t){var n,e=t.name;switch(t.type){case p.VisitorIdLocatorType.COOKIE:n=d.get(e);break;case p.VisitorIdLocatorType.JS_VARIABLE:n=I.getGlobalByPath(e);break;case p.VisitorIdLocatorType.LOCALSTORAGE:try{n=I.getGlobal("localStorage").getItem(e)}catch(t){throw new Error("Unable to read localStorage: "+t.toString())}break;case p.VisitorIdLocatorType.QUERY:n=w.getQueryParamValue(e)}
2try{if(!n)throw y.error("Visitor / Customer provided visitor id cannot be found. Type:",t.type," Name:",e),new Error("Failure to obtain visitor id from "+t.type);if("string"!=typeof n&&"number"!=typeof n)throw y.error("Visitor / Customer provided visitor id is not a string or number. Type:",t.type," Name:",e," Id Type:",(i=n)&&"undefined"!=typeof Symbol&&i.constructor===Symbol?"symbol":typeof i),new Error("Customer provided visitor id is not a string or number")}catch(t){throw k.getSampleRum()&&c.dispatch(s.RECORD_VISITOR_ID_ERROR,{isError:!0}),t}var i;k.getSampleRum()&&(c.dispatch(s.RECORD_VISITOR_ID_ERROR,{isError:!1}),c.dispatch(s.RECORD_VISITOR_ID_LOCATOR_USAGE,{visitorIdLocatorType:t.type,entityId:n}));return String(n)}(t):d.get(p.COOKIES.VISITOR_ID))},n.hasSomeData=function(){return b.keys().length>0},n.setId=function(t){var r,o,a=C.getBucketingId();c.dispatch(s.SET_VISITOR_ID,t),C.getBucketingId()!==a&&(!function(){e(6494).time("viewLoadData"),function(t,n){0;e(6494).time("loadBehaviorEvents"),f.initializeStore(t,n),e(6494).timeEnd("loadBehaviorEvents")}(j(E.EVENTS)||[],j(E.EVENT_QUEUE)||[]);var t=(i=E.LAYER_STATES,r=C.getBucketingId(),o=[],a=new RegExp(r+"\\$\\$([^$]+?)\\$\\$"+i),b.keys().forEach((function(t){var n=t.match(a);if(n){var e={namespace:n[1],userId:r,item:z(b.getItem(t))};o.push(e)}})),o);var i,r,o,a;t.forEach((function(t){t.item=(t.item||[]).map(P)})),l=t,d=[],l.forEach((function(t){t.item.forEach((function(n){n.namespace=t.namespace,d.push(n)}))})),h=d,c.dispatch(s.LOAD_PERSISTED_LAYER_STATES,{layerStates:h.filter((function(t){return!!t.decision}))}),v=j(E.SESSION_STATE)||{},v=Object.assign({lastSessionTimestamp:0,sessionId:null},v),c.dispatch(s.LOAD_SESSION_STATE,v),function(t){var n,e,i=O.getAllPlugins(p.PluginTypes.visitorProfileProviders),r=Object.keys(i||{}).map((function(t){return i[t]})).filter((function(t){return"function"==typeof t.restorer}));t.profile&&t.metadata?(n=t.profile,e=t.metadata):(n=t,e={});n=Object.keys(n||{}).reduce((function(t,e){var i=n[e],o=i,a=r.find((function(t){return t.provides===e}));return a&&(o=a.restorer(i)),t[e]=o,t}),{}),c.dispatch(s.LOAD_EXISTING_VISITOR_PROFILE,{profile:n,metadata:e})}(j(E.VISITOR_PROFILE)||{});var u=j(E.TRACKER_OPTIMIZELY);u&&(g=u,c.dispatch(s.SET_TRACKER_PERSISTABLE_STATE,g));var l,d;var v;var h;var g;n.loadForeignData(),n.removeLegacySessionStateCookies()}(),n.deleteOldLocalData(),i.deleteData(t));try{C.getVisitorIdLocator()||n.maybePersistVisitorId(t)}catch(t){if(y.error("Visitor / Unable to persist visitorId, disabling tracking"),c.dispatch(s.LOAD_DIRECTIVE,{trackingDisabled:!0}),r=t,null!=(o=d.MismatchError)&&"undefined"!=typeof Symbol&&o[Symbol.hasInstance]?o[Symbol.hasInstance](r):r instanceof o)throw y.error("Visitor / Cookie not set to correct value:",t),new Error("Cookie mismatch error while persisting visitorId");throw t}n.refreshSession()},n.getVariationIdMap=function(){return j(E.VARIATION_MAP)||{}},n.updateVariationIdMap=function(t,n,e){c.dispatch(s.UPDATE_VARIATION_ID_MAP,{layerId:t,experimentId:n,variationId:e})},n.persistVariationIdMap=function(){var t=D.getVariationIdMapString();M(E.VARIATION_MAP,t,!0)},n.getContextualMabMap=function(){return j(E.CONTEXTUAL_MAB)||{}},n.updateContextualMabMap=function(t,n,e){c.dispatch(s.UPDATE_CONTEXTUAL_MAB_MAP,{layerId:t,experimentId:n,variationData:e})},n.persistContextualMabMap=function(){var t=D.getContextualMabMapString();M(E.CONTEXTUAL_MAB,t,!0)},n.getOdpSegments=function(){return j(E.ODP_SEGMENTS)||{}},n.persistOdpSegments=function(t){M(E.ODP_SEGMENTS,t,!1)},n.getPreferredLayerMap=function(){return j(E.LAYER_MAP)||{}},n.updatePreferredLayerMap=function(t,n){c.dispatch(s.UPDATE_PREFERRED_LAYER_MAP,{groupId:t,layerId:n})},n.persistTrackerOptimizelyData=function(t){M(E.TRACKER_OPTIMIZELY,t)},n.refreshSession=function(){c.dispatch(s.REFRESH_SESSION)},n.populateEagerVisitorData=function(t,i){e(6494).time("populateEagerVisitorData");var r=Object.keys(t||{}).reduce((function(n,e){var i=t[e];return i.isLazy||n.push(i),n}),[]),o=n.populateVisitorData(r,i);return e(6494).time("populateEagerVisitorData"),o}
2,n.populateLazyVisitorData=function(t,e,i){y.debug("Populating lazy visitor. List of all providers",t);var r=Object.keys(t||{}).reduce((function(n,e){var r=t[e];return r.isLazy&&i.has(r.provides)&&n.push(r),n}),[]);return y.debug("Filtered list of providers that are lazy and present in audiences",r),n.populateVisitorData(r,e)},n.populateVisitorData=function(t,n){n=n||{};var i=function(t){return function(t,n){var e=function(t,e){var i;n.attributionType&&(i=v.now()),c.dispatch(s.SET_VISITOR_ATTRIBUTES,{attributes:[{key:t,value:e,metadata:{lastModified:i}}]})};if(!n.getter)return;var i=n.provides;Array.isArray(i)||(i=[i]),!1;var r,o=n.isSticky&&void 0!==m.getFieldValue(t,i);if(o)return;try{var a=h.evaluate(n.getter);"function"==typeof a&&(a=a((function(){return m.getFieldValue(t,i)}),(function(t){e(i,t)}))),void 0!==a&&(n.isAsync?(r=a.then((function(t){e(i,t)}),(function(t){y.warn('Failed to evaluate provider for "'+n.provides+'"; error was:',t)})),c.dispatch(s.SET_VISITOR_ATTRIBUTE_PENDING,{key:i,pending:r})):e(i,a))}catch(t){y.warn('Failed to evaluate getter for provider for "'+n.provides+'"; error was: '+t.message)}return r}(n,t)},r=t.filter((function(t){return!0===t.isAsync})).map(i).filter(Boolean);return e(6494).time("visitorSyncProviders"),t.filter((function(t){return!t.isAsync})).forEach(i),e(6494).timeEnd("visitorSyncProviders"),r.length>0?_.all(r):_.resolve()},n.persistBehaviorEvents=function(t){M(E.EVENTS,t)},n.persistBehaviorEventQueue=function(t){M(E.EVENT_QUEUE,t)},n.getPersistedBehaviorEventCount=function(){var t=j(E.EVENTS)||[],n=j(E.EVENT_QUEUE)||[];return l.deserialize(t).length+l.deserialize(n).length},n.persistLayerStates=function(){var t=S.getLayerStates(n.getNamespace());t=t.map((function(t){return a(t,["namespace"])})),M(E.LAYER_STATES,t)},n.persistSessionState=function(){M(E.SESSION_STATE,R.getState())},n.persistVisitorProfile=function(){var t=function(){var t=N.getVisitorProfile(),n=N.getVisitorProfileMetadata(),e=O.getAllPlugins(p.PluginTypes.visitorProfileProviders);if(e){var i=Object.keys(e).reduce((function(t,n){var i=e[n];return i.provides&&(t[i.provides]=i),t}),{});t=u(t,(function(t,n){var e=i[n];return e&&e.isTransient}))}return{profile:t,metadata:n}}();M(E.VISITOR_PROFILE,t)},n.persistVisitorBucketingStore=function(){var t;n.persistVariationIdMap(),t=D.getPreferredLayerMapString(),M(E.LAYER_MAP,t,!0),n.persistContextualMabMap()},n.getUserIdFromKey=function(t,e){var i;return t.includes(e)&&t.includes("_")&&t.includes("$$")&&t.slice(t.indexOf("$$")).includes(n.getNamespace())&&(i=t.slice(t.indexOf("_")+1,t.indexOf("$$"))),i},n.maybePersistVisitorId=function(t){t.randomId&&(A.getAutoRefresh()||n.getCurrentId()!==t.randomId?(d.set(p.COOKIES.VISITOR_ID,t.randomId),y.log("Persisting visitorId:",t.randomId)):y.log("Not persisting visitorId: value is not changed and also auto-refresh is disabled"))},n.getAttribute=function(t){return N.getAttribute(t)},n.getPendingAttributeValue=function(t){return x.getPendingAttributeValue(t)},n.isForeignKey=U,n.checkKeyForVisitorId=function(t){var e=C.getBucketingId()||n.getCurrentId(),i=n.getIdFromKey(t);return!i||i===e},n.getIdFromKey=function(t){var e=t.split("$$")[0],i=n.getStorageKeyFromKey(t);if(Object.keys(p.StorageKeys).some((function(t){return p.StorageKeys[t]===i})))return null;var r=e.indexOf("_");return-1===r?e:e.substring(r+1)},n.getStorageKeyFromKey=function(t){var n,e=t.split("$$").pop();if(e.indexOf("://")>-1){var i=e.indexOf("_");n=e.substring(i+1)}else n=e;return Object.keys(p.AllStorageKeys).some((function(t){return p.AllStorageKeys[t]===n}))?n:null},n.deleteOldLocalData=function(){b.keys().forEach((function(t){n.isForeignKey(t)||n.checkKeyForVisitorId(t)||b.removeItem(t)}))},n.deleteOldForeignData=function(){b.keys().forEach((function(t){n.isForeignKey(t)&&b.removeItem(t)}))},n.loadForeignData=function(){b.keys().forEach((function(t){var n=b.getItem(t);n&&V(t,n)}))},n.getNamespace=function(){return T.getNamespace()},n.serializeFieldKey=function(t){return Array.isArray(t)?t.join("$$"):t},n.removeLegacySessionStateCookies=function(){var t=d.getAll();Object.keys(t).forEach((function(t){0===t.indexOf(p.COOKIES.SESSION_STATE+"$$")&&d.remove(t)}))}},9663:function(t,n,e){var i=e(2044);n.normalizeClientData=function(t){!t.listTargetingKeys&&t.listTargetingCookies&&(t.listTargetingKeys=t.listTargetingCookies.map((function(t){return{type:i.ListTargetingKeyTypes.COOKIE,key:t}})),delete t.listTargetingCookies)}},8558:function(t,n,e){e(3221).register("env/jquery",e(3790))},3790:function(t,n,e){var i,r=e(3898);try{i=$}catch(t){}var o=r.getGlobal("$");t.exports=i&&i!==o?i:o},8813:function(t,n,e){e(279).isEmpty,e(9726).C,e(6310);var i=e(3221),r=(e(9751),e(3496));e(3898),e(4464),i.get("stores/client_metadata"),i.get("stores/global");n.handleError=function(t,n){n?r.error("Uncaught error:",t,n):r.error("Uncaught error:",t)}},3160:function(t,n,e){var i=e(279),r=i.cloneDeep,o=i.isEmpty,a=e(7065);n.transformVisibilityAttributesToCSS=function(t){if(!t.attributes)return t;if(t.attributes[a.selectorChangeType.HIDE]||t.attributes[a.selectorChangeType.REMOVE]){var n=Object.assign({css:{}},r(t));return t.attributes[a.selectorChangeType.HIDE]&&(n.css.visibility="hidden",delete n.attributes[a.selectorChangeType.HIDE]),t.attributes[a.selectorChangeType.REMOVE]&&(n.css.display="none",delete n.attributes[a.selectorChangeType.REMOVE]),n}return t},n.createStylesFromChange=function(t,n){if(o(n.css))return n.attributes.style;var e="",i=n.attributes.style||"";
2return Object.keys(n.css).forEach((function(t){var r=n.css[t];new RegExp(t+"\\s?:").test(i)||(e+=t+":"+r+";")})),void 0===n.attributes.style?(t||"")+e:e+i}},7601:function(t,n,e){var i=e(279),r=i.cloneDeep,o=i.isEmpty,a=e(1756),u=e(525),c=e(7042),s=e(9726).v,f=e(1894),l=e(1623),d=e(3221).get("stores/directive"),v=e(9751),h=e(7065),p=e(3941),g=e(3496),m=e(4462),b=(e(3341).Promise,e(1148)),y=e(6903),_=e(3898),w=e(3160),E=e(6139).create(),I={attributes:!0,childList:!0,subtree:!0,characterData:!0},A=1e3,T=10,S=s("ChangeOverheatError");function O(t,n){if(this.change=r(t),this.change=w.transformVisibilityAttributesToCSS(this.change),this.identifier=n.identifier,this.startTime=n.startTime,this.disconnectObserverQueue=[],d.shouldObserveChangesIndefinitely()){p.dispatch(a.INITIALIZE_CHANGE_METRICS),this.rateMeter=new b(A);var e=null===T?Number.POSITIVE_INFINITY:T;this.rateMeter.addListener(e,function(){g.warn("AttributeChange",this,"has overheated and will no longer apply or reapply"),this.cancel(),p.dispatch(a.RECORD_CHANGE_OVERHEATED),u.emitError(new S("Change "+this.identifier+" has overheated"),{layerId:n.action&&n.action.layerId,experimentId:n.action&&n.action.experimentId,variationId:n.action&&n.action.variationId,changeId:t.id,changeType:t.type,movingWindowMilliseconds:A,maxMacroTasksInMovingWindow:e})}.bind(this));for(var i=Math.min(e,50),o=0;o<=i;o++)this.rateMeter.addListener(o,function(t){return function(){p.dispatch(a.RECORD_CHANGE_MACROTASK_RATE,{changeMacrotaskRate:t})}}(o))}this.cancelled=!1}O.prototype.apply=function(){this.applyDeferred=l();try{if(o(this.change.attributes)&&o(this.change.css))return g.debug("Not applying empty AttributeChange"),this.applyDeferred.resolve(),this.applyDeferred;var t=this.applyDeferred.reject.bind(this.applyDeferred,new Error("Unable to find selector.")),n={};d.shouldObserveChangesUntilTimeout()?n={timeout:y.isTimedOut.bind(null,this.startTime),onTimeout:t}:d.isEditor()&&E.waitUntil(y.isTimedOut.bind(null,this.startTime)).then(t,t),this.unobserveSelector=E.observeSelector(this.change.selector,this.maybeApplyToElement.bind(this),n);var e=v.querySelectorAll(this.change.selector),i=this;e.forEach((function(t){i.maybeApplyToElement(t)}))}catch(t){this.applyDeferred.reject(t)}return this.applyDeferred},O.prototype.maybeApplyToElement=function(t){var n=c.CHANGE_ID_ATTRIBUTE_PREFIX+this.change.id;if(t.hasAttribute(n))return g.debug("AttributeChange not being applied. Element already changed, or is a child of an element that was changed by this AttributeChange. "+t),void this.applyDeferred.resolve();this.rateMeter&&this.rateMeter.countCurrentTick();var e=this.applyOrReapplyToElement.bind(this,t,n);e(!1);var i=function(){var n=function(){this.cancelled||m.observe(r,t,I)}.bind(this);return _.setTimeout(n)}.bind(this),r=m.create(function(){this.rateMeter&&this.rateMeter.countCurrentTick(),r.disconnect(),e(!0),i()}.bind(this));i(),this.disconnectObserverQueue.push(r.disconnect.bind(r)),this.applyDeferred.resolve()},O.prototype.applyOrReapplyToElement=function(t,n,e){void 0===e&&(e=!1);var i={},r=this.change.attributes;Object.keys(r).forEach((function(e){var o=r[e];switch(e){case h.selectorChangeType.CLASS:void 0!==t.className&&(i[h.selectorChangeType.CLASS]=t.getAttribute("class")||"",t.setAttribute("class",o));break;case h.selectorChangeType.HREF:void 0!==t.getAttribute("href")&&(i[h.selectorChangeType.HREF]=t.getAttribute("href")||"",t.setAttribute("href",o));break;case h.selectorChangeType.HTML:void 0!==t.innerHTML&&(i[h.selectorChangeType.HTML]=t.innerHTML,t.innerHTML=o,v.childrenOf(t).forEach((function(t){t.setAttribute(n,"")})));break;case h.selectorChangeType.SRC:void 0!==t.src&&(i[h.selectorChangeType.SRCSET]=t.src,t.srcset=o);break;case h.selectorChangeType.SRCSET:void 0!==t.srcset&&(i[h.selectorChangeType.SRCSET]=t.srcset,t.srcset=o);break;case h.selectorChangeType.STYLE:break;case h.selectorChangeType.TEXT:void 0!==t.textContent&&(i[h.selectorChangeType.TEXT]=t.textContent,t.textContent=o);break;default:throw new Error("Unrecognized attribute: "+e)}}));var o=w.createStylesFromChange(t.style.cssText,this.change);"string"==typeof o&&(i[h.selectorChangeType.STYLE]=t.style.cssText,t.style.cssText=o),t.setAttribute(n,""),e&&f.hasData(t)||f.setData(t,this.change.id,this.identifier,i)},O.prototype.cancel=function(){this.cancelled=!0,this.unobserveSelector&&this.unobserveSelector(),this.disconnectObserverQueue.forEach((function(t){try{t()}catch(t){}}))},t.exports=function(t){t.registerChangeApplier(h.changeType.ATTRIBUTE,O)}},936:function(t,n,e){var i=e(1623),r=e(7065),o=e(3816);function a(t,n){if("function"!=typeof t.value)throw new Error("Custom code must be a function");this.change=t}a.prototype.apply=function(){var t=i();try{o.apply(this.change.value),t.resolve()}catch(n){t.reject(n)}return t}
2,t.exports=function(t){t.registerChangeApplier(r.changeType.CUSTOM_CODE,a)}},2571:function(t,n,e){var i=e(3341).Promise,r=e(1756),o=e(2044),a=e(2658),u=e(9751),c=e(7065),s=e(3941),f=e(123),l=e(3496),d=e(3898);function v(t,n){if(this.change=t,this.config=n,this.reasonShouldNotRedirect=this._checkForReasonNotToRedirect(),!this.reasonShouldNotRedirect){if("string"!=typeof this.change.dest&&"function"!=typeof this.change.dest)throw new Error("Redirect destination is not a string or function");if("function"==typeof this.change.dest&&(this.change.dest=this.change.dest(),"string"!=typeof this.change.dest))throw new Error("Redirect destination function did not evaluate to a string");if(!this.config.action)throw new Error("Redirect changes require an action to be passed in the config.");s.dispatch(r.ANNOUNCE_PENDING_REDIRECT,{layerId:this.config.action.layerId})}}v.prototype.apply=function(){if(this.reasonShouldNotRedirect)return l.warn("Redirect change is not being applied because: "+this.reasonShouldNotRedirect),i.resolve(c.changeState.IGNORED);if(this.reasonInvalid)return l.error("Redirect change cannot be applied because:",this.reasonInvalid),i.reject(this.reasonInvalid);try{this.Te(this.Se())}catch(t){return i.reject(t)}return i.resolve(c.changeState.APPLIED)},v.prototype.Se=function(){var t=f.create(this.change.dest);if(this.change.preserveParameters){var n=d.getLocation().search;n&&(t.search?t.search+="&"+n.substr(1):t.search=n)}return t.toString()},v.prototype.Oe=function(){var t=u.createElement("style");t.setAttribute("type","text/css"),t.innerHTML="body{display:none;visibility:hidden;}",u.appendToHead(t)},v.prototype.Te=function(t){(!this.change.hasOwnProperty("hidePage")||this.change.hidePage)&&this.Oe(),d.setLocation(t)},v.prototype._checkForReasonNotToRedirect=function(){var t=a.get(o.COOKIES.REDIRECT);if(!this.change.allowAdditionalRedirect&&void 0!==t)return"Page was already redirected."},t.exports=function(t){t.registerChangeApplier(c.changeType.REDIRECT,v)}},7895:function(t,n,e){var i=e(1623),r=e(7065),o=e(879);e(5213);function a(t,n){this.change=Object.assign({},t),this.identifier=n.identifier}a.prototype.apply=function(){var t={type:"showWidget",name:this.change.widget_id,data:this.change};return o.emit(t),i().resolve(r.changeState.APPLIED)},t.exports=function(t){t.registerChangeApplier(r.changeType.WIDGET,a)}},3252:function(t,n,e){var i=e(279),r=i.isEmpty,o=i.pick,a=e(8917),u=e(6271),c=e(229).b,s=e(3496),f={decideLayer:function(t,n){if(!t.decisionMetadata||!t.decisionMetadata.experimentPriorities)throw new c("No decisionMetadata.experimentPriorities on layer.");for(var e=t.decisionMetadata.experimentPriorities.length,i=0;i<e;i++){var r=l(t,n.audienceIds,n.bucketingId,i);if(r){var o=u.selectVariation(r,n.audienceIds,n.bucketingId,n.activationId,n.preferredVariationMap);if(!t.decisionMetadata.offerConsistency||u.hasVariationActionsOnView(o,n.pageId))return{experiment:r,variation:o}}}throw new c("No eligible experiment and variation found.")},includePageIdInDecisionTicket:function(t){return!(!t.decisionMetadata||!t.decisionMetadata.offerConsistency)}};function l(t,n,e,i){var f,l,d=t.decisionMetadata.experimentPriorities[i],v=null==(f=t.decisionMetadata)?void 0:f.experimentWeights,h=(d||[]).reduce((function(e,i){var r=u.getExperimentById(t,i.entityId);return u.isValidExperiment(n,r)&&e.push(i.entityId),e}),[]);if(!r(h)){var p,g=(d||[]).filter((function(t){return null===t.endOfRange}));if(g&&g.length===d.length){if(p=function(t){for(var n=[],e=Math.round(1e4/t.length),i=0;i<t.length;i++)n.push({entityId:t[i],endOfRange:e*(i+1)});return n[n.length-1].endOfRange=1e4,n}(h),!r(v))try{p=function(t,n){var e=0,i=Object.values(n).reduce((function(t,n){return t+n}),0);if(i>1e4)throw new c("Weight distribution is invalid. Total weights: "+i);return t.map((function(r,o){var a=n[r];if("number"!=typeof a)throw new c("Weight distribution for Experiment ID "+r+" not found.");return i<1e4&&(a=o===t.length-1?1e4-e:Math.floor(n[r]/i*1e4)),{entityId:r,endOfRange:e+=a}}))}(h,o(v,h))}catch(t){s.warn("Decider / Could not allocate user-defined weights",t)}}else{if(0!==g.length)throw new c("You must specify weights for all or none of the experiments in each priority layer.");p=function(t,n){for(var e=[],i=[],o=0,a=0;a<n.length;a++){var u=0;t.indexOf(n[a].entityId)>=0&&(o+=u=0===a?n[0].endOfRange:n[a].endOfRange-n[a-1].endOfRange,i.push({entityId:n[a].entityId,points:u}))}if(r(e))for(var c=0,s=0;s<i.length;s++){var f=Math.round(1e4*i[s].points/o)+c;e.push({entityId:i[s].entityId,endOfRange:f}),c=f}return e[e.length-1].endOfRange=1e4,e}(h,d)}if(l=a.chooseWeightedCandidate(e,t.id,p)){var m=u.getExperimentById(t,l);if(m)return m}}}t.exports=function(t){t.registerDecider("equal_priority",f)}}
2,481:function(t,n,e){var i=e(6271),r=e(229).b,o={selectExperiment:function(t,n,e){if(t.experiments.length<1)throw new r("Unable to find experiment to bucket user into");var o=t.experiments[0];if(!i.isValidExperiment(n,o))throw new r('Audience conditions failed for experiment: "'+o.id+'".');return o}};t.exports=function(t){t.registerDecider("single_experiment",o),t.registerDecider("multivariate",o)}},9372:function(t,n,e){var i=function(t){return t&&"undefined"!=typeof Symbol&&t.constructor===Symbol?"symbol":typeof t},r=e(525),o=e(9726).v,a=e(9751),u=e(9655),c=n.Error=o("ClickDelegateError");function s(t){this.handler=t,this.events=[],this.unlistenFn=null,this.clickHandler=function(t){var n=this;this.events.forEach((function(e){try{var o=e.config&&e.config.selector?e.config.selector:e.eventFilter.selector;(function(t,n,e){var o=t.target,a=0;for(;o;){var s;try{s=u(o,n)}catch(u){var f={typeofElementValue:void 0===o?"undefined":i(o),nodeName:o?o.nodeName:null,nodeType:o?o.nodeType:null,targetName:t&&t.target?t.target.nodeName:null,targetType:t&&t.target?t.target.nodeType:null,numParentsTraversed:a,selector:n,errorMessage:u.message,eventId:e.id};return r.emitError(new c("Unable to evaluate match for element"),f),!1}if(s)return!0;o=o.parentElement,a++}return!1})(t,o,e)&&n.handler(e)}catch(t){r.emitError(new c("Unable to handle click for selector"+o+":"+t.message))}}))}.bind(this)}s.prototype.listen=function(){this.unlistenFn=a.addEventListener("click",this.clickHandler,!0)},s.prototype.unlisten=function(){this.unlistenFn&&(this.unlistenFn(),this.unlistenFn=null)},s.prototype.hasEvents=function(){return this.events.length>0},s.prototype.addEvent=function(t){this.events.push(t)},s.prototype.removeEvent=function(t){this.events=this.events.filter((function(n){return n.apiName!==t.apiName}))},t.exports=s},9076:function(t,n,e){var i=e(6967),r=e(9372),o=e(3496),a=e(3432);function u(t){return"apiName: "+t.apiName+", selector: "+t.eventFilter.selector}t.exports=function(t){var n=new r((function(t){a.updateAllViewTags(),i.trackClickEvent(t)?o.log("Tracking click event:",t):o.log("Not tracking click event:",t)}));t.registerEventImplementation("click",{attach:function(t){n.hasEvents()||n.listen(),n.addEvent(t),o.debug("Started listening for click event ("+u(t)+"):",t)},detach:function(t){n.removeEvent(t),n.hasEvents()||n.unlisten(),o.debug("Stopped listening for click event ("+u(t)+"):",t)}})}},6624:function(t,n,e){var i=e(279),r=i.cloneDeep,o=i.flatten,a=e(7512),u=e(7467),c=e(7518);function s(t,n){var e=u.buildFromSpecV0_1(n);if(1!==e.length)throw new Error("Invalid query descriptor; verify that no aggregators are specified");return c.execute(e[0],t)}function f(t,n){return t.map((function(t){return"function"==typeof t.toObject?t.toObject(n):t}))}function l(t,n){if(!t)return["Descriptor not defined"];var e=[];return t.count&&e.push('Unexpected "count" clause specified'),t.pick&&t.pick.modifier&&-1===n.indexOf(t.pick.modifier)&&e.push('Invalid "pick" modifier "'+t.pick.modifier+'"'),(t.filters||[]).forEach((function(t){void 0!==t.modifier&&e.push('Unexpected "filter" modifier "'+t.modifier+'"')})),e.length>0?e:void 0}function d(t,n){var e={revenueAsTag:!1,timeAsTimestamp:!0};if(void 0===n)return f(a.getEvents(t),e);if("number"==typeof n){if(n<=0)throw new Error("Count must be a positive integer, got "+n);return f(a.getEvents(t).slice(-n),e)}var i=l(n,Object.values(u.RECENCY_FILTERS));if(i)throw new Error(i.join("\n"));return f(s(a.getEvents(t),n),e)}function v(t,n){if(!(n=r(n)||{}).pick)throw new Error('No "pick" clause provided in query descriptor');if(!n.pick.name)throw new Error('No field name provided in "pick" clause');n.pick.modifier=n.pick.modifier||u.FREQUENCY_FILTERS.MOST_FREQUENT;var e=l(n,Object.values(u.FREQUENCY_FILTERS));if(e)throw new Error(e.join("\n"));return s(a.getEvents(t),n)}function h(t,n){var e=u.buildFromSpecV0_2(n),i=a.getEvents(t),r=f(c.execute(e,i),{revenueAsTag:!0,timeAsTimestamp:!1});return(n.pick||n.reduce&&"count"===n.reduce.aggregator)&&(r=o(r)),n.reduce&&(r=r[0]),r}t.exports=["stores/visitor_events",function(t){return{getEvents:d.bind(null,t),getByFrequency:v.bind(null,t),query:h.bind(null,t)}}]},9176:function(t,n,e){t.exports=function(t){t.registerApiModule("behavior",e(6624))}},5506:function(t,n,e){t.exports=function(t){t.registerVisitorProfileProvider(e(9853)),t.registerAudienceMatcher("behavior",e(8916))}},8916:function(t,n,e){var i=e(6388),r=e(7467),o=e(7518);t.exports={fieldsNeeded:["events"],match:function(t,n){var e=i.parse(n.value);return(void 0===e.version?[e]:r.buildFromSpecV0_1(e)).every((function(n){return o.isSatisfied(n,t.events)}))}}},9853:function(t,n,e){var i=e(7512);t.exports={provides:"events",isTransient:!0,getter:[function(){return i.getEvents()}]}},2062:function(t,n,e){t.exports=function(t){t.registerVisitorProfileProvider(e(4845))}},4845:function(t,n,e){var i=e(571),r=e(7512),o=e(6388),a=e(7467);t.exports={provides:"customBehavior",shouldTrack:!0,isLazy:!1,getter:["stores/global","stores/visitor_attribute_entity",function(t,n){var e=t.getProjectId(),u=n.getCustomBehavioralAttributes(e).map((function(t){try{return{id:t.id,granularity:i.GRANULARITY.ALL,rule:a.buildFromSpecV0_2(o.parse(t.rule_json))}}catch(t){return}})).filter((function(t){return t})),c=r.getEvents();return i.evaluate(u,c)}]}},8453:function(t,n,e){t.exports=function(t){t.registerVisitorProfileProvider(e(3121)),t.registerAudienceMatcher("first_session",e(6304))}}
2,6304:function(t){t.exports={fieldsNeeded:["first_session"],match:function(t){return!!t.first_session}}},3121:function(t,n,e){var i=e(6085),r=e(7512),o=e(2103).getFieldValue,a=e(8129).CURRENT_SESSION_INDEX;t.exports={provides:"first_session",shouldTrack:!0,getter:[function(){var t=r.getEvents();if(t&&t.length>0){var n=t[0];return o(n,[i.FIELDS.SESSION_INDEX])===a}return!0}]}},571:function(t,n,e){var i=e(3496),r={FIELDS:e(6085).FIELDS},o=e(7467),a=e(7518);n.GRANULARITY={ALL:"all",CURRENT_SESSION:"current_session",LAST_30_DAYS:"last_30_days",LAST_60_DAYS:"last_60_days"},n.evaluate=function(t,e){var u={};if(0===e.length){for(var c=0;c<t.length;c++)u[t[c].id]=t[c].defaultValue;return u}var s=function(t){if(0===t.length)return[];for(var n=t.length-1,e=r.FIELDS.SESSION_ID,i=t[n][e];n>0&&i===t[n-1][e];)n--;return t.slice(n)}(e),f=function(t,n){if(0===t.length||n<=0)return[];var e=+new Date-n*o.MILLIS_IN_A_DAY;e-=e%o.MILLIS_IN_A_DAY;for(var i=t.length;i>0&&e<=t[i-1][r.FIELDS.TIME];)i--;return t.slice(i)}(e,60);for(c=0;c<t.length;c++){var l=t[c],d=e;l.granularity===n.GRANULARITY.CURRENT_SESSION?d=s:l.granularity===n.GRANULARITY.LAST_60_DAYS&&(d=f);try{var v=d;l.rule&&(v=a.execute(l.rule,d)),u[l.id]=l.defaultValue,1===v.length?u[l.id]=v[0][0]||l.defaultValue:i.debug("Behavior / Rule for",l.id,"returned",v.length,"results, expected 1")}catch(t){i.error("Behavior / Rule for",l.id,"failed with",t.message||"")}}return u}},7512:function(t,n,e){var i=e(9990),r=e(3221).get("stores/visitor_events");n.getEvents=function(){var t=i.getEvents(),n=[].concat.apply([],Object.values(r.getForeignEvents())),e=[].concat.apply([],Object.values(r.getForeignEventQueues())),o=i.mergeAllEvents([t,n,e]);return o.slice(o.length-1e3)}},7467:function(t,n,e){var i=n,r={FIELDS:e(6085).FIELDS,FIELDS_V0_2:e(6085).FIELDS_V0_2},o=e(6388),a=e(3496),u=e(7518);i.MILLIS_IN_A_DAY=864e5,i.aggregateField=function(t,n){return"string"==typeof n&&(n=[n]),n=n||u.DEFAULT_FIELD,[u.generateAlias(t,n)]},i.groupField=function(t){return"string"==typeof t&&(t=[t]),[(t=t||u.DEFAULT_FIELD).join(".")]};var c={"<":"lt","<=":"lte",">":"gt",">=":"gte","=":"eq","==":"eq"};function s(t){return t=(t||"").toString().trim(),c[t]||t}function f(t,n,e){var o={where:n};if(t.count&&(o.limit=t.count),t.modifier===i.FREQUENCY_FILTERS.MOST_FREQUENT){var a=i.getFieldKeyPathForSource(t.name,e),u=i.aggregate("count"),c=i.aggregateField("count"),s=i.groupField(a);return Object.assign(o,{select:[{field:s}],groupBy:i.groupBy([a]),aggregate:[u],orderBy:[{field:c,direction:"DESC"}]})}return Object.assign(o,{orderBy:[{field:[r.FIELDS.TIME],direction:"DESC"}]})}i.fieldComparison=function(t,n,e){return"string"==typeof n&&(n=[n]),"exists"===(t=s(t))?{op:t,args:[{field:n}]}:{op:t,args:[{field:n},{value:e}]}},i.relativeTimeComparison=function(t,n){return{op:s(t),args:[{op:"-",args:[{eval:"now"},{field:[r.FIELDS.TIME]}]},{value:n*i.MILLIS_IN_A_DAY}]}},i.rangeTimeComparison=function(t){return Array.isArray(t)?{op:"between",args:[{field:[r.FIELDS.TIME]},{value:[t[0]||+new Date(0),t[1]||+new Date]}]}:(a.error("Rule builder","rangeTimeComparison passed invalid range",t),null)},i.groupBy=function(t){for(var n=[],e=0;e<t.length;e++)n[e]={field:t[e]};return n},i.aggregate=function(t,n){return"string"==typeof n&&(n=[n]),{op:t,args:[{field:n=n||u.DEFAULT_FIELD}]}},i.SOURCE_TYPES={BEHAVIOR:"events",CUSTOM_BEHAVIOR:"custom_behavior",DCP:"dcp"},i.FREQUENCY_FILTERS={MOST_FREQUENT:"most_frequent",LEAST_FREQUENT:"least_frequent"},i.RECENCY_FILTERS={MOST_RECENT:"most_recent",LEAST_RECENT:"least_recent"},i.getFieldKeyPathForSource=function(t,n){n=n||i.SOURCE_TYPES.BEHAVIOR;var e=[];return"string"==typeof t?(e=[t],n!==i.SOURCE_TYPES.BEHAVIOR||Object.values(r.FIELDS).includes(t)||(e=[r.FIELDS.OPTIONS,t])):e=t,e},i.buildFromSpecV0_1=function(t){if(!(t.action||t.filters&&0!==t.filters.length))throw new Error('Audience spec must have an "action" field or at least one "filter" '+o.stringify(t));var n=i.fieldComparison("gt",r.FIELDS.TIME,0),e=[],u=[];if(t.action&&(u.push(i.fieldComparison("eq",r.FIELDS.NAME,t.action.value)),t.action.type&&u.push(i.fieldComparison("eq",r.FIELDS.TYPE,t.action.type))),t.time)if("last_days"===t.time.type)u.push(i.relativeTimeComparison("lte",t.time.days));else if("range"===t.time.type){var c=i.rangeTimeComparison([t.time.start,t.time.stop]);c&&u.push(c)}else a.error("Rule builder",'Audience spec has bad "time" type',t.time.type);if(n={op:"and",args:u},t.count&&e.push({where:i.fieldComparison(t.count.comparator,"0",t.count.value),from:{select:[{field:i.aggregateField("count")}],where:n,aggregate:[i.aggregate("count")]}}
2),t.filters&&t.filters.forEach((function(o){var a,c,s=i.getFieldKeyPathForSource(o.name,t.source);if(o.modifier===i.FREQUENCY_FILTERS.MOST_FREQUENT?(a=i.aggregate("count"),c=i.aggregateField("count")):o.modifier===i.RECENCY_FILTERS.MOST_RECENT&&(a=i.aggregate("max",r.FIELDS.TIME),c=i.aggregateField("max",r.FIELDS.TIME)),a){var f=s,l=i.groupField(f);e.push({where:i.fieldComparison(o.comparator,"0",o.value),from:{select:[{field:l}],where:n,groupBy:i.groupBy([f]),aggregate:[a],orderBy:[{field:c,direction:"DESC"}],limit:1}})}else u.push(i.fieldComparison(o.comparator,s,o.value))})),t.pick){if(e.length>0)throw new Error('A "pick" clause must not be specified with "count" or "most_recent", "most_frequent" modifiers'+o.stringify(t));return[f(t.pick,n,t.source)]}return e.length>0?e:[{where:n}]},i.buildFromSpecV0_2=function(t){!function(t){var n=[];if(void 0===t)throw new Error("rule is undefined");if("object"!=typeof t||null===t)throw new Error("rule is not an Object");"0.2"!==t.version&&n.push('version: not "0.2"'),t.filter&&(Array.isArray(t.filter)?t.filter.forEach((function(t,e){var r=i.validateFieldKeyPathV0_2(t.field,i.FieldPurpose.FILTER);r&&n.push("filter["+e+"]: "+r);var o=i.validateComparatorAndValue(t.comparator,t.value);o&&n.push("filter["+e+"]: "+o)})):n.push("filter: not an array"));var e=[],r=[];t.sort&&(t.reduce&&t.reduce.aggregator&&"nth"!==t.reduce.aggregator&&n.push("sort: superfluous because we can apply aggregator "+o.stringify(t.reduce.aggregator)+" to unsorted items"),Array.isArray(t.sort)?t.sort.forEach((function(t,a){var u=i.validateFieldKeyPathV0_2(t.field,i.FieldPurpose.SORT);u&&n.push("sort["+a+"]: "+u),t.field&&"frequency"===t.field[0]?e.push(t):r.push(t);var c=function(t){var n="direction "+(o.stringify(t)||String(t));if(!["ascending","descending"].includes(t))return n+' is not "ascending" or "descending"'}(t.direction);c&&n.push("sort["+a+"]: "+c)})):n.push("sort: not an array"),e.length&&r.length&&n.push('sort: sorting by non-["frequency"] field is pointless because we are going to sort the picked values by ["frequency"]'),e.length&&!t.pick&&n.push('sort: sorting by ["frequency"] is impossible because no values have been picked'));if(t.pick){t.reduce&&"count"===t.reduce.aggregator&&n.push('pick: superfluous because we can apply aggregator "count" to raw events');var a=i.validateFieldKeyPathV0_2(t.pick.field);a&&n.push("pick: "+a)}if(t.reduce){var u=t.reduce.aggregator,c="aggregator "+(o.stringify(u)||String(u)),s=t.reduce.n,f="index "+(o.stringify(s)||String(s));["sum","avg","max","min","count","nth"].includes(u)||n.push("reduce: "+c+" is unknown"),["sum","avg","max","min"].includes(u)&&(t.pick||n.push("reduce: "+c+" is impossible to use because no values have been picked")),"nth"===u?(("number"!=typeof s||isNaN(s)||Math.floor(s)!==s||s<0)&&n.push("reduce: "+f+" is not a non-negative integer (mandated by "+c+")"),t.sort||n.push('reduce: aggregator "nth" is meaningless without a specific sort order')):void 0!==s&&n.push("reduce: "+f+" is defined (not mandated by "+c+")")}if(n.length)throw new Error(n.join("\n"))}(t);var n={where:{op:"and",args:(t.filter||[]).map((function(t){return"age"===t.field[0]?i.relativeTimeComparison(t.comparator||"eq",t.value/i.MILLIS_IN_A_DAY):i.fieldComparison(t.comparator||"eq",i.convertFieldKeyPathFromSpecV0_2(t.field),t.value)}))}};if(t.reduce&&"count"===t.reduce.aggregator)return Object.assign(n,{aggregate:[{op:"count",args:[{field:["*"]}]}],select:[{field:["_count_*"]}]});var e=[],r=[];if(t.sort&&(t.sort.forEach((function(t){["ascending","descending"].includes(t.direction)&&(["time","age"].includes(t.field[0])&&r.push(t),"frequency"===t.field[0]&&e.push(t))})),r.length&&!e.length&&(n.orderBy=r.map((function(t){return"time"===t.field[0]?{field:i.convertFieldKeyPathFromSpecV0_2(["time"]),direction:"ascending"===t.direction?"ASC":"DESC"}:"age"===t.field[0]?{field:i.convertFieldKeyPathFromSpecV0_2(["time"]),direction:"ascending"===t.direction?"DESC":"ASC"}:void 0})).filter((function(t){return t})))),t.pick&&t.pick.field){var a=i.convertFieldKeyPathFromSpecV0_2(t.pick.field);if(t.reduce&&["avg","max","min","sum"].includes(t.reduce.aggregator))return Object.assign(n,{aggregate:[{op:t.reduce.aggregator,args:[{field:a}]}],select:[{field:[u.generateAlias(t.reduce.aggregator,a)]}]});n=e.length?Object.assign(n,{groupBy:[{field:a}],aggregate:[{op:"count",args:[{field:["*"]}]}],orderBy:[{field:["_count_*"],direction:"ascending"===e[0].direction?"ASC":"DESC"}],select:[{field:[a.join(".")]}]}):Object.assign(n,{select:[{field:a}]})}if(t.reduce&&"nth"===t.reduce.aggregator){var c=t.reduce.n;if("number"==typeof c&&c>=0&&Number(c)===Math.floor(Number(c)))return Object.assign(n,{offset:c,limit:1})}return n},i.convertFieldKeyPathFromSpecV0_2=function(t){return"tags"===t[0]&&"revenue"===t[1]?["r"]:[r.FIELDS_V0_2[t[0]]].concat(t.slice(1))},i.FieldPurpose={FILTER:"filter",SORT:"sort",PICK:"pick"},i.validateFieldKeyPathV0_2=function(t,n){var e="field "+(o.stringify(t)||String(t));if(!Array.isArray(t)||!t.every((function(t){return"string"==typeof t}
2)))return e+" is not an array of strings";if("tags"===t[0]&&t.length>2||"tags"!==t[0]&&t.length>1)return e+" includes too many strings";if("tags"===t[0]&&t.length<2)return e+" does not specify an exact tag";if(t.length<1)return e+" does not specify a top-level field";var a=Object.keys(r.FIELDS_V0_2),u=["age","frequency"];return n===i.FieldPurpose.FILTER&&(a.push("age"),u=["frequency"]),n===i.FieldPurpose.SORT&&(a=["time","age","frequency"],u=["name","type","category","tags"]),u.includes(t[0])?e+" is not supported here":a.includes(t[0])?void 0:e+" is unknown"},i.validateComparatorAndValue=function(t,n){var e="comparator "+(o.stringify(t)||String(t)),i="value "+(o.stringify(n)||String(n));if("string"!=typeof t&&void 0!==t)return e+" is not a string";switch(t){case void 0:case"eq":case"is":case"contains":break;case"lt":case"gt":case"lte":case"gte":if("number"!=typeof n)return i+" is not a number (mandated by "+e+")";break;case"in":if(!Array.isArray(n))return i+" is not an array (mandated by "+e+")";break;case"between":if(!(Array.isArray(n)&&2===n.length&&"number"==typeof n[0]&&"number"==typeof n[1]&&n[0]<=n[1]))return i+" is not a pair of increasing numbers (mandated by "+e+")";break;case"regex":if(!("string"==typeof n||Array.isArray(n)&&2===n.length&&"string"==typeof n[0]&&"string"==typeof n[1]))return i+" is not a pattern string or a [pattern string, flags string] array (mandated by "+e+")";break;case"exists":if(void 0!==n)return i+" is not undefined (mandated by "+e+")";break;default:return e+" is unknown"}}},7518:function(t,n,e){var i=e(2044),r=e(3496),o=r.log.bind(r),a=e(6310),u=e(2103).getFieldValue,c=e(6388),s=function(t,n,e){if(t.getValueOrDefault)return t.getValueOrDefault(n,e);if(!Array.isArray(n))return e;var i=u(t,n);return void 0===i&&(i=e),i},f=function(t){return"string"==typeof t?t.trim().toLowerCase():t};n.clause={WHERE:"where",GROUP_BY:"groupBy",AGGREGATE:"aggregate",HAVING:"having",ORDER_BY:"orderBy",SELECT:"select",OFFSET:"offset",LIMIT:"limit",FROM:"from"},n.DEFAULT_FIELD=["*"],n.booleanOperators={eq:function(t){var n=t.map(f);return n[0]==n[1]},is:function(t){return t[0]===t[1]},gt:function(t){return t[0]>t[1]},lt:function(t){return t[0]<t[1]},gte:function(t){return t[0]>=t[1]},lte:function(t){return t[0]<=t[1]},in:function(t){return(t[1]||[]).map(f).includes(f(t[0]))},between:function(t){return t[1][0]<=t[0]&&t[0]<=t[1][1]},contains:function(t){var n=t.map((function(t){return"string"==typeof t?t.toLowerCase():t}));return-1!==(n[0]||"").indexOf(n[1])},regex:function(t){try{var n,e;return"string"==typeof t[1]?(n=t[1],e="i"):(n=t[1][0]||"",e=t[1][1]||""),new RegExp(n,e).test(t[0])}catch(t){return r.error("Rules",'In operator "regex", error: '+(t.message||"invalid RegExp /"+[n,e].join("/"))),!1}},exists:function(t){return void 0!==t[0]},and:function(t){return t.every((function(t){return t}))},or:function(t){return t.some((function(t){return t}))},not:function(t){return!t[0]}},n.arithmeticOperators={"+":function(t){return(t[0]||0)+(t[1]||0)},"-":function(t){return(t[0]||0)-(t[1]||0)},"/":function(t){return(t[0]||0)/(t[1]||1)},"%":function(t){return(t[0]||0)%(t[1]||1)}},n.aggregateOperators={sum:function(t,e){for(var i=t[0]||n.DEFAULT_FIELD,r=0,o=0;o<e.length;o++)r+=s(e[o],i,0);return r},avg:function(t,e){if(0===e.length)return 0;for(var i=t[0]||n.DEFAULT_FIELD,r=0,o=0;o<e.length;o++)r+=s(e[o],i,0);return r/e.length},max:function(t,e){for(var i=t[0]||n.DEFAULT_FIELD,r=Number.NEGATIVE_INFINITY,o=0;o<e.length;o++)r=Math.max(r,s(e[o],i,Number.NEGATIVE_INFINITY));return r},min:function(t,e){for(var i=t[0]||n.DEFAULT_FIELD,r=Number.POSITIVE_INFINITY,o=0;o<e.length;o++)r=Math.min(r,s(e[o],i,Number.POSITIVE_INFINITY));return r},count:function(t,n){return n.length}};var l={now:function(){return a.now()}},d=function(t,e){if(e.hasOwnProperty("value"))return e.value;if(e.hasOwnProperty("field"))return s(t,e.field);if(e.hasOwnProperty("eval"))return e.eval in l?l[e.eval]():void r.error("Rules","Unknown function: "+e.eval);if(e.op){var i,o=(i=e.op)in n.booleanOperators?n.booleanOperators[i]:i in n.arithmeticOperators?n.arithmeticOperators[i]:null;if(o){var a=d.bind(null,t);return o((e.args||[]).map((function(t){return a(t)})),t)}r.error("Rules","Unknown operator: "+e.op)}else r.error("Rules","No operator specified: "+c.stringify(e))};n.generateAlias=function(t,n){return"_"+t+"_"+n.join(".")};n.rewrite=function(t){var e=[],i={};function o(t,a){if(Array.isArray(t)&&("and"!==t[0]&&"or"!==t[0]&&"not"!==t[0]&&r.error("Rules","Unexpected operation "+t[0]+". Continuing optimistically."),t={op:t[0],args:t.slice(1)}),t.hasOwnProperty("field")||t.hasOwnProperty("value")||t.hasOwnProperty("eval"))return t;if(a&&t.op in n.aggregateOperators){var u=(t.args&&t.args[0]||{}).field||n.DEFAULT_FIELD,c=n.generateAlias(t.op,u);return c in i||(e.push({op:t.op,args:t.args}),i[c]=!0),{field:[c]}}for(var s=[],f=t.args||[],l=0;l<f.length;l++)s[l]=o(f[l],a);return{op:t.op,args:s}}var a={};t.hasOwnProperty(n.clause.WHERE)&&(a[n.clause.WHERE]=o(t[n.clause.WHERE],!1)),t.hasOwnProperty(n.clause.HAVING)&&(a[n.clause.HAVING]=o(t[n.clause.HAVING],!0)),(t.hasOwnProperty(n.clause.AGGREGATE)||e.length>0)&&(a[n.clause.AGGREGATE]=(t[n.clause.AGGREGATE]||[]).concat(e));for(var u=[n.clause.GROUP_BY,n.clause.ORDER_BY,n.clause.SELECT,n.clause.OFFSET,n.clause.LIMIT],c=0;c<u.length;c++)t.hasOwnProperty(u[c])&&(a[u[c]]=t[u[c]]);return t.hasOwnProperty(n.clause.FROM)&&(a[n.clause.FROM]=n.rewrite(t[n.clause.FROM])),a};var v=function(t,e){e=e||0;var i=[];if(t.hasOwnProperty(n.clause.WHERE)?t[n.clause.WHERE].op?t[n.clause.WHERE].op in n.booleanOperators||i.push("Non-boolean WHERE clause operator"):i.push("Missing WHERE clause operator"):i.push("Missing WHERE clause"),!t.hasOwnProperty(n.clause.HAVING)||(t[n.clause.HAVING].op?t[n.clause.HAVING].op in n.booleanOperators||i.push("Non-boolean HAVING clause operator"):i.push("Missing HAVING clause operator")),t.hasOwnProperty(n.clause.GROUP_BY)&&!t.hasOwnProperty(n.clause.AGGREGATE)&&i.push("No AGGREGATE clause specified with GROUP_BY clause"),t.hasOwnProperty(n.clause.SELECT)){var r=t[n.clause.SELECT];if(Array.isArray(r))for(var o=0;o<r.length;o++)r[o].op&&r[o].op in n.aggregateOperators&&i.push('In SELECT clause, aggregate operator "'+r[o].op+'" specified in selector at index '+o);else i.push("SELECT clause must be an array")}if(t.hasOwnProperty(n.clause.OFFSET)){var a=t[n.clause.OFFSET];("number"!=typeof a||Number(a)<0||Number(a)!==Math.floor(Number(a)))&&i.push("OFFSET must be a non-negative integer")}if(t.hasOwnProperty(n.clause.LIMIT)){var u=t[n.clause.LIMIT];("number"!=typeof u||Number(u)<0||Number(u)!==Math.floor(Number(u)))&&i.push("LIMIT must be a non-negative integer")}return e>0&&(i=i.map((function(t){return"Sub-rule "+e+": "+t}))),t.hasOwnProperty(n.clause.FROM)&&(i=i.concat(v(t[n.clause.FROM],e+1))),i},h=function(t,i){e(6494).time("rulesExecuteRecursively");var a,u,f,l=i;if(t.hasOwnProperty(n.clause.FROM)&&(r.debug("Evaluating FROM clause:",t[n.clause.FROM]),l=h(t[n.clause.FROM],l),r.debug("Results after FROM:",l)),r.debug("Evaluating WHERE clause:",t[n.clause.WHERE]),l=l.filter((function(e){return d(e,t[n.clause.WHERE])})),r.debug("Results after WHERE:",l),t.hasOwnProperty(n.clause.AGGREGATE)){r.debug("Evaluating AGGREGATE clause:",t[n.clause.AGGREGATE]);var v=function(t,n){var e={};if(void 0===t||!Array.isArray(t)||0===t.length)return e["*"]={fieldValues:{},events:n},e;
2for(var i=t.map((function(t){return t.field})),r=0;r<n.length;r++){for(var o=n[r],a=[],u={},f=0;f<i.length;f++){var l=i[f],d=s(o,l),v=l.join(".");u[v]=d,a.push(encodeURIComponent(v)+"="+encodeURIComponent(c.stringify(d)))}var h=a.join("&");e.hasOwnProperty(h)||(e[h]={fieldValues:u,events:[]}),e[h].events.push(o)}return e}(t[n.clause.GROUP_BY],l);l=function(t,n){var e=[];return Object.keys(t).forEach((function(i){var r=t[i],o=Object.assign({},r.fieldValues),a=n[i]||{};Object.assign(o,a),e.push(o)})),e}(v,(a=t[n.clause.AGGREGATE],u=v,f={},Object.keys(u).forEach((function(t){var e=u[t];f[t]={};for(var i=0;i<a.length;i++){var o=a[i],c=o.op;if(c in n.aggregateOperators){var s=(o.args&&o.args[0]||{}).field||n.DEFAULT_FIELD,l=n.generateAlias(c,s),d=n.aggregateOperators[c]([s],e.events);f[t][l]=d}else r.error("Rules","Unknown aggregate operator "+c)}})),f)),r.debug("Results after AGGREGATE:",l)}t.hasOwnProperty(n.clause.HAVING)&&(r.debug("Evaluating HAVING clause:",t[n.clause.HAVING]),l=l.filter((function(e){return d(e,t[n.clause.HAVING])})),r.debug("Results after HAVING:",l)),t.hasOwnProperty(n.clause.ORDER_BY)&&(r.debug("Evaluating ORDER_BY clause:",t[n.clause.ORDER_BY]),l=function(t,n){return Array.isArray(t)?0===t.length?n:n.sort((function(n,e){for(var i=0;i<t.length;i++){var r=t[i],o="ASC"===(r.direction||"ASC")?1:-1,a=r.field,u=s(n,a,0),c=s(e,a,0);if(u<c)return-o;if(u>c)return o}return 0})):(o("Rules","groupBy rule must be an array"),n)}(t[n.clause.ORDER_BY],l),r.debug("Results after ORDER_BY:",l));var p,g=0;return t.hasOwnProperty(n.clause.OFFSET)&&(r.debug("Evaluating OFFSET clause:",t[n.clause.OFFSET]),g=Number(t[n.clause.OFFSET])),t.hasOwnProperty(n.clause.LIMIT)&&(r.debug("Evaluating LIMIT clause:",t[n.clause.LIMIT]),p=g+Number(t[n.clause.LIMIT])),(g>0||void 0!==p)&&(l=l.slice(g,p),r.debug("Results after OFFSET/LIMIT:",l)),t.hasOwnProperty(n.clause.SELECT)&&(r.debug("Evaluating SELECT clause:",t[n.clause.SELECT]),l=function(t,n){return n.map((function(n){return t.map((function(t){return d(n,t)}))}))}(t[n.clause.SELECT],l),r.debug("Results after SELECT:",l)),e(6494).timeEnd("rulesExecuteRecursively"),l};n.execute=function(t,e){t=n.rewrite(t),r.shouldLog(i.LogLevel.DEBUG)&&r.groupCollapsed("Evaluating Behavioral Rule"),r.debug("Rule:",t,c.stringify(t)),r.debug("Events:",e);var o=v(t);if(o.length>0)throw new Error("Rule "+c.stringify(t)+" has violations: "+o.join("\n"));var a=h(t,e);return r.debug("Rule result:",a),r.shouldLog(i.LogLevel.DEBUG)&&r.groupEnd(),a},n.isSatisfied=function(t,e){try{return n.execute(t,e).length>0}catch(n){return r.error("Rules","Error "+n.toString()+" while evaluating rule "+c.stringify(t)),!1}}},3268:function(t,n,e){t.exports=function(t){t.registerDependency("sources/browser_id",e(8605)),t.registerVisitorProfileProvider(e(6065)),t.registerVisitorProfileProvider(e(9310)),t.registerAudienceMatcher("browser_version",e(2762))}},2762:function(t,n,e){var i=e(6036).G;t.exports={fieldsNeeded:["browserVersion","browserId"],match:function(t,n){var e=n.value,r=t.browserId,o=t.browserVersion;if(0===e.indexOf(r)){var a=e.substr(r.length);return 0===i(o,a)}return!1}}},6065:function(t){t.exports={provides:"browserId",shouldTrack:!0,isSticky:!0,getter:["sources/browser_id",function(t){return t.getId()}]}},9310:function(t){t.exports={provides:"browserVersion",getter:["sources/browser_id",function(t){return t.getVersion()}]}},8605:function(t,n,e){var i=e(7566);n.getId=function(){return i.get().browser.id},n.getVersion=function(){return i.get().browser.version}},679:function(t,n,e){t.exports=function(t){t.registerVisitorProfileProvider(e(3670)),t.registerAudienceMatcher("campaign",e(6233))}},6233:function(t,n,e){var i=e(8681);t.exports={fieldsNeeded:["campaign"],match:function(t,n){return i.hasMatch(n.value,n.match,t.campaign)}}},3670:function(t,n,e){var i=e(2122);t.exports={provides:"campaign",shouldTrack:!0,isSticky:!0,getter:[function(){return i.getQueryParamValue("utm_campaign")}]}},6539:function(t,n,e){var i=e(6388),r=e(4464),o=e(3496),a=e(7649),u=e(6484);n.getCmabPredictionUrl=function(){return"https://prediction.cmab.optimizely.com/predict"},n.getCmabPredictionUrlForExperiment=function(t){return t?n.getCmabPredictionUrl()+"/"+t:""},n.createPayload=function(t,n){var e=t.variations.map((function(t){return t.id})),i=u.getVisitorCmabAttributesForExperiment(t),r=u.getDcpCustomerIds(),o=u.getDcpServiceId();return{instances:[{visitorId:a.getCurrentId(),experimentId:t.id,cmabUUID:n,variationIds:e,attributes:i,dcpCustomerIds:r,dcpServiceId:o}]}},n.parseApiResponse=function(t){var n=null;try{n=i.parse(t)}catch(t){return o.error("CMAB API / Error parsing response:",t),null}return n&&n.predictions&&0!==n.predictions.length?n.predictions[0].variation_id.toString():(o.debug("CMAB API / No predictions foun
2d in API response",n),null)},n.validateVariationId=function(t,n){return t&&n.variations.find((function(n){return n.id===t}))?(o.debug("CMAB API / Variation ID:",t,"from API response found in experiment:",n.id),t):(o.debug("CMAB API / Variation ID:",t,"from API response not found in experiment:",n.id),null)},n.handleCmabApiResponse=function(t,e){if(200!==t.status)return o.error("CMAB API / API call failed with status:",t.status,t.statusText),null;o.debug("CMAB API / API call successful:",t.responseText);var i=n.parseApiResponse(t.responseText);return n.validateVariationId(i,e)},n.makeSyncApiCallWithXHR=function(t,e){o.debug("CMAB API / SYNC / Making a synchronous API call to get variation.");try{var r=n.createPayload(t,e),a=n.getCmabPredictionUrlForExperiment(t.id),u=new XMLHttpRequest;return u.open("POST",a,!1),u.send(i.stringify(r)),n.handleCmabApiResponse(u,t)}catch(t){return o.error("CMAB API / SYNC / API call encountered an error:",t),null}},n.makeAsyncApiCall=function(t,e){o.debug("CMAB API / ASYNC / Making an asynchronous API call to get variation.");var i=n.createPayload(t,e),a=n.getCmabPredictionUrlForExperiment(t.id);return r.request({url:a,contentType:"application/json",method:"POST",data:i})}},9819:function(t,n,e){var i=e(3496),r=e(6484),o=e(6539),a=e(7883);n.isCmabEnabled=function(){return!0},n.isExperimentSyncApiEnabled=function(t){return!0},n.defaultToSnippetBucketing=function(t,n,e){i.debug("CMAB / Defaulting to snippet bucketing with variation:",n,"for experiment:",t.id),r.setCmabVariationDataInVisitorStore(t,n,e)},n.processCmabExperiment=function(t,e){if(!n.isCmabEnabled())return i.debug("CMAB / Feature flag is disabled. Defaulting to snippet bucketing with variation:",e,"for experiment:",t.id),e;if(i.debug("CMAB / Feature flag is enabled. Processing CMAB for experiment:",t.id),r.activateTimeoutForNonBucketedCmabExperiments(t),!r.isCmabExperiment(t))return e;var u=r.getCmabVariationDataFromVisitorStore(t);if(!r.isCmabTimeout(t,u))return u.variationId;a.startCmabApiCallPerformanceTimer(t.id);
2var c=null,s=r.generateCmabUUID();if(n.isExperimentSyncApiEnabled(t))try{return(c=o.makeSyncApiCallWithXHR(t,s))?(r.setCmabVariationDataInVisitorStore(t,c,s),a.endCmabApiCallPerformanceTimer(t.id),c):(n.defaultToSnippetBucketing(t,e,s),a.endCmabApiCallPerformanceTimer(t.id),e)}catch(r){return i.error("CMAB API / SYNC / API call failed with error:",r),n.defaultToSnippetBucketing(t,e,s),a.endCmabApiCallPerformanceTimer(t.id),null}return o.makeAsyncApiCall(t,s).then((function(u){return(c=o.handleCmabApiResponse(u,t))?(i.debug("CMAB API / ASYNC / Variation:",c,"for experiment:",t.id),r.setCmabVariationDataInVisitorStore(t,c,s),a.endCmabApiCallPerformanceTimer(t.id),c):(n.defaultToSnippetBucketing(t,e,s),a.endCmabApiCallPerformanceTimer(t.id),e)}),(function(r){return i.error("CMAB API / ASYNC / API call failed with error:",r),n.defaultToSnippetBucketing(t,e,s),a.endCmabApiCallPerformanceTimer(t.id),null})),n.defaultToSnippetBucketing(t,e,s),a.endCmabApiCallPerformanceTimer(t.id),e},n.getCmabUUIDAndUpdateSessionTimeout=function(t,n){var e=r.getCmabUUIDFromVisitorStore(t,n);return r.updateCmabTimeoutInVisitorStore(t,n),e}},7883:function(t,n,e){var i=e(6494),r=e(3496),o=null;n.startCmabApiCallPerformanceTimer=function(t){i.timeEnd("sendCmabApiInitiateCall"),i.time("sendCmabApiCall"),o=(new Date).getTime(),r.debug("CMAB / Performance / API call initiated for experiment: "+t)},n.endCmabApiCallPerformanceTimer=function(t){i.timeEnd("sendCmabApiCall"),r.debug("CMAB / Performance / API call took: "+((new Date).getTime()-o)+"ms for experiment: "+t)}},6484:function(t,n,e){var i=e(279),r=i.omit,o=i.omitBy,a=e(243),u=e(6388),c=e(3221),s=e(167),f=e(7649),l=e(3496),d=e(2044),v=e(1190),h=c.get("stores/visitor"),p=c.get("stores/plugins"),g=c.get("stores/global"),m=c.get("stores/layer");n.generateCmabUUID=function(){return s.generate()},n.getCmabLayerIdFromExperiment=function(t){return t.experimentMetadata&&t.experimentMetadata.layerId||t.id},n.isCmabExperiment=function(t){var n=t.experimentMetadata&&t.experimentMetadata.allocationPolicy;return n?n===d.AllocationPolicyEnum.CMAB:(l.debug("No allocation policy found in metadata for experiment:",t.id),!1)},n.isVisitorInHoldback=function(t){if(g.isGlobalHoldback())return l.debug("CMAB / Visitor is in global holdback, no timeout"),!0;var e=n.getCmabLayerIdFromExperiment(t),i=m.getLayerState(e);return!!(i&&i.decision&&i.decision.isLayerHoldback)&&(l.debug("CMAB / Visitor is in layer holdback, no timeout"),!0)},n.isCmabTimeout=function(t,e){if(!n.isCmabVariationStorageDataValid(e))return l.debug("CMAB / Timeout CMAB Storage Variation Data is invalid"),!0;if(n.isVisitorInHoldback(t))return!1;var i=e.variationId;return t.variations.find((function(t){return t.id===i}))?n.hasSessionOrAbsoluteTimeoutOccurred(e)?(l.debug("CMAB / Timeout Session or absolute timeout occurred:",e.cmabUUID),!0):(l.debug("CMAB / No timeout happened for:",e.cmabUUID),!1):(l.debug("CMAB / Timeout Variation ID:",i,"not found in experiment:",t.id),!0)},n.hasSessionOrAbsoluteTimeoutOccurred=function(t){if(!n.isCmabVariationStorageDataValid(t))return l.debug("CMAB / Timeout CMAB Storage Variation Data is invalid"),!0;var e=(new Date).getTime();new Date(t.stSessionTimeout).getTime();
2return e-new Date(t.stAbsoluteTimeout).getTime()>864e5},n.isCmabVariationStorageDataValid=function(t){if(!t)return!1;for(var n=["variationId","visitorId","cmabUUID","stSessionTimeout","stAbsoluteTimeout","userAttributeHash"],e=0;e<n.length;e++){var i=n[e];if(!t[i])return l.debug("CMAB / Storage data not found: "+i+" is missing"),!1}return!0},n.activateTimeoutForNonBucketedCmabExperiments=function(t){var e=n.getCmabLayerIdFromExperiment(t),i=t.id;l.debug("CMAB / Visitor is bucketed into experiment id:",i+" from layer id:",e);var a=f.getContextualMabMap();if(a&&a[e]){var u=a[e];l.debug("CMAB / All CMAB experiments found for the layer id:",e,"are:",u),l.debug("CMAB / Excluding the currently bucketed experiment id: ",i," from all CMAB experiments found for the current layer id: ",e);var c=r(u,[i]);c=o(c,(function(t){return t.isTimeout})),l.debug("CMAB / Non-bucketed CMAB experiments which will be timed out:",c),Object.keys(c).forEach((function(t){var n=c[t];n.isTimeout=!0,f.updateContextualMabMap(e,t,n)})),f.persistContextualMabMap()}else l.debug("CMAB / No CMAB experiments found for the current layer id:",e)},n.getCmabUUIDFromVisitorStore=function(t,n){if(!n||!t)return null;var e=f.getContextualMabMap();return e&&e[t]&&e[t][n]?e[t][n].cmabUUID:null},n.updateCmabTimeoutInVisitorStore=function(t,e){var i;if(!e||!t)return null;var r=f.getContextualMabMap();if(null==r||null==(i=r[t])?void 0:i[e]){var o=r[t][e];o.isTimeout||n.hasSessionOrAbsoluteTimeoutOccurred(o)||(o.stSessionTimeout=(new Date).toISOString(),f.updateContextualMabMap(t,e,o),f.persistContextualMabMap())}},n.getCmabVariationDataFromVisitorStore=function(t){var e,i=n.getCmabLayerIdFromExperiment(t),r=f.getContextualMabMap();if(!(null==r||null==(e=r[i])?void 0:e[t.id]))return l.debug("CMAB / No CMAB store found for experiment:",t.id),null;l.debug("CMAB / CMAB store found for experiment:",t.id);var o=r[i][t.id];return l.debug("CMAB / Contextual MAB Variation Data:",o),o},n.setCmabVariationDataInVisitorStore=function(t,e,i){l.debug("CMAB / Setting CMAB store for experiment:",t.id+" with variation:",e+" and cmabUUID:",i);var r=(new Date).toISOString(),o=n.getCmabLayerIdFromExperiment(t),a=n.getVisitorAttributesHash(t),u={variationId:e,visitorId:f.getCurrentId(),cmabUUID:i,stSessionTimeout:r,stAbsoluteTimeout:r,userAttributeHash:a,isTimeout:!1};f.updateContextualMabMap(o,t.id,u),f.persistContextualMabMap()},n.getStringifiedValue=function(t){if("string"==typeof t)return t;var n="";try{n=u.stringify(t)}catch(n){l.error("CMAB / Failed to stringify value for attributes:",t,n)}return n},n.getVisitorCmabAttributesForExperiment=function(t,e){var i;void 0===e&&(e=!0);var r=[],o=p.getAllPlugins(d.PluginTypes.audienceMatchers);if(!(null==t||null==(i=t.experimentMetadata)?void 0:i.cmabAttributes))return r;var a=t.experimentMetadata.cmabAttributes,u=a.customAttributes,c=a.standardAttributes;return u.forEach((function(t){var e,i=(null==(e=h.getAttribute(["custom",t.id]))?void 0:e.value)||"";i=n.getStringifiedValue(i),r.push({id:t.id,type:t.type,value:i,client_api_name:t.clientApiName})})),c.forEach((function(t){var e,i,a=(null==(e=o[t.id])||null==(i=e.fieldsNeeded)?void 0:i[0])||t.id,u=h.getAttribute(a)||"";u=n.getStringifiedValue(u),r.push({id:t.id,type:t.type,value:u})})),l.debug("CMAB / Current Visitor CMAB Attributes for Experiment:",t.id,"are:",r),r},n.generateAttributesHash=function(t){return a.hashToHex(a.toByteString(u.stringify(t)),a.Seed.BEHAVIOR_EVENT)},n.getVisitorAttributesHash=function(t){var e=n.getVisitorCmabAttributesForExperiment(t,!1);return n.generateAttributesHash(e)},n.hasAttributesChanged=function(t,e){var i=n.getVisitorAttributesHash(t);return i!==e.userAttributeHash&&(l.debug("CMAB / User attributes hash has changed from:",e.userAttributeHash," to ",i),!0)},n.getDcpCustomerIds=function(){var t=g.getDCPKeyfieldLocators(),n=f.getCurrentId();return v.getCurrentAliases(t,n)},n.getDcpServiceId=function(){return g.getDCPServiceId()}},3585:function(t,n,e){t.exports=function(t){t.registerVisitorProfileProvider(e(9864));var n=e(917);t.registerAudienceMatcher("custom_attribute",n),t.registerAudienceMatcher("custom_dimension",n)}},917:function(t,n,e){var i=e(8681);n.match=function(t,n){var e;return t.custom&&(e=t.custom[n.name]),"object"==typeof e&&null!==e&&(e=e.value),i.hasMatch(n.value,
2n.match,e)}},9864:function(t,n,e){var i=e(2044),r=e(3496),o=e(3221).get("stores/dimension_data");t.exports={provides:"custom",attributionType:i.AttributionTypes.LAST_TOUCH,restorer:function(t){return Object.keys(t).reduce((function(n,e){var i=t[e],a=e,u=o.getByApiName(e),c=o.getById(e);return"object"!=typeof i||null===i?(r.error('Unable to restore custom attribute "'+e+'" because value is not an object'),n):(!i.id&&u&&(c=u,a=u.id,Object.assign(i,{id:c.segmentId||c.id})),!i.name&&c&&c.apiName&&(i.name=c.apiName),i.id||c||r.warn("Unable to determine ID for custom attribute:",e,"; segmentation is disabled."),n[a]=i,n)}),{})},shouldTrack:!0}},1190:function(t,n,e){var i=e(2122),r=e(2658),o=e(3496),a=e(3898),u="cookie",c="js_variable",s="query_param",f="uid";n.getCurrentAliases=function(t,n){var e={};return t.forEach((function(t){if(!t.is_optimizely){var l=t.dcp_datasource_id,d=t.type,v=t.name||"";if(l){var h=null;switch(d){case u:h=r.get(v);break;case c:try{h=a.getGlobal(v)}catch(t){o.warn("Failed getting global DCP keyfieldLocator:",v)}break;case s:h=i.getQueryParamValue(v);break;case f:h=n}("number"==typeof h||"string"==typeof h)&&(e[l]=h)}else o.error("DCP / No DCP datasource id specified")}})),e}},219:function(t,n,e){t.exports=function(t){t.registerDependency("sources/device",e(7618)),t.registerVisitorProfileProvider(e(3850)),t.registerAudienceMatcher("device",e(521))}},521:function(t){t.exports={fieldsNeeded:["device"],match:function(t,n){return t.device===n.value}}},3850:function(t){t.exports={provides:"device",shouldTrack:!0,isSticky:!0,getter:["sources/device",function(t){return t.getDevice()}]}},7618:function(t,n,e){var i=e(7566);n.getDevice=function(){var t=i.get().device;return"unknown"!==t.model?t.model:"tablet"===t.type?"tablet":t.isMobile?"mobile":"desktop"}},6384:function(t,n,e){t.exports=function(t){t.registerVisitorProfileProvider(e(5762)),t.registerAudienceMatcher("device_type",e(1797))}},1797:function(t){t.exports={fieldsNeeded:["device_type"],match:function(t,n){return t.device_type===n.value}}},5762:function(t,n,e){var i=e(7566);t.exports={provides:"device_type",shouldTrack:!0,isSticky:!0,getter:[function(){var t=i.get().device;switch(t.type){case"mobile":return"phone";case"tablet":case"desktop_laptop":return t.type;default:return"other"}}]}},8398:function(t,n,e){Object.defineProperty(n,"u",{value:!0});var i=e(279),r=i.flatten,o=i.isEmpty,a=e(8548),u=e(2658),c=e(9726).v,s=e(6388),f=e(3496),l=e(6494),d=e(1756),v=e(3941),h=e(8805),p=e(3341),g=e(3898),m=e(4464),b=e(950),y=e(7649),_=e(2044),w=e(8671),E=e(6310),I="odp",A="zaius",T="web_user_id",S=0,O={"zaius-staging":"https://v3-staging.api.zaius.com","zaius-us-endpoint":"https://api.zaius.com/v3","zaius-eu-endpoint":"https://api.eu1.odp.optimizely.com/v3","zaius-ap-endpoint":"https://api.au1.odp.optimizely.com/v3"},k="vuid",R="oeu_id",C="low",D="high",N=c("ODPError");function x(t,n){var e,i;if(t===k){var r=function(){var t=u.get("vuid");if(t)return t.split("|")[0].split("-").join("");var n=g.getGlobal(A);return n&&n.hasOwnProperty("VUID")&&(t=n.VUID)?t.split("-").join(""):void 0}();if(!r)return null;e="vuid",i=r}else t===R&&(i=y.getCurrentId(),e=n+"_web_user_id");return{identifier:e,user_id:i}}function j(t){var n,e,i=[];try{i=t.data.customer.audiences.edges.filter((function(t){return t.node&&"qualified"===t.node.state})).map((function(t){return t.node&&t.node.name}))}catch(t){if(n=t,!(null!=(e=TypeError)&&"undefined"!=typeof Symbol&&e[Symbol.hasInstance]?e[Symbol.hasInstance](n):n instanceof e))throw t;f.warn("ODP Data response object doesn't have the expected format")}return{audiences:i,custom:i}}function P(t){var n=[];if(Array.isArray(t)&&(n=n.concat(r(t.map(P)))),"object"==typeof t&&null!==t)if(t.conditions&&Array.isArray(t.conditions))n=n.concat(r(t.conditions.map(P)));else if(t.type&&"vendor.odp"===t.type)return[t.value];return n}function M(t,n){var e=g.getGlobal(A);if(e){var i={low_web_user_id:t};return n===D&&(i.high_web_user_id=t),e.onload((function(){e.customer(i,{})})),!0}}function L(t){var n=t.user_id||R,e=t.confidence_level||C;return h.pollFor(M.bind(null,y.getCurrentId(),e),50,20),h.pollFor(x.bind(null,n,e),50,20)}var V={provider:{isAsync:!0,isLazy:!0,isSticky:!0,isTransient:!0,getter:["stores/integration_settings","stores/audience_data","stores/visitor_bucketing",function(t,n,e){var i=t.get(I)||{};if(o(i))return p.reject(new N("ODP integration settings data missing."));var r=function(t,n){var e=t._neededSegments;
2return null==e&&(e=P(n),v.dispatch(d.SET_INTEGRATION_SETTINGS,{id:"odp",_neededSegments:e})),e}(i,n.getAll());if(o(r))return p.resolve({});var c=i.public_key,h=O[i.zaius_host]||"//api.zaius.com/v3",A=i.is_passthrough||"off";if(void 0===c)return p.reject(new N("ODP public_key missing from integration settings"));var k=I+"_"+S;return a.makeAsyncRequest(k,(function(){("on"===A?function(t){var n,e=t.identifier_type||_.VisitorIdLocatorType.COOKIE,i=t.identifier_name;switch(e){case _.VisitorIdLocatorType.COOKIE:n=u.get(i);break;case _.VisitorIdLocatorType.JS_VARIABLE:n=g.getGlobalByPath(i);break;case _.VisitorIdLocatorType.LOCALSTORAGE:try{n=g.getGlobal("localStorage").getItem(i)}catch(t){f.warn("ODP Cant access local storage")}break;case _.VisitorIdLocatorType.QUERY:n=b.getQueryParamValue(i);break;default:f.warn('ODP Passthrough Invalid identifier type: "'+e+'".')}return o(n)?(f.warn("ODP Passthrough, Identifier not found, does not exist or is empty at visitor bucketing time. Type: '"+e+"', Name: '"+i+"'."),n=void 0):"string"!=typeof n&&"number"!=typeof n&&(f.warn("ODP Passthrough Invalid user id or is neither string or number."),n=void 0),p.resolve({identifier:T,user_id:n})}(i):L(i)).then((function(t){var n=t.identifier,i=t.user_id;if(n===T&&void 0===i)return f.warn("ODP Passthrough.  Expect not fail if user_id cant be resolved."),p.resolve({audiences:[],custom:[]});var o=w.getHashForOdpSegments(r),a=e.getOdpSegments(),u=E.now(),g=(null==a?void 0:a.timestamp)&&a.timestamp+3e4>=u,b=(null==a?void 0:a.timestamp)&&a.segmentHash===o;return g&&b?(f.log("ODP / cache hit with data",a),a.segment):(f.log("ODP / cache miss, calling odp"),function(t,n,e,i,r){var o=t+"/graphql";return l.time("odpCall"),m.request({url:o,method:"POST",async:!0,contentType:"application/json",headers:{"x-api-key":n},data:{query:"query MyQuery {\n  customer("+e+': "'+i+'") {\n    vuid\n    audiences (subset: '+s.stringify(r)+") {\n      edges {\n        node {\n          is_ready\n          name\n          state\n        }\n      }\n    }\n  }\n}",variables:{}}}).then((function(t){var n;if(l.timeEnd("odpCall"),t.status>399)throw new N(t.status+": "+t.statusText);try{n=s.parse(t.response)}catch(t){throw new N("Error parsing response: "+t.toString())}if(!n)throw new N("No data received");return j(n)}),(function(t){throw l.timeEnd("odpCall"),new N(t.status+": "+t.statusText)}))}(h,c,n,i,r).then((function(t){var n={segment:t,segmentHash:w.getHashForOdpSegments(t.audiences),timestamp:E.now()};return v.dispatch(d.SET_ODP_SEGMENTS,n),y.persistOdpSegments(n),t})).catch((function(t){throw t})))})).then((function(t){return S+=1,t}),(function(t){throw S+=1,t})).then(a.resolveRequest.bind(null,k),a.rejectRequest.bind(null,k))}))}]},matcher:e(2682),vendor:I};t.exports=function(t){t.registerAudiencePlugin(V)}},6507:function(t,n,e){t.exports=function(t){t.registerVisitorProfileProvider(e(5947)),t.registerAudienceMatcher("query",e(4035))}},4035:function(t,n,e){var i=e(8681);n.fieldsNeeded=["queryParams"],n.match=function(t,n){var e=t.queryParams.find((function(t){return t[0]===n.name}));return i.hasMatch(n.value,n.match,e?e[1]:null)}},5947:function(t,n,e){var i=e(2122);t.exports={provides:"queryParams",getter:[function(){return i.getQueryParams()}]}},5038:function(t,n,e){t.exports=function(t){t.registerVisitorProfileProvider(e(4920)),t.registerAudienceMatcher("referrer",e(9301))}},9301:function(t,n,e){var i=e(2034);n.fieldsNeeded=["referrer"],n.match=function(t,n){return null!==t.referrer&&i(t.referrer,n)}},4920:function(t,n,e){var i=e(9751),r=e(2060);t.exports={provides:"referrer",shouldTrack:!0,isSticky:!0,getter:[function(){var t=r.getReferrer()||i.getReferrer();return""===t&&(t=null),t}]}},1653:function(t,n,e){t.exports=function(t){t.registerVisitorProfileProvider(e(4630)),t.registerAudienceMatcher("source_type",e(6755))}},6755:function(t,n,e){var i=e(8681);n.fieldsNeeded=["source_type"],n.match=function(t,n){return i.hasMatch(n.value,n.match,t.source_type)}},4630:function(t,n,e){var i=e(2122),r=e(9751),o=e(2060),a=e(2291),u=["google\\.\\w{2,3}(\\.\\w{2,3})?/(search|url)","bing\\.\\w{2,3}(\\.\\w{2,3})?/(search|url)","yahoo\\.\\w{2,3}(\\.\\w{2,3})?/search","baidu\\.\\w{2,3}(\\.\\w{2,3})?/","https://(www)?\\.google\\..*?/?$","https://search\\.yahoo\\..*?/?$","https://(www)?\\.bing\\..*?/?$"];t.exports={provides:"source_type",shouldTrack:!0,isSticky:!1,getter:[function(){return function(t,n){var e,c=t(),s=function(){if(i.getQueryParamValue("utm_source")||i.getQueryParamValue("utm_campaign")||i.getQueryParamValue("gclid")||i.getQueryParamValue("otm_source"))return"campaign";for(var t=o.getReferrer()||r.getReferrer(),n=0;n<u.length;n++){var e=u[n];if(t.match(e))return"search"}return t&&a.guessDomain(t)!==a.guessDomain(i.getUrl())?"referral":"direct"}();e=s,(!c||"direct"!==e)&&n(s)}}]}},927:function(t,n,e){t.exports=function(t){t.registerVisitorProfileProvider(e(632)),t.registerVisitorProfileProvider(e(2519)),t.registerAudienceMatcher("time_and_day",e(6177))}},6177:function(t,n,e){var i=e(3845);n.fieldsNeeded=["currentTimestamp"],n.match=function(t,n){return i.test(n.value,
2new Date(t.currentTimestamp))}},632:function(t,n,e){var i=e(6310);t.exports={provides:"currentTimestamp",shouldTrack:!0,isLazy:!0,getter:[function(){return i.now()}]}},2519:function(t){t.exports={provides:"offset",shouldTrack:!0,isLazy:!0,getter:[function(){return(new Date).getTimezoneOffset()}]}},3845:function(t,n){function e(t){var n=t.split(":");if(2!==n.length)throw new Error("optly.timeAndDayInterval.timeStringToMinutes: Invalid time string "+t);return 60*parseInt(n[0],10)+parseInt(n[1],10)}n.test=function(t,n){var i=function(t){var n=t.split("_");if(3!==n.length)throw new Error("Invalid time and day string "+t);var e=n[2].split(",");return{start_time:n[0],end_time:n[1],days:e}}(t),r=e(i.start_time),o=e(i.end_time),a=60*n.getHours()+n.getMinutes(),u=["sunday","monday","tuesday","wednesday","thursday","friday","saturday"][n.getDay()];return a>=r&&a<=o&&i.days.includes(u)}},1059:function(t,n,e){t.exports=function(t){t.registerVisitorProfileProvider(e(9898))}},9898:function(t){t.exports={provides:"visitorId",getter:["stores/visitor_id",function(t){return t.getRandomId()}]}},2700:function(t,n,e){var i,r=e(6388),o=e(3496),a=e(6494),u=e(8805),c=e(3454),s=e(3898),f=e(6210),l=e(3659),d=e(3341),v=e(3941),h=e(3221),p=e(1756),g=h.get("stores/tracker_optimizely"),m=(h.get("stores/rum"),h.get("stores/integration_settings")),b="google_analytics_4",y="gtag",_="dataLayer",w="optimizely_decision",E="experience_impression",I=[],A=function(){var t=g.canSend();if(o.debug("GA4 Integration: Got canSend:",t),t){for(var n=[];I.length>0;){o.debug("GA4 Integration: decisionEventQueue length:",I.length);var e=I.shift(),r=e.firstPartyData,a=e.customEventData;o.debug("GA4 Integration: calling track with decision data:",{firstPartyData:r,customEventData:a}),i?n.push(S(r,a).catch((function(t){o.warn("Tracker for GTM ",b,"failed:",t)}))):n.push(T(r,a).catch((function(t){o.warn("Tracker for ",b,"failed:",t)})))}return Promise.all(n)}return o.debug("GA4 Integration: Not sending events (holding)"),Promise.resolve()},T=function(t,n){return u.pollFor((function(){return function(t){var n=t.getGlobal(y);return n||null}(s)}),200,50).then((function(e){o.log("GA4 Integration: gtag finally exists, sending now."),a.timeEnd("gtagReady"),o.debug("GA4 Integration: calling ga4 with data: "+r.stringify(t)),e("event",E,t),o.debug("GA4 Integration: calling ga4 with custom event data: "+r.stringify(n)),e("event",w,n)}))};var S=function(t,n){var e=s.getGlobal(_);o.debug("GA4 Integration: found dataLayer object "+e),e||(o.debug("GA4 Integration: dataLayer object "+_+" is empty, initializing it"),s.setGlobal(_,[]),e=s.getGlobal(_)),o.log("GA4 Integration: attempting push to "+_);try{var i=Object.assign({event:E},t);o.debug("GA4 Integration: pushing data to GTM: "+r.stringify(i)),e.push(i);var a=Object.assign({event:w},n);o.debug("GA4 Integration: pushing custom data to GTM: "+r.stringify(a)),e.push(a)}catch(t){o.warn("GA4 Integration: Push to "+_+" failed: "+t)}return Promise.resolve()},O={preRedirectPolicy:c.PreRedirectPolicies.PERSIST_BEFORE_REDIRECT,postRedirectPolicy:c.PostRedirectPolicies.TRACK_IMMEDIATELY,nonRedirectPolicy:c.NonRedirectPolicies.TRACK_IMMEDIATELY,trackLayerDecision:function(t){var n=t.integrationSettings;if(!n||!n[b])return o.debug("GA4 Integration: did not find vendor "+b+" in integration settings, exiting"),d.resolve();if(!t.experimentId||!t.variationId)return o.debug("GA4 Integration: empty experiment or variation ID, exiting"),d.resolve();var e=m.get(b)||{};i="on"===e.global_is_use_gtm||"on"===n[b].is_using_gtm,o.debug("GA4 Integration: is using GTM: "+i);var r,a,u,c="equal_priority"===t.layerPolicy;return o.debug("GA4 Integration: is personalization campaign: "+c),c?(u="OPT-"+t.layerId+"("+t.layerName+")-"+t.experimentId+"("+t.experimentName+")-"+t.variationId+"("+t.variationName+")",a={Campaign:t.layerName+" ("+t.layerId+")",Variation:t.variationName+" ("+t.variationId+")",Holdback:t.isLayerHoldback}):(u="OPT-"+t.experimentId+"("+t.experimentName+")-"+t.variationId+"("+t.variationName+")",a={Experiment:t.experimentName+" ("+t.experimentId+")",Variation:t.variationName+" ("+t.variationId+")",Holdback:t.isLayerHoldback}
2),r={exp_variant_string:u,Holdback:t.isLayerHoldback},o.debug("GA4 Integration: pushing decision data to queue:",{firstPartyData:r,customEventData:a}),I.push({firstPartyData:r,customEventData:a}),A()},serializeSettings:r.stringify,deserializeSettings:r.parse};t.exports=function(t){t.registerAnalyticsTracker("google_analytics_4",O),f.on({filter:{type:l.TYPES.ANALYTICS,name:"sendEvents"},handler:function(){v.dispatch(p.SET_TRACKER_SEND_EVENTS,!0),A()}}),f.on({filter:{type:l.TYPES.ANALYTICS,name:"maybeSendEvents"},handler:function(){A()}})}},3707:function(t,n,e){var i=e(279),r=i.isEmpty,o=i.omit,a=i.pick,u=e(525),c=e(167),s=e(1756),f=e(9726).v,l=e(6388),d=e(3496),v=e(3454),h=e(7649),p=e(3898),g=e(4464),m=e(3221),b=(e(9751),e(6210)),y=e(3659),_=e(3941),w=m.get("stores/global"),E=m.get("stores/tracker_optimizely"),I=e(9819),A=n.Error=f("OptimizelyTrackerError"),T="client_activation",S="campaign_activated",O="view_activated",k={revenue:{validate:L,sanitize:Math.floor,excludeFeature:!0},quantity:{validate:L,sanitize:Math.floor,excludeFeature:!0},value:{validate:L,sanitize:function(t){return t}}},R="AUTO",C=[function(){return function(t){U(function(t){var n=Object.assign({entity_id:t.pageId,key:t.pageApiName,timestamp:t.timestamp,uuid:t.eventId,type:O},j(t.eventTags));return n}(t),t.userFeatures,J(t.layerStates))}}],D=[function(){return function(t){!function(t){var n=null!==w.getAnonymizeIP()?w.getAnonymizeIP():void 0,e={account_id:t.accountId,anonymize_ip:n,client_name:t.clientName,client_version:t.clientVersion,project_id:t.projectId,visitors:[]};e.revision=t.revision,e.enrich_decisions=!0;var i={session_id:B(t.sessionId),visitor_id:t.visitorId,attributes:[],snapshots:[]},r=J(t.layerStates);_.dispatch(s.REGISTER_TRACKER_VISITOR,{data:e,visitor:i,decisions:r}),Q()}(t),U(function(t){return{entity_id:null,type:T,uuid:t.eventId,timestamp:t.timestamp}}(t),t.userFeatures,J(t.layerStates))}}],N=[function(){return function(t){var n;U((n=t,Object.assign({entity_id:n.eventEntityId,key:n.eventApiName,timestamp:n.timestamp,uuid:n.eventId,type:n.eventCategory,properties:P(n.eventProperties)},j(n.eventTags))),t.userFeatures,J(t.layerStates))}}],x=[function(){return function(t){var n;U((n=t,Object.assign({entity_id:n.eventEntityId,key:n.eventApiName,timestamp:n.timestamp,uuid:n.eventId,type:n.eventCategory},j(n.eventTags))),t.userFeatures,J(t.layerStates))}}];function j(t){var n=function(t,n,e){try{M(e),t[n]=e}catch(t){u.emitError(new A("Bad value for eventTags["+n+"]: "+t.message))}return t},e=Object.keys(k),i=o(t,e),r=a(t,e),c=Object.keys(i).reduce((function(t,e){return n(t,e,i[e])}),{}),s=Object.keys(r).reduce((function(t,e){var i=r[e],o=k[e];o.excludeFeature||n(c,e,i);try{o.validate(i),t[e]=o.sanitize(i),c[e]=t[e]}catch(t){u.emitError(new A("Bad value for eventMetrics["+e+"]: "+t.message))}return t}),{});return s.tags=c,s}function P(t){var n={},e={},i=0;for(var r in t){var o=t[r];"object"==typeof o&&null!==o?e[r]=o:i<15?(n[r]=o,i++):e[r]=o}return Object.keys(e).length>0&&d.log("Custom event properties dropped (complex values, or over the 15-property limit). The following properties were not pushed",e),n}function M(t){if(null==t)throw new Error("Feature value is null");if("object"==typeof t){var n;try{n=l.stringify(t)}catch(t){}throw new Error('Feature value is complex: "'+n)}}function L(t){if(null==t)throw new Error("Metric value is null");if("number"!=typeof t)throw new Error("Metric value is not numeric")}function V(t){return(t||[]).reduce((function(t,n){try{M(n.value),t.push({entity_id:n.id||null,key:n.name,type:n.type,value:n.value})}catch(t){d.warn("Error evaluating user feature",n,t)}return t}),[])}function U(t,n,e){_.dispatch(s.REGISTER_TRACKER_EVENT,{event:t,decisions:e}),z(n),Q()}function z(t){var n=V(t);_.dispatch(s.UPDATE_TRACKER_VISITOR_ATTRIBUTES,{attributes:n})}function B(t){return R}function F(){if(E.canSend()){var t=E.hasEventsToSend(),n=E.hasPreviousBatchesToSend();t||n?(e(6494).time("sendEventsTrackerOpti"),n&&(E.getPreviousBatches().forEach(G),_.dispatch(s.RESET_TRACKER_PREVIOUS_BATCHES)),t&&(_.dispatch(s.FINALIZE_BATCH_SNAPSHOT),G(E.getEventBatch()),_.dispatch(s.RESET_TRACKER_EVENTS)),e(6494).timeEnd("sendEventsTrackerOpti")):d.debug("Not sending events because there are no events to send")}else d.debug("Not sending events (holding)")}function G(t){d.debug("Sending ticket:",t);var n=c.generate(),e={url:"https://logx.optimizely.com/v1/events",method:"POST",data:q(t)};E.shouldUseBeacon()?g.sendBeacon(e,n):g.retryableRequest(e,n)}function q(t){return Object.assign({},a(t,["account_id","anonymize_ip","client_name","client_version","enrich_decisions","project_id","revision"]),{visitors:t.visitors.map(H)})}function H(t){return{visitor_id:t.visitor_id,session_id:R,attributes:t.attributes.map(Y),snapshots:t.snapshots.map(K)}}function Y(t){return X(t,{entity_id:"e",key:"k",type:"t",value:"v"})}function K(t){var n=t.events;return n=function(t){var n=t.reduce((function(t,n){var e;if(t[e=n.type===O&&r(n.tags)&&r(a(n,Object.keys(k)))?n.type:n.uuid]){var i=t[e].timestamp;n.timestamp>i&&(i=n.timestamp),t[e]=Object.assign({},t[e],{key:t[e].key+"-"+(n.key||""),entity_id:t[e].entity_id+"-"+n.entity_id,timestamp:i})}else t[e]=n;return t}),{});return Object.values(n)}(n),{activationTimestamp:w.getActivationTimestamp(),decisions:t.decisions.map($),events:n.map(W)}}function $(t){var n=X(t,{campaign_id:"c",experiment_id:"x",is_campaign_holdback:"h",variation_id:"v"});return n.m=t.metadata?X(t.metadata,{cmab_uuid:"cmab_uuid"}):{},n}function W(t){return t.key===S&&(t.type=S,delete t.key),X(t,{entity_id:"e",key:"k",quantity:"q",revenue:"$",tags:"a",properties:"p",timestamp:"t",uuid:"u",value:"v",type:"y"})}function X(t,n){return Object.keys(t).reduce((function(e,i){var r=t[i];return i in n&&(e[n[i]||i]=r),e}),{})}function Q(){if(E.shouldBatch()){if(!E.isPolling()){p.setTimeout((function t(){F(),E.isPolling()&&p.setTimeout(t,1e3)}),1e3),_.dispatch(s.SET_TRACKER_POLLING,!0),p.setTimeout((function(){_.dispatch(s.SET_TRACKER_BATCHING,!1),_.dispatch(s.SET_TRACKER_POLLING,!1)}),1e4)}}else F()}function J(t){return t.map((function(t){var n=I.getCmabUUIDAndUpdateSessionTimeout(t.layerId,t.decision.experimentId),e=n?{cmab_uuid:n}:{};return{campaign_id:t.layerId,experiment_id:t.decision.experimentId,variation_id:t.decision.variationId,is_campaign_holdback:t.decision.isLayerHoldback,metadata:e}}))}function Z(){var t=E.getPersistableState();if(t)try{d.debug("Persisting pending batch:",t),h.persistTrackerOptimizelyData(t),_.dispatch(s.SET_TRACKER_DIRTY,!1)}catch(t){d.debug("Failed to persist pending batch:",t)}}var tt={trackLayerDecision:function(t){t.isLayerConcluded?d.debug("Layer is already concluded, skipping decision tracking"):t.timing===v.TrackLayerDecisionTimingFlags.postRedirectPolicy?function(t){var n=V(t.userFeatures),e={account_id:t.accountId,anonymize_ip:t.anonymizeIP,client_name:t.clientName,client_version:t.clientVersion,project_id:t.projectId,visitors:[{session_id:B(t.sessionId),visitor_id:t.visitorId,attributes:n,snapshots:[{decisions:[{campaign_id:t.layerId,experiment_id:t.experimentId,variation_id:t.variationId,is_campaign_holdback:t.isLayerHoldback}],events:[{uuid:t.decisionId,entity_id:t.layerId,timestamp:t.timestamp,type:S}]}]}]};_.dispatch(s.REGISTER_PREVIOUS_BATCH,e),Q()}(t):function(t){e(6494).time("onLayerDecisionOptimizely");var n={entity_id:t.layerId,type:S,uuid:t.decisionId,timestamp:t.timestamp};_.dispatch(s.REGISTER_TRACKER_DECISION,{decisionEvent:n,decisions:J(t.layerStates)}),z(t.userFeatures),e(6494).timeEnd("onLayerDecisionOptimizely"),Q()}(t)},preRedirectPolicy:v.PreRedirectPolicies.PERSIST_BEFORE_AND_TRACK_DURING_REDIRECT,postRedirectPolicy:v.PostRedirectPolicies.TRACK_AFT
2ER_SYNC,nonRedirectPolicy:v.NonRedirectPolicies.TRACK_IMMEDIATELY,onPageActivated:C,onClientActivation:D,onClickEvent:x,onCustomEvent:N};t.exports=function(t){t.registerAnalyticsTracker("optimizely",tt),b.on({filter:{type:y.TYPES.ANALYTICS,name:"sendEvents"},handler:function(){_.dispatch(s.SET_TRACKER_SEND_EVENTS,!0),F()}}),b.on({filter:{type:y.TYPES.ANALYTICS,name:"maybeSendEvents"},handler:function(){F()}}),b.on({filter:{type:y.TYPES.ANALYTICS,name:"holdEvents"},handler:function(){_.dispatch(s.SET_TRACKER_SEND_EVENTS,!1)}}),_.dispatch(s.SET_TRACKER_SEND_EVENTS,!0);var n=b.on({filter:{type:"lifecycle",name:"activated"},handler:function(){E.observe(Z),b.off(n)}})}},5215:function(t,n,e){var i=e(2306),r=e(8229);function o(t){if(!t||"string"!=typeof t.value)return{configured:!1,valid:!1,leg:null};var n=t.value.replace(/^\s+|\s+$/g,"");if(""===n||n.length<3)return{configured:!0,valid:!1,leg:null};var e=i.makeUaTest(n);return e?{configured:!0,valid:!0,leg:{value:n,test:e}}:{configured:!0,valid:!1,leg:null}}function a(t){if(!t)return{configured:!1,legs:[]};if("string"!=typeof t.value||""===t.value.replace(/^\s+|\s+$/g,""))return{configured:!0,legs:[]};for(var n=t.value.split(","),e=[],i=0;i<n.length;i++){var o=n[i].replace(/^\s+|\s+$/g,"");if(""!==o){var a=r.makeIpTest(o);a&&e.push({value:o,test:a})}}return{configured:!0,legs:e}}n.compile=function(t){var n=[];if(!t||!t.rules||!Array.isArray(t.rules))return n;for(var e=0;e<t.rules.length;e++){var i=t.rules[e];if(i){var r=String(e),u=o(i.user_agent),c=a(i.ip);if((u.configured||c.configured)&&(!u.configured||u.valid)){var s=u.configured?u.leg:null;if(c.configured){if(0===c.legs.length)continue;for(var f=0;f<c.legs.length;f++)n.push({id:r,ua:s,ip:c.legs[f]})}else n.push({id:r,ua:s,ip:null})}}}return n}},5296:function(t,n,e){var i=e(5215),r=e(6519),o=e(3898),a=e(3221),u=e(6210),c=e(3659),s=a.get("stores/directive"),f=a.get("stores/global"),l=null,d=!1,v=void 0,h=!1,p=!1;function g(){p&&(p=!1,u.emit({type:c.TYPES.ANALYTICS,name:"sendEvents"}))}function m(){return{enabled:!1,mode:"monitor",compiledRules:[],hasIpRules:!1,hasUaRules:!1,stats:r.createStats()}}function b(){return!!(s&&s.shouldBootstrapDataForPreview&&s.shouldBootstrapDataForPreview())}function y(){if(!d){try{var t=function(){try{return f&&f.getTrafficExclusion?f.getTrafficExclusion():void 0}catch(t){return}}();if(!t)return;d=!0,l=function(t){if(!t||"object"!=typeof t||!t.enabled)return m();for(var n="enforce"===t.mode?"enforce":"monitor",e=i.compile(t),o=!1,a=!1,u=0;u<e.length;u++)e[u].ip&&(o=!0),e[u].ua&&(a=!0);return{enabled:!0,mode:n,compiledRules:e,hasIpRules:o,hasUaRules:a,stats:r.createStats()}}(t)}catch(t){return d=!0,void(l=m())}try{l.enabled&&l.hasIpRules&&!b()&&function(){if(!p){p=!0,u.emit({type:c.TYPES.ANALYTICS,name:"holdEvents"});var t=_;o.setTimeout((function(){_===t&&g()}),2e3)}}()}catch(t){}}}n.evaluate=function(t,n){if(!l||!l.compiledRules)return{matched:!1,ruleId:void 0};for(var e=l.compiledRules,i=null!=n,r=0;r<e.length;r++){var o=e[r];if(o.ua||o.ip){var a=!o.ua||!!o.ua.test(t),u=!o.ip||i&&!!o.ip.test(n);if(a&&u)return{matched:!0,ruleId:o.id}}}return{matched:!1,ruleId:void 0}},n.shouldDrop=function(){try{if(y(),!l||!l.enabled||b()||!l.compiledRules.length)return!1;var t=o.getUserAgent();l.hasIpRules&&function(){if(h)return;h=!0;var t=_;try{var i=e(815).getIP();i&&"function"==typeof i.then&&i.then((function(i){if(_===t&&"string"==typeof i&&i&&(v=i,l&&l.enabled&&"enforce"===l.mode)){var a=o.getUserAgent(),u=n.evaluate(a,i);if(u.matched){r.recordMatch(l.stats,u.ruleId);try{var c=e(3941),s=e(1756);c.dispatch(s.RESET_TRACKER_EVENTS),c.dispatch(s.RESET_TRACKER_PREVIOUS_BATCHES)}catch(t){}}}g()}),(function(){g()}))}catch(t){}}();var i=l.hasIpRules?v:void 0;r.recordEvaluation(l.stats),l.hasUaRules&&("string"!=typeof t||!t)&&r.recordUnusableUa(l.stats),l.hasIpRules&&("string"!=typeof i||!i)&&r.recordUnresolvedIp(l.stats);var a=n.evaluate(t,i);return!!a.matched&&(r.recordMatch(l.stats,a.ruleId),"enforce"===l.mode)}catch(t){try{l&&r.recordThrew(l.stats)}catch(t){}return!1}};var _=0;n.config=function(){return y(),l},n.getStats=function(){if(y(),!l||!l.enabled)return null;var t=l.stats,n={};for(var e in t.perRuleHits)Object.prototype.hasOwnProperty.call(t.perRuleHits,e)&&(n[e]=t.perRuleHits[e]);for(var i=[],r=l.compiledRules,o=0;o<r.length;o++){var a=r[o];n[a.id]&&i.push({id:a.id,type:a.ua&&a.ip?"ua+ip":a.ua?"ua":"ip",ua:a.ua?a.ua.value:null,ip:a.ip?a.ip.value:null,drops:n[a.id]})}return{enabled:!0,mode:l.mode,evaluated:t.evaluated,matched:t.matched,unusableUa:t.unus
2ableUa,unresolvedIp:t.unresolvedIp,threw:t.threw,perRuleHits:n,matchedRules:i}}},8229:function(t,n){function e(t){if(!/^\d{1,3}$/.test(t))return!1;var n=Number(t);return!(n>255||String(n)!==t)}n.anonymizeIp=function(t){if("string"==typeof t&&""!==t){var n=t.split(".");if(4===n.length){for(var i=0;i<4;i++)if(!e(n[i]))return;return n[0]+"."+n[1]+"."+n[2]+".0"}}},n.makeIpTest=function(t){if(!t||"string"!=typeof t)return null;var e=n.anonymizeIp(t);return void 0===e||e!==t?null:function(e){var i=n.anonymizeIp(e);return void 0!==i&&i===t}}},2306:function(t,n){n.makeUaTest=function(t){if(!t||"string"!=typeof t)return null;var n=t.toLowerCase();return function(t){return"string"==typeof t&&-1!==t.toLowerCase().indexOf(n)}}},6519:function(t,n){n.createStats=function(){return{evaluated:0,matched:0,unusableUa:0,unresolvedIp:0,threw:0,perRuleHits:{}}},n.recordEvaluation=function(t){t&&t.evaluated++},n.recordUnusableUa=function(t){t&&t.unusableUa++},n.recordUnresolvedIp=function(t){t&&t.unresolvedIp++},n.recordThrew=function(t){t&&t.threw++},n.recordMatch=function(t,n){t&&(t.matched++,t.perRuleHits[n]=(t.perRuleHits[n]||0)+1)}},6167:function(t,n,e){var i=e(3816),r={match:function(t,n){return i.apply(n.value)}};t.exports=function(t){t.registerViewMatcher("custom_code",r)}},4328:function(t,n,e){t.exports=function(t){t.registerViewProvider(e(5164)),t.registerViewMatcher("url",e(9015))}},9015:function(t,n,e){var i=e(2034);t.exports={fieldsNeeded:["url"],match:function(t,n){return i(t.url,n)}}},5164:function(t,n,e){var i=e(2122);t.exports={provides:"url",getter:[function(){return i.getUrl()}]}},465:function(t,n,e){var i=e(7662),r=e(3816);t.exports=function(t,n){var e=t.locator;if("function"==typeof e){var o=r.apply(e,n);return i(t.valueType,o)}return null}},7662:function(t,n,e){var i=e(6952).enums,r=e(6952).Error;t.exports=function(t,n){if(null==n)return n;var e=n;switch(t){case i.valueType.STRING:"string"!=typeof n&&(e=String(n));break;case i.valueType.NUMBER:if(e=Number(n),isNaN(e))throw new r(n+" is not a number");break;case i.valueType.CURRENCY:if(e=Number(n),isNaN(e)||Math.floor(e)!==e)throw new r(n+" cannot be parsed as currency (must be an integer)");break;default:throw new r("Unknown ViewTag type: "+t)}return e}},9958:function(t,n,e){var i=e(6952).enums.locatorType;t.exports=function(t){t.registerViewTagLocator(i.JAVASCRIPT,e(465))}},4658:function(t,n,e){var i=e(9751),r=e(2858).nodeNames,o=e(6652);t.exports=function(t){var n,e=t.locator,a=i.querySelectorAll(e);if(a.length>0){var u=a[0];switch(u.nodeName){case r.INPUT:case r.SELECT:n=u.value.trim();break;default:n=(a[0].innerText||a[0].textContent).trim().replace(/\s+/g," ")}}return o(t.valueType,n)}},1043:function(t,n,e){var i=e(6952).enums.locatorType;t.exports=function(t){t.registerViewTagLocator(i.CSS_SELECTOR,e(4658))}},3789:function(t,n,e){var i=e(9751),r=e(2044),o=e(6210),a=e(7067),u=e(3432),c={token:void 0,setUpObserver:function(){a.createDOMChangedObserver(),this.token=o.on({filter:{type:"viewTrigger",name:"DOMChanged"},handler:function(){u.getViewsAndActivate(r.ViewActivationTypes.DOMChanged)}})},turnOffObserver:function(){o.off(this.token)}};t.exports=function(t){i.isReady()?c.setUpObserver():i.addReadyHandler(c.setUpObserver),t.registerViewTrigger("DOMChanged",c)}},843:function(t,n,e){var i=e(3221),r=e(6210),o=e(3659),a=e(3898),u=i.get("stores/directive"),c="optimizelyPreview",s=function(t){a.getGlobal(c).push(t)};n.initialize=function(t){u.isSlave()&&function(t){var n=!1;if(Array.isArray(window.optimizely)&&window.optimizely.forEach((function(e){Array.isArray(e)&&"verifyPreviewProject"===e[0]&&String(e[1])===t&&(n=!0)})),!n)throw new Error("Preview projectId: "+t+" does not match expected")}(t),r.on({filter:{type:o.TYPES.ANALYTICS,name:"trackEvent"},handler:s}),r.on({filter:{type:o.TYPES.LIFECYCLE,name:"viewActivated"},handler:s}),r.on({filter:{type:o.TYPES.LIFECYCLE,name:"layerDecided"},handler:s}),r.on({filter:{type:"error"},publicOnly:!0,handler:s})},n.setupPreviewGlobal=function(){a.getGlobal(c)||a.setGlobal(c,[])},n.pushToPreviewGlobal=function(t){s(t)}},8942:function(t,n,e){var i=e(279),r=i.isEmpty,o=i.mapValues,a=i.pick,u=i.pickBy,c=e(167),s=e(8548),f=e(6027),l=e(6310),d=e(9751),v=e(3941),h=e(7429).DT,p=e(3496),g=e(4645),m=e(3341).Promise,b=e(7649),y=e(3898),_=e(4464),w=e(1756),E=e(2044),I=e(3221),A=I.get("stores/async_request"),T=I.get("stores/client_metadata"),S=I.get("stores/global"),O=I.get("stores/rum"),k=I.get("stores/performance"),R=I.get("stores/xdomain"),C=I.get("stores/view_data"),D=e(5205),N="https://rum.optimizely.com/rum",x="1.0";function j(t){return r(t)?m.resolve():(n=function(){return _.request({url:N,method:"POST",data:t,withCredentials:!0}).then((function(t){return s.resolveRequest("RUM_FIRST_BEACON",t),t})).catch((function(t){throw p.error("POST to client-rum failed:",t),s.rejectRequest("RUM_FIRST_BEACON",t),t}))},(e=A.getPromise("RUM_FIRST_BEACON"))?e.then(n):s.makeAsyncRequest("RUM_FIRST_BEACON",n));var n,e}function P(){try{return!d.querySelector("body")}catch(t){return null}}function M(){var t=y.getGlobal("performance"),n=t?t.timing:{},e=k.getMarks()||{},i=O.getApiData(),r=O.getDOMObservationData(),o=D.get("state").getActiveExperimentIds(),a=O.getFeaturesNeededData(),u=d.parseUri(O.getScriptSrc()),c=O.getRumData()||{},s=c.extras||{};Object.assign(s,{apiCalls:i,DOMObservationData:r,paintTimings:V(),activeExperimentIds:o,numPages:C.getNumberOfPages(),snippet:{scheme:u.protocol.slice(0,-1),host:u.host,path:u.pathname},networkInfo:L(),experimental:S.getExperimental(),featuresNeeded:a,beacon:{cjsOnload:!0}});var f=y.getGlobal("Prototype");f&&void 0!==f.Version&&(s.prototypeJS=f.Version);var l=!1;l=!0;var v=R.getFrames();v.length&&(s.xdFramesLoaded=v.length);
2var m={id:O.getRumId(),v:x,project:S.getSnippetId()||S.getProjectId(),navigationTimings:n,userTimings:e,xd:l,apis:Object.keys(i),extras:s,sampleRate:c.sampleRate};(function(){var t=h.keys().map((function(t){var n=b.getStorageKeyFromKey(t);return n?{key:t,isForeign:b.isForeignKey(t),category:n,size:t.length+h.getItem(t).length}:null})).filter(Boolean),n=t.reduce((function(t,n){var e=n.key,i=b.getIdFromKey(e);return i?((n.isForeign?t.foreign:t.local)[i]=!0,t):t}),{local:{},foreign:{}}),e=t.filter((function(t){return!0===t.isForeign})).reduce((function(t,n){return t[n.key.split("_")[0]]=!0,t}),{}),i={local:0,foreign:0},r={local:{},foreign:{}};t.forEach((function(t){var n=t.isForeign?"foreign":"local";i[n]+=t.size,r[n][t.category]||(r[n][t.category]=0),r[n][t.category]+=t.size}));var o={numKeys:h.allKeys().length,sizeKeys:h.allKeys().toString().length,sizeValues:h.allValues().toString().length,idCounts:{local:Object.keys(n.local).length,foreign:Object.keys(n.foreign).length},foreignOriginCount:Object.keys(e).length,byteTotals:i,byteTotalsByCategory:r};return g.estimateStorage().then((function(t){return Object.assign(o,{storageEstimate:t})}))})().then((function(t){return j(Object.assign(m,{lsMetrics:t}))})).catch((function(t){p.warn("RUM / Error sending onload beacon:",t)}))}function L(){var t=y.getGlobal("navigator");if(t&&t.connection)return a(t.connection,["downlink","rtt","effectiveType"])}function V(){var t=y.getGlobal("performance");if(t)try{var n=t.getEntriesByType("paint");if(r(n))return;return n.reduce((function(t,n){return t[n.name]=Math.round(n.startTime),t}),{})}catch(t){return}}n.initialize=function(){var t,n=c.generate().replace(/-/g,"");t=Math.random()<1;var e=function(){var t=d.getCurrentScript();if(t)return t.src}();v.dispatch(w.SET_RUM_DATA,{id:n,RumHost:N,inRumSample:t,src:e,data:{id:n,sync:P(),timebase:l.now(),sampleRate:1,url:e,extras:{initialDOMState:d.getReadyState()}}})},n.queueBeacons=function(){return O.getSampleRum()?(function(){try{y.getGlobal("requestAnimationFrame")((function(){var t=O.getRumData().timebase;v.dispatch(w.SET_RUM_DATA,{data:{render:l.now()-(t||0)}})}))}catch(t){return}}(),d.isLoaded()?y.setTimeout(M,0):y.addEventListener("load",M),new m((function(t,n){y.setTimeout((function(){(function(){!function(){var t={id:O.getRumId(),v:x,account:S.getAccountId(),project:S.getSnippetId()||S.getProjectId(),snippet:S.getSnippetId(),revision:S.getRevision(),clientVersion:T.getClientVersion(),hasSlave:!1,wxhr:!0,extras:{}},n=b.getPersistedBehaviorEventCount(),e=f.getEventCount();t.numBehaviorEvents=e;var i=e-n;Object.assign(t.extras,{behaviorEventCountDiff:i,behaviorEventCountDecreased:i<0}),Object.assign(t,function(){var t=y.getGlobal("performance");if(!t)return;var n,e=O.getScriptSrc();try{if(e){p.debug("Using derived script src: ",e);var i=t.getEntriesByName(e);i.length>0&&(n=i[0])}if(!n){var r=/\/\/[^.]+\.optimizely\.(com|test)\/(js|api\/client)\/[\d]+\.js/gi;p.debug("Scanning resource timing entries with regex");var a=t.getEntriesByType("resource");n=a.find((function(t){return r.test(t.name)}))}if(n)return o(E.ResourceTimingAttributes,(function(t,e){var i=n[e];return"number"==typeof i?Math.round(1e3*(i||0))/1e3:"serverTiming"===e?function(t){if(!t||!t.length)return[];for(var n=[],e=0;e<t.length;e++)try{var i=t[e];n.push({name:i.name,duration:i.duration,description:i.description})}catch(t){}return n}(i):void 0}))}catch(t){return}}(),k.getDurationsFor(Object.values(E.RUMPerformanceTimingAttributes))),v.dispatch(w.SET_RUM_DATA,{data:t})}();var t=O.getRumData(),n=k.getMarks()||{};return t.extras=t.extras||{},t.extras.beacon={cjsTimeout:!0},t.userTimings=n,j(t=u(t,(function(t){return void 0!==t})))})().then(t,n)}),0)})).catch((function(t){p.warn("RUM / Error sending data:",t)}))):m.resolve()}},4779:function(t,n,e){var i;!function(r,o){"use strict";var a="function",u="undefined",c="object",s="string",f="major",l="model",d="name",v="type",h="vendor",p="version",g="architecture",m="console",b="mobile",y="tablet",_="smarttv",w="wearable",E="embedded",I="Amazon",A="Apple",T="ASUS",S="BlackBerry",O="Browser",k="Chrome",R="Firefox",C="Google",D="Honor",N="Huawei",x="LG",j="Microsoft",P="Motorola",M="Nvidia",L="OnePlus",V="Opera",U="OPPO",z="Samsung",B="Sharp",F="Sony",G="Xiaomi",q="Zebra",H="Facebook",Y="Chromium OS",K="Mac OS",$=" Browser",W=function(t){for(var n={},e=0;e<t.length;e++)n[t[e].toUpperCase()]=t[e];return n},X=function(t,n){return typeof t===s&&-1!==Q(n).indexOf(Q(t))},Q=function(t){return t.toLowerCase()},J=function(t,n){if(typeof t===s)return t=t.replace(/^\s\s*/,""),typeof n===u?t:t.substring(0,500)},Z=function(t,n){for(var e,i,r,u,s,f,l=0;l<n.length&&!s;){var d=n[l],v=n[l+1];for(e=i=0;e<d.length&&!s&&d[e];)if(s=d[e++].exec(t))for(r=0;r<v.length;r++)f=s[++i],typeof(u=v[r])===c&&u.length>0?2===u.length?typeof u[1]==a?this[u[0]]=u[1].call(this,f):this[u[0]]=u[1]:3===u.length?typeof u[1]!==a||u[1].exec&&u[1].test?this[u[0]]=f?f.replace(u[1],u[2]):o:this[u[0]]=f?u[1].call(this,f,u[2]):o:4===u.length&&(this[u[0]]=f?u[3].call(this,f.replace(u[1],u[2])):o):this[u]=f||o;l+=2}},tt=function(t,n){for(var e in n)if(typeof n[e]===c&&n[e].length>0){for(var i=0;i<n[e].length;i++)if(X(n[e][i],t))return"?"===e?o:e}else if(X(n[e],t))return"?"===e?o:e;return n.hasOwnProperty("*")?n["*"]:t},nt={ME:"4.90","NT 3.11":"NT3.51","NT 4.0":"NT4.0",2e3:"NT 5.0",XP:["NT 5.1","NT 5.2"],Vista:"NT 6.0",7:"NT 6.1",8:"NT 6.2",8.1:"NT 6.3",10:["NT 6.4","NT 10.0"],RT:"ARM"},et={browser:[[/\b(?:crmo|crios)\/([\w\.]+)/i],[p,[d,"Chrome"]],[/edg(?:e|ios|a)?\/([\w\.]+)/i],[p,[d,"Edge"]],[/(opera mini)\/([-\w\.]+)/i,/(opera [mobiletab]{3,6})\b.+version\/([-\w\.]+)/i,/(opera)(?:.+version\/|[\/ ]+)([\w\.]+)/i],[d,p],[/opios[\/ ]+([\w\.]+)/i],[p,[d,V+" Mini"]],[/\bop(?:rg)?x\/([\w\.]+)/i],[p,[d,V+" GX"]],[/\bopr\/([\w\.]+)/i],[p,[d,V]],[/\bb[ai]*d(?:uhd|[ub]*[aekoprswx]{5,6})[\/ ]?([\w\.]+)/i],[p,[d,"Baidu"]],[/\b(?:mxbrowser|mxios|myie2)\/?([-\w\.]*)\b/i],[p,[d,"Maxthon"]],[/(kindle)\/([\w\.]+)/i,/(lunascape|maxthon|netfront|jasmine|blazer|sleipnir)[\/ ]?([\w\.]*)/i,/(avant|iemobile|slim(?:browser|boat|jet))[\/ ]?([\d\.]*)/i,/(?:ms|\()(ie) ([\w\.]+)/i,/(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio|(?=comodo_)?dragon)\/([-\w\.]+)/i,/(heytap|ovi|115)browser\/([\d\.]+)/i,/(weibo)__([\d\.]+)/i],[d,p],[/quark(?:pc)?\/([-\w\.]+)/i],[p,[d,"Quark"]],[/\bddg\/([\w\.]+)/i],[p,[d,"DuckDuckGo"]],[/(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i],[p,[d,"UC"+O]],[/microm.+\bqbcore\/([\w\.]+)/i,/\bqbcore\/([\w\.]+).+microm/i,/micromessenger\/([\w\.]+)/i],[p,[d,"WeChat"]],[/konqueror\/([\w\.]+)/i],[p,[d,"Konqueror"]],[/trident.+rv[: ]([\w\.]{1,9})\b.+like gecko/i],[p,[d,"IE"]],[/ya(?:search)?browser\/([\w\.]+)/i],[p,[d,"Yandex"]],[/slbrowser\/([\w\.]+)/i],[p,[d,"Smart Lenovo "+O]],[/(avast|avg)\/([\w\.]+)/i],[[d,/(.+)/,"$1 Secure "+O],p],[/\bfocus\/([\w\.]+)/i],[p,[d,R+" Focus"]],[/\bopt\/([\w\.]+)/i],[p,[d,V+" Touch"]],[/coc_coc\w+\/([\w\.]+)/i],[p,[d,"Coc Coc"]],[/dolfin\/([\w\.]+)/i],[p,[d,"Dolphin"]],[/coast\/([\w\.]+)/i],[p,[d,V+" Coast"]],[/miuibrowser\/([\w\.]+)/i],[p,[d,"MIUI"+$]],[/fxios\/([\w\.-]+)/i],[p,[d,R]],[/\bqihoobrowser\/?([\w\.]*)/i],[p,[d,"360"]],[/\b(qq)\/([\w\.]+)/i],[[d,/(.+)/,"$1Browser"],p],[/(oculus|sailfish|huawei|vivo|pico)browser\/([\w\.]+)/i],[[d,/(.+)/,"$1"+$],p],[/samsungbrowser\/([\w\.]+)/i],[p,[d,z+" Internet"]],[/metasr[\/ ]?([\d\.]+)/i],[p,[d,"Sogou Explorer"]],[/(sogou)mo\w+\/([\d\.]+)/i],[[d,"Sogou Mobile"],p],[/(electron)\/([\w\.]+) safari/i,/(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i,/m?(qqbrowser|2345(?=browser|chrome|explorer))\w*[\/ ]?v?([\w\.]+)/i],[d,p],[/(lbbrowser|rekonq)/i,/\[(linkedin)app\]/i],[d],[/ome\/([\w\.]+) \w* ?(iron) saf/i,/ome\/([\w\.]+).+qihu (360)[es]e/i],[p,d],[/((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i],[[d,H],p],[/(Klarna)\/([\w\.]+)/i,/(kakao(?:talk|story))[\/ ]([\w\.]+)/i,/(naver)\(.*?(\d+\.[\w\.]+).*\)/i,/(daum)apps[\/ ]([\w\.]+)/i,/safari (line)\/([\w\.]+)/i,/\b(line)\/([\w\.]+)\/iab/i,/(alipay)client\/([\w\.]+)/i,/(twitter)(?:and| f.+e\/([\w\.]+))/i,/(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i],[d,p],[/\bgsa\/([\w\.]+) .*safari\//i],[p,[d,"GSA"]],[/musical_ly(?:.
vendor: 4,233 bytes, line 2
2+app_?version\/|_)([\w\.]+)/i],[p,[d,"TikTok"]],[/headlesschrome(?:\/([\w\.]+)| )/i],[p,[d,k+" Headless"]],[/ wv\).+(chrome)\/([\w\.]+)/i],[[d,k+" WebView"],p],[/droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i],[p,[d,"Android "+O]],[/(chrome|omniweb|arora|[tizenoka]{5} ?browser)\/v?([\w\.]+)/i],[d,p],[/version\/([\w\.\,]+) .*mobile\/\w+ (safari)/i],[p,[d,"Mobile Safari"]],[/version\/([\w(\.|\,)]+) .*(mobile ?safari|safari)/i],[p,d],[/webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i],[d,[p,tt,{"1.0":"/8",1.2:"/1",1.3:"/3","2.0":"/412","2.0.2":"/416","2.0.3":"/417","2.0.4":"/419","?":"/"}]],[/(webkit|khtml)\/([\w\.]+)/i],[d,p],[/(navigator|netscape\d?)\/([-\w\.]+)/i],[[d,"Netscape"],p],[/(wolvic|librewolf)\/([\w\.]+)/i],[d,p],[/mobile vr; rv:([\w\.]+)\).+firefox/i],[p,[d,R+" Reality"]],[/ekiohf.+(flow)\/([\w\.]+)/i,/(swiftfox)/i,/(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror)[\/ ]?([\w\.\+]+)/i,/(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([-\w\.]+)$/i,/(firefox)\/([\w\.]+)/i,/(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i,/(amaya|dillo|doris|icab|ladybird|lynx|mosaic|netsurf|obigo|polaris|w3m|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i,/\b(links) \(([\w\.]+)/i],[d,[p,/_/g,"."]],[/(cobalt)\/([\w\.]+)/i],[d,[p,/master.|lts./,""]]],cpu:[[/\b((amd|x|x86[-_]?|wow|win)64)\b/i],[[g,"amd64"]],[/(ia32(?=;))/i,/\b((i[346]|x)86)(pc)?\b/i],[[g,"ia32"]],[/\b(aarch64|arm(v?[89]e?l?|_?64))\b/i],[[g,"arm64"]],[/\b(arm(v[67])?ht?n?[fl]p?)\b/i],[[g,"armhf"]],[/( (ce|mobile); ppc;|\/[\w\.]+arm\b)/i],[[g,"arm"]],[/((ppc|powerpc)(64)?)( mac|;|\))/i],[[g,/ower/,"",Q]],[/ sun4\w[;\)]/i],[[g,"sparc"]],[/\b(avr32|ia64(?=;)|68k(?=\))|\barm(?=v([1-7]|[5-7]1)l?|;|eabi)|(irix|mips|sparc)(64)?\b|pa-risc)/i],[[g,Q]]],device:[[/\b(sch-i[89]0\d|shw-m380s|sm-[ptx]\w{2,4}|gt-[pn]\d{2,4}|sgh-t8[56]9|nexus 10)/i],[l,[h,z],[v,y]],[/\b((?:s[cgp]h|gt|sm)-(?![lr])\w+|sc[g-]?[\d]+a?|galaxy nexus)/i,/samsung[- ]((?!sm-[lr])[-\w]+)/i,/sec-(sgh\w+)/i],[l,[h,z],[v,b]],[/(?:\/|\()(ip(?:hone|od)[\w, ]*)(?:\/|;)/i],[l,[h,A],[v,b]],[/\((ipad);[-\w\),; ]+apple/i,/applecoremedia\/[\w\.]+ \((ipad)/i,/\b(ipad)\d\d?,\d\d?[;\]].+ios/i],[l,[h,A],[v,y]],[/(macintosh);/i],[l,[h,A]],[/\b(sh-?[altvz]?\d\d[a-ekm]?)/i],[l,[h,B],[v,b]],[/\b((?:brt|eln|hey2?|gdi|jdn)-a?[lnw]09|(?:ag[rm]3?|jdn2|kob2)-a?[lw]0[09]hn)(?: bui|\)|;)/i],[l,[h,D],[v,y]],[/honor([-\w ]+)[;\)]/i],[l,[h,D],[v,b]],[/\b((?:ag[rs][2356]?k?|bah[234]?|bg[2o]|bt[kv]|cmr|cpn|db[ry]2?|jdn2|got|kob2?k?|mon|pce|scm|sht?|[tw]gr|vrd)-[ad]?[lw][0125][09]b?|605hw|bg2-u03|(?:gem|fdr|m2|ple|t1)-[7a]0[1-4][lu]|t1-a2[13][lw]|mediapad[\w\. ]*(?= bui|\)))\b(?!.+d\/s)/i],[l,[h,N],[v,y]],[/(?:huawei)([-\w ]+)[;\)]/i,/\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][012359c][adn]?)\b(?!.+d\/s)/i],[l,[h,N],[v,b]],[/oid[^\)]+; (2[\dbc]{4}(182|283|rp\w{2})[cgl]|m2105k81a?c)(?: bui|\))/i,/\b((?:red)?mi[-_ ]?pad[\w- ]*)(?: bui|\))/i],[[l,/_/g," "],[h,G],[v,y]],[/\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i,/\b; (\w+) build\/hm\1/i,/\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i,/\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i,/oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|; wv|\))/i,/\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite|pro)?)(?: bui|\))/i,/ ([\w ]+) miui\/v?\d/i],[[l,/_/g," "],[h,G],[v,b]],[/; (\w+) bui.+ oppo/i,/\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i],[l,[h,U],[v,b]],[/\b(opd2(\d{3}a?))(?: bui|\))/i],[l,[h,tt,{OnePlus:["304","403","203"],"*":U}],[v,y]],[/vivo (\w+)(?: bui|\))/i,/\b(v[12]\d{3}\w?[at])(?: bui|;)/i],[l,[h,"Vivo"],[v,b]],[/\b(rmx[1-3]\d{3})(?: bui|;|\))/i],[l,[h,"Realme"],[v,b]],[/\b(milestone|droid(?:[2-4x]| (?:bionic|x2|pro|razr))?:?( 4g)?)\b[\w ]+build\//i,/\bmot(?:orola)?[- ](\w*)/i,/((?:moto(?! 360)[\w\(\) ]+|xt\d{3,4}|nexus 6)(?= bui|\)))/i],[l,[h,P],[v,b]],[/\b(mz60\d|xoom[2 ]{0,2}) build\//i],[l,[h,P],[v,y]],[/((?=lg)?[vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i],[l,[h,x],[v,y]],[/(lm(?:-?f100[nv]?|-[\w\.]+)(?= bui|\))|nexus [45])/i,/\blg[-e;\/ ]+((?!browser|netcast|android tv|watch)\w+)/i,/\blg-?([\d\w]+) bui/i],[l,[h,x],[v,b]],[/(ideatab[-\w ]+|602lv|d-42a|a101lv|a2109a|a3500-hv|s[56]000|pb-6505[my]|tb-?x?\d{3,4}
vendor: 4,494 bytes, line 2
2(?:f[cu]|xu|[av])|yt\d?-[jx]?\d+[lfmx])( bui|;|\)|\/)/i,/lenovo ?(b[68]0[08]0-?[hf]?|tab(?:[\w- ]+?)|tb[\w-]{6,7})( bui|;|\)|\/)/i],[l,[h,"Lenovo"],[v,y]],[/(nokia) (t[12][01])/i],[h,l,[v,y]],[/(?:maemo|nokia).*(n900|lumia \d+|rm-\d+)/i,/nokia[-_ ]?(([-\w\. ]*))/i],[[l,/_/g," "],[v,b],[h,"Nokia"]],[/(pixel (c|tablet))\b/i],[l,[h,C],[v,y]],[/droid.+; (pixel[\daxl ]{0,6})(?: bui|\))/i],[l,[h,C],[v,b]],[/droid.+; (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-a\w[4-7][12])(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i],[l,[h,F],[v,b]],[/sony tablet [ps]/i,/\b(?:sony)?sgp\w+(?: bui|\))/i],[[l,"Xperia Tablet"],[h,F],[v,y]],[/ (kb2005|in20[12]5|be20[12][59])\b/i,/(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i],[l,[h,L],[v,b]],[/(alexa)webm/i,/(kf[a-z]{2}wi|aeo(?!bc)\w\w)( bui|\))/i,/(kf[a-z]+)( bui|\)).+silk\//i],[l,[h,I],[v,y]],[/((?:sd|kf)[0349hijorstuw]+)( bui|\)).+silk\//i],[[l,/(.+)/g,"Fire Phone $1"],[h,I],[v,b]],[/(playbook);[-\w\),; ]+(rim)/i],[l,h,[v,y]],[/\b((?:bb[a-f]|st[hv])100-\d)/i,/\(bb10; (\w+)/i],[l,[h,S],[v,b]],[/(?:\b|asus_)(transfo[prime ]{4,10} \w+|eeepc|slider \w+|nexus 7|padfone|p00[cj])/i],[l,[h,T],[v,y]],[/ (z[bes]6[027][012][km][ls]|zenfone \d\w?)\b/i],[l,[h,T],[v,b]],[/(nexus 9)/i],[l,[h,"HTC"],[v,y]],[/(htc)[-;_ ]{1,2}([\w ]+(?=\)| bui)|\w+)/i,/(zte)[- ]([\w ]+?)(?: bui|\/|\))/i,/(alcatel|geeksphone|nexian|panasonic(?!(?:;|\.))|sony(?!-bra))[-_ ]?([-\w]*)/i],[h,[l,/_/g," "],[v,b]],[/droid [\w\.]+; ((?:8[14]9[16]|9(?:0(?:48|60|8[01])|1(?:3[27]|66)|2(?:6[69]|9[56])|466))[gqswx])\w*(\)| bui)/i],[l,[h,"TCL"],[v,y]],[/(itel) ((\w+))/i],[[h,Q],l,[v,tt,{tablet:["p10001l","w7001"],"*":"mobile"}]],[/droid.+; ([ab][1-7]-?[0178a]\d\d?)/i],[l,[h,"Acer"],[v,y]],[/droid.+; (m[1-5] note) bui/i,/\bmz-([-\w]{2,})/i],[l,[h,"Meizu"],[v,b]],[/; ((?:power )?armor(?:[\w ]{0,8}))(?: bui|\))/i],[l,[h,"Ulefone"],[v,b]],[/; (energy ?\w+)(?: bui|\))/i,/; energizer ([\w ]+)(?: bui|\))/i],[l,[h,"Energizer"],[v,b]],[/; cat (b35);/i,/; (b15q?|s22 flip|s48c|s62 pro)(?: bui|\))/i],[l,[h,"Cat"],[v,b]],[/((?:new )?andromax[\w- ]+)(?: bui|\))/i],[l,[h,"Smartfren"],[v,b]],[/droid.+; (a(?:015|06[35]|142p?))/i],[l,[h,"Nothing"],[v,b]],[/; (x67 5g|tikeasy \w+|ac[1789]\d\w+)( b|\))/i,/archos ?(5|gamepad2?|([\w ]*[t1789]|hello) ?\d+[\w ]*)( b|\))/i],[l,[h,"Archos"],[v,y]],[/archos ([\w ]+)( b|\))/i,/; (ac[3-6]\d\w{2,8})( b|\))/i],[l,[h,"Archos"],[v,b]],[/(imo) (tab \w+)/i,/(infinix) (x1101b?)/i],[h,l,[v,y]],[/(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|infinix|tecno|micromax|advan)[-_ ]?([-\w]*)/i,/; (hmd|imo) ([\w ]+?)(?: bui|\))/i,/(hp) ([\w ]+\w)/i,/(microsoft); (lumia[\w ]+)/i,/(lenovo)[-_ ]?([-\w ]+?)(?: bui|\)|\/)/i,/(oppo) ?([\w ]+) bui/i],[h,l,[v,b]],[/(kobo)\s(ereader|touch)/i,/(hp).+(touchpad(?!.+tablet)|tablet)/i,/(kindle)\/([\w\.]+)/i,/(nook)[\w ]+build\/(\w+)/i,/(dell) (strea[kpr\d ]*[\dko])/i,/(le[- ]+pan)[- ]+(\w{1,9}) bui/i,/(trinity)[- ]*(t\d{3}) bui/i,/(gigaset)[- ]+(q\w{1,9}) bui/i,/(vodafone) ([\w ]+)(?:\)| bui)/i],[h,l,[v,y]],[/(surface duo)/i],[l,[h,j],[v,y]],[/droid [\d\.]+; (fp\du?)(?: b|\))/i],[l,[h,"Fairphone"],[v,b]],[/(u304aa)/i],[l,[h,"AT&T"],[v,b]],[/\bsie-(\w*)/i],[l,[h,"Siemens"],[v,b]],[/\b(rct\w+) b/i],[l,[h,"RCA"],[v,y]],[/\b(venue[\d ]{2,7}) b/i],[l,[h,"Dell"],[v,y]],[/\b(q(?:mv|ta)\w+) b/i],[l,[h,"Verizon"],[v,y]],[/\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i],[l,[h,"Barnes & Noble"],[v,y]],[/\b(tm\d{3}\w+) b/i],[l,[h,"NuVision"],[v,y]],[/\b(k88) b/i],[l,[h,"ZTE"],[v,y]],[/\b(nx\d{3}j) b/i],[l,[h,"ZTE"],[v,b]],[/\b(gen\d{3}) b.+49h/i],[l,[h,"Swiss"],[v,b]],[/\b(zur\d{3}) b/i],[l,[h,"Swiss"],[v,y]],[/\b((zeki)?tb.*\b) b/i],[l,[h,"Zeki"],[v,y]],[/\b([yr]\d{2}) b/i,/\b(dragon[- ]+touch |dt)(\w{5}) b/i],[[h,"Dragon Touch"],l,[v,y]],[/\b(ns-?\w{0,9}) b/i],[l,[h,"Insignia"],[v,y]],[/\b((nxa|next)-?\w{0,9}) b/i],[l,[h,"NextBook"],[v,y]],[/\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i],[[h,"Voice"],l,[v,b]],[/\b(lvtel\-)?(v1[12]) b/i],[[h,"LvTel"],l,[v,b]],[/\b(ph-1) /i],[l,[h,"Essential"],[v,b]],[/\b(v(100md|700na|7011|917g).*\b) b/i],[l,[h,"Envizen"],[v,y]],[/\b(trio[-\w\. ]+) b/i],[l,[h,"MachSpeed"],[v,y]],[/\btu_(1491) b/i],[l,[h,"Rotor"],[v,y]],[/((?:tegranote|shield t(?!.+d tv))[\w- ]*?)(?: b|\))/i],[l,[h,M],[v,y]],[/(sprint) (\w+)/i],[h,l,[v,b]],[/(kin\.[onetw]{3})/i],[[l,/\./g," "],[h,j],[v,b]],[/droid.+; (cc6666?|et5[16]|mc[239][23]x?|vc8[03]x?)\)/i],[l,[h,q],[v,y]],[/droid.+; (ec30|ps20|tc[2-8]\d[kx])\)/i],[l,[h,q],[v,b]],[/smart-tv.+(samsung)/i],[h,[v,_]
vendor: 5,205 bytes, line 2
2],[/hbbtv.+maple;(\d+)/i],[[l,/^/,"SmartTV"],[h,z],[v,_]],[/(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i],[[h,x],[v,_]],[/(apple) ?tv/i],[h,[l,A+" TV"],[v,_]],[/crkey/i],[[l,k+"cast"],[h,C],[v,_]],[/droid.+aft(\w+)( bui|\))/i],[l,[h,I],[v,_]],[/(shield \w+ tv)/i],[l,[h,M],[v,_]],[/\(dtv[\);].+(aquos)/i,/(aquos-tv[\w ]+)\)/i],[l,[h,B],[v,_]],[/(bravia[\w ]+)( bui|\))/i],[l,[h,F],[v,_]],[/(mi(tv|box)-?\w+) bui/i],[l,[h,G],[v,_]],[/Hbbtv.*(technisat) (.*);/i],[h,l,[v,_]],[/\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i,/hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i],[[h,J],[l,J],[v,_]],[/droid.+; ([\w- ]+) (?:android tv|smart[- ]?tv)/i],[l,[v,_]],[/\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i],[[v,_]],[/(ouya)/i,/(nintendo) ([wids3utch]+)/i],[h,l,[v,m]],[/droid.+; (shield)( bui|\))/i],[l,[h,M],[v,m]],[/(playstation \w+)/i],[l,[h,F],[v,m]],[/\b(xbox(?: one)?(?!; xbox))[\); ]/i],[l,[h,j],[v,m]],[/\b(sm-[lr]\d\d[0156][fnuw]?s?|gear live)\b/i],[l,[h,z],[v,w]],[/((pebble))app/i,/(asus|google|lg|oppo) ((pixel |zen)?watch[\w ]*)( bui|\))/i],[h,l,[v,w]],[/(ow(?:19|20)?we?[1-3]{1,3})/i],[l,[h,U],[v,w]],[/(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i],[l,[h,A],[v,w]],[/(opwwe\d{3})/i],[l,[h,L],[v,w]],[/(moto 360)/i],[l,[h,P],[v,w]],[/(smartwatch 3)/i],[l,[h,F],[v,w]],[/(g watch r)/i],[l,[h,x],[v,w]],[/droid.+; (wt63?0{2,3})\)/i],[l,[h,q],[v,w]],[/droid.+; (glass) \d/i],[l,[h,C],[v,w]],[/(pico) (4|neo3(?: link|pro)?)/i],[h,l,[v,w]],[/; (quest( \d| pro)?)/i],[l,[h,H],[v,w]],[/(tesla)(?: qtcarbrowser|\/[-\w\.]+)/i],[h,[v,E]],[/(aeobc)\b/i],[l,[h,I],[v,E]],[/(homepod).+mac os/i],[l,[h,A],[v,E]],[/windows iot/i],[[v,E]],[/droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew).+? mobile safari/i],[l,[v,b]],[/droid .+?; ([^;]+?)(?: bui|\) applew).+?(?! mobile) safari/i],[l,[v,y]],[/\b((tablet|tab)[;\/]|focus\/\d(?!.+mobile))/i],[[v,y]],[/(phone|mobile(?:[;\/]| [ \w\/\.]*safari)|pda(?=.+windows ce))/i],[[v,b]],[/droid .+?; ([\w\. -]+)( bui|\))/i],[l,[h,"Generic"]]],engine:[[/windows.+ edge\/([\w\.]+)/i],[p,[d,"EdgeHTML"]],[/(arkweb)\/([\w\.]+)/i],[d,p],[/webkit\/537\.36.+chrome\/(?!27)([\w\.]+)/i],[p,[d,"Blink"]],[/(presto)\/([\w\.]+)/i,/(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna|servo)\/([\w\.]+)/i,/ekioh(flow)\/([\w\.]+)/i,/(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i,/(icab)[\/ ]([23]\.[\d\.]+)/i,/\b(libweb)/i],[d,p],[/ladybird\//i],[[d,"LibWeb"]],[/rv\:([\w\.]{1,9})\b.+(gecko)/i],[p,d]],os:[[/microsoft (windows) (vista|xp)/i],[d,p],[/(windows (?:phone(?: os)?|mobile|iot))[\/ ]?([\d\.\w ]*)/i],[d,[p,tt,nt]],[/windows nt 6\.2; (arm)/i,/windows[\/ ]([ntce\d\. ]+\w)(?!.+xbox)/i,/(?:win(?=3|9|n)|win 9x )([nt\d\.]+)/i],[[p,tt,nt],[d,"Windows"]],[/[adehimnop]{4,7}\b(?:.*os ([\w]+) like mac|; opera)/i,/(?:ios;fbsv\/|iphone.+ios[\/ ])([\d\.]+)/i,/cfnetwork\/.+darwin/i],[[p,/_/g,"."],[d,"iOS"]],[/(mac os x) ?([\w\. ]*)/i,/(macintosh|mac_powerpc\b)(?!.+haiku)/i],[[d,K],[p,/_/g,"."]],[/droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i],[p,d],[/(ubuntu) ([\w\.]+) like android/i],[[d,/(.+)/,"$1 Touch"],p],[/(android|bada|blackberry|kaios|maemo|meego|openharmony|qnx|rim tablet os|sailfish|series40|symbian|tizen|webos)\w*[-\/; ]?([\d\.]*)/i],[d,p],[/\(bb(10);/i],[p,[d,S]],[/(?:symbian ?os|symbos|s60(?=;)|series ?60)[-\/ ]?([\w\.]*)/i],[p,[d,"Symbian"]],[/mozilla\/[\d\.]+ \((?:mobile|tablet|tv|mobile; [\w ]+); rv:.+ gecko\/([\w\.]+)/i],[p,[d,R+" OS"]],[/web0s;.+rt(tv)/i,/\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i],[p,[d,"webOS"]],[/watch(?: ?os[,\/]|\d,\d\/)([\d\.]+)/i],[p,[d,"watchOS"]],[/crkey\/([\d\.]+)/i],[p,[d,k+"cast"]],[/(cros) [\w]+(?:\)| ([\w\.]+)\b)/i],[[d,Y],p],[/panasonic;(viera)/i,/(netrange)mmh/i,/(nettv)\/(\d+\.[\w\.]+)/i,/(nintendo|playstation) ([wids345portablevuch]+)/i,/(xbox); +xbox ([^\);]+)/i,/\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i,/(mint)[\/\(\) ]?(\w*)/i,/(mageia|vectorlinux)[; ]/i,/([kxln]?ubuntu|debian|suse|opensuse|gentoo|arch(?= linux)|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire)(?: gnu\/linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i,/(hurd|linux)(?: arm\w*| x86\w*| ?)([\w\.]*)/i,/(gnu) ?([\w\.]*)/i,/\b([-frentopcghs]{0,5}bsd|dragonfly)[\/ ]?(?!amd|[ix346]{1,2}86)([\w\.]*)/i,/(haiku) (\w+)/i],[d,p],[/(sunos) ?([\w\.\d]*)/i],[[d,"Solaris"],p],[/((?:open)?solaris)[-\/ ]?([\w\.]*)/i,/(aix) ((\d)(?=\.|\)| )[\w\.])*/i,/\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux|serenityos)/i,/(unix) ?([\w\.]*)/i],[d,p]]},it=function(t,n){if(typeof t===c&&(n=t,t=o),!(this instanceof it))return new it(t,n).getResult();var e=typeof r!==u&&r.navigator?r.navigator:o,i=t||(e&&e.userAgent?e.userAgent:""),m=e&&e.userAgentData?e.userAgentData:o,_=n?function(t,n){var e={};for(var i in t)n[i]&&n[i].length%2==0?e[i]=n[i].concat(t[i]):e[i]=t[i];return e}(et,n):et,w=e&&e.userAgent==i;return this.getBrowser=function(){var t,n={};return n[d]=o,n[p]=o,Z.call(n,i,_.browser),n[f]=typeof(t=n[p])===s?t.replace(/[^\d\.]/g,"").split(".")[0]:o,w&&e&&e.brave&&typeof e.brave.isBrave==a&&(n[d]="Brave"),n},this.getCPU=function(){var t={};return t[g]=o,Z.call(t,i,_.cpu),t},this.getDevice=function(){var t={};
2return t[h]=o,t[l]=o,t[v]=o,Z.call(t,i,_.device),w&&!t[v]&&m&&m.mobile&&(t[v]=b),w&&"Macintosh"==t[l]&&e&&typeof e.standalone!==u&&e.maxTouchPoints&&e.maxTouchPoints>2&&(t[l]="iPad",t[v]=y),t},this.getEngine=function(){var t={};return t[d]=o,t[p]=o,Z.call(t,i,_.engine),t},this.getOS=function(){var t={};return t[d]=o,t[p]=o,Z.call(t,i,_.os),w&&!t[d]&&m&&m.platform&&"Unknown"!=m.platform&&(t[d]=m.platform.replace(/chrome os/i,Y).replace(/macos/i,K)),t},this.getResult=function(){return{ua:this.getUA(),browser:this.getBrowser(),engine:this.getEngine(),os:this.getOS(),device:this.getDevice(),cpu:this.getCPU()}},this.getUA=function(){return i},this.setUA=function(t){return i=typeof t===s&&t.length>500?J(t,500):t,this},this.setUA(i),this};it.VERSION="1.0.41",it.BROWSER=W([d,p,f]),it.CPU=W([g]),it.DEVICE=W([l,h,v,m,b,_,y,w,E]),it.ENGINE=it.OS=W([d,p]),typeof n!==u?(t.exports&&(n=t.exports=it),n.UAParser=it):e.amdO?(i=function(){return it}.call(n,e,n,t))===o||(t.exports=i):typeof r!==u&&(r.UAParser=it);var rt=typeof r!==u&&(r.jQuery||r.Zepto);if(rt&&!rt.ua){var ot=new it;rt.ua=ot.getResult(),rt.ua.get=function(){return ot.getUA()},rt.ua.set=function(t){ot.setUA(t);var n=ot.getResult();for(var e in n)rt.ua[e]=n[e]}}}("object"==typeof window?window:this)},6694:function(){},3507:function(t,n){!function(t){"use strict";var n,e,i,r,o,a=-1,u=function(t){addEventListener("pageshow",(function(n){n.persisted&&(a=n.timeStamp,t(n))}),!0)},c=function(){return window.performance&&performance.getEntriesByType&&performance.getEntriesByType("navigation")[0]},s=function(){var t=c();return t&&t.activationStart||0},f=function(t,n){var e=c(),i="navigate";return a>=0?i="back-forward-cache":e&&(document.prerendering||s()>0?i="prerender":document.wasDiscarded?i="restore":e.type&&(i=e.type.replace(/_/g,"-"))),{name:t,value:void 0===n?-1:n,rating:"good",delta:0,entries:[],id:"v3-".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12),navigationType:i}},l=function(t,n,e){try{if(PerformanceObserver.supportedEntryTypes.includes(t)){var i=new PerformanceObserver((function(t){Promise.resolve().then((function(){n(t.getEntries())}))}));return i.observe(Object.assign({type:t,buffered:!0},e||{})),i}}catch(t){}},d=function(t,n,e,i){var r,o;return function(a){n.value>=0&&(a||i)&&((o=n.value-(r||0))||void 0===r)&&(r=n.value,n.delta=o,n.rating=function(t,n){return t>n[1]?"poor":t>n[0]?"needs-improvement":"good"}(n.value,e),t(n))}},v=function(t){requestAnimationFrame((function(){return requestAnimationFrame((function(){return t()}))}))},h=function(t){var n=function(n){"pagehide"!==n.type&&"hidden"!==document.visibilityState||t(n)};addEventListener("visibilitychange",n,!0),addEventListener("pagehide",n,!0)},p=function(t){var n=!1;return function(e){n||(t(e),n=!0)}},g=-1,m=function(){return"hidden"!==document.visibilityState||document.prerendering?1/0:0},b=function(t){"hidden"===document.visibilityState&&g>-1&&(g="visibilitychange"===t.type?t.timeStamp:0,_())},y=function(){addEventListener("visibilitychange",b,!0),addEventListener("prerenderingchange",b,!0)},_=function(){removeEventListener("visibilitychange",b,!0),removeEventListener("prerenderingchange",b,!0)},w=function(){return g<0&&(g=m(),y(),u((function(){setTimeout((function(){g=m(),y()}),0)}))),{get firstHiddenTime(){return g}}},E=function(t){document.prerendering?addEventListener("prerenderingchange",(function(){return t()}),!0):t()},I=[1800,3e3],A=function(t,n){n=n||{},E((function(){var e,i=w(),r=f("FCP"),o=l("paint",(function(t){t.forEach((function(t){"first-contentful-paint"===t.name&&(o.disconnect(),t.startTime<i.firstHiddenTime&&(r.value=Math.max(t.startTime-s(),0),r.entries.push(t),e(!0)))}))}));o&&(e=d(t,r,I,n.reportAllChanges),u((function(i){r=f("FCP"),e=d(t,r,I,n.reportAllChanges),v((function(){r.value=performance.now()-i.timeStamp,e(!0)}))})))}))},T=[.1,.25],S=function(t,n){n=n||{},A(p((function(){var e,i=f("CLS",0),r=0,o=[],a=function(t){t.forEach((function(t){if(!t.hadRecentInput){var n=o[0],e=o[o.length-1];r&&t.startTime-e.startTime<1e3&&t.startTime-n.startTime<5e3?(r+=t.value,o.push(t)):(r=t.value,o=[t])}})),r>
vendor: 4,779 bytes, line 2
2i.value&&(i.value=r,i.entries=o,e())},c=l("layout-shift",a);c&&(e=d(t,i,T,n.reportAllChanges),h((function(){a(c.takeRecords()),e(!0)})),u((function(){r=0,i=f("CLS",0),e=d(t,i,T,n.reportAllChanges),v((function(){return e()}))})),setTimeout(e,0))})))},O={passive:!0,capture:!0},k=new Date,R=function(t,r){n||(n=r,e=t,i=new Date,N(removeEventListener),C())},C=function(){if(e>=0&&e<i-k){var t={entryType:"first-input",name:n.type,target:n.target,cancelable:n.cancelable,startTime:n.timeStamp,processingStart:n.timeStamp+e};r.forEach((function(n){n(t)})),r=[]}},D=function(t){if(t.cancelable){var n=(t.timeStamp>1e12?new Date:performance.now())-t.timeStamp;"pointerdown"==t.type?function(t,n){var e=function(){R(t,n),r()},i=function(){r()},r=function(){removeEventListener("pointerup",e,O),removeEventListener("pointercancel",i,O)};addEventListener("pointerup",e,O),addEventListener("pointercancel",i,O)}(n,t):R(n,t)}},N=function(t){["mousedown","keydown","touchstart","pointerdown"].forEach((function(n){return t(n,D,O)}))},x=[100,300],j=function(t,i){i=i||{},E((function(){var o,a=w(),c=f("FID"),s=function(t){t.startTime<a.firstHiddenTime&&(c.value=t.processingStart-t.startTime,c.entries.push(t),o(!0))},v=function(t){t.forEach(s)},g=l("first-input",v);o=d(t,c,x,i.reportAllChanges),g&&h(p((function(){v(g.takeRecords()),g.disconnect()}))),g&&u((function(){var a;c=f("FID"),o=d(t,c,x,i.reportAllChanges),r=[],e=-1,n=null,N(addEventListener),a=s,r.push(a),C()}))}))},P=0,M=1/0,L=0,V=function(t){t.forEach((function(t){t.interactionId&&(M=Math.min(M,t.interactionId),L=Math.max(L,t.interactionId),P=L?(L-M)/7+1:0)}))},U=function(){return o?P:performance.interactionCount||0},z=function(){"interactionCount"in performance||o||(o=l("event",V,{type:"event",buffered:!0,durationThreshold:0}))},B=[200,500],F=0,G=function(){return U()-F},q=[],H={},Y=function(t){var n=q[q.length-1],e=H[t.interactionId];if(e||q.length<10||t.duration>n.latency){if(e)e.entries.push(t),e.latency=Math.max(e.latency,t.duration);else{var i={id:t.interactionId,latency:t.duration,entries:[t]};H[i.id]=i,q.push(i)}q.sort((function(t,n){return n.latency-t.latency})),q.splice(10).forEach((function(t){delete H[t.id]}))}},K=function(t,n){n=n||{},E((function(){var e;z();var i,r=f("INP"),o=function(t){t.forEach((function(t){t.interactionId&&Y(t),"first-input"===t.entryType&&!q.some((function(n){return n.entries.some((function(n){return t.duration===n.duration&&t.startTime===n.startTime}))}))&&Y(t)}));var n,e=(n=Math.min(q.length-1,Math.floor(G()/50)),q[n]);e&&e.latency!==r.value&&(r.value=e.latency,r.entries=e.entries,i())},a=l("event",o,{durationThreshold:null!==(e=n.durationThreshold)&&void 0!==e?e:40});i=d(t,r,B,n.reportAllChanges),a&&("PerformanceEventTiming"in window&&"interactionId"in PerformanceEventTiming.prototype&&a.observe({type:"first-input",buffered:!0}),h((function(){o(a.takeRecords()),r.value<0&&G()>0&&(r.value=0,r.entries=[]),i(!0)})),u((function(){q=[],F=U(),r=f("INP"),i=d(t,r,B,n.reportAllChanges)})))}))},$=[2500,4e3],W={},X=function(t,n){n=n||{},E((function(){var e,i=w(),r=f("LCP"),o=function(t){var n=t[t.length-1];n&&n.startTime<i.firstHiddenTime&&(r.value=Math.max(n.startTime-s(),0),r.entries=[n],e())},a=l("largest-contentful-paint",o);if(a){e=d(t,r,$,n.reportAllChanges);var c=p((function(){W[r.id]||(o(a.takeRecords()),a.disconnect(),W[r.id]=!0,e(!0))}));["keydown","click"].forEach((function(t){addEventListener(t,(function(){return setTimeout(c,0)}),!0)})),h(c),u((function(i){r=f("LCP"),e=d(t,r,$,n.reportAllChanges),v((function(){r.value=performance.now()-i.timeStamp,W[r.id]=!0,e(!0)}))}))}}))},Q=[800,1800],J=function t(n){document.prerendering?E((function(){return t(n)})):"complete"!==document.readyState?addEventListener("load",(function(){return t(n)}),!0):setTimeout(n,0)},Z=function(t,n){n=n||{};var e=f("TTFB"),i=d(t,e,Q,n.reportAllChanges);J((function(){var r=c();if(r){var o=r.responseStart;if(o<=0||o>performance.now())return;e.value=Math.max(o-s(),0),e.entries=[r],i(!0),u((function(){e=f("TTFB",0),(i=d(t,e,Q,n.reportAllChanges))(!0)}))}}))};t.CLSThresholds=T,t.FCPThresholds=I,t.FIDThresholds=x,t.INPThresholds=B,t.LCPThresholds=$,t.TTFBThresholds=Q,t.getCLS=S,t.getFCP=A,t.getFID=j,t.getINP=K,t.getLCP=X,t.getTTFB=Z,t.onCLS=S,t.onFCP=A,t.onFID=j,t.onINP=K,t.onLCP=X,t.onTTFB=Z}(n)}},__webpack_module_cache__={};function __webpack_require__(t){var n=__webpack_module_cache__[t];if(void 0!==n)return n.exports;var e=__webpack_module_cache__[t]={exports:{}};return __webpack_modules__[t].call(e.exports,e,e.exports,__webpack_require__),e.exports}__webpack_require__.amdO={},__webpack_require__.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}
2}();var __webpack_exports__={};function main(){var t=function(){var t=window.optiIQ;c.shouldOptOut()?Array.isArray(t)&&t.length?(t.length=0,a.log("Main / Opt-out: dropped the queued optiIQ initialize.")):t&&!Array.isArray(t)&&a.warn("Main / Opt-out after optiIQ booted; it keeps collecting. See ZAIUS-25161."):t?Array.isArray(t)&&t.length||t.initialize({api_key:"4f42a24e93f945bcb262bff01a9a1562",capabilities:{pageview:{enabled:!0},scroll_depth:{enabled:!0},click:{enabled:!0},dom_recorder:{enabled:!1}}}):n()},n=function(){var t=window.optiIQ=[];t.methods=["initialize","event"],t.factory=function(n){return function(){return t.push([n].concat(Array.prototype.slice.call(arguments))),t}},t.methods.forEach((function(n){t[n]=t.factory(n)})),t.initialize({api_key:"4f42a24e93f945bcb262bff01a9a1562",capabilities:{pageview:{enabled:!0},scroll_depth:{enabled:!0},click:{enabled:!0},dom_recorder:{enabled:!1}}}),d.addScriptAsync("https://tag.odp.optimizely.com/oiq/us/universal/oiq.min.js"),a.log("Main / Loading optiIQ from https://tag.odp.optimizely.com/oiq/us/universal/oiq.min.js")},e=function(t){var n=[__webpack_require__(1059)];n.push(__webpack_require__(5506)),n.push(__webpack_require__(2062)),n.push(__webpack_require__(8453)),n.push(__webpack_require__(9176)),n.push(__webpack_require__(3268)),n.push(__webpack_require__(679)),n.push(__webpack_require__(3585)),n.push(__webpack_require__(219)),n.push(__webpack_require__(6384)),n.push(__webpack_require__(6507)),n.push(__webpack_require__(5038)),n.push(__webpack_require__(1653)),n.push(__webpack_require__(927)),n.push(__webpack_require__(8398)),n.push(__webpack_require__(2700)),n.push(__webpack_require__(3707)),n.push(__webpack_require__(4328)),n.push(__webpack_require__(6167)),n.push(__webpack_require__(3789)),n.push(__webpack_require__(1043)),n.push(__webpack_require__(9958)),n.push(__webpack_require__(9076)),n.push(__webpack_require__(7601)),n.push(__webpack_require__(936)),n.push(__webpack_require__(2571)),n.push(__webpack_require__(7895)),n.push(__webpack_require__(3252)),n.push(__webpack_require__(481)),p.initializePlugins(n)};window.performance&&window.performance.mark&&window.performance.mark("optimizely:blockBegin"),__webpack_require__(8942).initialize();var i=__webpack_require__(6494);i.time("gtagReady");var r=function(t){var n=t.name,e=(t.delta,t.value);t.id,"CLS"===n&&(e*=100),i.setMark(n,i.now(),e)};i.time("odpTime"),i.time("sendDCP"),i.time("sendActivate"),i.time("sendViewActivate"),i.time("sendDecision"),i.time("flash"),i.time("render"),i.time("preInitialize"),i.time("sendCmabApiInitiateCall");var o=__webpack_require__(3507);o.onFCP(r),o.onFID(r),o.onTTFB(r),o.onLCP(r,{reportAllChanges:!0}),o.onCLS(r,{reportAllChanges:!0}),o.onINP(r,{reportAllChanges:!0});var a=__webpack_require__(3496),u=__webpack_require__(3221);__webpack_require__(8558);var c=u.get("stores/directive");if(!__webpack_require__(4464).isCORSSupported())throw new Error("CORS is not supported on this browser, aborting.");var s,f=__webpack_require__(2945),l=__webpack_require__(7946),d=__webpack_require__(7743),v=__webpack_require__(4143),h={"accountId": "26942930964", "namespace": "26942930964", "revision": "124991", "anonymizeIP": true, "enableForceParameters": false, "experimental": {"trimPages": false}, "projectId": "26942930964", "layers": [{"changes": null, "id": "4597086472110080", "name": "Opticon 26 personalized sponsor pages", "commitId": "5777584321986560", "groupId": null, "holdback": 0, "activation": {}, "integrationSettings": {"google_analytics_4": {}, "5196871765852160": {}}, "integrationStringVersion": 2, "experiments": [{"audienceIds": ["and", "4691728056188928"], "audienceName": "[Opticon 26 sponsor] Valtech", "changes": null, "id": "4538374386614272", "integrationSettings": null, "variations": [{"id": "6737397642166272", "name": "Variation #1", "actions": [{"viewId": "6567315645399040", "changes": [{"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "<div class=\"relative flex w-full flex-col bg-tertiary-2 pt-20 pb-8 px-7 mt-0 mb-0 justify-start rounded-t-default lg:rou
2nded-t-lg lg:pt-32 lg:pb-16 md:px-8 lg:px-16\" style=\"--separator-color:var(--color-tertiary-5);--timeline-bar-color:#0D3A29;--timeline-dot-color:#C4CFC0;--timeline-dot-active-color:#ABFF44\"><div class=\"container mx-auto flex grow flex-col justify-start\"><div class=\"grid grid-cols-12 items-start mt-0 md:mt-0 lg:mt-0 mb-0 md:mb-0 lg:mb-0 py-0 bg-transparent gap-x-4 gap-y-4\"><div class=\"flex flex-1 flex-col flex-nowrap justify-start col-span-full md:col-span-6 gap-4\"><div class=\"overline-element flex items-center gap-2\"><p class=\"font-body font-medium text-base -tracking-[0.32px] text-[var(--prose-type-color,var(--color-secondary-darkfir))]\">Agenda</p></div><h2 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-6xl md:text-8xl lg:text-10xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] text-left mb-4 z-1 text-(--color-secondary-darkfir)\"><span class=\"block\">What's waiting on the&nbsp;trail</span></h2><div class=\"element text-content-element px-0 pt-0 pr-0 pb-0 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none\"><p>Opticon New York pitches up at the Javits Center on September 1. Seth Godin on the bill, city-style energy, and a sharp lineup to match.</p></div></div></div><div style=\"--prose-type-color:var(--color-secondary-darkfir);--prose-table-bg:var(--color-tertiary-midfir);--prose-table-text:var(--color-primary-1);--prose-table-row-odd:rgba(25, 112, 80, 0.7);--prose-table-row-even:rgba(25, 112, 80, 0.4)\" class=\"flex flex-1 flex-col flex-nowrap justify-start col-span-10 md:col-span-4 col-start-2 md:col-start-9 gap-4 bg-(--color-pink-ltpink) rounded-sm lg:rounded-default p-6 lg:p-8\"><div class=\"overline-element flex items-center gap-2\"><p class=\"font-body font-medium text-base -tracking-[0.32px] text-[var(--prose-type-color,var(--color-secondary-darkfir))]\">Tent Talk with Valtech</p></div><h3 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-4xl md:text-4xl lg:text-5xl 2xl:text-5xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] 2xl:leading-[0.9] text-left z-1 text-(--color-secondary-darkfir)\"><span class=\"block\">10% Agent, 90% Enterprise Readiness</span></h3><div class=\"element text-content-element px-0 pt-0 pr-0 pb-4 lg:pb-6 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none prose-p-size-18\"><p>Lets map the readiness gap: supply chain, data, systems, teams, governance.</p></div></div><div class=\"inline-flex w-fit\"><a href=\"https://events.optimizely.com/Opticon26NY\" title=\"Save your seat\" target=\"\" data-epi-edit=\"Link\" class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-secondary-darkfir bg-tertiary-midfir text-tertiary-2 hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-secondary-darkfir h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px]\">Save your seat</a></div></div></div></div></div>"}, "type": "attribute", "id": "F7F4744E-1146-463A-B20C-0C7B7AE902F6", "selector": "div[data-state='active'] > div:nth-of-type(1) > div:nth-of-type(4)"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "<img src=\"https://images1.cmp.optimizely.com/Zz0wZWVlYzI0ODZjYzAxMWYxYTkyYzUyYjA3N2U2MmU1OA==?width=1920\" alt=\"\" class=\"pointer-events-none absolute inset-0 size-full object-cover object-bottom\" decoding=\"async\" loading=\"eager\" fetchpriority=\"high\">\n</img>\n<div class=\"container mx-auto flex grow flex-col justify-center relative z-10\">\n  <div class=\"grid grid-cols-1 mt-32 mb-0 py-8 bg-transparent gap-x-4 gap-y-4\">\n    <div class=\"flex flex-1 flex-col flex-nowrap justify-start gap-4 items-center\">\n      <div class=\"pt-0 pr-0 pb-0 pl-0\">\n        <h2 data-display-level=\"h1\" class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-5xl md:text-7xl lg:text-12xl leading-none text-center mb-8 z-1 text-(--prose-type-color)\">
2\n          <span class=\"block\">\n            Welcome to\n          </span>\n        </h2>\n      </div>\n      <figure class=\"image-display-block h-full overflow-hidden\">\n        <img loading=\"lazy\" decoding=\"async\" src=\"https://images1.cmp.optimizely.com/Zz0wOGEwN2NlMjZjYzAxMWYxODA4ODUyYjA3N2U2MmU1OA==\" alt=\"\" width=\"0\" height=\"0\" class=\"size-full object-contain\">\n        </img>\n      </figure>\n      <div style=\"--space-default:0px;--space-md:0px;--space-lg:40px;--space-xl:40px\" class=\"h-(--space-default) md:h-(--space-md) lg:h-(--space-lg) xl:h-(--space-xl)\" aria-hidden=\"true\">\n      </div>\n      <div class=\"inline-flex w-fit\">\n        <a href=\"/field-notes/opticon/#locations\" title=\"\" target=\"\" data-epi-edit=\"Link\" class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-primary-3 bg-tertiary-2 text-secondary-darkfir hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-primary-3)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-primary-3 h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px] lg:h-14 lg:rounded-[20px] lg:px-6 lg:text-body-med lg:tracking-[0.4px]\">\n          Begin your journey\n        </a>\n      </div>\n    </div>\n  </div>\n  <div class=\"grid grid-cols-12 mt-20 md:mt-24 lg:mt-32 mb-0 md:mb-0 lg:mb-0 py-0 bg-transparent gap-x-4 gap-y-4\"><div style=\"--prose-type-color:var(--color-secondary-darkfir);--prose-table-bg:var(--color-tertiary-midfir);--prose-table-text:var(--color-primary-1);--prose-table-row-odd:rgba(25, 112, 80, 0.7);--prose-table-row-even:rgba(25, 112, 80, 0.4)\" class=\"flex flex-1 flex-col flex-nowrap justify-start col-span-10 lg:col-span-8 col-start-2 lg:col-start-3 gap-4 items-start bg-(--color-pink-ltpink) rounded-sm lg:rounded-default p-6 lg:p-8\"><figure class=\"image-display-block h-[40px] overflow-hidden\"><img loading=\"lazy\" decoding=\"async\" src=\"https://images3.cmp.optimizely.com/Zz1lZGZlMTBkODcwMDgxMWYxYTBhYzNhZjNlODQ4NTFlNg==\" alt=\"\" width=\"0\" height=\"0\" class=\"size-full object-contain\"></figure><div style=\"--space-default:0px;--space-md:0px;--space-lg:0px;--space-xl:0px\" class=\"h-(--space-default) md:h-(--space-md) lg:h-(--space-lg) xl:h-(--space-xl)\" aria-hidden=\"true\"></div><div class=\"pt-0 pr-0 pb-0 pl-0\"><h3 data-display-level=\"h2\" class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-4xl md:text-6xl lg:text-10xl leading-none text-left z-1 text-(--prose-type-color)\"><span class=\"block\">The other 90%: Making AI agents production-ready</span></h3></div><div class=\"element text-content-element px-0 pt-4 lg:pt-8 pr-0 pb-0 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none\"><p>Assess where you stand on AI readiness\u2014supply chain, data, systems, team maturity, governance\u2014and what closing the gap requires.</p></div></div></div></div>\n</div>"}, "type": "attribute", "id": "ED7ACAF6-56FE-4B1B-B3B9-F21F9807AFA9", "selector": "body > main > div:nth-of-type(1) > div:nth-of-type(1)"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "<div class=\"overline-element flex items-center gap-2\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <p class=\"font-body font-medium text-base -tracking-[0.32px] text-[var(--prose-type-color,var(--color-secondary-darkfir))]\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    Tent Talk with Valtech\n  </p>\n</div>\n<h3 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-4xl md:text-4xl lg:text-5xl 2xl:text-5xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] 2xl:leading-[0.9] text-left z-1 text-(--color-secondary-darkfir)\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <span class=\"block\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    10% agent, 90% enterprise readiness\n  </span>\n</h3>\n<div class=\"element text-content-element px-0 pt-0 pr-0 pb-4 lg:pb-6 pl-0\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none prose-p-size-18\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    <p data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n      Lets map the readiness gap: supply chain, data, systems, teams, governance.\n    </p>\n  </div>\n</div>\n<div class=\"inline-flex w-fit\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <a href=\"https://events.optimizely.com/Opticon26NY\" title=\"Save your seat\" target=\"\" data-epi-edit=\"Link\" class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-secondary-darkfir bg-tertiary-mi
2dfir text-tertiary-2 hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-secondary-darkfir h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px]\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    Save your seat\n  </a>\n</div>"}, "type": "attribute", "id": "CC01A782-EF0F-4CF8-8D49-F8B365B5ECCB", "selector": "div[data-state='active'] > div:nth-of-type(1) > div:nth-of-type(4) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(2)"}, {"id": "ED191952-B857-4CAA-8719-A49F1C894762", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "[data-display-level='h2'] > span", "attributes": {"html": "Your website is dead. Put your money into GEO."}, "css": {}}, {"id": "8D4D9DFB-3142-4F43-91B8-82641D5FEC32", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "body > main > div:nth-of-type(1) > div:nth-of-type(1) > div > div > div > div > [data-epi-edit='MainBody'] > p", "attributes": {"html": "A live pro/con debate: is your website still worth the budget in 2026? Vote live and leave with a clear plan to prove ROI to your board."}, "css": {}}, {"id": "39AA082C-E150-409A-B8E3-F694D290896D", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div > div > div > div > h3 > span", "attributes": {"html": "Your website is dead. Invest in GEO."}, "css": {}}, {"id": "06E22789-ADAD-4272-AE19-DA984F6F9BDD", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(2) > div:nth-of-type(1) > p", "attributes": {"html": "Session with Valtech"}, "css": {}}, {"id": "04084402-7BAA-4708-902E-5871E5699231", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div > div > div > div:nth-of-type(2) > div > [data-epi-edit='MainBody'] > p", "attributes": {"html": "A live debate for CMOs on whether GEO or the website should win your budget."}, "css": {}}, {"id": "03502EB3-518D-410F-B107-8F5AACD32345", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div > div > div > div > div > [data-epi-edit='Link']", "attributes": {"href": "https://events.optimizely.com/Opticon26London"}, "css": {}}]}]}], "weightDistributions": null, "name": "Valtech (London)", "bucketingStrategy": null, "experimentMetadata": {"allocationPolicy": "manual", "layerId": "4597086472110080"}, "deployed": false}, {"audienceIds": ["and", "5070167707746304"], "audienceName": "[Opticon 26 sponsor] Columbus", "changes": null, "id": "5658970119995392", "integrationSettings": null, "variations": [{"id": "5518550029303808", "name": "Variation #1", "actions": [{"viewId": "6567315645399040", "changes": [{"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "<div class=\"relative flex w-full flex-col bg-tertiary-2 pt-20 pb-8 px-7 mt-0 mb-0 justify-start rounded-t-default lg:rounded-t-lg lg:pt-32 lg:pb-16 md:px-8 lg:px-16\" style=\"--separator-color:var(--color-tertiary-5);--timeline-bar-color:#0D3A29;--timeline-dot-color:#C4CFC0;--timeline-dot-active-color:#ABFF44\"><div class=\"container mx-auto flex grow flex-col justify-start\"><div class=\"grid grid-cols-12 items-start mt-0 md:mt-0 lg:mt-0 mb-0 md:mb-0 lg:mb-0 py-0 bg-transparent gap-x-4 gap-y-4\"><div class=\"flex flex-1 flex-col flex-nowrap justify-start col-span-full md:col-span-6 gap-4\"><div class=\"overline-element flex items-center gap-2\"><p class=\"font-body font-medium text-base -tracking-[0.32px] text-[var(--prose-type-color,var(--color-secondary-darkfir))]\">Agenda</p></div><h2 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-6xl md:text-8xl lg:text-10xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] text-left mb-4 z-1 text-(--color-secondary-darkfir)\"><span class=\"block\">What's waiting on the&nbsp;trail</span></h2><div class=\"element text-content-element px-0 pt-0 pr-0 pb-0 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none\"><p>Opticon New York pitches up at the Javits Center on September 1. Seth Godin on the bill, city-style energy, and a sharp lineup to match.</p></div></div></div><div style=\"--prose-type-color:var(--color-secondary-darkfir);
2--prose-table-bg:var(--color-tertiary-midfir);--prose-table-text:var(--color-primary-1);--prose-table-row-odd:rgba(25, 112, 80, 0.7);--prose-table-row-even:rgba(25, 112, 80, 0.4)\" class=\"flex flex-1 flex-col flex-nowrap justify-start col-span-10 md:col-span-4 col-start-2 md:col-start-9 gap-4 bg-(--color-pink-ltpink) rounded-sm lg:rounded-default p-6 lg:p-8\"><div class=\"overline-element flex items-center gap-2\"><p class=\"font-body font-medium text-base -tracking-[0.32px] text-[var(--prose-type-color,var(--color-secondary-darkfir))]\">Tent Talk with Valtech</p></div><h3 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-4xl md:text-4xl lg:text-5xl 2xl:text-5xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] 2xl:leading-[0.9] text-left z-1 text-(--color-secondary-darkfir)\"><span class=\"block\">10% Agent, 90% Enterprise Readiness</span></h3><div class=\"element text-content-element px-0 pt-0 pr-0 pb-4 lg:pb-6 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none prose-p-size-18\"><p>Lets map the readiness gap: supply chain, data, systems, teams, governance.</p></div></div><div class=\"inline-flex w-fit\"><a href=\"https://events.optimizely.com/Opticon26NY\" title=\"Save your seat\" target=\"\" data-epi-edit=\"Link\" class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-secondary-darkfir bg-tertiary-midfir text-tertiary-2 hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-secondary-darkfir h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px]\">Save your seat</a></div></div></div></div></div>"}, "type": "attribute", "id": "4318867E-5E04-4D34-946B-F18FCA15C11B", "selector": "div[data-state='active'] > div:nth-of-type(1) > div:nth-of-type(4)"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "<img src=\"https://images1.cmp.optimizely.com/Zz0wZWVlYzI0ODZjYzAxMWYxYTkyYzUyYjA3N2U2MmU1OA==?width=1920\" alt=\"\" class=\"pointer-events-none absolute inset-0 size-full object-cover object-bottom\" decoding=\"async\" loading=\"eager\" fetchpriority=\"high\">\n</img>\n<div class=\"container mx-auto flex grow flex-col justify-center relative z-10\">\n  <div class=\"grid grid-cols-1 mt-32 mb-0 py-8 bg-transparent gap-x-4 gap-y-4\">\n    <div class=\"flex flex-1 flex-col flex-nowrap justify-start gap-4 items-center\">\n      <div class=\"pt-0 pr-0 pb-0 pl-0\">\n        <h2 data-display-level=\"h1\" class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-5xl md:text-7xl lg:text-12xl leading-none text-center mb-8 z-1 text-(--prose-type-color)\">\n          <span class=\"block\">\n            Welcome to\n          </span>\n        </h2>\n      </div>\n      <figure class=\"image-display-block h-full overflow-hidden\">\n        <img loading=\"lazy\" decoding=\"async\" src=\"https://images1.cmp.optimizely.com/Zz0wOGEwN2NlMjZjYzAxMWYxODA4ODUyYjA3N2U2MmU1OA==\" alt=\"\" width=\"0\" height=\"0\" class=\"size-full object-contain\">\n        </img>\n      </figure>\n      <div style=\"--space-default:0px;--space-md:0px;--space-lg:40px;--space-xl:40px\" class=\"h-(--space-default) md:h-(--space-md) lg:h-(--space-lg) xl:h-(--space-xl)\" aria-hidden=\"true\">\n      </div>\n      <div class=\"inline-flex w-fit\">\n        <a href=\"/field-notes/opticon/#locations\" title=\"\" target=\"\" data-epi-edit=\"Link\" class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-primary-3 bg-tertiary-2 text-secondary-darkfir hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-primary-3)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-primary-3 h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px] lg:h-14 lg:rounded-[20px] lg:px-6 lg:text-body-med lg:tracking-[0.4px]\">\n          Begin your journey\n        </a>\n      </div>\n    </div>\n  </div>\n  <div class=\"grid grid-cols-12 mt-20 md:mt-24 lg:mt-32 mb-0 md:mb-0 lg:mb-0 py-0 bg-transparent gap-x-4 gap-y-4\"><div style=\"--prose-type-color:var(--color-secondary-darkfir);
2--prose-table-bg:var(--color-tertiary-midfir);--prose-table-text:var(--color-primary-1);--prose-table-row-odd:rgba(25, 112, 80, 0.7);--prose-table-row-even:rgba(25, 112, 80, 0.4)\" class=\"flex flex-1 flex-col flex-nowrap justify-start col-span-10 lg:col-span-8 col-start-2 lg:col-start-3 gap-4 items-start bg-(--color-pink-ltpink) rounded-sm lg:rounded-default p-6 lg:p-8\"><figure class=\"image-display-block h-[40px] overflow-hidden\"><img loading=\"lazy\" decoding=\"async\" src=\"https://images3.cmp.optimizely.com/Zz1lZGZlMTBkODcwMDgxMWYxYTBhYzNhZjNlODQ4NTFlNg==\" alt=\"\" width=\"0\" height=\"0\" class=\"size-full object-contain\"></figure><div style=\"--space-default:0px;--space-md:0px;--space-lg:0px;--space-xl:0px\" class=\"h-(--space-default) md:h-(--space-md) lg:h-(--space-lg) xl:h-(--space-xl)\" aria-hidden=\"true\"></div><div class=\"pt-0 pr-0 pb-0 pl-0\"><h3 data-display-level=\"h2\" class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-4xl md:text-6xl lg:text-10xl leading-none text-left z-1 text-(--prose-type-color)\"><span class=\"block\">The other 90%: Making AI agents production-ready</span></h3></div><div class=\"element text-content-element px-0 pt-4 lg:pt-8 pr-0 pb-0 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none\"><p>Assess where you stand on AI readiness\u2014supply chain, data, systems, team maturity, governance\u2014and what closing the gap requires.</p></div></div></div></div>\n</div>"}, "type": "attribute", "id": "09E00120-D404-4D6A-AE71-670A230B7B12", "selector": "body > main > div:nth-of-type(1) > div:nth-of-type(1)"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "<div class=\"overline-element flex items-center gap-2\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <p class=\"font-body font-medium text-base -tracking-[0.32px] text-[var(--prose-type-color,var(--color-secondary-darkfir))]\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    Tent Talk with Valtech\n  </p>\n</div>\n<h3 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-4xl md:text-4xl lg:text-5xl 2xl:text-5xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] 2xl:leading-[0.9] text-left z-1 text-(--color-secondary-darkfir)\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <span class=\"block\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    10% agent, 90% enterprise readiness\n  </span>\n</h3>\n<div class=\"element text-content-element px-0 pt-0 pr-0 pb-4 lg:pb-6 pl-0\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none prose-p-size-18\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    <p data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n      Lets map the readiness gap: supply chain, data, systems, teams, governance.\n    </p>\n  </div>\n</div>\n<div class=\"inline-flex w-fit\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <a href=\"https://events.optimizely.com/Opticon26NY\" title=\"Save your seat\" target=\"\" data-epi-edit=\"Link\" class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-secondary-darkfir bg-tertiary-midfir text-tertiary-2 hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-secondary-darkfir h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px]\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    Save your seat\n  </a>\n</div>"}, "type": "attribute", "id": "5F8648A7-50F5-4FA9-994B-6B8130F9D240", "selector": "div[data-state='active'] > div:nth-of-type(1) > div:nth-of-type(4) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(2)"}, {"id": "C0E12569-D9DC-408C-A108-4FD5C2C4EC3B", "type": "attribute", "depen
2dencies": [], "name": "", "isOpalManaged": false, "selector": "body > main > div > div:nth-of-type(1) > div > div:nth-of-type(2) > div > figure > img", "attributes": {"src": "https://images2.cmp.optimizely.com/Zz01M2NhZjdmYzc0MmIxMWYxYmI0MzBlZGNmZGM1ZjYxNQ==", "srcset": "https://images1.cmp.optimizely.com/Zz01M2Q0ZGU0ODc0MmIxMWYxYTlmNmJlMDExMWJhMTJkOA==", "style": ""}, "css": {}}, {"id": "120CA5DF-7A3E-486D-8C29-F3E6D0046C2B", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "[data-display-level='h2'] > span", "attributes": {"html": "Columbus: your trusted enterprise digital transformation partner "}, "css": {}}, {"id": "418FFBF2-0709-4F01-87DC-CA4520E3EE71", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "body > main > div:nth-of-type(1) > div:nth-of-type(1) > div > div > div > div > [data-epi-edit='MainBody'] > p", "attributes": {"html": "We design, implement and optimise mission-critical digital platforms and IT solutions that help enterprises transform, grow and future-proof their business"}, "css": {}}, {"id": "631562FE-EB77-4BDE-B65B-57517D991A3A", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(2) > div:nth-of-type(1) > p", "attributes": {"html": "Session with Columbus"}, "css": {}}, {"id": "462224F6-064C-41B3-8F01-7F24379FFB7D", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div > div > div > div > h3 > span", "attributes": {"html": "One North Star: Experiences for Humans & AI "}, "css": {}}, {"id": "AA97CB62-7B55-47A4-B0DA-C55E1424EA02", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div > div > div > div:nth-of-type(2) > div > [data-epi-edit='MainBody'] > p", "attributes": {"html": "A practical framework for defining your own North Star digital customer experience."}, "css": {}}, {"id": "05D30816-44FB-4071-9B78-3633418D4E04", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div > div > div > div > div > [data-epi-edit='Link']", "attributes": {"href": "https://events.optimizely.com/Opticon26London"}, "css": {}}]}]}], "weightDistributions": null, "name": "Columbus (London)", "bucketingStrategy": null, "experimentMetadata": {"allocationPolicy": "manual", "layerId": "4597086472110080"}, "deployed": false}, {"audienceIds": ["and", "5119077402804224"], "audienceName": "[Opticon 26 sponsor] Netcel", "changes": null, "id": "6238772474413056", "integrationSettings": null, "variations": [{"id": "6221920073416704", "name": "Variation #1", "actions": [{"viewId": "6567315645399040", "changes": [{"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "<div class=\"relative flex w-full flex-col bg-tertiary-2 pt-20 pb-8 px-7 mt-0 mb-0 justify-start rounded-t-default lg:rounded-t-lg lg:pt-32 lg:pb-16 md:px-8 lg:px-16\" style=\"--separator-color:var(--color-tertiary-5);--timeline-bar-color:#0D3A29;--timeline-dot-color:#C4CFC0;--timeline-dot-active-color:#ABFF44\"><div class=\"container mx-auto flex grow flex-col justify-start\"><div class=\"grid grid-cols-12 items-start mt-0 md:mt-0 lg:mt-0 mb-0 md:mb-0 lg:mb-0 py-0 bg-transparent gap-x-4 gap-y-4\"><div class=\"flex flex-1 flex-col flex-nowrap justify-start col-span-full md:col-span-6 gap-4\"><div class=\"overline-element flex items-center gap-2\"><p class=\"font-body font-medium text-base -tracking-[0.32px] text-[var(--prose-type-color,var(--color-secondary-darkfir))]\">Agenda</p></div><h2 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-6xl md:text-8xl lg:text-10xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] text-left mb-4 z-1 text-(--color-secondary-darkfir)\"><span class=\"block\">What's waiting on the&nbsp;trail</span></h2><div class=\"element text-content-element px-0 pt-0 pr-0 pb-0 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none\"><p>Opticon New York pitches up at the Javits Center on September 1. Seth Godin on the bill, city-style energy, and a sharp lineup to match.</p></div></div></div><div style=\"--prose-type-color:var(--color-secondary-darkfir);
2--prose-table-bg:var(--color-tertiary-midfir);--prose-table-text:var(--color-primary-1);--prose-table-row-odd:rgba(25, 112, 80, 0.7);--prose-table-row-even:rgba(25, 112, 80, 0.4)\" class=\"flex flex-1 flex-col flex-nowrap justify-start col-span-10 md:col-span-4 col-start-2 md:col-start-9 gap-4 bg-(--color-pink-ltpink) rounded-sm lg:rounded-default p-6 lg:p-8\"><div class=\"overline-element flex items-center gap-2\"><p class=\"font-body font-medium text-base -tracking-[0.32px] text-[var(--prose-type-color,var(--color-secondary-darkfir))]\">Tent Talk with Valtech</p></div><h3 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-4xl md:text-4xl lg:text-5xl 2xl:text-5xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] 2xl:leading-[0.9] text-left z-1 text-(--color-secondary-darkfir)\"><span class=\"block\">10% Agent, 90% Enterprise Readiness</span></h3><div class=\"element text-content-element px-0 pt-0 pr-0 pb-4 lg:pb-6 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none prose-p-size-18\"><p>Lets map the readiness gap: supply chain, data, systems, teams, governance.</p></div></div><div class=\"inline-flex w-fit\"><a href=\"https://events.optimizely.com/Opticon26NY\" title=\"Save your seat\" target=\"\" data-epi-edit=\"Link\" class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-secondary-darkfir bg-tertiary-midfir text-tertiary-2 hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-secondary-darkfir h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px]\">Save your seat</a></div></div></div></div></div>"}, "type": "attribute", "id": "EEB3DC06-2FDE-4DDE-91AE-819821AB70C3", "selector": "div[data-state='active'] > div:nth-of-type(1) > div:nth-of-type(4)"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "<img src=\"https://images1.cmp.optimizely.com/Zz0wZWVlYzI0ODZjYzAxMWYxYTkyYzUyYjA3N2U2MmU1OA==?width=1920\" alt=\"\" class=\"pointer-events-none absolute inset-0 size-full object-cover object-bottom\" decoding=\"async\" loading=\"eager\" fetchpriority=\"high\">\n</img>\n<div class=\"container mx-auto flex grow flex-col justify-center relative z-10\">\n  <div class=\"grid grid-cols-1 mt-32 mb-0 py-8 bg-transparent gap-x-4 gap-y-4\">\n    <div class=\"flex flex-1 flex-col flex-nowrap justify-start gap-4 items-center\">\n      <div class=\"pt-0 pr-0 pb-0 pl-0\">\n        <h2 data-display-level=\"h1\" class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-5xl md:text-7xl lg:text-12xl leading-none text-center mb-8 z-1 text-(--prose-type-color)\">\n          <span class=\"block\">\n            Welcome to\n          </span>\n        </h2>\n      </div>\n      <figure class=\"image-display-block h-full overflow-hidden\">\n        <img loading=\"lazy\" decoding=\"async\" src=\"https://images1.cmp.optimizely.com/Zz0wOGEwN2NlMjZjYzAxMWYxODA4ODUyYjA3N2U2MmU1OA==\" alt=\"\" width=\"0\" height=\"0\" class=\"size-full object-contain\">\n        </img>\n      </figure>\n      <div style=\"--space-default:0px;--space-md:0px;--space-lg:40px;--space-xl:40px\" class=\"h-(--space-default) md:h-(--space-md) lg:h-(--space-lg) xl:h-(--space-xl)\" aria-hidden=\"true\">\n      </div>\n      <div class=\"inline-flex w-fit\">\n        <a href=\"/field-notes/opticon/#locations\" title=\"\" target=\"\" data-epi-edit=\"Link\" class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-primary-3 bg-tertiary-2 text-secondary-darkfir hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-primary-3)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-primary-3 h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px] lg:h-14 lg:rounded-[20px] lg:px-6 lg:text-body-med lg:tracking-[0.4px]\">\n          Begin your journey\n        </a>\n      </div>\n    </div>\n  </div>\n  <div class=\"grid grid-cols-12 mt-20 md:mt-24 lg:mt-32 mb-0 md:mb-0 lg:mb-0 py-0 bg-transparent gap-x-4 gap-y-4\"><div style=\"--prose-type-color:var(--color-secondary-darkfir);
2--prose-table-bg:var(--color-tertiary-midfir);--prose-table-text:var(--color-primary-1);--prose-table-row-odd:rgba(25, 112, 80, 0.7);--prose-table-row-even:rgba(25, 112, 80, 0.4)\" class=\"flex flex-1 flex-col flex-nowrap justify-start col-span-10 lg:col-span-8 col-start-2 lg:col-start-3 gap-4 items-start bg-(--color-pink-ltpink) rounded-sm lg:rounded-default p-6 lg:p-8\"><figure class=\"image-display-block h-[40px] overflow-hidden\"><img loading=\"lazy\" decoding=\"async\" src=\"https://images3.cmp.optimizely.com/Zz1lZGZlMTBkODcwMDgxMWYxYTBhYzNhZjNlODQ4NTFlNg==\" alt=\"\" width=\"0\" height=\"0\" class=\"size-full object-contain\"></figure><div style=\"--space-default:0px;--space-md:0px;--space-lg:0px;--space-xl:0px\" class=\"h-(--space-default) md:h-(--space-md) lg:h-(--space-lg) xl:h-(--space-xl)\" aria-hidden=\"true\"></div><div class=\"pt-0 pr-0 pb-0 pl-0\"><h3 data-display-level=\"h2\" class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-4xl md:text-6xl lg:text-10xl leading-none text-left z-1 text-(--prose-type-color)\"><span class=\"block\">The other 90%: Making AI agents production-ready</span></h3></div><div class=\"element text-content-element px-0 pt-4 lg:pt-8 pr-0 pb-0 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none\"><p>Assess where you stand on AI readiness\u2014supply chain, data, systems, team maturity, governance\u2014and what closing the gap requires.</p></div></div></div></div>\n</div>"}, "type": "attribute", "id": "D482649C-2DAB-41B2-B240-29BDA1D64261", "selector": "body > main > div:nth-of-type(1) > div:nth-of-type(1)"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "<div class=\"overline-element flex items-center gap-2\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <p class=\"font-body font-medium text-base -tracking-[0.32px] text-[var(--prose-type-color,var(--color-secondary-darkfir))]\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    Tent Talk with Valtech\n  </p>\n</div>\n<h3 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-4xl md:text-4xl lg:text-5xl 2xl:text-5xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] 2xl:leading-[0.9] text-left z-1 text-(--color-secondary-darkfir)\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <span class=\"block\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    10% agent, 90% enterprise readiness\n  </span>\n</h3>\n<div class=\"element text-content-element px-0 pt-0 pr-0 pb-4 lg:pb-6 pl-0\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none prose-p-size-18\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    <p data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n      Lets map the readiness gap: supply chain, data, systems, teams, governance.\n    </p>\n  </div>\n</div>\n<div class=\"inline-flex w-fit\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <a href=\"https://events.optimizely.com/Opticon26NY\" title=\"Save your seat\" target=\"\" data-epi-edit=\"Link\" class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-secondary-darkfir bg-tertiary-midfir text-tertiary-2 hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-secondary-darkfir h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px]\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    Save your seat\n  </a>\n</div>"}, "type": "attribute", "id": "33BE9DAF-02A5-4BB0-8DDA-792BF80DC6F8", "selector": "div[data-state='active'] > div:nth-of-type(1) > div:nth-of-type(4) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(2)"}, {"id": "CF6AA7D9-D921-4285-ACC0-BE46005D20FD", "type": "attribute", "depen
2dencies": [], "name": "", "isOpalManaged": false, "selector": "body > main > div > div:nth-of-type(1) > div > div:nth-of-type(2) > div > figure > img", "attributes": {"src": "https://images1.cmp.optimizely.com/Zz0yMjkyZjk0MDdmMmIxMWYxYjJiOTFlYmU1MDBkNTZkOA=="}, "css": {}}, {"id": "6FFEF6CF-E946-4B49-B4FB-0FECB7138124", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "[data-display-level='h2'] > span", "attributes": {"html": "Life beyond the website\u2014Agentic Customer Experiences"}, "css": {}}, {"id": "AB8756EB-C9CE-492A-B4D0-F29A71EE914F", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "body > main > div:nth-of-type(1) > div:nth-of-type(1) > div > div > div > div > [data-epi-edit='MainBody'] > p", "attributes": {"html": "Is the website in decline as Agentic Customer Experience rises? Discover how Huel, Pets and Spire Healthcare are adapting to this paradigm shift."}, "css": {}}, {"id": "7F555C96-8ADE-4BBD-9F40-02CD764E7633", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(2) > div:nth-of-type(1) > p", "attributes": {"html": "Session with Netcel"}, "css": {}}, {"id": "9B57C970-FA68-485D-9D50-1E8C925065B2", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div > div > div > div > h3 > span", "attributes": {"html": "Life beyond the website - Agentic CX"}, "css": {}}, {"id": "2286B617-BDEE-47C6-9B55-083C9612B24D", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div > div > div > div:nth-of-type(2) > div > [data-epi-edit='MainBody'] > p", "attributes": {"html": "How Huel, Pets & Spire are adapting as AI reshapes customer experience."}, "css": {}}, {"id": "0354C2BA-9EDE-4D37-A726-B4DF6A2AC760", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div > div > div > div > div > [data-epi-edit='Link']", "attributes": {"href": "https://events.optimizely.com/Opticon26London"}, "css": {}}, {"id": "AD82298E-557E-4C08-AEDA-BC0ADD5E31C5", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "body > main > div:nth-of-type(1) > div:nth-of-type(1) > div > div:nth-of-type(2) > div > div:nth-of-type(3)", "attributes": {"html": "<div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none\" data-optly-d482649c-2dab-41b2-b240-29bda1d64261=\"\">\n  <p data-optly-d482649c-2dab-41b2-b240-29bda1d64261=\"\" data-optly-ab8756eb-c9ce-492a-b4d0-f29a71ee914f=\"\">\n    Visit the UNRVLD stand to learn how we're enabling Optimizely clients to ready their digital platforms for the intelligent future.\n  </p>\n</div>"}, "css": {}}]}]}], "weightDistributions": null, "name": "Netcel (London)", "bucketingStrategy": null, "experimentMetadata": {"allocationPolicy": "manual", "layerId": "4597086472110080"}, "deployed": false}, {"audienceIds": ["and", "4949734828277760"], "audienceName": "[Opticon 26 sponsor] MSQ DX", "changes": null, "id": "6493760253591552", "integrationSettings": null, "variations": [{"id": "5364217274957824", "name": "Variation #1", "actions": [{"viewId": "6567315645399040", "changes": [{"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "<div class=\"relative flex w-full flex-col bg-tertiary-2 pt-20 pb-8 px-7 mt-0 mb-0 justify-start rounded-t-default lg:rounded-t-lg lg:pt-32 lg:pb-16 md:px-8 lg:px-16\" style=\"--separator-color:var(--color-tertiary-5);--timeline-bar-color:#0D3A29;--timeline-dot-color:#C4CFC0;--timeline-dot-active-color:#ABFF44\"><div class=\"container mx-auto flex grow flex-col justify-start\"><div class=\"grid grid-cols-12 items-start mt-0 md:mt-0 lg:mt-0 mb-0 md:mb-0 lg:mb-0 py-0 bg-transparent gap-x-4 gap-y-4\"><div class=\"flex flex-1 flex-col flex-nowrap justify-start col-span-full md:col-span-6 gap-4\"><div class=\"overline-element flex items-center gap-2\"><p class=\"font-body font-medium text-base -tracking-[0.32px] text-[var(--prose-type-color,var(--color-secondary-darkfir))]\">Agenda</p></div><h2 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-6xl md:text-8xl lg:text-10xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] text-left mb-4 z-1 text-(--color-secondary-darkfir)\"><span class=\"block\">What's waiting on the&nbsp;trail</span></h2><div class=\"element text-content-element px-0 pt-0 pr-0 pb-0 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none\"><p>Opticon New York pitches up at the Javits Center on September 1. Seth Godin on the bill, city-style energy, and a sharp lineup to match.</p></div></div></div><div style=\"--prose-type-color:var(--color-secondary-darkfir);
2--prose-table-bg:var(--color-tertiary-midfir);--prose-table-text:var(--color-primary-1);--prose-table-row-odd:rgba(25, 112, 80, 0.7);--prose-table-row-even:rgba(25, 112, 80, 0.4)\" class=\"flex flex-1 flex-col flex-nowrap justify-start col-span-10 md:col-span-4 col-start-2 md:col-start-9 gap-4 bg-(--color-pink-ltpink) rounded-sm lg:rounded-default p-6 lg:p-8\"><div class=\"overline-element flex items-center gap-2\"><p class=\"font-body font-medium text-base -tracking-[0.32px] text-[var(--prose-type-color,var(--color-secondary-darkfir))]\">Tent Talk with Valtech</p></div><h3 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-4xl md:text-4xl lg:text-5xl 2xl:text-5xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] 2xl:leading-[0.9] text-left z-1 text-(--color-secondary-darkfir)\"><span class=\"block\">10% Agent, 90% Enterprise Readiness</span></h3><div class=\"element text-content-element px-0 pt-0 pr-0 pb-4 lg:pb-6 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none prose-p-size-18\"><p>Lets map the readiness gap: supply chain, data, systems, teams, governance.</p></div></div><div class=\"inline-flex w-fit\"><a href=\"https://events.optimizely.com/Opticon26NY\" title=\"Save your seat\" target=\"\" data-epi-edit=\"Link\" class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-secondary-darkfir bg-tertiary-midfir text-tertiary-2 hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-secondary-darkfir h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px]\">Save your seat</a></div></div></div></div></div>"}, "type": "attribute", "id": "8037FFFB-0162-4571-B3A1-B96092EB8721", "selector": "div[data-state='active'] > div:nth-of-type(1) > div:nth-of-type(4)"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "<img src=\"https://images1.cmp.optimizely.com/Zz0wZWVlYzI0ODZjYzAxMWYxYTkyYzUyYjA3N2U2MmU1OA==?width=1920\" alt=\"\" class=\"pointer-events-none absolute inset-0 size-full object-cover object-bottom\" decoding=\"async\" loading=\"eager\" fetchpriority=\"high\">\n</img>\n<div class=\"container mx-auto flex grow flex-col justify-center relative z-10\">\n  <div class=\"grid grid-cols-1 mt-32 mb-0 py-8 bg-transparent gap-x-4 gap-y-4\">\n    <div class=\"flex flex-1 flex-col flex-nowrap justify-start gap-4 items-center\">\n      <div class=\"pt-0 pr-0 pb-0 pl-0\">\n        <h2 data-display-level=\"h1\" class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-5xl md:text-7xl lg:text-12xl leading-none text-center mb-8 z-1 text-(--prose-type-color)\">\n          <span class=\"block\">\n            Welcome to\n          </span>\n        </h2>\n      </div>\n      <figure class=\"image-display-block h-full overflow-hidden\">\n        <img loading=\"lazy\" decoding=\"async\" src=\"https://images1.cmp.optimizely.com/Zz0wOGEwN2NlMjZjYzAxMWYxODA4ODUyYjA3N2U2MmU1OA==\" alt=\"\" width=\"0\" height=\"0\" class=\"size-full object-contain\">\n        </img>\n      </figure>\n      <div style=\"--space-default:0px;--space-md:0px;--space-lg:40px;--space-xl:40px\" class=\"h-(--space-default) md:h-(--space-md) lg:h-(--space-lg) xl:h-(--space-xl)\" aria-hidden=\"true\">\n      </div>\n      <div class=\"inline-flex w-fit\">\n        <a href=\"/field-notes/opticon/#locations\" title=\"\" target=\"\" data-epi-edit=\"Link\" class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-primary-3 bg-tertiary-2 text-secondary-darkfir hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-primary-3)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-primary-3 h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px] lg:h-14 lg:rounded-[20px] lg:px-6 lg:text-body-med lg:tracking-[0.4px]\">\n          Begin your journey\n        </a>\n      </div>\n    </div>\n  </div>\n  <div class=\"grid grid-cols-12 mt-20 md:mt-24 lg:mt-32 mb-0 md:mb-0 lg:mb-0 py-0 bg-transparent gap-x-4 gap-y-4\"><div style=\"--prose-type-color:var(--color-secondary-darkfir);
2--prose-table-bg:var(--color-tertiary-midfir);--prose-table-text:var(--color-primary-1);--prose-table-row-odd:rgba(25, 112, 80, 0.7);--prose-table-row-even:rgba(25, 112, 80, 0.4)\" class=\"flex flex-1 flex-col flex-nowrap justify-start col-span-10 lg:col-span-8 col-start-2 lg:col-start-3 gap-4 items-start bg-(--color-pink-ltpink) rounded-sm lg:rounded-default p-6 lg:p-8\"><figure class=\"image-display-block h-[40px] overflow-hidden\"><img loading=\"lazy\" decoding=\"async\" src=\"https://images3.cmp.optimizely.com/Zz1lZGZlMTBkODcwMDgxMWYxYTBhYzNhZjNlODQ4NTFlNg==\" alt=\"\" width=\"0\" height=\"0\" class=\"size-full object-contain\"></figure><div style=\"--space-default:0px;--space-md:0px;--space-lg:0px;--space-xl:0px\" class=\"h-(--space-default) md:h-(--space-md) lg:h-(--space-lg) xl:h-(--space-xl)\" aria-hidden=\"true\"></div><div class=\"pt-0 pr-0 pb-0 pl-0\"><h3 data-display-level=\"h2\" class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-4xl md:text-6xl lg:text-10xl leading-none text-left z-1 text-(--prose-type-color)\"><span class=\"block\">The other 90%: Making AI agents production-ready</span></h3></div><div class=\"element text-content-element px-0 pt-4 lg:pt-8 pr-0 pb-0 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none\"><p>Assess where you stand on AI readiness\u2014supply chain, data, systems, team maturity, governance\u2014and what closing the gap requires.</p></div></div></div></div>\n</div>"}, "type": "attribute", "id": "416E4551-F6ED-4AEA-957A-F315E128C916", "selector": "body > main > div:nth-of-type(1) > div:nth-of-type(1)"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "<div class=\"overline-element flex items-center gap-2\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <p class=\"font-body font-medium text-base -tracking-[0.32px] text-[var(--prose-type-color,var(--color-secondary-darkfir))]\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    Tent Talk with Valtech\n  </p>\n</div>\n<h3 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-4xl md:text-4xl lg:text-5xl 2xl:text-5xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] 2xl:leading-[0.9] text-left z-1 text-(--color-secondary-darkfir)\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <span class=\"block\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    10% agent, 90% enterprise readiness\n  </span>\n</h3>\n<div class=\"element text-content-element px-0 pt-0 pr-0 pb-4 lg:pb-6 pl-0\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none prose-p-size-18\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    <p data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n      Lets map the readiness gap: supply chain, data, systems, teams, governance.\n    </p>\n  </div>\n</div>\n<div class=\"inline-flex w-fit\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <a href=\"https://events.optimizely.com/Opticon26NY\" title=\"Save your seat\" target=\"\" data-epi-edit=\"Link\" class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-secondary-darkfir bg-tertiary-midfir text-tertiary-2 hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-secondary-darkfir h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px]\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    Save your seat\n  </a>\n</div>"}, "type": "attribute", "id": "694E6090-74EF-4960-88F1-E2513D78E1E6", "selector": "div[data-state='active'] > div:nth-of-type(1) > div:nth-of-type(4) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(2)"}, {"css": {}
2, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"src": "https://images1.cmp.optimizely.com/Zz0xYTZiMjliMDc5NjExMWYxYjZlZDQyMzkzZWE3OTA0NA==", "srcset": "https://images3.cmp.optimizely.com/Zz0yZGY5ZjIzYWIwYTgxMWYxYTRlYjZhMDk2ZTYzZjRjZQ=="}, "type": "attribute", "id": "2754C33A-AB97-491A-9B77-2C8DDC1E1F40", "selector": "body > main > div > div:nth-of-type(1) > div > div:nth-of-type(2) > div > figure > img"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "He's just not that into you: Supercharged personalisation"}, "type": "attribute", "id": "DA884087-C232-4D8F-BD09-F4B774F55030", "selector": "[data-display-level='h2'] > span"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "How decoding dating signals can supercharge your personalisation strategy. It's not what you know about a customer, it's knowing what to do with it."}, "type": "attribute", "id": "81685F61-B0C8-426E-AC21-B45AB84559B8", "selector": "body > main > div:nth-of-type(1) > div:nth-of-type(1) > div > div > div > div > [data-epi-edit='MainBody'] > p"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "Session with MSQ DX"}, "type": "attribute", "id": "DE88CA46-920E-48D3-B135-28CC879D8E73", "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(2) > div:nth-of-type(1) > p"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "He's just not that into you"}, "type": "attribute", "id": "3229CD3C-392D-4E8A-8DEC-DC6A5C9725A1", "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div > div > div > div > h3 > span"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "How decoding dating signals can supercharge your personalisation strategy."}, "type": "attribute", "id": "6DEFA2DA-2601-43DF-AE50-39AFE467EE05", "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div > div > div > div:nth-of-type(2) > div > [data-epi-edit='MainBody'] > p"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"href": "https://events.optimizely.com/Opticon26London"}, "type": "attribute", "id": "8AA6112C-A25F-4384-82D0-EFFC6D2DEFDF", "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div > div > div > div > div > [data-epi-edit='Link']"}]}]}], "weightDistributions": null, "name": "MSQ DX (London)", "bucketingStrategy": null, "experimentMetadata": {"allocationPolicy": "manual", "layerId": "4597086472110080"}, "deployed": false}, {"audienceIds": ["and", "5465773739606016"], "audienceName": "[Opticon 26 sponsor] Niteco", "changes": null, "id": "6521495139123200", "integrationSettings": null, "variations": [{"id": "5113912218091520", "name": "Variation #1", "actions": [{"viewId": "6567315645399040", "changes": [{"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "<div class=\"relative flex w-full flex-col bg-tertiary-2 pt-20 pb-8 px-7 mt-0 mb-0 justify-start rounded-t-default lg:rounded-t-lg lg:pt-32 lg:pb-16 md:px-8 lg:px-16\" style=\"--separator-color:var(--color-tertiary-5);--timeline-bar-color:#0D3A29;--timeline-dot-color:#C4CFC0;--timeline-dot-active-color:#ABFF44\"><div class=\"container mx-auto flex grow flex-col justify-start\"><div class=\"grid grid-cols-12 items-start mt-0 md:mt-0 lg:mt-0 mb-0 md:mb-0 lg:mb-0 py-0 bg-transparent gap-x-4 gap-y-4\"><div class=\"flex flex-1 flex-col flex-nowrap justify-start col-span-full md:col-span-6 gap-4\"><div class=\"overline-element flex items-center gap-2\"><p class=\"font-body font-medium text-base -tracking-[0.32px] text-[var(--prose-type-color,var(--color-secondary-darkfir))]\">Agenda</p></div><h2 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-6xl md:text-8xl lg:text-10xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] text-left mb-4 z-1 text-(--color-secondary-darkfir)\"><span class=\"block\">What's waiting on the&nbsp;trail</span></h2><div class=\"element text-content-element px-0 pt-0 pr-0 pb-0 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none\"><p>Opticon New York pitches up at the Javits Center on September 1. Seth Godin on the bill, city-style energy, and a sharp lineup to match.</p></div></div></div><div style=\"--prose-type-color:var(--color-secondary-darkfir);
2--prose-table-bg:var(--color-tertiary-midfir);--prose-table-text:var(--color-primary-1);--prose-table-row-odd:rgba(25, 112, 80, 0.7);--prose-table-row-even:rgba(25, 112, 80, 0.4)\" class=\"flex flex-1 flex-col flex-nowrap justify-start col-span-10 md:col-span-4 col-start-2 md:col-start-9 gap-4 bg-(--color-pink-ltpink) rounded-sm lg:rounded-default p-6 lg:p-8\"><div class=\"overline-element flex items-center gap-2\"><p class=\"font-body font-medium text-base -tracking-[0.32px] text-[var(--prose-type-color,var(--color-secondary-darkfir))]\">Tent Talk with Valtech</p></div><h3 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-4xl md:text-4xl lg:text-5xl 2xl:text-5xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] 2xl:leading-[0.9] text-left z-1 text-(--color-secondary-darkfir)\"><span class=\"block\">10% Agent, 90% Enterprise Readiness</span></h3><div class=\"element text-content-element px-0 pt-0 pr-0 pb-4 lg:pb-6 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none prose-p-size-18\"><p>Lets map the readiness gap: supply chain, data, systems, teams, governance.</p></div></div><div class=\"inline-flex w-fit\"><a href=\"https://events.optimizely.com/Opticon26NY\" title=\"Save your seat\" target=\"\" data-epi-edit=\"Link\" class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-secondary-darkfir bg-tertiary-midfir text-tertiary-2 hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-secondary-darkfir h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px]\">Save your seat</a></div></div></div></div></div>"}, "type": "attribute", "id": "3F3A557D-6884-42D0-BD50-7AF4D19D5F2C", "selector": "div[data-state='active'] > div:nth-of-type(1) > div:nth-of-type(4)"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "<img src=\"https://images1.cmp.optimizely.com/Zz0wZWVlYzI0ODZjYzAxMWYxYTkyYzUyYjA3N2U2MmU1OA==?width=1920\" alt=\"\" class=\"pointer-events-none absolute inset-0 size-full object-cover object-bottom\" decoding=\"async\" loading=\"eager\" fetchpriority=\"high\">\n</img>\n<div class=\"container mx-auto flex grow flex-col justify-center relative z-10\">\n  <div class=\"grid grid-cols-1 mt-32 mb-0 py-8 bg-transparent gap-x-4 gap-y-4\">\n    <div class=\"flex flex-1 flex-col flex-nowrap justify-start gap-4 items-center\">\n      <div class=\"pt-0 pr-0 pb-0 pl-0\">\n        <h2 data-display-level=\"h1\" class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-5xl md:text-7xl lg:text-12xl leading-none text-center mb-8 z-1 text-(--prose-type-color)\">\n          <span class=\"block\">\n            Welcome to\n          </span>\n        </h2>\n      </div>\n      <figure class=\"image-display-block h-full overflow-hidden\">\n        <img loading=\"lazy\" decoding=\"async\" src=\"https://images1.cmp.optimizely.com/Zz0wOGEwN2NlMjZjYzAxMWYxODA4ODUyYjA3N2U2MmU1OA==\" alt=\"\" width=\"0\" height=\"0\" class=\"size-full object-contain\">\n        </img>\n      </figure>\n      <div style=\"--space-default:0px;--space-md:0px;--space-lg:40px;--space-xl:40px\" class=\"h-(--space-default) md:h-(--space-md) lg:h-(--space-lg) xl:h-(--space-xl)\" aria-hidden=\"true\">\n      </div>\n      <div class=\"inline-flex w-fit\">\n        <a href=\"/field-notes/opticon/#locations\" title=\"\" target=\"\" data-epi-edit=\"Link\" class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-primary-3 bg-tertiary-2 text-secondary-darkfir hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-primary-3)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-primary-3 h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px] lg:h-14 lg:rounded-[20px] lg:px-6 lg:text-body-med lg:tracking-[0.4px]\">\n          Begin your journey\n        </a>\n      </div>\n    </div>\n  </div>\n  <div class=\"grid grid-cols-12 mt-20 md:mt-24 lg:mt-32 mb-0 md:mb-0 lg:mb-0 py-0 bg-transparent gap-x-4 gap-y-4\"><div style=\"--prose-type-color:var(--color-secondary-darkfir);
2--prose-table-bg:var(--color-tertiary-midfir);--prose-table-text:var(--color-primary-1);--prose-table-row-odd:rgba(25, 112, 80, 0.7);--prose-table-row-even:rgba(25, 112, 80, 0.4)\" class=\"flex flex-1 flex-col flex-nowrap justify-start col-span-10 lg:col-span-8 col-start-2 lg:col-start-3 gap-4 items-start bg-(--color-pink-ltpink) rounded-sm lg:rounded-default p-6 lg:p-8\"><figure class=\"image-display-block h-[40px] overflow-hidden\"><img loading=\"lazy\" decoding=\"async\" src=\"https://images3.cmp.optimizely.com/Zz1lZGZlMTBkODcwMDgxMWYxYTBhYzNhZjNlODQ4NTFlNg==\" alt=\"\" width=\"0\" height=\"0\" class=\"size-full object-contain\"></figure><div style=\"--space-default:0px;--space-md:0px;--space-lg:0px;--space-xl:0px\" class=\"h-(--space-default) md:h-(--space-md) lg:h-(--space-lg) xl:h-(--space-xl)\" aria-hidden=\"true\"></div><div class=\"pt-0 pr-0 pb-0 pl-0\"><h3 data-display-level=\"h2\" class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-4xl md:text-6xl lg:text-10xl leading-none text-left z-1 text-(--prose-type-color)\"><span class=\"block\">The other 90%: Making AI agents production-ready</span></h3></div><div class=\"element text-content-element px-0 pt-4 lg:pt-8 pr-0 pb-0 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none\"><p>Assess where you stand on AI readiness\u2014supply chain, data, systems, team maturity, governance\u2014and what closing the gap requires.</p></div></div></div></div>\n</div>"}, "type": "attribute", "id": "98E319DB-EB53-4FC4-B4D2-DD6F350C9C5A", "selector": "body > main > div:nth-of-type(1) > div:nth-of-type(1)"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "<div class=\"overline-element flex items-center gap-2\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <p class=\"font-body font-medium text-base -tracking-[0.32px] text-[var(--prose-type-color,var(--color-secondary-darkfir))]\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    Tent Talk with Valtech\n  </p>\n</div>\n<h3 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-4xl md:text-4xl lg:text-5xl 2xl:text-5xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] 2xl:leading-[0.9] text-left z-1 text-(--color-secondary-darkfir)\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <span class=\"block\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    10% agent, 90% enterprise readiness\n  </span>\n</h3>\n<div class=\"element text-content-element px-0 pt-0 pr-0 pb-4 lg:pb-6 pl-0\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none prose-p-size-18\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    <p data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n      Lets map the readiness gap: supply chain, data, systems, teams, governance.\n    </p>\n  </div>\n</div>\n<div class=\"inline-flex w-fit\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <a href=\"https://events.optimizely.com/Opticon26NY\" title=\"Save your seat\" target=\"\" data-epi-edit=\"Link\" class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-secondary-darkfir bg-tertiary-midfir text-tertiary-2 hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-secondary-darkfir h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px]\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    Save your seat\n  </a>\n</div>"}, "type": "attribute", "id": "7DA24728-6866-452C-A1BC-BDDD3A12A6AA", "selector": "div[data-state='active'] > div:nth-of-type(1) > div:nth-of-type(4) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(2)"}, {"css": {}
2, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"src": "https://images1.cmp.optimizely.com/Zz0xYTZiMjliMDc5NjExMWYxYjZlZDQyMzkzZWE3OTA0NA==", "srcset": "https://images1.cmp.optimizely.com/Zz1lZDdlMWZiMjc0MjMxMWYxYWJhODI2Zjk0OTAxMGY4MA=="}, "type": "attribute", "id": "E79E8506-60BD-4C39-8C02-CD9F49E4A9B1", "selector": "body > main > div > div:nth-of-type(1) > div > div:nth-of-type(2) > div > figure > img"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "The human is obsolete. Long live the human."}, "type": "attribute", "id": "CB0621AD-4556-4E72-84E5-D5A6EA52BD33", "selector": "[data-display-level='h2'] > span"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "What's the argument for you if the machine is faster, cheaper, and available at 3am? This talk breaks down the human algorithm."}, "type": "attribute", "id": "BF2D2EAA-EC87-433E-9AC3-913519E8001E", "selector": "body > main > div:nth-of-type(1) > div:nth-of-type(1) > div > div > div > div > [data-epi-edit='MainBody'] > p"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "Session with Niteco"}, "type": "attribute", "id": "8ECF64F0-8C7B-4CA6-8CBE-4B6A0781BEE6", "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(2) > div:nth-of-type(1) > p"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "The human is obsolete. Long live the human."}, "type": "attribute", "id": "A10CF5E1-2E0F-4E0B-A527-3845DEC1D477", "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div > div > div > div > h3 > span"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "I'll argue the human is finished. Can I be proved wrong?"}, "type": "attribute", "id": "4B6AD7ED-E071-4617-86FC-977A9A4F4FD5", "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div > div > div > div:nth-of-type(2) > div > [data-epi-edit='MainBody'] > p"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"href": "https://events.optimizely.com/Opticon26London"}, "type": "attribute", "id": "1BF89F06-BA72-4590-83A2-87C0721FDEBF", "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div > div > div > div > div > [data-epi-edit='Link']"}]}]}], "weightDistributions": null, "name": "Niteco (London)", "bucketingStrategy": null, "experimentMetadata": {"allocationPolicy": "manual", "layerId": "4597086472110080"}, "deployed": false}, {"audienceIds": ["and", "6717352492924928"], "audienceName": "[Opticon 26 sponsor] UNRVLD", "changes": null, "id": "6654811595079680", "integrationSettings": null, "variations": [{"id": "6156635519844352", "name": "Variation #1", "actions": [{"viewId": "6567315645399040", "changes": [{"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "<div class=\"relative flex w-full flex-col bg-tertiary-2 pt-20 pb-8 px-7 mt-0 mb-0 justify-start rounded-t-default lg:rounded-t-lg lg:pt-32 lg:pb-16 md:px-8 lg:px-16\" style=\"--separator-color:var(--color-tertiary-5);--timeline-bar-color:#0D3A29;--timeline-dot-color:#C4CFC0;--timeline-dot-active-color:#ABFF44\"><div class=\"container mx-auto flex grow flex-col justify-start\"><div class=\"grid grid-cols-12 items-start mt-0 md:mt-0 lg:mt-0 mb-0 md:mb-0 lg:mb-0 py-0 bg-transparent gap-x-4 gap-y-4\"><div class=\"flex flex-1 flex-col flex-nowrap justify-start col-span-full md:col-span-6 gap-4\"><div class=\"overline-element flex items-center gap-2\"><p class=\"font-body font-medium text-base -tracking-[0.32px] text-[var(--prose-type-color,var(--color-secondary-darkfir))]\">Agenda</p></div><h2 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-6xl md:text-8xl lg:text-10xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] text-left mb-4 z-1 text-(--color-secondary-darkfir)\"><span class=\"block\">What's waiting on the&nbsp;trail</span></h2><div class=\"element text-content-element px-0 pt-0 pr-0 pb-0 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none\"><p>Opticon New York pitches up at the Javits Center on September 1. Seth Godin on the bill, city-style energy, and a sharp lineup to match.</p></div></div></div><div style=\"--prose-type-color:var(--color-secondary-darkfir);
2--prose-table-bg:var(--color-tertiary-midfir);--prose-table-text:var(--color-primary-1);--prose-table-row-odd:rgba(25, 112, 80, 0.7);--prose-table-row-even:rgba(25, 112, 80, 0.4)\" class=\"flex flex-1 flex-col flex-nowrap justify-start col-span-10 md:col-span-4 col-start-2 md:col-start-9 gap-4 bg-(--color-pink-ltpink) rounded-sm lg:rounded-default p-6 lg:p-8\"><div class=\"overline-element flex items-center gap-2\"><p class=\"font-body font-medium text-base -tracking-[0.32px] text-[var(--prose-type-color,var(--color-secondary-darkfir))]\">Tent Talk with Valtech</p></div><h3 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-4xl md:text-4xl lg:text-5xl 2xl:text-5xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] 2xl:leading-[0.9] text-left z-1 text-(--color-secondary-darkfir)\"><span class=\"block\">10% Agent, 90% Enterprise Readiness</span></h3><div class=\"element text-content-element px-0 pt-0 pr-0 pb-4 lg:pb-6 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none prose-p-size-18\"><p>Lets map the readiness gap: supply chain, data, systems, teams, governance.</p></div></div><div class=\"inline-flex w-fit\"><a href=\"https://events.optimizely.com/Opticon26NY\" title=\"Save your seat\" target=\"\" data-epi-edit=\"Link\" class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-secondary-darkfir bg-tertiary-midfir text-tertiary-2 hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-secondary-darkfir h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px]\">Save your seat</a></div></div></div></div></div>"}, "type": "attribute", "id": "EEB3DC06-2FDE-4DDE-91AE-819821AB70C3", "selector": "div[data-state='active'] > div:nth-of-type(1) > div:nth-of-type(4)"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "<img src=\"https://images1.cmp.optimizely.com/Zz0wZWVlYzI0ODZjYzAxMWYxYTkyYzUyYjA3N2U2MmU1OA==?width=1920\" alt=\"\" class=\"pointer-events-none absolute inset-0 size-full object-cover object-bottom\" decoding=\"async\" loading=\"eager\" fetchpriority=\"high\">\n</img>\n<div class=\"container mx-auto flex grow flex-col justify-center relative z-10\">\n  <div class=\"grid grid-cols-1 mt-32 mb-0 py-8 bg-transparent gap-x-4 gap-y-4\">\n    <div class=\"flex flex-1 flex-col flex-nowrap justify-start gap-4 items-center\">\n      <div class=\"pt-0 pr-0 pb-0 pl-0\">\n        <h2 data-display-level=\"h1\" class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-5xl md:text-7xl lg:text-12xl leading-none text-center mb-8 z-1 text-(--prose-type-color)\">\n          <span class=\"block\">\n            Welcome to\n          </span>\n        </h2>\n      </div>\n      <figure class=\"image-display-block h-full overflow-hidden\">\n        <img loading=\"lazy\" decoding=\"async\" src=\"https://images1.cmp.optimizely.com/Zz0wOGEwN2NlMjZjYzAxMWYxODA4ODUyYjA3N2U2MmU1OA==\" alt=\"\" width=\"0\" height=\"0\" class=\"size-full object-contain\">\n        </img>\n      </figure>\n      <div style=\"--space-default:0px;--space-md:0px;--space-lg:40px;--space-xl:40px\" class=\"h-(--space-default) md:h-(--space-md) lg:h-(--space-lg) xl:h-(--space-xl)\" aria-hidden=\"true\">\n      </div>\n      <div class=\"inline-flex w-fit\">\n        <a href=\"/field-notes/opticon/#locations\" title=\"\" target=\"\" data-epi-edit=\"Link\" class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-primary-3 bg-tertiary-2 text-secondary-darkfir hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-primary-3)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-primary-3 h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px] lg:h-14 lg:rounded-[20px] lg:px-6 lg:text-body-med lg:tracking-[0.4px]\">\n          Begin your journey\n        </a>\n      </div>\n    </div>\n  </div>\n  <div class=\"grid grid-cols-12 mt-20 md:mt-24 lg:mt-32 mb-0 md:mb-0 lg:mb-0 py-0 bg-transparent gap-x-4 gap-y-4\"><div style=\"--prose-type-color:var(--color-secondary-darkfir);
2--prose-table-bg:var(--color-tertiary-midfir);--prose-table-text:var(--color-primary-1);--prose-table-row-odd:rgba(25, 112, 80, 0.7);--prose-table-row-even:rgba(25, 112, 80, 0.4)\" class=\"flex flex-1 flex-col flex-nowrap justify-start col-span-10 lg:col-span-8 col-start-2 lg:col-start-3 gap-4 items-start bg-(--color-pink-ltpink) rounded-sm lg:rounded-default p-6 lg:p-8\"><figure class=\"image-display-block h-[40px] overflow-hidden\"><img loading=\"lazy\" decoding=\"async\" src=\"https://images3.cmp.optimizely.com/Zz1lZGZlMTBkODcwMDgxMWYxYTBhYzNhZjNlODQ4NTFlNg==\" alt=\"\" width=\"0\" height=\"0\" class=\"size-full object-contain\"></figure><div style=\"--space-default:0px;--space-md:0px;--space-lg:0px;--space-xl:0px\" class=\"h-(--space-default) md:h-(--space-md) lg:h-(--space-lg) xl:h-(--space-xl)\" aria-hidden=\"true\"></div><div class=\"pt-0 pr-0 pb-0 pl-0\"><h3 data-display-level=\"h2\" class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-4xl md:text-6xl lg:text-10xl leading-none text-left z-1 text-(--prose-type-color)\"><span class=\"block\">The other 90%: Making AI agents production-ready</span></h3></div><div class=\"element text-content-element px-0 pt-4 lg:pt-8 pr-0 pb-0 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none\"><p>Assess where you stand on AI readiness\u2014supply chain, data, systems, team maturity, governance\u2014and what closing the gap requires.</p></div></div></div></div>\n</div>"}, "type": "attribute", "id": "D482649C-2DAB-41B2-B240-29BDA1D64261", "selector": "body > main > div:nth-of-type(1) > div:nth-of-type(1)"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "<div class=\"overline-element flex items-center gap-2\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <p class=\"font-body font-medium text-base -tracking-[0.32px] text-[var(--prose-type-color,var(--color-secondary-darkfir))]\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    Tent Talk with Valtech\n  </p>\n</div>\n<h3 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-4xl md:text-4xl lg:text-5xl 2xl:text-5xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] 2xl:leading-[0.9] text-left z-1 text-(--color-secondary-darkfir)\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <span class=\"block\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    10% agent, 90% enterprise readiness\n  </span>\n</h3>\n<div class=\"element text-content-element px-0 pt-0 pr-0 pb-4 lg:pb-6 pl-0\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none prose-p-size-18\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    <p data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n      Lets map the readiness gap: supply chain, data, systems, teams, governance.\n    </p>\n  </div>\n</div>\n<div class=\"inline-flex w-fit\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <a href=\"https://events.optimizely.com/Opticon26NY\" title=\"Save your seat\" target=\"\" data-epi-edit=\"Link\" class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-secondary-darkfir bg-tertiary-midfir text-tertiary-2 hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-secondary-darkfir h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px]\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    Save your seat\n  </a>\n</div>"}, "type": "attribute", "id": "33BE9DAF-02A5-4BB0-8DDA-792BF80DC6F8", "selector": "div[data-state='active'] > div:nth-of-type(1) > div:nth-of-type(4) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(2)"}, {"id": "3FF5AF9D-D0E4-4AC7-B03E-9D43FBA362CF", "type": "attribute", "depen
2dencies": [], "name": "", "isOpalManaged": false, "selector": "body > main > div > div:nth-of-type(1) > div > div:nth-of-type(2) > div > figure > img", "attributes": {"src": "https://images1.cmp.optimizely.com/Zz0xYTZiMjliMDc5NjExMWYxYjZlZDQyMzkzZWE3OTA0NA=="}, "css": {}}, {"id": "9E9F91C1-4DE2-4C6C-B1D7-664025D6EDFB", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "[data-display-level='h2'] > span", "attributes": {"html": "Optimizely's Solution Partner of the Year"}, "css": {}}, {"id": "04F04D64-6243-428C-9392-CEB7A5EC7EEE", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "body > main > div:nth-of-type(1) > div:nth-of-type(1) > div > div > div > div > [data-epi-edit='MainBody'] > p", "attributes": {"html": "Visit the UNRVLD stand to learn how we're enabling Optimizely clients to ready their digital platforms for the intelligent future."}, "css": {}}, {"id": "2C149E76-5A32-4DDB-A456-693BF5682C07", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(2) > div:nth-of-type(1) > p", "attributes": {"html": "Session with UNRVLD"}, "css": {}}, {"id": "19397736-6692-493E-AAAD-022E3CA6358D", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div > div > div > div > h3 > span", "attributes": {"html": "100 days in: Onboarding Opal"}, "css": {}}, {"id": "77DB2D95-B10E-4BCF-A3BD-42C2C268A1BE", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div > div > div > div:nth-of-type(2) > div > [data-epi-edit='MainBody'] > p", "attributes": {"html": "Get the lowdown on orchestrating AI from global insurer Sompo & UNRVLD."}, "css": {}}, {"id": "3F947DF1-6360-42F4-9558-7B4E1010E0ED", "type": "attribute", "dependencies": [], "name": "", "isOpalManaged": false, "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div > div > div > div > div > [data-epi-edit='Link']", "attributes": {"href": "https://events.optimizely.com/Opticon26London"}, "css": {}}]}]}], "weightDistributions": null, "name": "UNRVLD (London)", "bucketingStrategy": null, "experimentMetadata": {"allocationPolicy": "manual", "layerId": "4597086472110080"}, "deployed": false}, {"audienceIds": ["and", "6080144903766016"], "audienceName": "[Opticon 26 sponsor] DEPT", "changes": null, "id": "5716420783243264", "integrationSettings": null, "variations": [{"id": "4597420657475584", "name": "Variation #1", "actions": [{"viewId": "6567315645399040", "changes": [{"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "<div class=\"relative flex w-full flex-col bg-tertiary-2 pt-20 pb-8 px-7 mt-0 mb-0 justify-start rounded-t-default lg:rounded-t-lg lg:pt-32 lg:pb-16 md:px-8 lg:px-16\" style=\"--separator-color:var(--color-tertiary-5);--timeline-bar-color:#0D3A29;--timeline-dot-color:#C4CFC0;--timeline-dot-active-color:#ABFF44\"><div class=\"container mx-auto flex grow flex-col justify-start\"><div class=\"grid grid-cols-12 items-start mt-0 md:mt-0 lg:mt-0 mb-0 md:mb-0 lg:mb-0 py-0 bg-transparent gap-x-4 gap-y-4\"><div class=\"flex flex-1 flex-col flex-nowrap justify-start col-span-full md:col-span-6 gap-4\"><div class=\"overline-element flex items-center gap-2\"><p class=\"font-body font-medium text-base -tracking-[0.32px] text-[var(--prose-type-color,var(--color-secondary-darkfir))]\">Agenda</p></div><h2 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-6xl md:text-8xl lg:text-10xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] text-left mb-4 z-1 text-(--color-secondary-darkfir)\"><span class=\"block\">What's waiting on the&nbsp;trail</span></h2><div class=\"element text-content-element px-0 pt-0 pr-0 pb-0 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none\"><p>Opticon New York pitches up at the Javits Center on September 1. Seth Godin on the bill, city-style energy, and a sharp lineup to match.</p></div></div></div><div style=\"--prose-type-color:var(--color-secondary-darkfir);
2--prose-table-bg:var(--color-tertiary-midfir);--prose-table-text:var(--color-primary-1);--prose-table-row-odd:rgba(25, 112, 80, 0.7);--prose-table-row-even:rgba(25, 112, 80, 0.4)\" class=\"flex flex-1 flex-col flex-nowrap justify-start col-span-10 md:col-span-4 col-start-2 md:col-start-9 gap-4 bg-(--color-pink-ltpink) rounded-sm lg:rounded-default p-6 lg:p-8\"><div class=\"overline-element flex items-center gap-2\"><p class=\"font-body font-medium text-base -tracking-[0.32px] text-[var(--prose-type-color,var(--color-secondary-darkfir))]\">Tent Talk with Valtech</p></div><h3 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-4xl md:text-4xl lg:text-5xl 2xl:text-5xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] 2xl:leading-[0.9] text-left z-1 text-(--color-secondary-darkfir)\"><span class=\"block\">10% Agent, 90% Enterprise Readiness</span></h3><div class=\"element text-content-element px-0 pt-0 pr-0 pb-4 lg:pb-6 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none prose-p-size-18\"><p>Lets map the readiness gap: supply chain, data, systems, teams, governance.</p></div></div><div class=\"inline-flex w-fit\"><a href=\"https://events.optimizely.com/Opticon26NY\" title=\"Save your seat\" target=\"\" data-epi-edit=\"Link\" class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-secondary-darkfir bg-tertiary-midfir text-tertiary-2 hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-secondary-darkfir h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px]\">Save your seat</a></div></div></div></div></div>"}, "type": "attribute", "id": "896DE76F-BA74-44C9-ADD6-06EADAB6AAF8", "selector": "div[data-state='active'] > div:nth-of-type(1) > div:nth-of-type(4)"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "<img src=\"https://images1.cmp.optimizely.com/Zz0wZWVlYzI0ODZjYzAxMWYxYTkyYzUyYjA3N2U2MmU1OA==?width=1920\" alt=\"\" class=\"pointer-events-none absolute inset-0 size-full object-cover object-bottom\" decoding=\"async\" loading=\"eager\" fetchpriority=\"high\">\n</img>\n<div class=\"container mx-auto flex grow flex-col justify-center relative z-10\">\n  <div class=\"grid grid-cols-1 mt-32 mb-0 py-8 bg-transparent gap-x-4 gap-y-4\">\n    <div class=\"flex flex-1 flex-col flex-nowrap justify-start gap-4 items-center\">\n      <div class=\"pt-0 pr-0 pb-0 pl-0\">\n        <h2 data-display-level=\"h1\" class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-5xl md:text-7xl lg:text-12xl leading-none text-center mb-8 z-1 text-(--prose-type-color)\">\n          <span class=\"block\">\n            Welcome to\n          </span>\n        </h2>\n      </div>\n      <figure class=\"image-display-block h-full overflow-hidden\">\n        <img loading=\"lazy\" decoding=\"async\" src=\"https://images1.cmp.optimizely.com/Zz0wOGEwN2NlMjZjYzAxMWYxODA4ODUyYjA3N2U2MmU1OA==\" alt=\"\" width=\"0\" height=\"0\" class=\"size-full object-contain\">\n        </img>\n      </figure>\n      <div style=\"--space-default:0px;--space-md:0px;--space-lg:40px;--space-xl:40px\" class=\"h-(--space-default) md:h-(--space-md) lg:h-(--space-lg) xl:h-(--space-xl)\" aria-hidden=\"true\">\n      </div>\n      <div class=\"inline-flex w-fit\">\n        <a href=\"/field-notes/opticon/#locations\" title=\"\" target=\"\" data-epi-edit=\"Link\" class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-primary-3 bg-tertiary-2 text-secondary-darkfir hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-primary-3)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-primary-3 h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px] lg:h-14 lg:rounded-[20px] lg:px-6 lg:text-body-med lg:tracking-[0.4px]\">\n          Begin your journey\n        </a>\n      </div>\n    </div>\n  </div>\n  <div class=\"grid grid-cols-12 mt-20 md:mt-24 lg:mt-32 mb-0 md:mb-0 lg:mb-0 py-0 bg-transparent gap-x-4 gap-y-4\"><div style=\"--prose-type-color:var(--color-secondary-darkfir);
2--prose-table-bg:var(--color-tertiary-midfir);--prose-table-text:var(--color-primary-1);--prose-table-row-odd:rgba(25, 112, 80, 0.7);--prose-table-row-even:rgba(25, 112, 80, 0.4)\" class=\"flex flex-1 flex-col flex-nowrap justify-start col-span-10 lg:col-span-8 col-start-2 lg:col-start-3 gap-4 items-start bg-(--color-pink-ltpink) rounded-sm lg:rounded-default p-6 lg:p-8\"><figure class=\"image-display-block h-[40px] overflow-hidden\"><img loading=\"lazy\" decoding=\"async\" src=\"https://images3.cmp.optimizely.com/Zz1lZGZlMTBkODcwMDgxMWYxYTBhYzNhZjNlODQ4NTFlNg==\" alt=\"\" width=\"0\" height=\"0\" class=\"size-full object-contain\"></figure><div style=\"--space-default:0px;--space-md:0px;--space-lg:0px;--space-xl:0px\" class=\"h-(--space-default) md:h-(--space-md) lg:h-(--space-lg) xl:h-(--space-xl)\" aria-hidden=\"true\"></div><div class=\"pt-0 pr-0 pb-0 pl-0\"><h3 data-display-level=\"h2\" class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-4xl md:text-6xl lg:text-10xl leading-none text-left z-1 text-(--prose-type-color)\"><span class=\"block\">The other 90%: Making AI agents production-ready</span></h3></div><div class=\"element text-content-element px-0 pt-4 lg:pt-8 pr-0 pb-0 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none\"><p>Assess where you stand on AI readiness\u2014supply chain, data, systems, team maturity, governance\u2014and what closing the gap requires.</p></div></div></div></div>\n</div>"}, "type": "attribute", "id": "C6AC8488-6AD8-4C09-8798-DD80D70C3C61", "selector": "body > main > div:nth-of-type(1) > div:nth-of-type(1)"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "<div class=\"overline-element flex items-center gap-2\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <p class=\"font-body font-medium text-base -tracking-[0.32px] text-[var(--prose-type-color,var(--color-secondary-darkfir))]\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    Tent Talk with Valtech\n  </p>\n</div>\n<h3 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-4xl md:text-4xl lg:text-5xl 2xl:text-5xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] 2xl:leading-[0.9] text-left z-1 text-(--color-secondary-darkfir)\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <span class=\"block\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    10% agent, 90% enterprise readiness\n  </span>\n</h3>\n<div class=\"element text-content-element px-0 pt-0 pr-0 pb-4 lg:pb-6 pl-0\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none prose-p-size-18\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    <p data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n      Lets map the readiness gap: supply chain, data, systems, teams, governance.\n    </p>\n  </div>\n</div>\n<div class=\"inline-flex w-fit\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n  <a href=\"https://events.optimizely.com/Opticon26NY\" title=\"Save your seat\" target=\"\" data-epi-edit=\"Link\" class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-secondary-darkfir bg-tertiary-midfir text-tertiary-2 hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-secondary-darkfir h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px]\" data-optly-ac9c2691-ebbd-404d-a5ae-191f5fe22a31=\"\">\n    Save your seat\n  </a>\n</div>"}, "type": "attribute", "id": "39857C14-C3B0-46B0-B598-0B6D163AF43E", "selector": "div[data-state='active'] > div:nth-of-type(1) > div:nth-of-type(4) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(2)"}, {"css": {}
2, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"src": "https://images1.cmp.optimizely.com/Zz0xYTZiMjliMDc5NjExMWYxYjZlZDQyMzkzZWE3OTA0NA==", "srcset": "https://images4.cmp.optimizely.com/Zz01ZjM0YzkyYThjNTMxMWYxOWM5MWNlMzc0MzYwYjhlNw=="}, "type": "attribute", "id": "1173A66D-2953-4644-8048-A310759BD1AB", "selector": "body > main > div > div:nth-of-type(1) > div > div:nth-of-type(2) > div > figure > img"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "Your AI tools are ready. Is your team?"}, "type": "attribute", "id": "F7227AF5-B4EF-4E3C-B6A6-FB27A7063558", "selector": "[data-display-level='h2'] > span"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "Skip the sales pitch and sit down with DEPT\u00ae specialists. Talk through your real challenges and walk away knowing exactly where to start."}, "type": "attribute", "id": "FE1E562B-98CD-4B7E-A337-9861B92291C4", "selector": "body > main > div:nth-of-type(1) > div:nth-of-type(1) > div > div > div > div > [data-epi-edit='MainBody'] > p"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "Session with DEPT"}, "type": "attribute", "id": "7AAF878E-0289-4E0C-8F2C-BA13142E9151", "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(2) > div:nth-of-type(1) > p"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "Agents everywhere, transformation nowhere"}, "type": "attribute", "id": "25D81289-81D3-4EEA-AB0A-882DE528B5E4", "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div > div > div > div > h3 > span"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"html": "How DEPT\u00ae and Opal turn AI tools into one plan your CMO and CTO can sign."}, "type": "attribute", "id": "3FC001F6-3988-490F-83B3-63C7AD5CA557", "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div > div > div > div:nth-of-type(2) > div > [data-epi-edit='MainBody'] > p"}, {"css": {}, "isOpalManaged": false, "dependencies": [], "name": "", "attributes": {"href": "https://events.optimizely.com/Opticon26London"}, "type": "attribute", "id": "72B6D05D-6704-4D7E-824C-FC60BC0B626C", "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div > div > div > div > div > [data-epi-edit='Link']"}]}]}], "weightDistributions": null, "name": "DEPT (London)", "bucketingStrategy": null, "experimentMetadata": {"allocationPolicy": "manual", "layerId": "4597086472110080"}, "deployed": false}], "policy": "equal_priority", "viewIds": ["6567315645399040"], "weightDistributions": null, "decisionMetadata": {"experimentPriorities": [[{"entityId": "4538374386614272", "endOfRange": null}], [{"entityId": "5658970119995392", "endOfRange": null}], [{"entityId": "6238772474413056", "endOfRange": null}], [{"entityId": "6493760253591552", "endOfRange": null}], [{"entityId": "6521495139123200", "endOfRange": null}], [{"entityId": "6654811595079680", "endOfRange": null}, {"entityId": "5716420783243264", "endOfRange": null}]], "experimentWeights": null, "offerConsistency": null}, "concluded": false}, {"changes": [{"id": "E3D9A26E-C3BB-4019-935F-664EF5AFB547", "type": "custom_code", "value": function($){/* WebMCP tools for optimizely - generated by the Opal WebMCP Site Analyzer */
3(function () {
4  'use strict';
5  var _mcRaw = (typeof document !== 'undefined' && document.modelContext) ||
6               (typeof navigator !== 'undefined' && navigator.modelContext);
7  if (!_mcRaw || typeof _mcRaw.registerTool !== 'function') {
8    console.warn('[WebMCP] No modelContext available; tools not registered.');
9    return;
10  }
11
12  function trackWebMCPTool(name) {
13    try {
14      window.optimizely = window.optimizely || [];
15      window.optimizely.push({
16        type: 'event', eventName: 'WebMCPTool',
17        properties: { WebMCPTool: name || 'unknown' }
18      });
19    } catch (e) { /* tracking must never break a tool */ }
20  }
21
22  function _wrapHandler(fn, toolName) {
23    return function () { trackWebMCPTool(toolName); return fn.apply(this, arguments); };
24  }
25
26  /* Wrap a descriptor's handlers for tracking exactly once — descriptors are
27     re-registered across SPA route changes and must not be double-wrapped. */
28  function _wrapDescriptor(descriptor) {
29    if (!descriptor || typeof descriptor !== 'object' || descriptor.__webmcpWrapped) {
30      return descriptor;
31    }
32    var _name = descriptor.name;
33    Object.keys(descriptor).forEach(function (k) {
34      if (typeof descriptor[k] === 'function') {
35        descriptor[k] = _wrapHandler(descriptor[k], _name);
36      }
37    });
38    try { descriptor.__webmcpWrapped = true; } catch (e) { /* frozen — already safe */ }
39    return descriptor;
40  }
41
42  /* "/x/**" matches "/x" itself as well as anything below it; "*" stays
43     within one path segment. The site strips trailing slashes, so the bare
44     base path is the normal case for section hubs. */
45  function _pathMatches(pattern) {
46    if (!pattern || pattern === '/**') { return true; }
47    var path = location.pathname.replace(/\/+$/, '') || '/';
48    var pat  = String(pattern).replace(/\/+$/, '') || '/';
49    if (pat === '/') { return path === '/'; }
50    var rx = pat
51      .replace(/[.+?^${}()|[\]\\]/g, '\\$&')
52      .replace(/\/\*\*/g, '\u0000')
53      .replace(/\*\*/g, '\u0001')
54      .replace(/\*/g, '[^/]*')
55      .replace(/\u0000/g, '(?:\\/.*)?')
56      .replace(/\u0001/g, '.*');
57    try { return new RegExp('^' + rx + '$').test(path); } catch (e) { return true; }
58  }
59
60  /* Conditional registrations are reconciled against the live URL on every
61     route change - including SPA pushState/replaceState navigations that
62     never reload the document - so one injection per document covers every
63     path the visitor (or an agent) reaches client-side. Where the host
64     model-context offers no way to unregister, tools that stop matching
65     simply stay registered; each tool validates the live page itself and
66     reports a clear error payload when called on the wrong one. */
67  var _conditional = [];
68
69  function _tryUnregister(entry) {
70    if (!entry.active) { return; }
71    entry.active = false;
72    try {
73      if (entry.handle && typeof entry.handle.unregister === 'function') {
74        entry.handle.unregister();
75        entry.handle = null;
76        return;
77      }
78    } catch (e) { /* fall through to unregisterTool */ }
79    entry.handle = null;
80    try {
81      if (typeof _mcRaw.unregisterTool === 'function') {
82        _mcRaw.unregisterTool(entry.descriptor.name);
83      }
84    } catch (e) { /* host cannot unregister — tool stays, guarded by its own page checks */ }
85  }
86
87  function _reconcileEntry(entry) {
88    var wanted = _pathMatches(entry.pattern);
89    if (wanted && !entry.active) {
90      entry.active = true;
91      try {
92        var out = _mcRaw.registerTool(entry.descriptor);
93        entry.handle = null;
94        if (out && typeof out.unregister === 'function') {
95          entry.handle = out;
96        } else if (out && typeof out.then === 'function') {
97          out.then(function (h) {
98            if (entry.active && h && typeof h.unregister === 'function') { entry.handle = h; }
99          }, function () { /* registration rejected — leave handle null */ });
100        }
101      } catch (e) {
102        entry.active = false;
103      }
104    } else if (!wanted && entry.active) {
105      _tryUnregister(entry);
106    }
107  }
108
109  function _reconcileAll() {
110    for (var i = 0; i < _conditional.length; i++) { _reconcileEntry(_conditional[i]); }
111  }
112
113  var mc = {
114    registerTool: function (descriptor) {
115      return _mcRaw.registerTool(_wrapDescriptor(descriptor));
116    },
117    /* Register on pages where the tool can work - and keep that decision
118       fresh as the SPA moves between paths without reloading. */
119    registerToolIf: function (pattern, descriptor) {
120      var entry = {
121        pattern: pattern,
122        descriptor: _wrapDescriptor(descriptor),
123        active: false,
124        handle: null
125      };
126      _conditional.push(entry);
127      _reconcileEntry(entry);
128      return null;
129    }
130  };
131
132  /* Route watcher: the history API is patched because SPA navigations fire
133     no document-level event; the small delay lets the router settle the URL
134     before the toolset flips. Kept idempotent per document. */
135  (function _watchRoutes() {
136    if (window.__webmcpRouteWatcher) { return; }
137    window.__webmcpRouteWatcher = true;
138    var lastPath = location.pathname;
139    var timer = null;
140    function onMaybeChange() {
141      if (timer) { clearTimeout(timer); }
142      timer = setTimeout(function () {
143        timer = null;
144        var path = location.pathname;
145        if (path === lastPath) { return; }
146        lastPath = path;
147        _reconcileAll();
148      }, 150);
149    }
150    try {
151      var _push = history.pushState;
152      history.pushState = function () {
153        var r = _push.apply(this, arguments);
154        onMaybeChange();
155        return r;
156      };
157      var _replace = history.replaceState;
158      history.replaceState = function () {
159        var r = _replace.apply(this, arguments);
160        onMaybeChange();
161        return r;
162      };
163    } catch (e) { /* history not patchable — popstate/hashchange still cover back/forward */ }
164    window.addEventListener('popstate', onMaybeChange);
165    window.addEventListener('hashchange', onMaybeChange);
166  })();
167
168  function waitForElement(selector, timeout) {
169    timeout = timeout || 5000;
170    return new Promise(function (resolve, reject) {
171      var found = document.querySelector(selector);
172      if (found) { return resolve(found); }
173      var obs = new MutationObserver(function () {
174        var el = document.querySelector(selector);
175        if (el) { obs.disconnect(); clearTimeout(timer); resolve(el); }
176      });
177      obs.observe(document.documentElement, { childList: true, subtree: true });
178      var timer = setTimeout(function () {
179        obs.disconnect();
180        reject(new Error('[WebMCP] timeout waiting for ' + selector));
181      }, timeout);
182    });
183  }
184
185  function setNativeValue(el, value) {
186    var desc = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(el), 'value');
187    if (desc && desc.set) { desc.set.call(el, value); } else { el.value = value; }
188  }
189
190  async function fillInput(selector, value) {
191    var el = await waitForElement(selector);
192    el.focus();
193    setNativeValue(el, value);
194    el.dispatchEvent(new Event('input',  { bubbles: true }));
195    el.dispatchEvent(new Event('change', { bubbles: true }));
196    return el;
197  }
198
199  async function fillSelect(selector, value) {
200    var el = await waitForElement(selector);
201    el.value = value;
202    if (el.selectedIndex === -1) {
203      var opt = Array.prototype.find.call(el.options, function (o) {
204        return o.textContent.trim().toLowerCase() === String(value).trim().toLowerCase();
205      });
206      if (opt) { el.value = opt.value; }
207    }
208    el.dispatchEvent(new Event('change', { bubbles: true }));
209    return el;
210  }
211
212  async function fillCheckbox(selector, checked) {
213    var el = await waitForElement(selector);
214    if (el.checked !== !!checked) { el.click(); }
215    el.dispatchEvent(new Event('change', { bubbles: true }));
216    return el;
217  }
218
219  /* Read ONE field off ONE leaf node. '@attr' reads an attribute of the item
220     itself; 'sel @attr' reads an attribute of a descendant. Never returns the
221     concatenated text of a whole container. */
222  function _field(root, spec) {
223    if (!spec) { return null; }
224    spec = String(spec).trim();
225    var at = spec.indexOf('@');
226    if (at === 0) { return root.getAttribute(spec.slice(1)); }
227    if (at > 0) {
228      var el0 = root.querySelector(spec.slice(0, at).trim());
229      return el0 ? el0.getAttribute(spec.slice(at + 1)) : null;
230    }
231    var el = root.querySelector(spec);
232    if (!el) { return null; }
233    return (el.textContent || '').replace(/\s+/g, ' ').trim();
234  }
235
236  function extractItems(itemSelector, fieldMap, limit) {
237    limit = limit || 10;
238    var nodes = Array.prototype.slice.call(document.querySelectorAll(itemSelector));
239    return {
240      resultCount: nodes.length,
241      truncated: nodes.length > limit,
242      results: nodes.slice(0, limit).map(function (n) {
243        var o = {};
244        Object.keys(fieldMap || {}
244).forEach(function (k) {
245          var v = _field(n, fieldMap[k]);
246          if (v !== null && v !== undefined && v !== '') { o[k] = String(v).slice(0, 300); }
247        });
248        return o;
249      })
250    };
251  }
252
253  /* Parse the integer the page itself states, e.g. "5 items" -> 5. */
254  function readResultCount(selector, fallback) {
255    if (!selector) { return fallback; }
256    var el = document.querySelector(selector);
257    if (!el) { return fallback; }
258    var m = (el.textContent || '').replace(/,/g, '').match(/\d+/);
259    return m ? parseInt(m[0], 10) : fallback;
260  }
261
262  function buildUrl(template, args) {
263    return template.replace(/\{(\w+)\}/g, function (_, k) {
264      return args && args[k] != null ? encodeURIComponent(args[k]) : '';
265    }).replace(/[?&]\w+=(?=&|$)/g, '').replace(/\?&/, '?').replace(/[?&]$/, '');
266  }
267
268  /* ---- optimizely-request-demo-lead-form, optimizely-request-demo-lead-form-submit ---- */
269mc.registerTool({
270  name: "optimizely-request-demo-lead-form",
271  description: "Fills the Optimizely lead capture form embedded in the page's main region (the 'Let's grow' / 'Get started' contact form) so a buyer can request a demo, pricing or a custom plan. It scrolls to the form, highlights it, writes First name, Last name, Work email, Company, Job title, Phone and the free-text 'What products are you interested in?' note, and picks the country from the option list the form's own combobox renders. Returns the values read back out of each input, the required fields it could not populate, whether the form is ready to send, which optional controls (state selection, marketing-consent choice, captcha) it found rendered and their state, the Optimizely end-user id carried by the form, and the name of the companion submit tool. Nothing is sent until that submit tool is called.",
272  inputSchema: {
273    type: "object",
274    properties: {
275      firstName: { type: "string", description: "Value for the First name field." },
276      lastName: { type: "string", description: "Value for the Last name field." },
277      email: { type: "string", description: "Value for the Work email field." },
278      company: { type: "string", description: "Value for the Company field." },
279      jobTitle: { type: "string", description: "Value for the Job title field." },
280      phone: { type: "string", description: "Value for the Phone field, for example +1 234 567 8901." },
281      country: { type: "string", description: "Country name to choose in the Country combobox. It is matched against the options the page renders at call time; if it does not match, the tool returns the options that were actually on screen." },
282      state: { type: "string", description: "Optional state name. The state control is rendered by the page only for some countries; the tool fills it when it is present." },
283      productsOfInterest: { type: "string", description: "Optional free text for the 'What products are you interested in?' box." },
284      marketingConsent: { type: "string", description: "Optional marketing email preference, matched against the choices the consent control renders at call time." }
285    },
286    required: ["firstName", "lastName", "email", "company", "jobTitle", "phone", "country"]
287  },
288  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
289  execute: async function (args) {
290    trackWebMCPTool("optimizely-request-demo-lead-form");
291    var a = args || {};
292    var urlBefore = location.href;
293    var FORM_SEL = "main form";
294    var SEL = {
295      firstName: "main form > fieldset > div > div:nth-child(1) > div:nth-child(1) > div > div > input",
296      lastName: "main form > fieldset > div > div:nth-child(1) > div:nth-child(2) > div > div > input",
297      email: "main form > fieldset > div > div:nth-child(2) > div:nth-child(1) > div > div > input",
298      company: "main form > fieldset > div > div:nth-child(3) > div:nth-child(1) > div > div > input",
299      jobTitle: "main form > fieldset > div > div:nth-child(3) > div:nth-child(2) > div > div > input",
300      phone: "main form > fieldset > div > div:nth-child(4) > div:nth-child(1) > div > div > input",
301      country: "main form > fieldset > div > div:nth-child(4) > div:nth-child(2) > div > div > div > div > input",
302      countryToggle: "main form > fieldset > div > div:nth-child(4) > div:nth-child(2) > div > div > div > div > button",
303      countryHidden: 'main form > fieldset > div > div:nth-child(4) > div:nth-child(2) > div > input[type="hidden"]',
304      products: "main form > fieldset > div > div:nth-child(6) > div:nth-child(1) > div > div > textarea",
305      stateRow: "main form > fieldset > div > div:nth-child(5)",
306      stateInput: 'main form > fieldset > div > div:nth-child(5) input:not([type="hidden"])',
307      stateHidden: 'main form > fieldset > div > div:nth-child(5) input[type="hidden"]',
308      consentRow: "main form > fieldset > div > div:nth-child(7)",
309      captchaRow: "main form > fieldset > div > div:nth-child(8)"
310    };
311
312    function out(payload) {
313      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
314    }
315    function sleep(ms) {
316      return new Promise(function (r) { setTimeout(r, ms); });
317    }
318    async function waitFor(fn, timeout) {
319      var end = Date.now() + (timeout || 5000);
320      for (;;) {
321        var v = null;
322        try { v = fn(); } catch (e) { v = null; }
323        if (v) return v;
324        if (Date.now() > end) return null;
325        await sleep(150);
326      }
327    }
328    function txt(el) {
329      return el && el.textContent ? el.textContent.replace(/\s+/g, " ").trim() : "";
330    }
331    function visible(el) {
332      if (!el) return false;
333      try { if (el.closest && el.closest('[aria-hidden="true"]')) return false; } catch (e) {}
334      var r = el.getBoundingClientRect ? el.getBoundingClientRect() : { width: 0, height: 0 };
335      return !!(el.offsetParent || r.width || r.height);
336    }
337    function nativeSet(el, value) {
338      var proto = el.tagName === "TEXTAREA" ? window.HTMLTextAreaElement.prototype : window.HTMLInputElement.prototype;
339      var desc = Object.getOwnPropertyDescriptor(proto, "value");
340      if (desc && desc.set) { desc.set.call(el, value); } else { el.value = value; }
341      el.dispatchEvent(new Event("input", { bubbles: true }));
342      el.dispatchEvent(new Event("change", { bubbles: true }));
343    }
344    function labelString(l) {
345      return (l && l.textContent ? l.textContent : "").replace(/\*/g, " ").replace(/\s+/g, " ").trim();
346    }
347    function byLabel(root, want) {
348      if (!root) return null;
349      var w = String(want).toLowerCase();
350      var labels = root.querySelectorAll("label");
351      for (var i = 0; i < labels.length; i++) {
352        var t = labelString(labels[i]).toLowerCase();
353        if (t !== w && t.indexOf(w) !== 0) continue;
354        var forId = labels[i].getAttribute("for");
355        if (forId) {
356          var byId = null;
357          try { byId = root.querySelector("#" + (window.CSS && CSS.escape ? CSS.escape(forId) : forId)); } catch (e) { byId = null; }
358          if (byId) return byId;
359        }
360        var scope = labels[i].parentElement;
361        for (var d = 0; d < 4 && scope && scope !== root; d++) {
362          var ctls = scope.querySelectorAll('input:not([type="hidden"]), textarea, select');
363          if (ctls.length === 1) return ctls[0];
364          if (ctls.length > 1) break;
365          scope = scope.parentElement;
366        }
367      }
368      return null;
369    }
370    function resolveField(want, selector) {
371      var root = document.querySelector(FORM_SEL);
372      var el = root ? byLabel(root, want) : null;
373      if (el) return el;
374      return selector ? document.querySelector(selector) : null;
375    }
376    function setValue(el, value) {
377      nativeSet(el, value);
378    }
379    function optionNodes(anchor) {
380      var found = [];
381      function push(list) {
382        for (var i = 0; i < list.length; i++) {
383          var n = list[i];
384          if (found.indexOf(n) === -1 && visible(n) && txt(n)) found.push(n);
385        }
386      }
387      push(document.querySelectorAll('[role="option"]'));
388      if (!found.length) push(document.querySelectorAll('[role="listbox"] li, [role="listbox"] button, [role="listbox"] [role="button"]'));
389      if (!found.length && anchor) {
390        var scope = anchor;
391        for (var d = 0; d < 4 && scope.parentElement; d++) scope = scope.parentElement;
392        push(scope.querySelectorAll('li, [role="option"]'));
393      }
394      return found;
395    }
396    async function pickFromCombobox(input, toggle, hiddenSelector, value) {
397      /* The option list can render asynchronously on a cold first interaction
398         (observed on Country): one type+wait+toggle cycle finds nothing, and
399         calling this tool a second time used to be the only way through,
400         because the second call's cycle starts after the first call's
401         focus/click/type already triggered the render. Loop the cycle here
402         instead of making the caller retry the whole tool. */
403      var opts = null;
404      var attempts = 3;
405      for (var attempt = 0; attempt < attempts && !opts; attempt++) {
406        if (attempt > 0) {
407          try { nativeSet(input, ""); } catch (e) {}
408          await sleep(250);
409        }
410        try { input.focus(); } catch (e) {}
411        try { input.click(); } catch (e) {}
412        nativeSet(input, value);
413        opts = await waitFor(function () { var o = optionNodes(input); return o.length ? o : null; }, 4000);
414        if (!opts && toggle) {
415          try { toggle.click(); } catch (e) {}
416          opts = await waitFor(function () { var o = optionNodes(input); return o.length ? o : null; }, 3000);
417        }
418      }
419      if (!opts) {
420        return { ok: false, listMissing: true, reason: "The option list did not render after typing '" + value + "' into the combobox across " + attempts + " attempts.", options: [] };
421      }
422      var want = String(value).toLowerCase().trim();
423      var match = null;
424      var i;
425      for (i = 0; i < opts.length; i++) { if (txt(opts[i]).toLowerCase() === want) { match = opts[i]; break; } }
426      if (!match) { for (i = 0; i < opts.length; i++) { if (txt(opts[i]).toLowerCase().indexOf(want) === 0) { match = opts[i]; break; } } }
427      if (!match) {
428        nativeSet(input, "");
429        if (toggle) { try { toggle.click(); } catch (e) {} }
430        var all = await waitFor(function () { var o = optionNodes(input); return o.length ? o : null; }, 3000);
431        if (!all) all = opts;
432        var names = [];
433        for (i = 0; i < all.length; i++) {
434          var n = txt(all[i]);
435          if (n && names.indexOf(n) === -1) names.push(n);
436        }
437        return {
438          ok: false,
439          notMatched: true,
440          reason: "'" + value + "' is not one of the options the combobox rendered on this page.",
441          options: names.slice(0, 300),
442          truncated: names.length > 300
443        };
444      }
445      var chosen = txt(match);
446      var mk = function (t) { return new MouseEvent(t, { bubbles: true, cancelable: true, view: window }); };
447      try { match.dispatchEvent(mk("mousedown")); match.dispatchEvent(mk("mouseup")); } catch (e) {}
448      try { match.click(); } catch (e) {}
449      await sleep(500);
450      var applied = "";
451      var hid = hiddenSelector ? document.querySelector(hiddenSelector) : null;
452      if (hid && hid.value) applied = hid.value;
453      if (!applied) {
454        var fresh = document.querySelector(FORM_SEL) ? input : input;
455        if (fresh && fresh.value) applied = fresh.value;
456      }
457      return { ok: !!applied, applied: applied, chosen: chosen };
458    }
459    function choiceLabel(el) {
460      if (!el) return "";
461      var al = el.getAttribute("aria-label");
462      if (al) return al.trim();
463      if (el.tagName === "BUTTON") return txt(el);
464      var wrap = el.closest ? el.closest("label") : null;
465      if (wrap) return labelString(wrap);
466      var id = el.getAttribute("id");
467      if (id) {
468        var lab = null;
469        try { lab = document.querySelector('label[for="' + id + '"]'); } catch (e) { lab = null; }
470        if (lab) return labelString(lab);
471      }
472      var sib = el.nextElementSibling;
473      if (sib && txt(sib)) return txt(sib);
474      var parentText = el.parentElement ? txt(el.parentElement) : "";
475      if (parentText && parentText.length < 40) return parentText;
476      return (el.value || "").trim();
477    }
478
479    try {
480      var form = null;
481      try { form = await waitForElement(FORM_SEL, 12000); } catch (e) { form = document.querySelector(FORM_SEL); }
482      if (!form) form = document.querySelector(FORM_SEL);
483      if (!form) {
484        return out({
485          status: "error",
486          action: "fill the Optimizely lead form",
487          url: location.href,
488          observed: false,
489          reason: "No form was found inside the page's main region, so this page does not carry the lead capture form.",
490          nextStep: "Open the /get-started page (or another page that shows the contact form) and call this tool again.",
491          summary: "This page does not carry the Optimizely lead form."
492        });
493      }
494      var isLeadForm = !!byLabel(form, "Work email") || !!document.querySelector(SEL.email);
495      if (!isLeadForm) {
496        return out({
497          status: "error",
498          action: "fill the Optimizely lead form",
499          url: location.href,
500          observed: false,
501          reason: "A form is present but it has no 'Work email' field, so it is not the Optimizely lead capture form.",
502          nextStep: "Open the /get-started page and call this tool again.",
503          summary: "The form on this page is not the lead capture form."
504        });
505      }
506
507      try { form.scrollIntoView({ behavior: "smooth", block: "center" }); } catch (e) {}
508      form.style.outline = "3px solid #0037ff";
509      form.style.outlineOffset = "4px";
510      await sleep(350);
511
512      var plan = [
513        { key: "firstName", label: "First name", sel: SEL.firstName, value: a.firstName, required: true },
514        { key: "lastName", label: "Last name", sel: SEL.lastName, value: a.lastName, required: true },
515        { key: "email", label: "Work email", sel: SEL.email, value: a.email, required: true },
516        { key: "company", label: "Company", sel: SEL.company, value: a.company, required: true },
517        { key: "jobTitle", label: "Job title", sel: SEL.jobTitle, value: a.jobTitle, required: true },
518        { key: "phone", label: "Phone", sel: SEL.phone, value: a.phone, required: true },
519        { key: "productsOfInterest", label: "What products are you interested in?", sel: SEL.products, value: a.productsOfInterest, required: false }
520      ];
521
522      var fields = {};
523      var missing = [];
524      var notes = [];
525      var i, f, el, back;
526      for (i = 0; i < plan.length; i++) {
527        f = plan[i];
528        el = resolveField(f.label, f.sel);
529        if (!el) {
530          notes.push("Could not locate the '" + f.label + "' control inside the form.");
531          if (f.required) missing.push(f.label);
532          fields[f.key] = "";
533          continue;
534        }
535        if (f.value !== undefined && f.value !== null && String(f.value) !== "") {
536          setValue(el, String(f.value));
537          await sleep(90);
538        }
539        back = resolveField(f.label, f.sel);
540        fields[f.key] = back ? (back.value || "") : "";
541        if (f.required && !fields[f.key]) missing.push(f.label);
542      }
543
544      var countryInput = resolveField("Country", SEL.country);
545      var countryToggle = document.querySelector(SEL.countryToggle);
546      if (!countryToggle && countryInput && countryInput.parentElement) {
547        countryToggle = countryInput.parentElement.querySelector("button");
548      }
549      if (!countryInput) {
550        fields.country = "";
551        missing.push("Country");
552        notes.push("Could not locate the Country combobox inside the form.");
553      } else {
554        var cRes = await pickFromCombobox(countryInput, countryToggle, SEL.countryHidden, String(a.country || ""));
555        var cHidden = document.querySelector(SEL.countryHidden);
556        var cFresh = resolveField("Country", SEL.country);
557        fields.country = (cHidden && cHidden.value) ? cHidden.value : (cFresh ? (cFresh.value || "") : "");
558        if (cRes && cRes.notMatched) {
559          return out({
560            status: "error",
561            action: "fill the Optimizely lead form",
562            url: location.href,
563            observed: true,
564            reason: cRes.reason,
565            availableOptions: cRes.options,
566            availableOptionsTruncated: !!cRes.truncated,
567            fields: fields,
568            missing: missing.concat(["Country"]),
569            ready: false,
570            submitToolName: "optimizely-request-demo-lead-form-submit",
571            nextStep: "Call this tool again with a country taken from availableOptions.",
572            summary: "Filled the other fields, but the requested country is not in the list the page rendered."
573          });
574        }
575        if (cRes && cRes.listMissing) notes.push(cRes.reason);
576        if (!fields.country) missing.push("Country");
577      }
578
579      await sleep(700);
580      var stateInfo = { rendered: false, filled: false, value: "" };
581      var stateEl = resolveField("State", SEL.stateInput);
582      if (stateEl && visible(stateEl)) {
583        stateInfo.rendered = true;
584        if (a.state) {
585          var stToggle = stateEl.parentElement ? stateEl.parentElement.querySelector("button") : null;
586          var sRes = await pickFromCombobox(stateEl, stToggle, SEL.stateHidden, String(a.state));
587          if (sRes && sRes.notMatched) {
588            notes.push("The state '" + String(a.state) + "' did not match the rendered choices: " + sRes.options.slice(0, 60).join(", ") + ".");
589          }
590        }
591        var sHidden = document.querySelector(SEL.stateHidden);
592        var sFresh = resolveField("State", SEL.stateInput);
593        stateInfo.value = (sHidden && sHidden.value) ? sHidden.value : (sFresh ? (sFresh.value || "") : "");
594        stateInfo.filled = !!stateInfo.value;
595        if (!stateInfo.value) {
596          missing.push("State");
597          notes.push("A state control is rendered for the chosen country and is still empty.");
598        } else {
599          fields.state = stateInfo.value;
600        }
601      }
602
603      var consent = { rendered: false, set: false, value: null, choices: [] };
604      var consentScope = document.querySelector(SEL.consentRow);
605      if (!consentScope || !consentScope.querySelector('input, button, [role="radio"]')) {
606        consentScope = null;
607        var divs = form.querySelectorAll("div");
608        for (i = 0; i < divs.length; i++) {
609          var dt = txt(divs[i]);
610          if (dt && dt.length < 400 && /receive email invitations|stay in touch|events and webinars/i.test(dt) && divs[i].querySelector('input, button, [role="radio"]')) {
611            consentScope = divs[i];
612            break;
613          }
614        }
615      }
616      if (consentScope) {
617        var choices = consentScope.querySelectorAll('input[type="radio"], input[type="checkbox"], [role="radio"], button');
618        if (choices.length) {
619          consent.rendered = true;
620          var ci, lbl;
621          for (ci = 0; ci < choices.length; ci++) {
622            lbl = choiceLabel(choices[ci]);
623            if (lbl && consent.choices.indexOf(lbl) === -1) consent.choices.push(lbl);
624          }
625          if (a.marketingConsent) {
626            var wantC = String(a.marketingConsent).toLowerCase().trim();
627            var hit = null;
628            for (ci = 0; ci < choices.length; ci++) {
629              var cv = (choices[ci].value || "").toLowerCase();
630              var ct = choiceLabel(choices[ci]).toLowerCase();
631              if (cv === wantC || ct === wantC || (ct && ct.indexOf(wantC) === 0)) { hit = choices[ci]; break; }
632            }
633            if (hit) {
634              if (!hit.checked) { try { hit.click(); } catch (e) {} }
635              consent.set = true;
636              await sleep(150);
637            } else {
638              notes.push("The marketing consent choice '" + String(a.marketingConsent) + "' did not match the rendered choices: " + consent.choices.join(", ") + ".");
639            }
640          }
641          for (ci = 0; ci < choices.length; ci++) {
642            if (choices[ci].checked || choices[ci].getAttribute("aria-checked") === "true") {
643              consent.value = choiceLabel(choices[ci]) || choices[ci].value || null;
644            }
645          }
646        }
647      }
648
649      var captcha = { rendered: false, tokenPresent: false };
650      var capToken = document.querySelector('textarea[name="g-recaptcha-response"]');
651      var capNode = document.querySelector('iframe[src*="recaptcha"], .g-recaptcha, [data-sitekey]');
652      captcha.rendered = !!(capToken || capNode || window.grecaptcha);
653      captcha.tokenPresent = !!(capToken && capToken.value);
654
655      var hiddens = form.querySelectorAll('input[type="hidden"]');
656      var endUserId = "";
657      for (i = 0; i < hiddens.length; i++) {
658        var hv = hiddens[i].value || "";
659        if (/^oeu\d/.test(hv)) endUserId = hv;
660      }
661      if (!endUserId) {
662        try {
663          if (window.optimizely && window.optimizely.get) {
664            var vid = window.optimizely.get("visitor_id");
665            if (vid) endUserId = typeof vid === "string" ? vid : (vid.randomId || "");
666          }
667        } catch (e) {}
668      }
669
670      if (location.href !== urlBefore) {
671        return out({
672          status: "navigated",
673          action: "fill the Optimizely lead form",
674          url: location.href,
675          nextStep: "The browser moved to a different page while the form was being filled. Call optimizely-request-demo-lead-form again on the page that is now loaded.",
676          summary: "The page navigated while the form was being filled, so the field values were not confirmed."
677        });
678      }
679
680      var observedAny = false;
681      for (var k in fields) { if (fields[k]) { observedAny = true; break; } }
682
683      var payload = {
684        status: observedAny ? (missing.length ? "partial" : "ok") : "partial",
685        action: "fill the Optimizely lead form",
686        url: location.href,
687        observed: observedAny,
688        fields: fields,
689        missing: missing,
690        ready: observedAny && missing.length === 0,
691        optionalControls: { stateSelection: stateInfo, marketingConsent: consent, captcha: captcha },
692        trackingInputsLeftUntouched: hiddens.length,
693        optimizelyEndUserId: endUserId || null,
694        submitToolName: "optimizely-request-demo-lead-form-submit",
695        notes: notes,
696        summary: ""
697      };
698      if (!observedAny) {
699        payload.reason = "No value could be read back out of any field after filling, so the form state could not be confirmed.";
700        payload.nextStep = "Let the form finish loading and call this tool again.";
701        payload.summary = "The form was found but no field value could be read back.";
702      } else if (missing.length) {
703        payload.reason = "These required fields are still empty after filling: " + missing.join(", ") + ".";
704        payload.nextStep = "Supply values for the listed fields and call this tool again before submitting.";
705        payload.summary = "Filled the Optimizely lead form, but " + missing.length + " required field(s) are still empty.";
706      } else {
707        payload.nextStep = "Review the values, then call optimizely-request-demo-lead-form-submit to send the lead to Optimizely.";
708        payload.summary = "Filled and verified the Optimizely lead form for " + (fields.email || "the supplied contact") + "; it is ready to submit.";
709      }
710      return out(payload);
711    } catch (err) {
712      return out({
713        status: "error",
714        action: "fill the Optimizely lead form",
715        url: location.href,
716        observed: false,
717        reason: "The fill attempt threw an error: " + String(err && err.message ? err.message : err),
718        nextStep: "Let the form finish loading and call this tool again.",
719        summary: "Could not complete filling the Optimizely lead form."
720      });
721    }
722  }
723});
724
725mc.registerTool({
726  name: "optimizely-request-demo-lead-form-submit",
727  description: "Clicks the submit button on the Optimizely lead capture form that optimizely-request-demo-lead-form filled, waits for the page to settle, and reports what it observes: the confirmation or thank-you text if the lead was accepted, each per-field validation message paired with the label of the field it belongs to, any captcha message, and the URL after the click. This submits the form to Optimizely, so fill and review the values first.",
728  inputSchema: {
729    type: "object",
730    properties: {
731      confirm: { type: "boolean", description: "Set true to confirm the reviewed form should be sent. Defaults to true; set false to abort without clicking." }
732    },
733    required: []
734  },
735  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
736  execute: async function (args) {
737    trackWebMCPTool("optimizely-request-demo-lead-form-submit");
738    var a = args || {};
739    var FORM_SEL = "main form";
740    var SEL = {
741      country: "main form > fieldset > div > div:nth-child(4) > div:nth-child(2) > div > div > div > div > input",
742      countryToggle: "main form > fieldset > div > div:nth-child(4) > div:nth-child(2) > div > div > div > div > button",
743      submit: "main form > fieldset > div > div:nth-child(9) > div > div > button"
744    };
745
746    function out(payload) {
747      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
748    }
749    function sleep(ms) {
750      return new Promise(function (r) { setTimeout(r, ms); });
751    }
752    function txt(el) {
753      return el && el.textContent ? el.textContent.replace(/\s+/g, " ").trim() : "";
754    }
755    function visible(el) {
756      if (!el) return false;
757      try { if (el.closest && el.closest('[aria-hidden="true"]')) return false; } catch (e) {}
758      var r = el.getBoundingClientRect ? el.getBoundingClientRect() : { width: 0, height: 0 };
759      return !!(el.offsetParent || r.width || r.height);
760    }
761    function labelString(l) {
762      return (l && l.textContent ? l.textContent : "").replace(/\*/g, " ").replace(/\s+/g, " ").trim();
763    }
764    function fieldLabelFor(node, root) {
765      var scope = node;
766      for (var d = 0; d < 5 && scope && scope !== root; d++) {
767        scope = scope.parentElement;
768        if (!scope) break;
769        var lab = scope.querySelector("label");
770        if (lab && labelString(lab)) return labelString(lab);
771      }
772      return null;
773    }
774    function readErrors(root) {
775      var out2 = [];
776      var seen = {};
777      if (!root) return out2;
778      var nodes = root.querySelectorAll('span, p, [role="alert"], [aria-live]');
779      for (var i = 0; i < nodes.length; i++) {
780        var n = nodes[i];
781        if (n.children && n.children.length) continue;
782        if (n.tagName === "LABEL") continue;
783        var t = txt(n);
784        if (!t || t.length > 200) continue;
785        if (!/this field|is required|valid|invalid|should be in|format|captcha|robot|try again/i.test(t)) continue;
786        if (!visible(n)) continue;
787        var key = t.toLowerCase();
788        if (seen[key]) continue;
789        seen[key] = true;
790        out2.push({ field: fieldLabelFor(n, root), message: t });
791      }
792      var invalid = root.querySelectorAll('[aria-invalid="true"]');
793      for (var j = 0; j < invalid.length; j++) {
794        var m = invalid[j].validationMessage || "The field is marked invalid.";
795        var k2 = (fieldLabelFor(invalid[j], root) || "") + "|" + m.toLowerCase();
796        if (seen[k2]) continue;
797        seen[k2] = true;
798        out2.push({ field: fieldLabelFor(invalid[j], root), message: m });
799      }
800      return out2;
801    }
802    function newText(before, after) {
803      if (!after) return "";
804      if (before && after.indexOf(before) === 0) return after.slice(before.length).trim().slice(0, 400);
805      var chunks = after.split(/(?:\.\s+|\n+)/);
806      var keep = [];
807      for (var i = 0; i < chunks.length; i++) {
808        var c = chunks[i].trim();
809        if (c && (!before || before.indexOf(c) === -1)) keep.push(c);
810      }
811      return keep.join(". ").slice(0, 400);
812    }
813
814    try {
815      if (a.confirm === false) {
816        return out({
817          status: "error",
818          action: "submit the Optimizely lead form",
819          url: location.href,
820          observed: false,
821          validationErrors: [],
822          confirmation: null,
823          reason: "confirm was false, so the form was not clicked and nothing was sent.",
824          nextStep: "Call this tool again with confirm set to true when the reviewed values should be sent.",
825          summary: "Submission aborted at the caller's request; nothing was sent."
826        });
827      }
828
829      var form = document.querySelector(FORM_SEL);
830      if (!form) {
831        return out({
832          status: "error",
833          action: "submit the Optimizely lead form",
834          url: location.href,
835          observed: false,
836          validationErrors: [],
837          confirmation: null,
838          reason: "No form was found inside the page's main region, so there is nothing to submit here.",
839          nextStep: "Open the page that carries the lead form, call optimizely-request-demo-lead-form, then call this tool.",
840          summary: "No lead form is present on this page."
841        });
842      }
843
844      var container = form.parentElement || document.querySelector("main") || document.body;
845      var beforeText = txt(container);
846      var urlBefore = location.href;
847
848      var btn = null;
849      var buttons = form.querySelectorAll("button");
850      var countryInput = document.querySelector(SEL.country);
851      var comboRoot = countryInput && countryInput.closest ? countryInput.closest("div") : null;
852      for (var bi = buttons.length - 1; bi >= 0; bi--) {
853        var b = buttons[bi];
854        if (comboRoot && comboRoot.contains(b)) continue;
855        if (b === document.querySelector(SEL.countryToggle)) continue;
856        if (b.disabled) continue;
857        if (!visible(b)) continue;
858        if (!txt(b)) continue;
859        btn = b;
860        break;
861      }
862      if (!btn) btn = document.querySelector(SEL.submit);
863      if (!btn) {
864        return out({
865          status: "error",
866          action: "submit the Optimizely lead form",
867          url: location.href,
868          observed: false,
869          validationErrors: [],
870          confirmation: null,
871          reason: "The form's submit button could not be located inside 'main form'.",
872          nextStep: "Let the form finish loading, call optimizely-request-demo-lead-form again, then retry this tool.",
873          summary: "Could not find the submit button on the lead form."
874        });
875      }
876
877      var actionAttr = form.getAttribute("action");
878      if (actionAttr) {
879        var navUrl = buildUrl(actionAttr, {});
880        var navPayload = {
881          status: "navigated",
882          action: "submit the Optimizely lead form",
883          url: navUrl,
884          nextStep: "The browser is loading " + navUrl + ". Read the confirmation on the page that loads.",
885          summary: "Submitted the lead form through its native submit; the browser is navigating to the form's action URL."
886        };
887        if (form.requestSubmit) { form.requestSubmit(btn); } else { form.submit(); }
888        return out(navPayload);
889      }
890
891      try { btn.scrollIntoView({ behavior: "smooth", block: "center" }); } catch (e) {}
892      btn.click();
893
894      var deadline = Date.now() + 20000;
895      var errors = [];
896      var confirmation = "";
897      var navigated = false;
898      while (Date.now() < deadline) {
899        await sleep(400);
900        if (location.href !== urlBefore) { navigated = true; break; }
901        var liveForm = document.querySelector(FORM_SEL);
902        errors = readErrors(liveForm || container);
903        if (errors.length) break;
904        var stillThere = document.contains(form) && visible(form);
905        var nowText = txt(container);
906        if (!stillThere) {
907          confirmation = newText(beforeText, nowText) || nowText.slice(0, 400);
908          break;
909        }
910        if (nowText !== beforeText) {
911          var added = newText(beforeText, nowText);
912          if (added && /thank|thanks|received|in touch|shortly|success|contact you|reach out|we will/i.test(added)) {
913            confirmation = added;
914            break;
915          }
916        }
917      }
918
919      if (navigated) {
920        return out({
921          status: "navigated",
922          action: "submit the Optimizely lead form",
923          url: location.href,
924          nextStep: "The browser moved to a new page after the click. Read the confirmation on the page that is now loaded.",
925          summary: "The lead form was submitted and the browser navigated to " + location.href + "."
926        });
927      }
928
929      var captchaError = null;
930      var fieldErrors = [];
931      for (var ei = 0; ei < errors.length; ei++) {
932        if (/captcha|robot/i.test(errors[ei].message)) { captchaError = errors[ei].message; }
933        else { fieldErrors.push(errors[ei]); }
934      }
935
936      var payload = {
937        status: "partial",
938        action: "submit the Optimizely lead form",
939        url: location.href,
940        observed: !!(confirmation || errors.length),
941        validationErrors: fieldErrors,
942        confirmation: confirmation || null,
943        captchaError: captchaError,
944        summary: ""
945      };
946
947      if (fieldErrors.length || captchaError) {
948        payload.status = "error";
949        payload.reason = captchaError && !fieldErrors.length
950          ? "The form reported a captcha problem: " + captchaError
951          : "The form reported validation errors on " + fieldErrors.length + " field(s).";
952        payload.nextStep = "Correct the listed fields with optimizely-request-demo-lead-form, then call this tool again.";
953        payload.summary = "The lead form was not accepted; the page showed validation messages.";
954      } else if (confirmation) {
955        payload.status = "ok";
956        var liveForm2 = document.querySelector(FORM_SEL);
957        if (liveForm2) { liveForm2.style.outline = ""; liveForm2.style.outlineOffset = ""; }
958        payload.summary = "The lead form was submitted and the page showed a confirmation.";
959      } else {
960        payload.reason = "No confirmation content and no validation message appeared within 20 seconds of the click, so the outcome could not be read.";
961        payload.nextStep = "Check the page: if the form is still displayed, call optimizely-request-demo-lead-form to re-check the values and submit again.";
962        payload.summary = "Clicked submit but could not read a confirmation or an error from the page.";
963      }
964      return out(payload);
965    } catch (err) {
966      return out({
967        status: "error",
968        action: "submit the Optimizely lead form",
969        url: location.href,
970        observed: false,
971        validationErrors: [],
972        confirmation: null,
973        reason: "The submit attempt threw an error: " + String(err && err.message ? err.message : err),
974        nextStep: "Refill the form with optimizely-request-demo-lead-form and try again.",
975        summary: "Could not complete submitting the Optimizely lead form."
976      });
977    }
978  }
979});
980
981  /* ---- optimizely-search-optimizely-site, optimizely-search-optimizely-site-results 
981---- */
982mc.registerToolIf("/search", {
983  name: "optimizely-search-optimizely-site",
984  description: "Run a keyword search on the Optimizely site search page and return the matching results as structured objects: title, description excerpt, absolute URL, link title and thumbnail image, plus the total match count read from the page's own 'Showing X-Y of N results' line. Takes a keyword and an optional 1-based page number (12 results per page), carried as the search and page URL parameters. When applying the keyword or the page requires loading a new results URL the tool returns status 'navigated' with that URL, and the caller should then run optimizely-search-optimizely-site-results to read the settled results.",
985  inputSchema: {
986    type: "object",
987    properties: {
988      keyword: {
989        type: "string",
990        description: "Free-text keyword to search the site for, for example 'experimentation', 'A/B testing' or 'Snowflake'."
991      },
992      page: {
993        type: "integer",
994        minimum: 1,
995        description: "1-based results page to read (12 results per page). Defaults to 1."
996      },
997      limit: {
998        type: "integer",
999        minimum: 1,
1000        maximum: 50,
1001        description: "Maximum number of result cards to return from the page that is read. Defaults to 12."
1002      }
1003    },
1004    required: ["keyword"]
1005  },
1006  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
1007  execute: async function (args) {
1008    trackWebMCPTool("optimizely-search-optimizely-site");
1009
1010    var COUNT_SELECTOR = 'div:has(> nav[aria-label="Pagination"]) > p';
1011    var RESULTS_SELECTOR = "#SearchResults";
1012    var EXTRACTORS = [
1013      {
1014        sel: "#SearchResults div.h-full",
1015        map: { title: "h2", description: "p", url: "a @href", linkTitle: "a @title", thumbnail: "img.rounded-sm @src", thumbnailAlt: "img.rounded-sm @alt" }
1016      },
1017      {
1018        sel: '#SearchResults [class*="h-full"]',
1019        map: { title: "h2", description: "p", url: "a @href", linkTitle: "a @title", thumbnail: "img @src", thumbnailAlt: "img @alt" }
1020      },
1021      {
1022        sel: "#SearchResults a[href][title]",
1023        map: { title: "h2", description: "p", url: "@href", linkTitle: "@title", thumbnail: "img @src", thumbnailAlt: "img @alt" }
1024      }
1025    ];
1026
1027    function done(p) {
1028      return { content: [{ type: "text", text: JSON.stringify(p) }] };
1029    }
1030    function q1(sel) {
1031      try { return document.querySelector(sel); } catch (e) { return null; }
1032    }
1033    function qa(sel) {
1034      try { return Array.prototype.slice.call(document.querySelectorAll(sel)); } catch (e) { return []; }
1035    }
1036    function sleep(ms) {
1037      return new Promise(function (r) { setTimeout(r, ms); });
1038    }
1039    function urlKeyword() {
1040      var p = new URLSearchParams(location.search);
1041      return (p.get("search") || p.get("q") || "").trim();
1042    }
1043    function countElement() {
1044      var el = q1(COUNT_SELECTOR);
1045      if (el) return el;
1046      var ps = qa("main p").concat(qa("p"));
1047      for (var i = 0; i < ps.length; i++) {
1048        if (/showing\s+[\d,]+\s*-\s*[\d,]+\s+of\s+[\d,]+/i.test(ps[i].textContent || "")) return ps[i];
1049      }
1050      return null;
1051    }
1052    function countInfo() {
1053      var el = countElement();
1054      var text = el ? (el.textContent || "").replace(/\s+/g, " ").trim() : "";
1055      var total = null, from = null, to = null;
1056      var m = text.match(/of\s+([\d,]+)/i);
1057      if (m) total = parseInt(m[1].replace(/,/g, ""), 10);
1058      var r = text.match(/([\d,]+)\s*-\s*([\d,]+)/);
1059      if (r) { from = parseInt(r[1].replace(/,/g, ""), 10); to = parseInt(r[2].replace(/,/g, ""), 10); }
1060      if ((total === null || !isFinite(total)) && el) {
1061        try {
1062          var rc = readResultCount(COUNT_SELECTOR, null);
1063          total = (typeof rc === "number" && isFinite(rc)) ? rc : null;
1064        } catch (e) { total = null; }
1065      }
1066      return {
1067        element: el,
1068        text: text || null,
1069        total: (typeof total === "number" && isFinite(total)) ? total : null,
1070        from: (typeof from === "number" && isFinite(from)) ? from : null,
1071        to: (typeof to === "number" && isFinite(to)) ? to : null
1072      };
1073    }
1074    function firstTitle() {
1075      var h = q1("#SearchResults h2");
1076      return h ? (h.textContent || "").replace(/\s+/g, " ").trim() : "";
1077    }
1078    function itemCount() {
1079      for (var i = 0; i < EXTRACTORS.length; i++) {
1080        var n = qa(EXTRACTORS[i].sel).length;
1081        if (n) return n;
1082      }
1083      return 0;
1084    }
1085    function absolutize(list) {
1086      return (list || []).map(function (it) {
1087        var o = {};
1088        for (var k in it) { if (Object.prototype.hasOwnProperty.call(it, k)) o[k] = it[k]; }
1089        if (o.url) { try { o.url = new URL(o.url, location.origin).href; } catch (e) {} }
1090        if (o.thumbnail) {
1091          try {
1092            var u = new URL(o.thumbnail, location.origin);
1093            o.thumbnail = u.href;
1094            if (/cmp\.optimizely\.com$/i.test(u.hostname)) {
1095              u.searchParams.set("width", "1200");
1096              o.thumbnailWide = u.toString();
1097            }
1098          } catch (e) {}
1099        }
1100        if (o.thumbnailAlt === "") delete o.thumbnailAlt;
1101        return o;
1102      });
1103    }
1104    function highlight(el) {
1105      if (!el) return;
1106      try {
1107        el.scrollIntoView({ behavior: "smooth", block: "center" });
1108        el.style.outline = "3px solid #0037ff";
1109        el.style.outlineOffset = "2px";
1110        setTimeout(function () { try { el.style.outline = ""; el.style.outlineOffset = ""; } catch (e) {} }, 6000);
1111      } catch (e) {}
1112    }
1113    function visibleControl(sel) {
1114      var list = qa(sel);
1115      var i;
1116      for (i = 0; i < list.length; i++) {
1117        if (list[i].disabled) continue;
1118        if (list[i].offsetParent !== null || (list[i].getClientRects && list[i].getClientRects().length)) return list[i];
1119      }
1120      for (i = 0; i < list.length; i++) { if (!list[i].disabled) return list[i]; }
1121      return null;
1122    }
1123    function renderedPageNumbers() {
1124      var seen = [];
1125      qa('nav[aria-label="Pagination"] button[aria-label^="Page "]').forEach(function (b) {
1126        var v = (b.getAttribute("aria-label") || "").replace(/^Page\s+/i, "").trim();
1127        if (v && seen.indexOf(v) === -1) seen.push(v);
1128      });
1129      return seen;
1130    }
1131    async function settle(maxMs) {
1132      try { await waitForElement(RESULTS_SELECTOR, 8000); } catch (e) {}
1133      var deadline = Date.now() + (maxMs || 6000);
1134      var lastSignature = "";
1135      var stableFor = 0;
1136      while (Date.now() < deadline) {
1137        var info = countInfo();
1138        var signature = String(info.text) + "|" + itemCount() + "|" + firstTitle();
1139        if (signature === lastSignature && itemCount() > 0) {
1140          stableFor += 250;
1141          if (stableFor >= 500) break;
1142        } else {
1143          stableFor = 0;
1144          lastSignature = signature;
1145        }
1146        await sleep(250);
1147      }
1148    }
1149    async function readResults(limit) {
1150      for (var i = 0; i < EXTRACTORS.length; i++) {
1151        try {
1152          var got = await extractItems(EXTRACTORS[i].sel, EXTRACTORS[i].map, limit);
1153          if (got && got.results && got.results.length) return got;
1154        } catch (e) {}
1155      }
1156      return null;
1157    }
1158    function readPayload(keyword, page, extracted, info, appliedBy) {
1159      var results = extracted ? absolutize(extracted.results) : [];
1160      var totalFromPage = info.total;
1161      var observed = results.length > 0 || totalFromPage !== null;
1162      var resultCount = totalFromPage !== null ? totalFromPage : (results.length ? results.length : null);
1163      var p = {
1164        status: observed ? "ok" : "partial",
1165        observed: observed,
1166        action: "read the Optimizely site search results",
1167        url: location.href,
1168        keyword: keyword,
1169        page: page,
1170        appliedBy: appliedBy,
1171        resultCount: resultCount,
1172        countSource: totalFromPage !== null ? "page count element" : (results.length ? "counted result cards" : null),
1173        countText: info.text,
1174        rangeStart: info.from,
1175        rangeEnd: info.to,
1176        returnedCount: results.length,
1177        truncated: extracted ? !!extracted.truncated : false,
1178        results: results,
1179        availablePages: renderedPageNumbers(),
1180        resultsToolName: "optimizely-search-optimizely-site-results",
1181        summary: observed
1182          ? ("Search for '" + keyword + "' shows " + (resultCount === null ? "an unknown number of" : resultCount) + " matches; returned " + results.length + " result(s) from page " + page + ".")
1183          : ("Could not read any search results for '" + keyword + "' on page " + page + ".")
1184      };
1185      if (!observed) {
1186        p.reason = "The results region (" + RESULTS_SELECTOR + ") and the 'Showing X-Y of N results' count were both unreadable after waiting for the page to settle.";
1187        p.nextStep = "Reload /search?search=" + encodeURIComponent(keyword) + " and call optimizely-search-optimizely-site-results once the results list has rendered.";
1188      }
1189      return p;
1190    }
1191
1192    try {
1193      args = args || {};
1194      var keyword = typeof args.keyword === "string" ? args.keyword.trim() : "";
1195      var page = parseInt(args.page, 10);
1196      if (!page || page < 1) page = 1;
1197      var limit = parseInt(args.limit, 10);
1198      if (!limit || limit < 1) limit = 12;
1199      if (limit > 50) limit = 50;
1200
1201      if (!keyword) {
1202        return done({
1203          status: "error",
1204          action: "run a site search",
1205          url: location.href,
1206          reason: "The keyword argument was empty, so there is nothing to search for.",
1207          nextStep: "Call optimizely-search-optimizely-site again with a non-empty keyword, for example 'experimentation'.",
1208          summary: "No keyword was supplied, so no search was run."
1209        });
1210      }
1211
1212      // Outcome 5: read the URL before acting, so navigation is decided from the live page.
1213      var urlBefore = location.href;
1214      var params = new URLSearchParams(location.search);
1215      var currentKeyword = urlKeyword();
1216      var currentPage = parseInt(params.get("page") || "1", 10);
1217      if (!currentPage || currentPage < 1) currentPage = 1;
1218      var sameKeyword = currentKeyword.toLowerCase() === keyword.toLowerCase();
1219
1220      // Guard against a page number that the live result set cannot have.
1221      if (sameKeyword) {
1222        var pre = countInfo();
1223        if (pre.total !== null) {
1224          var pageSize = (pre.from !== null && pre.to !== null && pre.to >= pre.from) ? (pre.to - pre.from + 1) : 12;
1225          if (pageSize > 0) {
1226            var maxPage = Math.max(1, Math.ceil(pre.total / pageSize));
1227            if (page > maxPage) {
1228              return done({
1229                status: "error",
1230                action: "open a results page that does not exist",
1231                url: location.href,
1232                reason: "Page " + page + " was requested but the page reports " + pre.total + " results at " + pageSize + " per page, so the last page is " + maxPage + ".",
1233                availableOptions: renderedPageNumbers(),
1234                maxPage: maxPage,
1235                countText: pre.text,
1236                nextStep: "Call optimizely-search-optimizely-site again with a page between 1 and " + maxPage + ".",
1237                summary: "Page " + page + " is beyond the last page (" + maxPage + ") for '" + keyword + "'."
1238              });
1239            }
1240          }
1241        }
1242      }
1243
1244      var beforeCountText = countInfo().text;
1245      var beforeFirstTitle = firstTitle();
1246      var i;
1247
1248      // Case A: the live URL already carries this keyword and page - read it in place.
1249      if (sameKeyword && currentPage === page && q1(RESULTS_SELECTOR)) {
1250        var input = q1("#search") || q1('form input[type="search"]') || q1('input[type="search"]');
1251        if (input && page === 1 && (input.value || "").trim().toLowerCase() !== keyword.toLowerCase()) {
1252          var sel = input.id === "search" ? "#search" : 'input[type="search"]';
1253          try { await fillInput(sel, keyword); } catch (e) {}
1254          if ((input.value || "") !== keyword) {
1255            try {
1256              var d0 = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value");
1257              if (d0 && d0.set) { d0.set.call(input, keyword); } else { input.value = keyword; }
1258              input.dispatchEvent(new Event("input", { bubbles: true }));
1259              input.dispatchEvent(new Event("change", { bubbles: true }));
1260            } catch (e) {}
1261          }
1262        }
1263        await settle(7000);
1264        if (location.href !== urlBefore) {
1265          return done({
1266            status: "navigated",
1267            action: "the page moved to a new search URL while reading",
1268            url: location.href,
1269            keyword: keyword,
1270            page: page,
1271            nextStep: "Call optimizely-search-optimizely-site-results on the loaded page to read the settled results and the total count.",
1272            summary: "The search page changed URL, so the results were not read here."
1273          });
1274        }
1275        var infoA = countInfo();
1276        var extractedA = await readResults(limit);
1277        highlight(q1(RESULTS_SELECTOR) || infoA.element);
1278        return done(readPayload(keyword, page, extractedA, infoA, "already loaded URL"));
1279      }
1280
1281      // Case B1: same keyword, different page - the pagination controls are client side, try them first.
1282      if (sameKeyword && q1('nav[aria-label="Pagination"]')) {
1283        var btn = visibleControl('nav[aria-label="Pagination"] button[aria-label="Page ' + page + '"]');
1284        if (!btn && page === currentPage + 1) btn = visibleControl('nav[aria-label="Pagination"] button[aria-label="Next page"]');
1285        if (!btn && page === currentPage - 1) btn = visibleControl('nav[aria-label="Pagination"] button[aria-label="Previous page"]');
1286        if (btn) {
1287          try { btn.click(); } catch (e) {}
1288          var movedB1 = false;
1289          var deadlineB1 = Date.now() + 6000;
1290          while (Date.now() < deadlineB1) {
1291            if (location.href !== urlBefore) {
1292              return done({
1293                status: "navigated",
1294                action: "click the pagination control for page " + page,
1295                url: location.href,
1296                keyword: keyword,
1297                page: page,
1298                nextStep: "Call optimizely-search-optimizely-site-results on the loaded page to read the settled results and the total count.",
1299                summary: "Paging to page " + page + " changed the URL; the results page is loading."
1300              });
1301            }
1302            var nowInfo = countInfo();
1303            if ((nowInfo.text && nowInfo.text !== beforeCountText) || (firstTitle() && firstTitle() !== beforeFirstTitle)) { movedB1 = true; break; }
1304            await sleep(250);
1305          }
1306          if (movedB1) {
1307            await settle(6000);
1308            if (location.href !== urlBefore) {
1309              return done({
1310                status: "navigated",
1311                action: "click the pagination control for page " + page,
1312                url: location.href,
1313                keyword: keyword,
1314                page: page,
1315                nextStep: "Call optimizely-search-optimizely-site-results on the loaded page to read the settled results and the total count.",
1316                summary: "Paging to page " + page + " changed the URL; the results page is loading."
1317              });
1318            }
1319            var infoB1 = countInfo();
1320            var extractedB1 = await readResults(limit);
1321            highlight(q1(RESULTS_SELECTOR) || infoB1.element);
1322            return done(readPayload(keyword, page, extractedB1, infoB1, "clicked the pagination control"));
1323          }
1324        }
1325      }
1326
1327      // Case B2: new keyword - type it into the page's own search input and submit it in place.
1328      if (!sameKeyword) {
1329        var box = q1("#search") || q1('form input[type="search"]') || q1('input[type="search"]');
1330        if (box) {
1331          var boxSel = box.id === "search" ? "#search" : 'input[type="search"]';
1332          try { box.focus(); } catch (e) {}
1333          try { await fillInput(boxSel, keyword); } catch (e) {}
1334          if ((box.value || "") !== keyword) {
1335            try {
1336              var d1 = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value");
1337              if (d1 && d1.set) { d1.set.call(box, keyword); } else { box.value = keyword; }
1338              box.dispatchEvent(new Event("input", { bubbles: true }));
1339              box.dispatchEvent(new Event("change", { bubbles: true }));
1340            } catch (e) {}
1341          }
1342          // The search form has no action attribute; keep a stray native submit from
1343          // unloading the document before this call can return its payload.
1344          try {
1345            var formEl = box.form || box.closest("form");
1346            if (formEl) {
1347              formEl.addEventListener("submit", function (ev) { ev.preventDefault(); }, { once: true, capture: true });
1348            }
1349          } catch (e) {}
1350          try {
1351            ["keydown", "keypress", "keyup"].forEach(function (t) {
1352              box.dispatchEvent(new KeyboardEvent(t, { key: "Enter", code: "Enter", keyCode: 13, which: 13, bubbles: true, cancelable: true }));
1353            });
1354          } catch (e) {}
1355
1356          var appliedInPage = false;
1357          var deadlineB2 = Date.now() + 3000;
1358          while (Date.now() < deadlineB2) {
1359            if (location.href !== urlBefore) {
1360              return done({
1361                status: "navigated",
1362                action: "submit the keyword in the site search box",
1363                url: location.href,
1364                keyword: keyword,
1365                page: page,
1366                nextStep: "Call optimizely-search-optimizely-site-results on the loaded page to read the settled results and the total count.",
1367                summary: "Submitting '" + keyword + "' moved the browser to a new search URL, which is now loading."
1368              });
1369            }
1370            var live = countInfo();
1371            if ((live.text && live.text !== beforeCountText) || (firstTitle() && firstTitle() !== beforeFirstTitle)) { appliedInPage = true; break; }
1372            await sleep(250);
1373          }
1374
1375          if (appliedInPage && page === 1) {
1376            await settle(6000);
1377            if (location.href !== urlBefore) {
1378              return done({
1379                status: "navigated",
1380                action: "submit the keyword in the site search box",
1381                url: location.href,
1382                keyword: keyword,
1383                page: page,
1384                nextStep: "Call optimizely-search-optimizely-site-results on the loaded page to read the settled results and the total count.",
1385                summary: "The search page changed URL while settling; the results page is loading."
1386              });
1387            }
1388            var infoB2 = countInfo();
1389            var extractedB2 = await readResults(limit);
1390            highlight(q1(RESULTS_SELECTOR) || infoB2.element);
1391            return done(readPayload(keyword, page, extractedB2, infoB2, "typed the keyword into the search box"));
1392          }
1393        }
1394      }
1395
1396      // Fallback: load the results URL. Payload is built first and nothing is awaited after the navigation.
1397      var target = buildUrl("/search?search={keyword}&page={page}", { keyword: keyword, page: page > 1 ? String(page) : "" });
1398      var absolute = target;
1399      try { absolute = new URL(target, location.href).href; } catch (e) {}
1400      var navPayload = {
1401        status: "navigated",
1402        action: "load the search results URL for the keyword",
1403        url: absolute,
1404        keyword: keyword,
1405        page: page,
1406        limit: limit,
1407        resultsToolName: "optimizely-search-optimizely-site-results",
1408        nextStep: "The search results page is now loading; call optimizely-search-optimizely-site-results on it to read the settled results and the total count.",
1409        summary: "Loading Optimizely site search for '" + keyword + "' (page " + page + ")."
1410      };
1411      var navText = JSON.stringify(navPayload);
1412      location.assign(target);
1413      return { content: [{ type: "text", text: navText }] };
1414    } catch (err) {
1415      return done({
1416        status: "error",
1417        action: "run a site search",
1418        url: location.href,
1419        reason: "The search tool failed while driving the page: " + (err && err.message ? err.message : String(err)),
1420        nextStep: "Reload /search and try optimizely-search-optimizely-site again, or call optimizely-search-optimizely-site-results to read whatever is currently listed.",
1421        summary: "The site search could not be completed."
1422      });
1423    }
1424  }
1425});
1426
1427mc.registerToolIf("/search", {
1428  name: "optimizely-search-optimizely-site-results",
1429  description: "Read the Optimizely search results page that is currently loaded and return its results as structured objects: title, description excerpt, absolute URL, link title and thumbnail image, together with the total match count parsed from the page's own 'Showing X-Y of N results' line, the keyword and page number taken from the URL, and the page numbers offered by the pagination controls. Scrolls to and highlights the results region it read.",
1430  inputSchema: {
1431    type: "object",
1432    properties: {
1433      limit: {
1434        type: "integer",
1435        minimum: 1,
1436        maximum: 50,
1437        description: "Maximum number of result cards to return from the current page. Defaults to 12."
1438      }
1439    },
1440    required: []
1441  },
1442  annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
1443  execute: async function (args) {
1444    trackWebMCPTool("optimizely-search-optimizely-site-results");
1445
1446    var COUNT_SELECTOR = 'div:has(> nav[aria-label="Pagination"]) > p';
1447    var RESULTS_SELECTOR = "#SearchResults";
1448    var EXTRACTORS = [
1449      {
1450        sel: "#SearchResults div.h-full",
1451        map: { title: "h2", description: "p", url: "a @href", linkTitle: "a @title", thumbnail: "img.rounded-sm @src", thumbnailAlt: "img.rounded-sm @alt" }
1452      },
1453      {
1454        sel: '#SearchResults [class*="h-full"]',
1455        map: { title: "h2", description: "p", url: "a @href", linkTitle: "a @title", thumbnail: "img @src", thumbnailAlt: "img @alt" }
1456      },
1457      {
1458        sel: "#SearchResults a[href][title]",
1459        map: { title: "h2", description: "p", url: "@href", linkTitle: "@title", thumbnail: "img @src", thumbnailAlt: "img @alt" }
1460      }
1461    ];
1462
1463    function done(p) {
1464      return { content: [{ type: "text", text: JSON.stringify(p) }] };
1465    }
1466    function q1(sel) {
1467      try { return document.querySelector(sel); } catch (e) { return null; }
1468    }
1469    function qa(sel) {
1470      try { return Array.prototype.slice.call(document.querySelectorAll(sel)); } catch (e) { return []; }
1471    }
1472    function sleep(ms) {
1473      return new Promise(function (r) { setTimeout(r, ms); });
1474    }
1475    function countElement() {
1476      var el = q1(COUNT_SELECTOR);
1477      if (el) return el;
1478      var ps = qa("main p").concat(qa("p"));
1479      for (var i = 0; i < ps.length; i++) {
1480        if (/showing\s+[\d,]+\s*-\s*[\d,]+\s+of\s+[\d,]+/i.test(ps[i].textContent || "")) return ps[i];
1481      }
1482      return null;
1483    }
1484    function countInfo() {
1485      var el = countElement();
1486      var text = el ? (el.textContent || "").replace(/\s+/g, " ").trim() : "";
1487      var total = null, from = null, to = null;
1488      var m = text.match(/of\s+([\d,]+)/i);
1489      if (m) total = parseInt(m[1].replace(/,/g, ""), 10);
1490      var r = text.match(/([\d,]+)\s*-\s*([\d,]+)/);
1491      if (r) { from = parseInt(r[1].replace(/,/g, ""), 10); to = parseInt(r[2].replace(/,/g, ""), 10); }
1492      if ((total === null || !isFinite(total)) && el) {
1493        try {
1494          var rc = readResultCount(COUNT_SELECTOR, null);
1495          total = (typeof rc === "number" && isFinite(rc)) ? rc : null;
1496        } catch (e) { total = null; }
1497      }
1498      return {
1499        element: el,
1500        text: text || null,
1501        total: (typeof total === "number" && isFinite(total)) ? total : null,
1502        from: (typeof from === "number" && isFinite(from)) ? from : null,
1503        to: (typeof to === "number" && isFinite(to)) ? to : null
1504      };
1505    }
1506    function firstTitle() {
1507      var h = q1("#SearchResults h2");
1508      return h ? (h.textContent || "").replace(/\s+/g, " ").trim() : "";
1509    }
1510    function itemCount() {
1511      for (var i = 0; i < EXTRACTORS.length; i++) {
1512        var n = qa(EXTRACTORS[i].sel).length;
1513        if (n) return n;
1514      }
1515      return 0;
1516    }
1517    function absolutize(list) {
1518      return (list || []).map(function (it) {
1519        var o = {};
1520        for (var k in it) { if (Object.prototype.hasOwnProperty.call(it, k)) o[k] = it[k]; }
1521        if (o.url) { try { o.url = new URL(o.url, location.origin).href; } catch (e) {} }
1522        if (o.thumbnail) {
1523          try {
1524            var u = new URL(o.thumbnail, location.origin);
1525            o.thumbnail = u.href;
1526            if (/cmp\.optimizely\.com$/i.test(u.hostname)) {
1527              u.searchParams.set("width", "1200");
1528              o.thumbnailWide = u.toString();
1529            }
1530          } catch (e) {}
1531        }
1532        if (o.thumbnailAlt === "") delete o.thumbnailAlt;
1533        return o;
1534      });
1535    }
1536    function highlight(el) {
1537      if (!el) return;
1538      try {
1539        el.scrollIntoView({ behavior: "smooth", block: "center" });
1540        el.style.outline = "3px solid #0037ff";
1541        el.style.outlineOffset = "2px";
1542        setTimeout(function () { try { el.style.outline = ""; el.style.outlineOffset = ""; } catch (e) {} }, 6000);
1543      } catch (e) {}
1544    }
1545    function renderedPageNumbers() {
1546      var seen = [];
1547      qa('nav[aria-label="Pagination"] button[aria-label^="Page "]').forEach(function (b) {
1548        var v = (b.getAttribute("aria-label") || "").replace(/^Page\s+/i, "").trim();
1549        if (v && seen.indexOf(v) === -1) seen.push(v);
1550      });
1551      return seen;
1552    }
1553    function nextEnabled() {
1554      var list = qa('nav[aria-label="Pagination"] button[aria-label="Next page"]');
1555      for (var i = 0; i < list.length; i++) { if (!list[i].disabled) return true; }
1556      return false;
1557    }
1558
1559    try {
1560      args = args || {};
1561      var limit = parseInt(args.limit, 10);
1562      if (!limit || limit < 1) limit = 12;
1563      if (limit > 50) limit = 50;
1564
1565      var params = new URLSearchParams(location.search);
1566      var keyword = (params.get("search") || params.get("q") || "").trim();
1567      var page = parseInt(params.get("page") || "1", 10);
1568      if (!page || page < 1) page = 1;
1569
1570      try { await waitForElement(RESULTS_SELECTOR, 10000); } catch (e) {}
1571
1572      var deadline = Date.now() + 8000;
1573      var lastSignature = "";
1574      var stableFor = 0;
1575      while (Date.now() < deadline) {
1576        var info0 = countInfo();
1577        var signature = String(info0.text) + "|" + itemCount() + "|" + firstTitle();
1578        if (signature === lastSignature && itemCount() > 0) {
1579          stableFor += 250;
1580          if (stableFor >= 500) break;
1581        } else {
1582          stableFor = 0;
1583          lastSignature = signature;
1584        }
1585        await sleep(250);
1586      }
1587
1588      var info = countInfo();
1589      var extracted = null;
1590      for (var i = 0; i < EXTRACTORS.length && !extracted; i++) {
1591        try {
1592          var got = await extractItems(EXTRACTORS[i].sel, EXTRACTORS[i].map, limit);
1593          if (got && got.results && got.results.length) extracted = got;
1594        } catch (e) {}
1595      }
1596
1597      var results = extracted ? absolutize(extracted.results) : [];
1598      var observed = results.length >
1598 0 || info.total !== null;
1599      var resultCount = info.total !== null ? info.total : (results.length ? results.length : null);
1600      highlight(q1(RESULTS_SELECTOR) || info.element);
1601
1602      var payload = {
1603        status: observed ? "ok" : "partial",
1604        observed: observed,
1605        action: "read the settled Optimizely search results page",
1606        url: location.href,
1607        keyword: keyword || null,
1608        page: page,
1609        resultCount: resultCount,
1610        countSource: info.total !== null ? "page count element" : (results.length ? "counted result cards" : null),
1611        countText: info.text,
1612        rangeStart: info.from,
1613        rangeEnd: info.to,
1614        returnedCount: results.length,
1615        truncated: extracted ? !!extracted.truncated : false,
1616        results: results,
1617        availablePages: renderedPageNumbers(),
1618        hasNextPage: nextEnabled(),
1619        summary: observed
1620          ? ("Read " + results.length + " result(s) from page " + page + (keyword ? " for '" + keyword + "'" : "") + "; the page reports " + (resultCount === null ? "an unknown total" : resultCount + " matches") + ".")
1621          : ("No search results could be read from this page" + (keyword ? " for '" + keyword + "'" : "") + ".")
1622      };
1623      if (!observed) {
1624        payload.reason = "Neither the results region (" + RESULTS_SELECTOR + ") nor the 'Showing X-Y of N results' count could be read after waiting for the page to settle.";
1625        payload.nextStep = "Confirm the browser is on /search with a search parameter, give the page a moment to finish loading, then call optimizely-search-optimizely-site-results again.";
1626      }
1627      return done(payload);
1628    } catch (err) {
1629      return done({
1630        status: "error",
1631        action: "read the Optimizely search results page",
1632        url: location.href,
1633        reason: "Reading the results failed: " + (err && err.message ? err.message : String(err)),
1634        nextStep: "Reload the /search URL and call optimizely-search-optimizely-site-results again.",
1635        summary: "The search results could not be read."
1636      });
1637    }
1638  }
1639});
1640
1641  /* ---- optimizely-filter-search-facets ---- */
1642mc.registerToolIf("/search", {
1643  name: "optimizely-filter-search-facets",
1644  description: "Narrows the results on the Optimizely search page (/search) by ticking or unticking named facet checkboxes in the sidebar 'Topics' and 'Products' groups, and can click the 'Clear' button next to the 'Filters (n)' label to drop every applied facet. Facet names are matched against the label text present on the page at call time, and a group's '...' button is used to reveal further values before matching. Returns the page's own result count text and total, the 'Filters (n)' label, the facet names each group currently shows as selected, the facet names each group currently lists (with any group that still hides values behind its '...' button flagged), the current URL query string, and the visible result cards with title, summary, link and image.",
1645  inputSchema: {
1646    type: "object",
1647    properties: {
1648      topics: {
1649        type: "array",
1650        items: { type: "string" },
1651        description: "Facet names in the sidebar 'Topics' group to tick, written the way they read on the page, for example 'A/B Testing' or 'Content management'. Matched case-insensitively against the facet labels present at call time."
1652      },
1653      products: {
1654        type: "array",
1655        items: { type: "string" },
1656        description: "Facet names in the sidebar 'Products' group to tick, for example 'Configured Commerce' or 'Content Management System'. Matched case-insensitively against the facet labels present at call time."
1657      },
1658      deselectTopics: {
1659        type: "array",
1660        items: { type: "string" },
1661        description: "Facet names in the 'Topics' group to untick, if they are currently applied."
1662      },
1663      deselectProducts: {
1664        type: "array",
1665        items: { type: "string" },
1666        description: "Facet names in the 'Products' group to untick, if they are currently applied."
1667      },
1668      clearFilters: {
1669        type: "boolean",
1670        description: "Click the 'Clear' button next to the 'Filters (n)' label first, remov
1670ing every applied facet before the requested facets are ticked."
1671      },
1672      limit: {
1673        type: "number",
1674        description: "Maximum number of result cards to return from the rendered page. Defaults to 12."
1675      }
1676    },
1677    required: []
1678  },
1679  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
1680  execute: async function (args) {
1681    trackWebMCPTool("optimizely-filter-search-facets");
1682
1683    var a = args || {};
1684    var ACTION = "toggle search facets and read results";
1685    var COUNT_SEL = 'main main > div > div > div:nth-of-type(2) > div:nth-of-type(2) > div > div:nth-of-type(2) > div > div > p';
1686    var ITEM_SEL = 'main main > div > div > div:nth-of-type(2) > div:nth-of-type(2) > div > div > div > div';
1687    var ITEM_SEL_FALLBACK = 'main main a[href^="/field-notes/"]';
1688    var CLEAR_SEL = 'aside > div:nth-of-type(1) > div:nth-of-type(2) > button';
1689    var FILTERS_SEL = 'aside > div:nth-of-type(1) > div:nth-of-type(1)';
1690    var GROUP_SEL = 'aside > div:nth-of-type(2) > div';
1691
1692    var startPath = location.pathname;
1693    var unloading = false;
1694    try {
1695      window.addEventListener("beforeunload", function () { unloading = true; }, { once: true });
1696    } catch (e) {}
1697
1698    function pack(p) {
1699      p.url = location.href;
1700      return { content: [{ type: "text", text: JSON.stringify(p) }] };
1701    }
1702    function txt(el) {
1703      return el ? String(el.textContent || "").replace(/\s+/g, " ").trim() : "";
1704    }
1705    function norm(s) {
1706      return String(s == null ? "" : s).toLowerCase().replace(/\s+/g, " ").trim();
1707    }
1708    function squash(s) {
1709      return norm(s).replace(/[^a-z0-9]/g, "");
1710    }
1711    function sleep(ms) {
1712      return new Promise(function (r) { setTimeout(r, ms); });
1713    }
1714    function toArray(v) {
1715      if (Array.isArray(v)) return v.filter(function (x) { return typeof x === "string" && x.trim(); });
1716      if (typeof v === "string" && v.trim()) return [v];
1717      return [];
1718    }
1719    function unique(list) {
1720      var out = [];
1721      list.forEach(function (x) { if (out.indexOf(x) === -1) out.push(x); });
1722      return out;
1723    }
1724    function isVisible(el) {
1725      if (!el) return false;
1726      var r = el.getBoundingClientRect();
1727      return !!(el.offsetParent || r.width || r.height);
1728    }
1729
1730    function findFiltersLabel() {
1731      var el = document.querySelector(FILTERS_SEL);
1732      if (el && /filters/i.test(txt(el))) return el;
1733      var aside = document.querySelector("aside");
1734      if (!aside) return el || null;
1735      var nodes = Array.prototype.slice.call(aside.querySelectorAll("div, span, p, h2, h3, h4"));
1736      var hit = null;
1737      nodes.forEach(function (n) {
1738        if (hit) return;
1739        if (/^filters\s*\(\d+\)/i.test(txt(n))) hit = n;
1740      });
1741      return hit || el || null;
1742    }
1743    function filtersCount() {
1744      var t = txt(findFiltersLabel());
1745      var m = t.match(/\((\d+)\)/);
1746      return m ? parseInt(m[1], 10) : null;
1747    }
1748    function findClearButton() {
1749      var el = document.querySelector(CLEAR_SEL);
1750      if (el && /clear/i.test(txt(el))) return el;
1751      var aside = document.querySelector("aside");
1752      if (!aside) return null;
1753      var buttons = Array.prototype.slice.call(aside.querySelectorAll("button"));
1754      var hit = null;
1755      buttons.forEach(function (b) {
1756        if (hit) return;
1757        if (/^clear(\s+all)?$/i.test(txt(b))) hit = b;
1758      });
1759      return hit || el || null;
1760    }
1761    function findCountEl() {
1762      var el = document.querySelector(COUNT_SEL);
1763      if (el && /\d/.test(txt(el))) return el;
1764      var scope = document.querySelector("main") || document.body;
1765      var nodes = Array.prototype.slice.call(scope.querySelectorAll("p, span, div"));
1766      var hit = null;
1767      nodes.forEach(function (n) {
1768        if (hit) return;
1769        if (n.children.length === 0 && /\bof\s+[\d,]+\s+results?\b/i.test(txt(n))) hit = n;
1770      });
1771      return hit || el || null;
1772    }
1773
1774    function isSelected(label, btn) {
1775      var input = label ? label.querySelector('input[type="checkbox"]') : null;
1776      if (input) return !!input.checked;
1777      var probes = [btn, label];
1778      for (var i = 0; i < probes.length; i++) {
1779        var el = probes[i];
1780        if (!el || !el.getAttribute) continue;
1781        var ac = el.getAttribute("aria-checked");
1782        if (ac === "true") return true;
1783        if (ac === "false") return false;
1784        var ap = el.getAttribute("aria-pressed");
1785        if (ap === "true") return true;
1786        if (ap === "false") return false;
1787        var ds = el.getAttribute("data-state") || el.getAttribute("data-checked") || el.getAttribute("data-selected");
1788        if (ds) {
1789          if (/^(unchecked|off|false)$/i.test(ds)) return false;
1790          if (/(checked|selected|active|on|true)/i.test(ds)) return true;
1791        }
1792        var cls = el.getAttribute("class") || "";
1793        if (/(^|[\s_-])(is-)?(checked|selected|active)([\s_-]|$)/i.test(cls)) return true;
1794      }
1795      return null;
1796    }
1797
1798    function readGroups() {
1799      var aside = document.querySelector("aside");
1800      var groups = [];
1801      if (!aside) return groups;
1802      var nodes = Array.prototype.slice.call(document.querySelectorAll(GROUP_SEL));
1803      if (!nodes.length) {
1804        nodes = Array.prototype.slice.call(aside.querySelectorAll("div")).filter(function (d) {
1805          return d.querySelector("label button");
1806        });
1807      }
1808      nodes.forEach(function (node) {
1809        var header = node.querySelector(":scope > div > div:nth-of-type(1)") || node.querySelector(":scope > div") || null;
1810        var body = node.querySelector(":scope > div > div:nth-of-type(2)") || node;
1811        var heading = header ? header.querySelector("h2, h3, h4, h5, h6") : null;
1812        if (!body.querySelector("label button") && !(heading && txt(heading))) return;
1813        var name = txt(header).replace(/\btoggle\b/gi, "").trim();
1814        if (!name) name = "Group " + (groups.length + 1);
1815        var expander = null;
1816        Array.prototype.slice.call(body.querySelectorAll(":scope > button")).forEach(function (b) {
1817          if (expander) return;
1818          var t = txt(b);
1819          if (/^\.{2,3}$/.test(t) || /^…$/.test(t) || /(show\s+)?more/i.test(t)) expander = b;
1820        });
1821        var collapse = header ? header.querySelector("button") : null;
1822        if (groups.filter(function (g) { return g.body === body; }).length) return;
1823        groups.push({ name: name, node: node, body: body, expander: expander, collapse: collapse });
1824      });
1825      return groups;
1826    }
1827
1828    function entriesOf(g) {
1829      if (!g || !g.body) return [];
1830      var labels = Array.prototype.slice.call(g.body.querySelectorAll("label")).filter(function (l) {
1831        return l.querySelector("button");
1832      });
1833      return labels.map(function (l) {
1834        var btn = l.querySelector("button");
1835        var spans = Array.prototype.slice.call(l.querySelectorAll("span")).filter(function (s) {
1836          return s.getAttribute("aria-hidden") !== "true" && txt(s);
1837        });
1838        var name = spans.length ? txt(spans[0]) : "";
1839        if (!name && btn) name = btn.getAttribute("aria-label") || btn.getAttribute("title") || "";
1840        if (!name) name = txt(l);
1841        name = name.replace(/\s*\(\d+\)\s*$/, "").trim();
1842        return { name: name, label: l, btn: btn, selected: isSelected(l, btn) };
1843      }).filter(function (e) { return e.name && e.btn; });
1844    }
1845
1846    function pickGroup(groups, kind) {
1847      var hit = null;
1848      groups.forEach(function (g) {
1849        if (hit) return;
1850        if (norm(g.name).indexOf(kind) !== -1) hit = g;
1851      });
1852      if (hit) return hit;
1853      if (kind === "topics" && groups.length >= 1) return groups[0];
1854      if (kind === "products" && groups.length >= 2) return groups[1];
1855      return null;
1856    }
1857
1858    function matchFacet(entries, requested) {
1859      var want = norm(requested);
1860      var hit = null;
1861      entries.forEach(function (e) { if (!hit && norm(e.name) === want) hit = e; });
1862      if (hit) return hit;
1863      var sq = squash(requested);
1864      entries.forEach(function (e) { if (!hit && squash(e.name) === sq) hit = e; });
1865      if (hit) return hit;
1866      var partial = entries.filter(function (e) { return norm(e.name).indexOf(want) !== -1 || want.indexOf(norm(e.name)) !== -1; });
1867      if (partial.length === 1) return partial[0];
1868      return null;
1869    }
1870
1871    function signature() {
1872      var items = document.querySelectorAll(ITEM_SEL);
1873      var first = items.length ? txt(items[0].querySelector("h2")) : "";
1874      return txt(findCountEl()) + "|" + txt(findFiltersLabel()) + "|" + items.length + "|" + first;
1875    }
1876
1877    async function settle(prevSig, timeout) {
1878      var t0 = Date.now();
1879      var changed = false;
1880      while (Date.now() - t0 < (timeout || 6000)) {
1881        await sleep(120);
1882        if (signature() !== prevSig) { changed = true; break; }
1883      }
1884      var last = signature();
1885      for (var i = 0; i < 12; i++) {
1886        await sleep(150);
1887        var s = signature();
1888        if (s === last) break;
1889        last = s;
1890      }
1891      return changed;
1892    }
1893
1894    async function ensureGroupOpen(kind) {
1895      var g = pickGroup(readGroups(), kind);
1896      if (!g) return;
1897      var open = entriesOf(g).some(function (e) { return isVisible(e.label); });
1898      if (open) return;
1899      if (g.collapse) {
1900        g.collapse.click();
1901        await sleep(400);
1902      }
1903    }
1904
1905    async function expandGroup(kind) {
1906      var g = pickGroup(readGroups(), kind);
1907      if (!g || !g.expander) return false;
1908      var before = entriesOf(g).length;
1909      g.expander.click();
1910      for (var i = 0; i < 20; i++) {
1911        await sleep(150);
1912        var g2 = pickGroup(readGroups(), kind);
1913        if (g2 && entriesOf(g2).length > before) return true;
1914      }
1915      return false;
1916    }
1917
1918    function navigatedPayload() {
1919      return {
1920        status: "navigated",
1921        action: ACTION,
1922        nextStep: "The browser left the search page while the facets were being applied. Wait for the new page to load, then call optimizely-filter-search-facets again on /search.",
1923        summary: "The page navigated away from /search before the facet results could be read."
1924      };
1925    }
1926
1927    try {
1928      try { await waitForElement("aside", 12000); } catch (e) {}
1929      var aside = document.querySelector("aside");
1930      if (!aside) {
1931        return pack({
1932          status: "error",
1933          action: ACTION,
1934          reason: "The search sidebar (aside) never appeared, so no facet checkbox could be located.",
1935          nextStep: "Reload https://www.optimizely.com/search, let it finish hydrating, then call optimizely-filter-search-facets again.",
1936          summary: "Could not find the search filter sidebar on this page."
1937        });
1938      }
1939      try { await waitForElement(COUNT_SEL, 8000); } catch (e) {}
1940
1941      try {
1942        aside.style.outline = "3px solid #0037ff";
1943        aside.scrollIntoView({ behavior: "smooth", block: "start" });
1944      } catch (e) {}
1945
1946      var groups = readGroups();
1947      if (!groups.length) {
1948        return pack({
1949          status: "error",
1950          action: ACTION,
1951          reason: "No facet groups with checkbox labels were found inside the sidebar.",
1952          nextStep: "Reload /search, confirm the Topics and Products filters are visible, then call optimizely-filter-search-facets again.",
1953          summary: "The Topics and Products facet groups were not present in the sidebar."
1954        });
1955      }
1956
1957      var requests = [];
1958      toArray(a.topics).forEach(function (n) { requests.push({ kind: "topics", name: n, desired: true }); });
1959      toArray(a.products).forEach(function (n) { requests.push({ kind: "products", name: n, desired: true }); });
1960      toArray(a.deselectTopics).forEach(function (n) { requests.push({ kind: "topics", name: n, desired: false }); });
1961      toArray(a.deselectProducts).forEach(function (n) { requests.push({ kind: "products", name: n, desired: false }); });
1962
1963      var kinds = unique(requests.map(function (r) { return r.kind; }));
1964      var missingGroups = [];
1965
1966      for (var ki = 0; ki < kinds.length; ki++) {
1967        var kind = kinds[ki];
1968        var g0 = pickGroup(readGroups(), kind);
1969        if (!g0) { missingGroups.push(kind); continue; }
1970        await ensureGroupOpen(kind);
1971        var wanted = requests.filter(function (r) { return r.kind === kind; });
1972        var gNow = pickGroup(readGroups(), kind);
1973        var ents = entriesOf(gNow);
1974        var allFound = wanted.every(function (r) { return !!matchFacet(ents, r.name); });
1975        if (!allFound) await expandGroup(kind);
1976      }
1977
1978      if (missingGroups.length) {
1979        return pack({
1980          status: "error",
1981          action: ACTION,
1982          reason: "No sidebar facet group could be resolved for: " + missingGroups.join(", ") + ". Groups found on the page: " + groups.map(function (g) { return g.name; }).join(", ") + ".",
1983          availableOptions: groups.map(function (g) { return g.name; }),
1984          nextStep: "Call optimizely-filter-search-facets again using a facet from one of the groups listed in availableOptions.",
1985          summary: "Could not resolve the requested facet group in the sidebar."
1986        });
1987      }
1988
1989      var unmatched = [];
1990      var affectedKinds = kinds.slice();
1991      requests.forEach(function (r) {
1992        var g = pickGroup(readGroups(), r.kind);
1993        var ents = entriesOf(g);
1994        if (!matchFacet(ents, r.name)) unmatched.push((r.kind === "topics" ? "Topics" : "Products") + ": " + r.name);
1995      });
1996
1997      function facetMap(kindList) {
1998        var out = {};
1999        var gs = readGroups();
2000        gs.forEach(function (g) {
2001          var k = norm(g.name).indexOf("topic") !== -1 ? "topics" : (norm(g.name).indexOf("product") !== -1 ? "products" : null);
2002          if (kindList && k && kindList.indexOf(k) === -1) return;
2003          out[g.name] = entriesOf(g).map(function (e) { return e.name; });
2004        });
2005        return out;
2006      }
2007
2008      if (unmatched.length) {
2009        var avail = [];
2010        var map = facetMap(affectedKinds);
2011        Object.keys(map).forEach(function (k) { avail = avail.concat(map[k]); });
2012        return pack({
2013          status: "error",
2014          action: ACTION,
2015          reason: "These facet names are not among the checkboxes currently on the page: " + unmatched.join(", ") + ".",
2016          availableOptions: unique(avail),
2017          availableFacets: facetMap(null),
2018          nextStep: "Call optimizely-filter-search-facets again with a facet name taken from availableOptions.",
2019          summary: "One or more requested facet names do not exist in the sidebar right now."
2020        });
2021      }
2022
2023      var changes = [];
2024      var cleared = false;
2025
2026      if (a.clearFilters === true) {
2027        var clearBtn = findClearButton();
2028        if (!clearBtn) {
2029          changes.push({ group: "all", facet: "Clear", requested: "cleared", applied: false, verified: false, note: "The Clear button next to the Filters label could not be located." });
2030        } else {
2031          var sigC = signature();
2032          clearBtn.click();
2033          await settle(sigC, 8000);
2034          cleared = true;
2035          if (unloading || location.pathname !== startPath) return pack(navigatedPayload());
2036          changes.push({ group: "all", facet: "Clear", requested: "cleared", applied: true, verified: filtersCount() === 0 || filtersCount() === null, appliedFilterCount: filtersCount() });
2037        }
2038      }
2039
2040      for (var ri = 0; ri < requests.length; ri++) {
2041        var req = requests[ri];
2042        var groupNow = pickGroup(readGroups(), req.kind);
2043        var groupLabel = groupNow ? groupNow.name : (req.kind === "topics" ? "Topics" : "Products");
2044        var entry = groupNow ? matchFacet(entriesOf(groupNow), req.name) : null;
2045        if (!entry) {
2046          changes.push({ group: groupLabel, facet: req.name, requested: req.desired ? "selected" : "deselected", applied: false, verified: false, note: "The facet row disappeared from the sidebar before it could be clicked." });
2047          continue;
2048        }
2049        if (entry.selected === req.desired) {
2050          changes.push({ group: groupLabel, facet: entry.name, requested: req.desired ? "selected" : "deselected", applied: false, verified: true, state: req.desired ? "selected" : "not selected", note: "Already in the requested state." });
2051          continue;
2052        }
2053
2054        var before = filtersCount();
2055        var sig = signature();
2056        entry.btn.click();
2057        await settle(sig, 8000);
2058        if (unloading || location.pathname !== startPath) return pack(navigatedPayload());
2059
2060        var groupAfter = pickGroup(readGroups(), req.kind);
2061        var entryAfter = groupAfter ? matchFacet(entriesOf(groupAfter), entry.name) : null;
2062        var state = entryAfter ? entryAfter.selected : null;
2063        var after = filtersCount();
2064        if (state === null && before !== null && after !== null && after !== before) {
2065          state = after > before;
2066        }
2067
2068        if (state !== null && state !== req.desired) {
2069          var sig2 = signature();
2070          if (entryAfter && entryAfter.btn) {
2071            entryAfter.btn.click();
2072            await settle(sig2, 8000);
2073            if (unloading || location.pathname !== startPath) return pack(navigatedPayload());
2074            var groupFinal = pickGroup(readGroups(), req.kind);
2075            var entryFinal = groupFinal ? matchFacet(entriesOf(groupFinal), entry.name) : null;
2076            state = entryFinal ? entryFinal.selected : null;
2077            var after2 = filtersCount();
2078            if (state === null && after !== null && after2 !== null && after2 !== after) state = after2 > after;
2079          }
2080        }
2081
2082        changes.push({
2083          group: groupLabel,
2084          facet: entry.name,
2085          requested: req.desired ? "selected" : "deselected",
2086          applied: true,
2087          verified: state === req.desired,
2088          state: state === null ? "unknown" : (state ? "selected" : "not selected"),
2089          appliedFilterCount: filtersCount()
2090        });
2091      }
2092
2093      await settle(signature(), 1200);
2094      if (unloading || location.pathname !== startPath) return pack(navigatedPayload());
2095
2096      var finalGroups = readGroups();
2097      var selectedFacets = {};
2098      var availableFacets = {};
2099      var hiddenOptionsRemain = [];
2100      var anyStateReadable = false;
2101      finalGroups.forEach(function (g) {
2102        var ents = entriesOf(g);
2103        availableFacets[g.name] = ents.length ? ents.map(function (e) { return e.name; }) : null;
2104        var known = ents.filter(function (e) { return e.selected !== null; });
2105        if (known.length) {
2106          anyStateReadable = true;
2107          selectedFacets[g.name] = ents.filter(function (e) { return e.selected === true; }).map(function (e) { return e.name; });
2108        } else {
2109          selectedFacets[g.name] = null;
2110        }
2111        if (g.expander || !ents.length) hiddenOptionsRemain.push(g.name);
2112      });
2113
2114      var countEl = findCountEl();
2115      var countText = countEl ? txt(countEl) : null;
2116      var total = null;
2117      if (countText) {
2118        var m = countText.replace(/,/g, "").match(/of\s+(\d+)/i);
2119        if (m) total = parseInt(m[1], 10);
2120      }
2121      if (total === null) {
2122        try {
2123          var rc = readResultCount(COUNT_SEL, null);
2124          if (typeof rc === "number" && !isNaN(rc)) total = rc;
2125        } catch (e) {}
2126      }
2127
2128      var limit = typeof a.limit === "number" && a.limit > 0 ? Math.min(Math.floor(a.limit), 50) : 12;
2129      var extracted = null;
2130      try {
2131        extracted = await extractItems(ITEM_SEL, { title: "h2", summary: "p", url: "a @href", image: "img @src" }, limit);
2132      } catch (e) { extracted = null; }
2133      var results = extracted && Array.isArray(extracted.results) ? extracted.results : [];
2134      results = results.filter(function (r) { return r && (r.title || r.url); });
2135      if (!results.length) {
2136        try {
2137          var alt = await extractItems(ITEM_SEL_FALLBACK, { title: "h2", summary: "p", url: "@href", image: "img @src" }, limit);
2138          if (alt && Array.isArray(alt.results)) {
2139            results = alt.results.filter(function (r) { return r && (r.title || r.url); });
2140            extracted = alt;
2141          }
2142        } catch (e) {}
2143      }
2144      var seen = {};
2145      results = results.filter(function (r) {
2146        var key = (r.url || "") + "|" + (r.title || "");
2147        if (seen[key]) return false;
2148        seen[key] = true;
2149        return true;
2150      });
2151
2152      try {
2153        var firstItem = document.querySelector(ITEM_SEL) || document.querySelector(ITEM_SEL_FALLBACK);
2154        var region = firstItem ? (firstItem.parentElement || firstItem) : countEl;
2155        if (region && region.style) {
2156          region.style.outline = "3px solid #0037ff";
2157          region.scrollIntoView({ behavior: "smooth", block: "center" });
2158        }
2159      } catch (e) {}
2160
2161      var filtersText = txt(findFiltersLabel()) || null;
2162      var appliedFilterCount = filtersCount();
2163      var observed = total !== null || results.length > 0;
2164      var unverified = changes.filter(function (c) { return c.applied && !c.verified; });
2165
2166      var payload = {
2167        status: "ok",
2168        action: ACTION,
2169        observed: observed,
2170        clearedFilters: cleared,
2171        changes: changes,
2172        filtersLabel: filtersText,
2173        appliedFilterCount: appliedFilterCount,
2174        selectedFacets: selectedFacets,
2175        facetStateReadable: anyStateReadable,
2176        availableFacets: availableFacets,
2177        groupsWithMoreOptionsHidden: hiddenOptionsRemain,
2178        resultCountText: countText,
2179        resultCount: total,
2180        renderedCount: results.length,
2181        truncated: extracted && typeof extracted.truncated === "boolean" ? extracted.truncated : (total !== null && results.length < total),
2182        results: results,
2183        urlQuery: location.search || "",
2184        summary: ""
2185      };
2186
2187      if (!observed) {
2188        payload.status = "partial";
2189        payload.reason = "The result count element and the result cards could not be read after the facets were toggled, so no result data was observed.";
2190        payload.nextStep = "Wait for the search results to finish rendering and call optimizely-filter-search-facets again to read the count and cards.";
2191        payload.summary = "Facet changes were attempted but no result count or result cards could be read from the page.";
2192      } else if (unverified.length) {
2193        payload.status = "partial";
2194        payload.reason = "These facet toggles were clicked but their selected state could not be confirmed from the page: " + unverified.map(function (c) { return c.group + " / " + c.facet; }).join(", ") + ".";
2195        payload.nextStep = "Check selectedFacets and filtersLabel, and call optimizely-filter-search-facets again if the facet is not applied as expected.";
2196        payload.summary = "Applied the facet clicks and read " + (total === null ? results.length + " rendered" : total) + " results, but could not confirm every checkbox state.";
2197      } else {
2198        var appliedNames = [];
2199        Object.keys(selectedFacets).forEach(function (k) {
2200          if (Array.isArray(selectedFacets[k]) && selectedFacets[k].length) {
2201            appliedNames.push(k + ": " + selectedFacets[k].join(", "));
2202          }
2203        });
2204        payload.summary = (cleared ? "Cleared the applied facets and " : "") +
2205          (total === null
2206            ? "read " + results.length + " result cards from the search page"
2207            : "read " + total + " results (" + results.length + " cards on this page)") +
2208          (filtersText ? " with " + filtersText : "") +
2209          (appliedNames.length ? " - " + appliedNames.join("; ") : "") + ".";
2210      }
2211
2212      return pack(payload);
2213    } catch (err) {
2214      return pack({
2215        status: "error",
2216        action: ACTION,
2217        reason: "The facet interaction failed: " + (err && err.message ? err.message : String(err)),
2218        nextStep: "Reload https://www.optimizely.com/search and call optimizely-filter-search-facets again.",
2219        summary: "Could not apply the requested search facets because the page interaction failed."
2220      });
2221    }
2222  }
2223});
2224
2225  /* ---- optimizely-search-connectors-directory ---- */
2226mc.registerToolIf("/connectors", {
2227  name: "optimizely-search-connectors-directory",
2228  description: "Search and filter the connectors directory listing on /connectors. A keyword or a category facet is applied through the listing's own URL parameters, which reloads the page: those calls return status navigated, and optimizely-extract-connector-listing reads the settled results. The Optimizely certified switch and the pagination are click-driven and are applied in place, returning the listing result count, the connector cards then rendered (name, description, the full Works with category list, partner and detail page URL), the filters that ended up applied and the category labels currently available on the page. Category labels are read off the page at call time; call once with no arguments to see them in availableCategories.",
2229  inputSchema: {
2230    type: "object",
2231    properties: {
2232      keyword: {
2233        type: "string",
2234        description: "Text to type into the sidebar Refine by search field, for example wordpress, salesforce or amazon. Pass an empty string to clear the search box."
2235      },
2236      categories: {
2237        type: "array",
2238        items: { type: "string" },
2239        description: "Category facet labels to tick, spelled as they appear in the sidebar Categories list. Call once without categories and read availableCategories to see the labels currently on the page."
2240      },
2241      certifiedOnly: {
2242        type: "boolean",
2243        description: "Set the Optimizely certified switch on (true) or off (false)."
2244      },
2245      page: {
2246        type: "integer",
2247        description: "Results page to open using the listing pagination buttons, for example 2."
2248      },
2249      clearExistingFilters: {
2250        type: "boolean",
2251        description: "Click the sidebar Clear button before applying the requested filters."
2252      },
2253      limit: {
2254        type: "integer",
2255        description: "Maximum number of connector cards to return. Defaults to 8, the number the listing renders per page."
2256      }
2257    },
2258    additionalProperties: false
2259  },
2260  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
2261  execute: async function (args) {
2262    trackWebMCPTool("optimizely-search-connectors-directory");
2263
2264    var ACTION = "search and filter the connectors directory";
2265    var LIST = "#ListingResults";
2266    var ITEM = "#ListingResults div.grid > div.h-full";
2267    var COUNT = "#ListingResults p.text-body-xxs";
2268    var CHIP = "span.font-overline";
2269    var FIELDS = {
2270      url: "a.group @href",
2271      name: "h3",
2272      description: "p",
2273      categories: "span.font-overline",
2274      partner: "span.text-body-s"
2275    };
2276
2277    var a = args || {};
2278    var startPath = location.pathname;
2279
2280    function out(p) {
2281      return { content: [{ type: "text", text: JSON.stringify(p) }] };
2282    }
2283    function sleep(ms) {
2284      return new Promise(function (r) { setTimeout(r, ms); });
2285    }
2286    function txt(el) {
2287      return el ? (el.textContent || "").replace(/\s+/g, " ").trim() : "";
2288    }
2289    function visible(el) {
2290      return !!(el && (el.offsetWidth || el.offsetHeight || (el.getClientRects && el.getClientRects().length)));
2291    }
2292    function isOn(el) {
2293      if (!el) return false;
2294      var s = el.getAttribute("aria-checked") || el.getAttribute("data-state") || "";
2295      return s === "true" || s === "checked";
2296    }
2297    function asideRoot() {
2298      return document.querySelector("aside") || document.body || document;
2299    }
2300    function sig() {
2301      var c = document.querySelector(COUNT);
2302      var cards = document.querySelectorAll(ITEM);
2303      var first = cards.length ? txt(cards[0]).slice(0, 90) : "";
2304      return txt(c) + "||" + cards.length + "||" + first + "||" + location.search;
2305    }
2306    async function settle(before, timeout) {
2307      var deadline = Date.now() + (timeout || 5000);
2308      while (Date.now() < deadline) {
2309        await sleep(150);
2310        if (location.pathname !== startPath) return true;
2311        if (sig() !== before) { await sleep(500); return true; }
2312      }
2313      await sleep(300);
2314      return false;
2315    }
2316    function navigatedAway() {
2317      return location.pathname !== startPath;
2318    }
2319    function navPayload() {
2320      return out({
2321        status: "navigated",
2322        action: ACTION,
2323        url: location.href,
2324        nextStep: "Wait for the new document to finish loading, then call optimizely-search-connectors-directory again once a /connectors listing page is open.",
2325        summary: "The interaction moved the browser to " + location.href + ", so the connectors listing was not read here."
2326      });
2327    }
2328    function reactSet(el, v) {
2329      var d = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value");
2330      if (d && d.set) { d.set.call(el, v); } else { el.value = v; }
2331      el.dispatchEvent(new Event("input", { bubbles: true }));
2332      el.dispatchEvent(new Event("change", { bubbles: true }));
2333    }
2334    function categoryControls() {
2335      var root = asideRoot();
2336      var found = [];
2337      var seen = [];
2338      var labels = root.querySelectorAll("label");
2339      var i;
2340      for (i = 0; i < labels.length; i++) {
2341        var lb = labels[i];
2342        var btn = lb.querySelector('button[role="checkbox"]');
2343        if (!btn) continue;
2344        var inp = lb.querySelector('input[type="checkbox"]');
2345        var nm = (inp && inp.getAttribute("name")) || btn.getAttribute("aria-label") || txt(lb);
2346        nm = (nm || "").replace(/\s+/g, " ").trim();
2347        if (!nm) continue;
2348        found.push({ name: nm, button: btn });
2349        seen.push(btn);
2350      }
2351      var loose = root.querySelectorAll('button[role="checkbox"]');
2352      for (i = 0; i < loose.length; i++) {
2353        if (seen.indexOf(loose[i]) !== -1) continue;
2354        var nm2 = loose[i].getAttribute("aria-label") || loose[i].getAttribute("title") || txt(loose[i].parentElement);
2355        nm2 = (nm2 || "").replace(/\s+/g, " ").trim();
2356        if (!nm2) continue;
2357        found.push({ name: nm2, button: loose[i] });
2358      }
2359      return found;
2360    }
2361    async function expandCategories() {
2362      var root = asideRoot();
2363      var containers = root.querySelectorAll('div[id^="radix-"]');
2364      for (var i = 0; i < containers.length; i++) {
2365        var c = containers[i];
2366        if (!c.querySelector('button[role="checkbox"]')) continue;
2367        var kids = c.children || [];
2368        for (var k = 0; k < kids.length; k++) {
2369          var b = kids[k];
2370          if (!b || b.tagName !== "BUTTON") continue;
2371          if (b.getAttribute("aria-expanded") !== null) continue;
2372          if (/toggle/i.test(txt(b))) continue;
2373          if (!visible(b)) continue;
2374          var before = categoryControls().length;
2375          b.click();
2376          await sleep(450);
2377          return categoryControls().length > before;
2378        }
2379      }
2380      return false;
2381    }
2382    function certifiedSwitch() {
2383      return asideRoot().querySelector('button[role="switch"]') || document.querySelector('button[role="switch"]');
2384    }
2385    function clearButton() {
2386      var btns = asideRoot().querySelectorAll("button");
2387      for (var i = 0; i < btns.length; i++) {
2388        var t = txt(btns[i]);
2389        if (/^clear( all)?$/i.test(t) || /^reset filters$/i.test(t)) return btns[i];
2390      }
2391      return null;
2392    }
2393
2394    try {
2395      try { await waitForElement(LIST, 8000); } catch (e) {}
2396
2397      var limit = (typeof a.limit === "number" && a.limit > 0) ? Math.min(Math.floor(a.limit), 48) : 8;
2398
2399      var searchSel = null;
2400      var candidates = ['aside form input[type="search"]', 'form input[type="search"]', 'input[type="search"]'];
2401      for (var ci = 0; ci < candidates.length; ci++) {
2402        if (document.querySelector(candidates[ci])) { searchSel = candidates[ci]; break; }
2403      }
2404
2405      if (typeof a.keyword === "string" && !searchSel) {
2406        return out({
2407          status: "error",
2408          action: ACTION,
2409          url: location.href,
2410          reason: "No search input could be found in the connectors sidebar, so the keyword could not be typed.",
2411          nextStep: "Confirm a /connectors listing page is open and fully loaded, then call this tool again, or filter by category only.",
2412          summary: "The Refine by search field was not present, so the keyword was not applied."
2413        });
2414      }
2415
2416      if (a.clearExistingFilters === true) {
2417        var cb = clearButton();
2418        if (cb) {
2419          var s0 = sig();
2420          cb.click();
2421          await settle(s0, 5000);
2422          if (navigatedAway()) return navPayload();
2423        }
2424      }
2425
2426      try { await expandCategories(); } catch (e) {}
2427      if (navigatedAway()) return navPayload();
2428
2429      var controls = categoryControls();
2430      var available = controls.map(function (c) { return c.name; });
2431
2432      var wanted = [];
2433      if (Array.isArray(a.categories)) {
2434        wanted = a.categories.filter(function (v) { return typeof v === "string" && v.trim() !== ""; });
2435      } else if (typeof a.categories === "string" && a.categories.trim() !== "") {
2436        wanted = [a.categories];
2437      }
2438
2439      var resolved = [];
2440      var unknown = [];
2441      for (var wi = 0; wi < wanted.length; wi++) {
2442        var want = wanted[wi].replace(/\s+/g, " ").trim().toLowerCase();
2443        var hit = null;
2444        for (var mi = 0; mi < controls.length; mi++) {
2445          if (controls[mi].name.toLowerCase() === want) { hit = controls[mi]; break; }
2446        }
2447        if (!hit) {
2448          for (var mj = 0; mj < controls.length; mj++) {
2449            if (controls[mj].name.toLowerCase().indexOf(want) !== -1) { hit = controls[mj]; break; }
2450          }
2451        }
2452        if (hit) { resolved.push(hit); } else { unknown.push(wanted[wi]); }
2453      }
2454
2455      if (unknown.length) {
2456        return out({
2457          status: "error",
2458          action: ACTION,
2459          url: location.href,
2460          reason: "These category labels are not on the page right now: " + unknown.join(", ") + ".",
2461          availableOptions: available,
2462          nextStep: "Call the tool again with one of the labels listed in availableOptions, or with no categories to search across all of them.",
2463          summary: "The requested category filter was not found in the sidebar, so nothing was applied."
2464        });
2465      }
2466
2467      var sigBefore = sig();
2468      var didRequest = false;
2469
2470      // Keyword and category facets both go through the listing's own URL
2471      // parameters rather than through the sidebar controls.
2472      //
2473      //   - The Refine by search field applies on submit, not on input, so
2474      //     typing into it leaves the listing on its previous result set. It
2475      //     carries no name attribute, so a scripted form submit posts an empty
2476      //     query and reloads unfiltered.
2477      //   - Ticking a category facet does nothing while a search is applied —
2478      //     a real human click fails the same way, so it is the page's own
2479      //     behaviour, not a scripting artefact.
2480      //
2481      // ?search= and a comma-joined ?product= apply both in any state, which is
2482      // how the listing encodes them itself. This mirrors the partner directory
2483      // and agent directory tools, which also drive their filters by URL.
2484      var wantsKeyword = (typeof a.keyword === "string");
2485      var wantsCategories = (resolved.length > 0);
2486
2487      if (wantsKeyword || wantsCategories) {
2488        var target = new URL(location.href);
2489
2490        if (wantsKeyword) {
2491          var wantSearch = a.keyword.trim();
2492          if (wantSearch === "") { target.searchParams.delete("search"); }
2493          else { target.searchParams.set("search", wantSearch); }
2494        }
2495
2496        var appliedCategories = [];
2497        if (wantsCategories) {
2498          for (var ai = 0; ai < resolved.length; ai++) { appliedCategories.push(resolved[ai].name); }
2499          target.searchParams.set("product", appliedCategories.join(","));
2500        }
2501        target.searchParams.delete("page");
2502
2503        if (target.href !== location.href) {
2504          // The switch and the pagination are click-driven and do not survive
2505          // the reload, so say plainly that they still need a follow-up call.
2506          var deferred = [];
2507          if (typeof a.certifiedOnly === "boolean") deferred.push("certifiedOnly");
2508          if (typeof a.page === "number" && a.page > 0) deferred.push("page");
2509
2510          location.assign(target.href);
2511          return out({
2512            status: "navigated",
2513            action: ACTION,
2514            url: target.href,
2515            keyword: wantsKeyword ? (a.keyword.trim() || null) : null,
2516            categoriesApplied: appliedCategories,
2517            deferred: deferred,
2518            nextStep: deferred.length
2519              ? "Wait for the listing to load, then call this tool again with " + deferred.join(" and ") + " to apply the rest."
2520              : "Wait for the listing to load, then call optimizely-extract-connector-listing to read the results.",
2521            summary: "Applied " +
2522              (wantsKeyword && a.keyword.trim() !== "" ? "the keyword \"" + a.keyword.trim() + "\"" : "")+
2523              (wantsKeyword && a.keyword.trim() === "" ? "an empty keyword" : "") +
2524              (wantsKeyword && wantsCategories ? " and " : "") +
2525              (wantsCategories ? "the category filter " + appliedCategories.join(", ") : "") +
2526              " through the listing's own URL parameters, so the connectors listing is reloading." +
2527              (deferred.length ? " " + deferred.join(" and ") + " still need a follow-up call." : "")
2528          });
2529        }
2530
2531        // The listing is already in the requested state, so nothing needs to
2532        // reload — keep the visible search field in step and carry on.
2533        if (wantsKeyword) {
2534          var sEl = document.querySelector(searchSel);
2535          if (sEl && sEl.value !== a.keyword) {
2536            try { await fillInput(searchSel, a.keyword); } catch (e) {}
2537            if (sEl.value !== a.keyword) { reactSet(sEl, a.keyword); }
2538          }
2539        }
2540      }
2541
2542      if (typeof a.certifiedOnly === "boolean") {
2543        var sw = certifiedSwitch();
2544        if (sw) {
2545          didRequest = true;
2546          if (isOn(sw) !== a.certifiedOnly) { sw.click(); await sleep(350); }
2547        }
2548      }
2549
2550      // Category facets are applied by the URL branch above; clicking them here
2551      // is a no-op once a search is in play, so nothing is clicked.
2552
2553      if (didRequest) {
2554        await settle(sigBefore, 9000);
2555        if (navigatedAway()) return navPayload();
2556      }
2557
2558      var pageNote = null;
2559      if (typeof a.page === "number" && a.page > 0) {
2560        var pageNo = Math.floor(a.page);
2561        var pbtns = document.querySelectorAll('#ListingResults button[aria-label="Page ' + pageNo + '"]');
2562        var pbtn = null;
2563        for (var pi = 0; pi < pbtns.length; pi++) {
2564          if (visible(pbtns[pi])) { pbtn = pbtns[pi]; break; }
2565        }
2566        if (!pbtn && pbtns.length) pbtn = pbtns[0];
2567        if (pbtn) {
2568          var s2 = sig();
2569          pbtn.click();
2570          await settle(s2, 9000);
2571          if (navigatedAway()) return navPayload();
2572        } else {
2573          pageNote = "Page " + pageNo + " has no pagination button in the current result set, so the first page of results is reported.";
2574        }
2575      }
2576
2577      var changed = sig() !== sigBefore;
2578
2579      var region = document.querySelector(LIST);
2580      if (region) {
2581        try { region.scrollIntoView({ behavior: "smooth", block: "start" }); } catch (e) {}
2582        var prevOutline = region.style.outline;
2583        region.style.outline = "3px solid #0037ff";
2584        setTimeout(function () { region.style.outline = prevOutline; }, 5000);
2585      }
2586
2587      var extracted = { resultCount: 0, truncated: false, results: [] };
2588      try { extracted = await extractItems(ITEM, FIELDS, limit); } catch (e) {}
2589      var results = (extracted && extracted.results) || [];
2590      var nodesFound = (extracted && typeof extracted.resultCount === "number") ? extracted.resultCount : results.length;
2591
2592      var cardNodes = Array.prototype.slice.call(document.querySelectorAll(ITEM));
2593      for (var xi = 0; xi < results.length; xi++) {
2594        var cardNode = cardNodes[xi];
2595        if (!cardNode || !results[xi]) continue;
2596        var chips = cardNode.querySelectorAll(CHIP);
2597        var chipList = [];
2598        for (var xj = 0; xj < chips.length; xj++) {
2599          var chipText = txt(chips[xj]);
2600          if (chipText && chipList.indexOf(chipText) === -1) chipList.push(chipText);
2601        }
2602        if (chipList.length) results[xi].categories = chipList;
2603      }
2604
2605      var countEl = document.querySelector(COUNT);
2606      var countText = txt(countEl);
2607      var total = null;
2608      var countSource = null;
2609      var m = countText.match(/of\s+([\d.,]+)/i);
2610      if (m) {
2611        total = parseInt(m[1].replace(/[.,]/g, ""), 10);
2612        countSource = "count line";
2613      }
2614      if (total === null && countEl) {
2615        try {
2616          var rc = await readResultCount(COUNT, null);
2617          if (typeof rc === "number" && !isNaN(rc)) { total = rc; countSource = "count line"; }
2618        } catch (e) {}
2619      }
2620      var listText = region ? txt(region) : "";
2621      if (total === null && /no results found/i.test(listText)) {
2622        total = 0;
2623        countSource = "empty state";
2624      }
2625      if (total === null && nodesFound > 0) {
2626        total = nodesFound;
2627        countSource = "cards on this page";
2628      }
2629
2630      var searchBack = searchSel ? document.querySelector(searchSel) : null;
2631      var certBack = certifiedSwitch();
2632      var nowControls = categoryControls();
2633      var checkedNow = [];
2634      for (var ni = 0; ni < nowControls.length; ni++) {
2635        if (isOn(nowControls[ni].button)) checkedNow.push(nowControls[ni].name);
2636      }
2637      var filtersLabel = null;
2638      var flCandidates = asideRoot().querySelectorAll("div");
2639      for (var fi = 0; fi < flCandidates.length; fi++) {
2640        var ft = txt(flCandidates[fi]);
2641        if (/^Filters\s*\(\d+\)$/i.test(ft)) { filtersLabel = ft; break; }
2642      }
2643
2644      var observed = (total !== null) || results.length > 0;
2645
2646      var payload = {
2647        status: "ok",
2648        action: ACTION,
2649        url: location.href,
2650        observed: observed,
2651        keyword: searchBack ? searchBack.value : null,
2652        certifiedOnly: certBack ? isOn(certBack) : null,
2653        categoriesApplied: checkedNow,
2654        activeFilters: filtersLabel,
2655        resultCount: total,
2656        resultCountText: countText || null,
2657        resultCountSource: countSource,
2658        resultsOnPage: results.length,
2659        truncated: !!(extracted && extracted.truncated),
2660        results: results,
2661        availableCategories: (function () {
2662          var names = [];
2663          for (var i = 0; i < nowControls.length; i++) {
2664            if (names.indexOf(nowControls[i].name) === -1) names.push(nowControls[i].name);
2665          }
2666          return names;
2667        })(),
2668        summary: ""
2669      };
2670
2671      if (!observed) {
2672        payload.status = "partial";
2673        payload.observed = false;
2674        payload.reason = "The listing region reported no count line and no connector cards could be read after the filters were applied.";
2675        payload.nextStep = "Reload /connectors, wait for the listing to render, then call this tool again.";
2676        payload.summary = "The connectors listing did not settle into a readable state, so no count or cards were read.";
2677        return out(payload);
2678      }
2679
2680      if (pageNote) {
2681        payload.status = "partial";
2682        payload.reason = pageNote;
2683        payload.nextStep = "Check resultCount to work out how many pages the filtered list has, then request a page within that range.";
2684      } else if (didRequest && !changed) {
2685        var suggested = null;
2686        try {
2687          suggested = buildUrl("/connectors?search={keyword}&product={categories}&page={page}", {
2688            keyword: (typeof a.keyword === "string" ? a.keyword : ""),
2689            categories: checkedNow.join(","),
2690            page: (typeof a.page === "number" && a.page > 1) ? String(Math.floor(a.page)) : ""
2691          });
2692        } catch (e) { suggested = null; }
2693        payload.status = "partial";
2694        payload.reason = "The result count and cards did not change after the filters were set, so the returned list may still be the previous one.";
2695        payload.nextStep = "Call this tool again, or open " + (suggested || "/connectors with search and product query parameters") + " and then call this tool to read the settled listing.";
2696      }
2697
2698      var head = payload.resultCountText ? ("The listing reports " + payload.resultCountText) : ("The listing shows " + payload.resultCount + " matching connectors");
2699      payload.summary = head + " and " + payload.resultsOnPage + " connector cards were read from the page.";
2700      if (payload.status === "partial" && payload.reason) {
2701        payload.summary = payload.summary + " " + payload.reason;
2702      }
2703
2704      return out(payload);
2705    } catch (err) {
2706      return out({
2707        status: "error",
2708        action: ACTION,
2709        url: location.href,
2710        reason: "The tool hit an unexpected problem while driving the connectors filters: " + ((err && err.message) ? err.message : 
2710String(err)),
2711        nextStep: "Reload /connectors and call this tool again with a simpler set of filters.",
2712        summary: "The connectors search could not be completed."
2713      });
2714    }
2715  }
2716});
2717
2718  /* ---- optimizely-extract-connector-listing ---- */
2719mc.registerToolIf("/connectors/**", {
2720  name: "optimizely-extract-connector-listing",
2721  description: "Reads the Optimizely connector directory on the current page and returns it as structured data. On the connectors listing it reads the connector cards rendered in the results region and returns an array of records with the connector name, description, the 'Works with' product list, the publisher name, the publisher logo image URL and the absolute detail URL, together with the total result count and the current page position parsed from the page's own 'Showing X-Y of N results' line. On an individual connector page it returns the single record: name, description, the publisher shown under 'Built by', the 'Works with' product list, the logo image URL, the pricing note and the documentation link URL. It reads the live DOM at call time, so the payload reflects whichever filters, search text or pagination page is active, and it scrolls to and highlights the region the data was read from.",
2722  inputSchema: {
2723    type: "object",
2724    properties: {
2725      limit: {
2726        type: "number",
2727        description: "Maximum number of connector cards to return from the listing page. Defaults to 100."
2728      },
2729      logoWidth: {
2730        type: "number",
2731        description: "Optional pixel width to request for each CMP logo image URL; adds a width query parameter to the returned logo URL."
2732      }
2733    },
2734    required: []
2735  },
2736  annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
2737  execute: async function (args) {
2738    trackWebMCPTool("optimizely-extract-connector-listing");
2739    var a = args || {};
2740    var startUrl = location.href;
2741    var reply = function (payload) {
2742      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
2743    };
2744
2745    try {
2746      var settle = function (ms) { return new Promise(function (r) { setTimeout(r, ms); }); };
2747
2748      var text = function (el) {
2749        if (!el || typeof el.textContent !== "string") return "";
2750        return el.textContent.replace(/\s+/g, " ").trim();
2751      };
2752
2753      var visibleText = function (el) {
2754        if (!el) return "";
2755        if (el.getAttribute && el.getAttribute("aria-hidden") === "true") return "";
2756        var hidden = el.querySelectorAll ? el.querySelectorAll('[aria-hidden="true"]') : [];
2757        if (!hidden || !hidden.length) return text(el);
2758        var whole = text(el);
2759        for (var h = 0; h < hidden.length; h++) {
2760          var part = text(hidden[h]);
2761          if (part) whole = whole.split(part).join(" ");
2762        }
2763        return whole.replace(/\s+/g, " ").trim();
2764      };
2765
2766      var absUrl = function (href) {
2767        if (!href) return null;
2768        try { return new URL(href, location.origin).href; } catch (e) { return href; }
2769      };
2770
2771      var num = function (s) {
2772        if (s === null || s === undefined) return null;
2773        var digits = String(s).replace(/[^0-9]/g, "");
2774        if (!digits) return null;
2775        var n = parseInt(digits, 10);
2776        return isNaN(n) ? null : n;
2777      };
2778
2779      var logoFromStyle = function (style) {
2780        if (!style) return null;
2781        var m = /mask-image\s*:\s*url\(\s*['"]?([^'")]+)['"]?\s*\)/i.exec(style);
2782        return m ? m[1] : null;
2783      };
2784
2785      var withWidth = function (u) {
2786        if (!u || typeof a.logoWidth !== "number" || !(a.logoWidth > 0)) return u || null;
2787        try {
2788          var url = new URL(u, location.origin);
2789          url.searchParams.set("width", String(Math.floor(a.logoWidth)));
2790          return url.toString();
2791        } catch (e) { return u; }
2792      };
2793
2794      var highlight = function (el) {
2795        if (!el || !el.style) return false;
2796        try {
2797          var previous = el.style.outline;
2798          el.style.outline = "3px solid #0037ff";
2799          if (el.scrollIntoView) el.scrollIntoView({ behavior: "smooth", block: "center" });
2800          setTimeout(function () { el.style.outline = previous; }, 5000);
2801          return true;
2802        } catch (e) { return false; }
2803      };
2804
2805      var path = location.pathname.replace(/\/+$/, "");
2806      var marker = "/connectors";
2807      var idx = path.toLowerCase().indexOf(marker);
2808      var rest = idx >= 0 ? path.slice(idx + marker.length) : "";
2809      var isDetail = rest.replace(/^\/+/, "").length > 0;
2810
2811      /* ---------------------------------------------------------------- */
2812      /* LISTING PAGE                                                      */
2813      /* ---------------------------------------------------------------- */
2814      if (!isDetail) {
2815        var region = null;
2816        try {
2817          region = await waitForElement("#ListingResults", 12000);
2818        } catch (e) {
2819          region = document.querySelector("#ListingResults");
2820        }
2821
2822        if (!region) {
2823          return reply({
2824            status: "partial",
2825            observed: false,
2826            action: "read the connector listing",
2827            pageType: "connector-listing",
2828            resultCount: null,
2829            totalResults: null,
2830            renderedCount: 0,
2831            results: [],
2832            url: location.href,
2833            reason: "The results region #ListingResults was not present on this page within 12 seconds, so no connector cards could be read.",
2834            nextStep: "Confirm the connectors listing has finished loading (and that no consent overlay is blocking it), then call this tool again.",
2835            summary: "The connector results region did not appear, so nothing was read from the page."
2836          });
2837        }
2838
2839        try { await waitForElement("#ListingResults a.group", 8000); } catch (e) { /* cards may legitimately be absent */ }
2840        await settle(400);
2841        var highlighted = highlight(region);
2842
2843        var limit = (typeof a.limit === "number" && a.limit > 0) ? Math.floor(a.limit) : 100;
2844
2845        var extracted = await extractItems("#ListingResults a.group", {
2846          detailPath: "@href",
2847          name: "h3.font-headline",
2848          description: "p.font-body",
2849          publisher: "span.font-body.text-body-s",
2850          publisherLogoLabel: 'span[role="img"] @aria-label',
2851          logoMaskStyle: 'span[role="img"] @style'
2852        }, limit) || {};
2853
2854        var rows = Array.isArray(extracted.results) ? extracted.results : [];
2855        var cards = Array.prototype.slice.call(document.querySelectorAll("#ListingResults a.group"));
2856
2857        var results = rows.map(function (row, i) {
2858          var card = cards[i] || null;
2859          var works = [];
2860          if (card) {
2861            Array.prototype.forEach.call(card.querySelectorAll("span.font-overline"), function (chip) {
2862              var t = visibleText(chip);
2863              var normalised = t.replace(/:$/, "").toLowerCase();
2864              if (!t || normalised === "works with") return;
2865              if (works.indexOf(t) === -1) works.push(t);
2866            });
2867          }
2868          var logoUrl = withWidth(logoFromStyle(row.logoMaskStyle));
2869          return {
2870            name: row.name || null,
2871            description: row.description || null,
2872            worksWith: works,
2873            publisher: row.publisher || row.publisherLogoLabel || null,
2874            logoUrl: logoUrl || null,
2875            logoAltText: row.publisherLogoLabel || null,
2876            detailUrl: absUrl(row.detailPath)
2877          };
2878        });
2879
2880        var countEl = document.querySelector("#ListingResults p.text-body-xxs");
2881        var countText = text(countEl);
2882        var m = /showing\s+([\d,]+)\s*[-–—]\s*([\d,]+)\s+of\s+([\d,]+)/i.exec(countText);
2883        var showingFrom = m ? num(m[1]) : null;
2884        var showingTo = m ? num(m[2]) : null;
2885        var totalResults = m ? num(m[3]) : null;
2886        var countSource = m ? "page count line" : null;
2887
2888        if (totalResults === null && countEl) {
2889          var rc = null;
2890          try { rc = await readResultCount("#ListingResults p.text-body-xxs", null); } catch (e) { rc = null; }
2891          if (typeof rc === "number" && !isNaN(rc)) {
2892            totalResults = rc;
2893            countSource = "readResultCount";
2894          }
2895        }
2896
2897        var currentPage = null;
2898        var pageButton = document.querySelector('#ListingResults nav[aria-label="Pagination"] button[aria-current="page"]');
2899        if (pageButton) currentPage = num(pageButton.getAttribute("aria-label") || text(pageButton));
2900
2901        var lastPageNumber = null;
2902        Array.prototype.forEach.call(
2903          document.querySelectorAll('#ListingResults nav[aria-label="Pagination"] button[aria-label]'),
2904          function (b) {
2905            var label = b.getAttribute("aria-label") || "";
2906            if (/^page\s+\d+$/i.test(label.trim())) {
2907              var n = num(label);
2908              if (n !== null && (lastPageNumber === null || n > lastPageNumber)) lastPageNumber = n;
2909            }
2910          }
2911        );
2912
2913        var renderedCount = results.length;
2914        var resultCount = totalResults !== null ? totalResults : (renderedCount > 0 ? renderedCount : null);
2915        if (countSource === null && renderedCount > 0) countSource = "rendered cards";
2916
2917        var status;
2918        var reason = null;
2919        var nextStep = null;
2920        if (renderedCount > 0) {
2921          status = "ok";
2922        } else if (totalResults === 0) {
2923          status = "ok";
2924        } else {
2925          status = "partial";
2926          reason = "The results region was present but no connector cards matching #ListingResults a.group could be read from it.";
2927          nextStep = "Let the listing finish rendering after the current filter, search or pagination change, then call this tool again.";
2928        }
2929
2930        if (location.href !== startUrl) {
2931          return reply({
2932            status: "navigated",
2933            action: "read the connector listing",
2934            url: location.href,
2935            nextStep: "The page navigated while it was being read. Call optimizely-extract-connector-listing again on the page that is now loading.",
2936            summary: "The page navigated during the read, so the listing was not reported."
2937          });
2938        }
2939
2940        var listingSummary;
2941        if (status === "ok" && totalResults !== null) {
2942          listingSummary = "Read " + renderedCount + " connector card" + (renderedCount === 1 ? "" : "s") +
2943            " from the listing, out of " + totalResults + " total result" + (totalResults === 1 ? "" : "s") +
2944            (currentPage !== null ? " (page " + currentPage + (lastPageNumber !== null ? " of " + lastPageNumber : "") + ")" : "") + ".";
2945        } else if (status === "ok") {
2946          listingSummary = "Read " + renderedCount + " connector card" + (renderedCount === 1 ? "" : "s") + " from the listing.";
2947        } else {
2948          listingSummary = "The results region was found but no connector cards could be read from it.";
2949        }
2950
2951        var listingPayload = {
2952          status: status,
2953          observed: renderedCount > 0 || totalResults !== null,
2954          action: "read the connector listing",
2955          pageType: "connector-listing",
2956          resultCount: resultCount,
2957          totalResults: totalResults,
2958          renderedCount: renderedCount,
2959          showingFrom: showingFrom,
2960          showingTo: showingTo,
2961          countText: countText || null,
2962          countSource: countSource,
2963          currentPage: currentPage,
2964          lastPageNumber: lastPageNumber,
2965          truncated: extracted.truncated === true,
2966          highlighted: highlighted,
2967          results: results,
2968          url: location.href,
2969          summary: listingSummary
2970        };
2971        if (reason) listingPayload.reason = reason;
2972        if (nextStep) listingPayload.nextStep = nextStep;
2973        return reply(listingPayload);
2974      }
2975
2976      /* ---------------------------------------------------------------- */
2977      /* CONNECTOR DETAIL PAGE                                             */
2978      /* ---------------------------------------------------------------- */
2979      var heading = null;
2980      try {
2981        heading = await waitForElement("h1", 12000);
2982      } catch (e) {
2983        heading = document.querySelector("h1");
2984      }
2985      await settle(400);
2986
2987      var root = document.querySelector("article") || document.querySelector("main") || document.body;
2988      if (heading && root && !root.contains(heading)) root = document.body;
2989      var headingName = visibleText(heading) || null;
2990
2991      var KNOWN_LABELS = ["built by", "works with", "documentation", "pricing", "category", "categories"];
2992
2993      var findLabel = function (scope, wanted) {
2994        var wantedLower = wanted.toLowerCase();
2995        var nodes = (scope || document).querySelectorAll("span,p,div,h2,h3,h4,h5,h6,dt,strong,label,li");
2996        for (var i = 0; i < nodes.length; i++) {
2997          var n = nodes[i];
2998          if (n.children && n.children.length > 0) continue;
2999          var t = visibleText(n).replace(/:$/, "").trim().toLowerCase();
3000          if (t === wantedLower) return n;
3001        }
3002        return null;
3003      };
3004
3005      var valuesFor = function (labelEl) {
3006        var out = [];
3007        var push = function (t) {
3008          if (!t) return;
3009          var normalised = t.replace(/:$/, "").trim().toLowerCase();
3010          if (KNOWN_LABELS.indexOf(normalised) !== -1) return;
3011          if (out.indexOf(t) === -1) out.push(t);
3012        };
3013        if (!labelEl) return out;
3014
3015        var sibling = labelEl.nextElementSibling;
3016        while (sibling) {
3017          var siblingText = visibleText(sibling);
3018          if (KNOWN_LABELS.indexOf(siblingText.replace(/:$/, "").trim().toLowerCase()) !== -1) break;
3019          if (siblingText) {
3020            if (!sibling.children || sibling.children.length === 0) {
3021              push(siblingText);
3022            } else {
3023              var leaves = sibling.querySelectorAll("span,a,li,p,dd");
3024              var added = false;
3025              for (var i = 0; i < leaves.length; i++) {
3026                if (leaves[i].children && leaves[i].children.length > 0) continue;
3027                var leafText = visibleText(leaves[i]);
3028                if (leafText) { push(leafText); added = true; }
3029              }
3030              if (!added) push(siblingText);
3031            }
3032          }
3033          sibling = sibling.nextElementSibling;
3034        }
3035
3036        if (!out.length && labelEl.parentElement) {
3037          var kids = labelEl.parentElement.children;
3038          for (var j = 0; j < kids.length; j++) {
3039            var kid = kids[j];
3040            if (kid === labelEl || kid.contains(labelEl)) continue;
3041            push(visibleText(kid));
3042          }
3043        }
3044        return out;
3045      };
3046
3047      var linkNear = function (labelEl) {
3048        if (!labelEl) return null;
3049        var sibling = labelEl.nextElementSibling;
3050        while (sibling) {
3051          if (sibling.matches && sibling.matches("a[href]")) {
3052            return { url: absUrl(sibling.getAttribute("href")), text: visibleText(sibling) || null };
3053          }
3054          var nested = sibling.querySelector ? sibling.querySelector("a[href]") : null;
3055          if (nested) return { url: absUrl(nested.getAttribute("href")), text: visibleText(nested) || null };
3056          sibling = sibling.nextElementSibling;
3057        }
3058        var scoped = labelEl.parentElement ? labelEl.parentElement.querySelector("a[href]") : null;
3059        if (scoped) return { url: absUrl(scoped.getAttribute("href")), text: visibleText(scoped) || null };
3060        return null;
3061      };
3062
3063      var pickLogo = function (scope) {
3064        var imgs = Array.prototype.slice.call((scope || document).querySelectorAll("img[src]"));
3065        for (var i = 0; i < imgs.length; i++) {
3066          var im = imgs[i];
3067          if (im.closest && im.closest("nav, footer")) continue;
3068          if (im.getAttribute("aria-hidden") === "true") continue;
3069          var src = im.getAttribute("src") || "";
3070          if (/cmp\.optimizely\.com/i.test(src)) {
3071            return { url: withWidth(absUrl(src)), alt: im.getAttribute("alt") || null };
3072          }
3073        }
3074        var masked = (scope || document).querySelector('span[role="img"][style*="mask-image"]');
3075        if (masked) {
3076          return {
3077            url: withWidth(logoFromStyle(masked.getAttribute("style"))),
3078            alt: masked.getAttribute("aria-label") || null
3079          };
3080        }
3081        return null;
3082      };
3083
3084      var descriptionFromDom = null;
3085      if (heading) {
3086        var headingScope = heading.parentElement || root;
3087        var candidateParagraph = headingScope ? headingScope.querySelector("p") : null;
3088        if (!candidateParagraph) {
3089          var afterHeading = heading.nextElementSibling;
3090          while (afterHeading && !candidateParagraph) {
3091            if (afterHeading.matches && afterHeading.matches("p")) candidateParagraph = afterHeading;
3092            else if (afterHeading.querySelector) candidateParagraph = afterHeading.querySelector("p");
3093            afterHeading = afterHeading.nextElementSibling;
3094          }
3095        }
3096        descriptionFromDom = visibleText(candidateParagraph) || null;
3097      }
3098
3099      var builtByLabel = findLabel(root, "Built by");
3100      var worksWithLabel = findLabel(root, "Works with");
3101      var documentationLabel = findLabel(root, "Documentation");
3102
3103      var publisherValues = valuesFor(builtByLabel);
3104      var publisherFromDom = publisherValues.length ? publisherValues[0] : null;
3105
3106      var worksWithFromDom = valuesFor(worksWithLabel);
3107      if (!worksWithFromDom.length) {
3108        Array.prototype.forEach.call(root.querySelectorAll("span.font-overline"), function (chip) {
3109          if (chip.closest && chip.closest("nav")) return;
3110          var t = visibleText(chip);
3111          if (t && worksWithFromDom.indexOf(t) === -1 && KNOWN_LABELS.indexOf(t.replace(/:$/, "").toLowerCase()) === -1) {
3112            worksWithFromDom.push(t);
3113          }
3114        });
3115      }
3116
3117      var documentationLink = linkNear(documentationLabel);
3118      if (!documentationLink) {
3119        var supportAnchor = root.querySelector('a[href*="support.optimizely.com"]') || document.querySelector('a[href*="support.optimizely.com"]');
3120        if (supportAnchor) {
3121          documentationLink = { url: absUrl(supportAnchor.getAttribute("href")), text: visibleText(supportAnchor) || null };
3122        }
3123      }
3124
3125      var pricingNote = null;
3126      var pricingHeadings = root.querySelectorAll("h2,h3,h4");
3127      for (var p = 0; p < pricingHeadings.length; p++) {
3128        if (visibleText(pricingHeadings[p]).replace(/:$/, "").toLowerCase() === "pricing") {
3129          var afterPricing = pricingHeadings[p].nextElementSibling;
3130          while (afterPricing && !pricingNote) {
3131            var pricingText = visibleText(afterPricing);
3132            if (pricingText) pricingNote = pricingText;
3133            afterPricing = afterPricing.nextElementSibling;
3134          }
3135          break;
3136        }
3137      }
3138
3139      var logo = pickLogo(root);
3140
3141      /* Next.js data island, used only to fill fields the DOM did not yield,
3142         and only when the record clearly matches this connector page. */
3143      var slug = (rest.replace(/^\/+/, "").split("/")[0] || "").toLowerCase();
3144      var islandRecord = null;
3145      try {
3146        var island = document.getElementById("__NEXT_DATA__");
3147        var pageProps = island ? (JSON.parse(island.textContent || "{}").props || {}).pageProps : null;
3148        if (pageProps) {
3149          var matches = function (rec) {
3150            var recName = typeof rec.name === "string" ? rec.name : (typeof rec.title === "string" ? rec.title : null);
3151            if (headingName && recName && recName.trim().toLowerCase() === headingName.trim().toLowerCase()) return true;
3152            var recSlug = rec.slug || rec.url || rec.href || rec.path;
3153            if (slug && typeof recSlug === "string" && recSlug.toLowerCase().indexOf(slug) !== -1) return true;
3154            return false;
3155          };
3156          var queue = [{ value: pageProps, depth: 0 }];
3157          var budget = 3000;
3158          while (queue.length && budget > 0) {
3159            budget--;
3160            var node = queue.shift();
3161            var value = node.value;
3162            if (!value || typeof value !== "object" || node.depth > 6) continue;
3163            if (!Array.isArray(value)) {
3164              var hasName = typeof value.name === "string" || typeof value.title === "string";
3165              var hasDescription = typeof value.description === "string" || typeof value.summary === "string";
3166              if (hasName && hasDescription && matches(value)) { islandRecord = value; break; }
3167            }
3168            var children = Array.isArray(value) ? value : Object.keys(value).map(function (k) { return value[k]; });
3169            for (var c = 0; c < children.length; c++) {
3170              if (children[c] && typeof children[c] === "object") queue.push({ value: children[c], depth: node.depth + 1 });
3171            }
3172          }
3173        }
3174      } catch (e) { islandRecord = null; }
3175
3176      var fieldSources = {
3177        name: headingName ? "dom" : null,
3178        description: descriptionFromDom ? "dom" : null,
3179        publisher: publisherFromDom ? "dom" : null,
3180        worksWith: worksWithFromDom.length ? "dom" : null,
3181        logoUrl: (logo && logo.url) ? "dom" : null,
3182        pricingNote: pricingNote ? "dom" : null,
3183        documentationUrl: (documentationLink && documentationLink.url) ? "dom" : null
3184      };
3185
3186      var name = headingName;
3187      var description = descriptionFromDom;
3188      var publisher = publisherFromDom;
3189
3190      if (islandRecord) {
3191        if (!name && (typeof islandRecord.name === "string" || typeof islandRecord.title === "string")) {
3192          name = islandRecord.name || islandRecord.title;
3193          fieldSources.name = "next-data-island";
3194        }
3195        if (!description && (typeof islandRecord.description === "string" || typeof islandRecord.summary === "string")) {
3196          description = islandRecord.description || islandRecord.summary;
3197          fieldSources.description = "next-data-island";
3198        }
3199        if (!publisher) {
3200          var vendor = islandRecord.publisher || islandRecord.vendor || islandRecord.company || islandRecord.builtBy;
3201          if (typeof vendor === "string") { publisher = vendor; fieldSources.publisher = "next-data-island"; }
3202          else if (vendor && typeof vendor === "object" && typeof vendor.name === "string") { publisher = vendor.name; fieldSources.publisher = "next-data-island"; }
3203        }
3204      }
3205
3206      var connector = {
3207        name: name || null,
3208        description: description || null,
3209        publisher: publisher || null,
3210        worksWith: worksWithFromDom,
3211        logoUrl: (logo && logo.url) || null,
3212        logoAltText: (logo && logo.alt) || null,
3213        pricingNote: pricingNote || null,
3214        documentationUrl: (documentationLink && documentationLink.url) || null,
3215        documentationLinkText: (documentationLink && documentationLink.text) || null,
3216        detailUrl: location.href
3217      };
3218
3219      var missingFields = [];
3220      ["name", "description", "publisher", "logoUrl", "pricingNote", "documentationUrl"].forEach(function (key) {
3221        if (!connector[key]) missingFields.push(key);
3222      });
3223      if (!connector.worksWith.length) missingFields.push("worksWith");
3224
3225      var detailHighlighted = highlight(heading ? (heading.closest("section, article, header, div") || heading) : root);
3226
3227      if (location.href !== startUrl) {
3228        return reply({
3229          status: "navigated",
3230          action: "read the connector detail page",
3231          url: location.href,
3232          nextStep: "The page navigated while it was being read. Call optimizely-extract-connector-listing again on the page that is now loading.",
3233          summary: "The page navigated during the read, so the connector record was not reported."
3234        });
3235      }
3236
3237      var observedDetail = !!(connector.name || connector.description);
3238      if (!observedDetail) {
3239        return reply({
3240          status: "partial",
3241          observed: false,
3242          action: "read the connector detail page",
3243          pageType: "connector-detail",
3244          resultCount: null,
3245          results: [],
3246          connector: connector,
3247          missingFields: missingFields,
3248          fieldSources: fieldSources,
3249          highlighted: detailHighlighted,
3250          url: location.href,
3251          reason: "Neither the connector name heading nor its description paragraph could be read from the settled page.",
3252          nextStep: "Let the connector page finish rendering, then call this tool again.",
3253          summary: "The connector page was open but no name or description could be read from it."
3254        });
3255      }
3256
3257      var detailStatus = missingFields.length ? "partial" : "ok";
3258      var detailPayload = {
3259        status: detailStatus,
3260        observed: true,
3261        action: "read the connector detail page",
3262        pageType: "connector-detail",
3263        resultCount: 1,
3264        results: [connector],
3265        connector: connector,
3266        missingFields: missingFields,
3267        fieldSources: fieldSources,
3268        highlighted: detailHighlighted,
3269        url: location.href,
3270        summary: "Read the connector record for " + (connector.name || "this page") +
3271          (connector.publisher ? " published by " + connector.publisher : "") +
3272          (connector.worksWith.length ? ", works with " + connector.worksWith.join(", ") : "") + "."
3273      };
3274      if (missingFields.length) {
3275        detailPayload.reason = "These fields were not present on the rendered page: " + missingFields.join(", ") + ".";
3276        detailPayload.nextStep = "Use the fields that were read; if a missing field is required, open the connector page again once it has fully rendered and call this tool a second time.";
3277      }
3278      return reply(detailPayload);
3279
3280    } catch (err) {
3281      return reply({
3282        status: "error",
3283        action: "read the connector directory data",
3284        url: location.href,
3285        reason: "The extraction failed: " + (err && err.message ? err.message : String(err)),
3286        nextStep: "Reload the connectors page, wait for the results region to render, then call this tool again.",
3287        summary: "The connector data could not be read because the extraction raised an error."
3288      });
3289    }
3290  }
3291});
3292
3293  /* ---- optimizely-request-connector-contact-publisher, optimizely-request-connector-contact-publisher-submit ---- */
3294mc.registerToolIf("/connectors/*", {
3295  name: "optimizely-request-connector-contact-publisher",
3296  description: "Fills the 'Request this connector' enquiry form on an Optimizely connector detail page. Scrolls to and highlights the request section, writes first name, last name, email, phone including country code and message into the form fields, and ticks the 'I agree to share my information with Optimizely and the provider of this listing' consent control. Returns the values read back out of each field, the page_key and form_key as rendered for this connector, which required fields are still empty, and whether the form is ready to send. This tool does not send anything - call optimizely-request-connector-contact-publisher-submit to send the request to the publisher.",
3297  inputSchema: {
3298    type: "object",
3299    properties: {
3300      firstName: { type: "string", description: "Value for the First name field." },
3301      lastName: { type: "string", description: "Value for the Last name field." },
3302      email: { type: "string", description: "Value for the Email field, for example [email protected]." },
3303      phone: { type: "string", description: "Value for the 'Phone incl. country code' field, for example +1 415 555 0134." },
3304      message: { type: "string", description: "Message to the connector publisher describing what is being requested." },
3305      consent: { type: "boolean", description: "Whether to tick the consent control 'I agree to share my information with Optimizely and the provider of this listing'. Defaults to true; the request is not accepted without it." }
3306    },
3307    required: ["firstName", "lastName", "email"]
3308  },
3309  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
3310  execute: async function (args) {
3311    trackWebMCPTool("optimizely-request-connector-contact-publisher");
3312    var a = args || {};
3313    var reply = function (payload) { return { content: [{ type: "text", text: JSON.stringify(payload) }] }; };
3314    var sleep = function (ms) { return new Promise(function (r) { setTimeout(r, ms); }); };
3315    var ACTION = "fill the connector request form";
3316    try {
3317      var FORM_SEL = "form.opti-form";
3318      var form = document.querySelector(FORM_SEL);
3319      if (!form) {
3320        var anchor = document.querySelector('a[href*="#connector-vb"]');
3321        if (anchor) { try { anchor.click(); } catch (e0) {} }
3322        try { form = await waitForElement(FORM_SEL, 8000); } catch (e1) { form = document.querySelector(FORM_SEL); }
3323      }
3324      if (!form) {
3325        return reply({
3326          status: "error",
3327          action: ACTION,
3328          url: location.href,
3329          reason: "No element matching form.opti-form was found on this page.",
3330          nextStep: "Open a connector detail page under /connectors/ (for example /connectors/wordpress/) and call this tool again.",
3331          summary: "The connector request form is not present on this page."
3332        });
3333      }
3334
3335      var section = document.querySelector("#connector-vb") || form;
3336      try { section.scrollIntoView({ behavior: "smooth", block: "center" }); } catch (e2) {}
3337      try { form.style.outline = "3px solid #0037ff"; form.style.outlineOffset = "4px"; } catch (e3) {}
3338      await sleep(250);
3339
3340      var nativeSet = function (el, value) {
3341        var proto = el.tagName === "TEXTAREA" ? window.HTMLTextAreaElement.prototype : window.HTMLInputElement.prototype;
3342        var desc = Object.getOwnPropertyDescriptor(proto, "value");
3343        if (desc && desc.set) { desc.set.call(el, value); } else { el.value = value; }
3344        el.dispatchEvent(new Event("input", { bubbles: true }));
3345        el.dispatchEvent(new Event("change", { bubbles: true }));
3346      };
3347
3348      var findControl = function (name, sel, labelText) {
3349        var el = sel ? document.querySelector(sel) : null;
3350        if (el) return el;
3351        el = form.querySelector('[name="' + name + '"]');
3352        if (el) return el;
3353        var labels = form.querySelectorAll("label");
3354        for (var i = 0; i < labels.length; i++) {
3355          var t = (labels[i].textContent || "").trim().toLowerCase();
3356          if (labelText && t.indexOf(labelText) === 0) {
3357            var forId = labels[i].getAttribute("for");
3358            if (forId) { var byId = document.getElementById(forId); if (byId) return byId; }
3359            var inner = labels[i].querySelector("input, textarea");
3360            if (inner) return inner;
3361          }
3362        }
3363        return null;
3364      };
3365
3366      var specs = [
3367        { key: "firstName", sel: 'form.opti-form input[name="firstName"]', label: "first name", value: a.firstName },
3368        { key: "lastName", sel: 'form.opti-form input[name="lastName"]', label: "last name", value: a.lastName },
3369        { key: "email", sel: 'form.opti-form input[name="email"]', label: "email", value: a.email },
3370        { key: "phone", sel: 'form.opti-form input[name="phone"]', label: "phone", value: a.phone },
3371        { key: "message", sel: 'form.opti-form textarea[name="message"]', label: "message", value: a.message }
3372      ];
3373
3374      var fields = {};
3375      var notFound = [];
3376      var mismatched = [];
3377
3378      for (var s = 0; s < specs.length; s++) {
3379        var spec = specs[s];
3380        var el = findControl(spec.key, spec.sel, spec.label);
3381        if (!el) {
3382          if (spec.value !== undefined && spec.value !== null && String(spec.value) !== "") { notFound.push(spec.key); }
3383          fields[spec.key] = null;
3384          continue;
3385        }
3386        if (spec.value !== undefined && spec.value !== null && String(spec.value) !== "") {
3387          var wanted = String(spec.value);
3388          var filled = false;
3389          if (spec.sel && document.querySelector(spec.sel)) {
3390            try { await fillInput(spec.sel, wanted); filled = true; } catch (e4) { filled = false; }
3391          }
3392          if (!filled) { nativeSet(el, wanted); }
3393          await sleep(80);
3394          if (String(el.value || "") !== wanted) {
3395            nativeSet(el, wanted);
3396            await sleep(120);
3397          }
3398          if (String(el.value || "") !== wanted) { mismatched.push(spec.key); }
3399        }
3400        fields[spec.key] = String(el.value || "");
3401      }
3402
3403      var consentWanted = (a.consent === undefined || a.consent === null) ? true : !!a.consent;
3404      var consentBtn = document.querySelector('form.opti-form button[role="checkbox"]') || form.querySelector('[role="checkbox"]');
3405      var consentInput = document.querySelector('form.opti-form input[type="checkbox"]') || form.querySelector('input[type="checkbox"]');
3406      var consentMirror = document.querySelector('form.opti-form input[name="i_have_read_and_agree_to_the_terms_and_conditions"]');
3407
3408      var consentState = function () {
3409        if (consentBtn) {
3410          var ac = consentBtn.getAttribute("aria-checked");
3411          if (ac !== null) { return ac === "true"; }
3412          if (consentBtn.getAttribute("data-state")) { return consentBtn.getAttribute("data-state") === "checked"; }
3413        }
3414        if (consentInput) { return !!consentInput.checked; }
3415        if (consentMirror) {
3416          return String(consentMirror.value || "").trim() !== "";
3417        }
3418        return false;
3419      };
3420
3421      var consentControlFound = !!(consentBtn || consentInput);
3422      if (consentControlFound) {
3423        if (consentBtn && consentState() !== consentWanted) {
3424          try { consentBtn.click(); } catch (e5) {}
3425          await sleep(200);
3426        }
3427        if (consentState() !== consentWanted && consentInput) {
3428          var done = false;
3429          if (document.querySelector('form.opti-form input[type="checkbox"]')) {
3430            try { await fillCheckbox('form.opti-form input[type="checkbox"]', consentWanted); done = true; } catch (e6) { done = false; }
3431          }
3432          if (!done) { try { consentInput.click(); } catch (e7) {} }
3433          await sleep(200);
3434        }
3435      }
3436      var consentNow = consentControlFound ? consentState() : false;
3437      fields.consent = consentNow;
3438
3439      var readHidden = function (sel, name) {
3440        var el = document.querySelector(sel) || form.querySelector('[name="' + name + '"]');
3441        return el ? String(el.value || "") : null;
3442      };
3443      var pageKey = readHidden('form.opti-form input[name="page_key"]', "page_key");
3444      var formKey = readHidden('form.opti-form input[name="form_key"]', "form_key");
3445      var consentMirrorValue = consentMirror ? String(consentMirror.value || "") : null;
3446
3447      var missing = [];
3448      var requiredKeys = ["firstName", "lastName", "email"];
3449      for (var r = 0; r < requiredKeys.length; r++) {
3450        var k = requiredKeys[r];
3451        if (fields[k] === null || fields[k] === undefined || String(fields[k]).trim() === "") { missing.push(k); }
3452      }
3453      if (!consentControlFound) { missing.push("consent (control not found)"); }
3454      else if (!consentNow) { missing.push("consent"); }
3455
3456      var captchaEl = form.querySelector('iframe[src*="recaptcha"], iframe[src*="hcaptcha"], .g-recaptcha, .h-captcha, [data-sitekey]');
3457
3458      var ready = missing.length === 0;
3459      var problems = notFound.length > 0 || mismatched.length > 0 || (consentControlFound && consentNow !== consentWanted) || !consentControlFound;
3460      var status = problems ? "partial" : (ready ? "ok" : "partial");
3461
3462      var payload = {
3463        status: status,
3464        action: ACTION,
3465        url: location.href,
3466        fields: fields,
3467        missing: missing,
3468        ready: ready,
3469        pageKey: pageKey,
3470        formKey: formKey,
3471        consentMirrorValue: consentMirrorValue,
3472        captchaDetected: !!captchaEl,
3473        submitToolName: "optimizely-request-connector-contact-publisher-submit",
3474        summary: ready
3475          ? "Filled the connector request form and ticked the consent control; the values are in the form and ready to send."
3476          : "Filled the connector request form, but " + missing.length + " required item(s) are still outstanding: " + missing.join(", ") + "."
3477      };
3478      if (status !== "ok") {
3479        var reasons = [];
3480        if (notFound.length) { reasons.push("no control on the page for: " + notFound.join(", ")); }
3481        if (mismatched.length) { reasons.push("the page did not keep the value written into: " + mismatched.join(", ")); }
3482        if (!consentControlFound) { reasons.push("the consent control was not found inside form.opti-form"); }
3483        else if (consentNow !== consentWanted) { reasons.push("the consent control stayed " + (consentNow ? "ticked" : "unticked")); }
3484        if (!reasons.length && missing.length) { reasons.push("required fields still empty: " + missing.join(", ")); }
3485        payload.reason = reasons.join("; ") + ".";
3486        payload.nextStep = "Call this tool again with the outstanding values, or fill them by hand on the page, then call optimizely-request-connector-contact-publisher-submit.";
3487      } else {
3488        payload.nextStep = "Call optimizely-request-connector-contact-publisher-submit to send the request to the connector publisher.";
3489      }
3490      return reply(payload);
3491    } catch (err) {
3492      return reply({
3493        status: "error",
3494        action: ACTION,
3495        url: location.href,
3496        reason: "The tool threw while filling the form: " + (err && err.message ? err.message : String(err)),
3497        nextStep: "Check that the 'Request this connector' section is present on the page, then try again.",
3498        summary: "Could not complete filling the connector request form."
3499      });
3500    }
3501  }
3502});
3503
3504mc.registerToolIf("/connectors/*", {
3505  name: "optimizely-request-connector-contact-publisher-submit",
3506  description: "Sends the connector request that is currently filled in on this Optimizely connector detail page b
3506y clicking 'Contact publisher', waits for the page to respond, and returns the confirmation text from the success panel that replaces the form, any validation messages read from the form, the values that were sent and the connector's page_key and form_key. Run optimizely-request-connector-contact-publisher first; sending contacts the connector publisher and cannot be undone.",
3507  inputSchema: {
3508    type: "object",
3509    properties: {
3510      confirm: { type: "boolean", description: "Set true to confirm sending the request to the connector publisher. Defaults to true." }
3511    }
3512  },
3513  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
3514  execute: async function (args) {
3515    trackWebMCPTool("optimizely-request-connector-contact-publisher-submit");
3516    var a = args || {};
3517    var reply = function (payload) { return { content: [{ type: "text", text: JSON.stringify(payload) }] }; };
3518    var sleep = function (ms) { return new Promise(function (r) { setTimeout(r, ms); }); };
3519    var ACTION = "submit the connector request form";
3520    try {
3521      if (a.confirm === false) {
3522        return reply({
3523          status: "error",
3524          action: ACTION,
3525          url: location.href,
3526          validationErrors: [],
3527          confirmation: null,
3528          reason: "The caller passed confirm: false, so nothing was sent.",
3529          nextStep: "Call this tool again with confirm: true when the request should go to the connector publisher.",
3530          summary: "Submission cancelled; the connector request was not sent."
3531        });
3532      }
3533
3534      var FORM_SEL = "form.opti-form";
3535      var form = document.querySelector(FORM_SEL);
3536      if (!form) {
3537        try { form = await waitForElement(FORM_SEL, 6000); } catch (e0) { form = document.querySelector(FORM_SEL); }
3538      }
3539      if (!form) {
3540        return reply({
3541          status: "error",
3542          action: ACTION,
3543          url: location.href,
3544          validationErrors: [],
3545          confirmation: null,
3546          reason: "No element matching form.opti-form was found on this page.",
3547          nextStep: "Open a connector detail page under /connectors/, fill the form with optimizely-request-connector-contact-publisher, then call this tool again.",
3548          summary: "The connector request form is not present on this page."
3549        });
3550      }
3551
3552      var val = function (sel, name) {
3553        var el = document.querySelector(sel) || form.querySelector('[name="' + name + '"]');
3554        return el ? String(el.value || "") : null;
3555      };
3556      var fieldsSent = {
3557        firstName: val('form.opti-form input[name="firstName"]', "firstName"),
3558        lastName: val('form.opti-form input[name="lastName"]', "lastName"),
3559        email: val('form.opti-form input[name="email"]', "email"),
3560        phone: val('form.opti-form input[name="phone"]', "phone"),
3561        message: val('form.opti-form textarea[name="message"]', "message")
3562      };
3563      var pageKey = val('form.opti-form input[name="page_key"]', "page_key");
3564      var formKey = val('form.opti-form input[name="form_key"]', "form_key");
3565
3566      var consentBtn = document.querySelector('form.opti-form button[role="checkbox"]') || form.querySelector('[role="checkbox"]');
3567      var consentInput = document.querySelector('form.opti-form input[type="checkbox"]') || form.querySelector('input[type="checkbox"]');
3568      var consentMirror = document.querySelector('form.opti-form input[name="i_have_read_and_agree_to_the_terms_and_conditions"]');
3569      var consentState = function () {
3570        if (consentBtn) {
3571          var ac = consentBtn.getAttribute("aria-checked");
3572          if (ac !== null) { return ac === "true"; }
3573          if (consentBtn.getAttribute("data-state")) { return consentBtn.getAttribute("data-state") === "checked"; }
3574        }
3575        if (consentInput) { return !!consentInput.checked; }
3576        if (consentMirror) {
3577          return String(consentMirror.value || "").trim() !== "";
3578        }
3579        return false;
3580      };
3581      fieldsSent.consent = consentState();
3582
3583      if (!fieldsSent.consent) {
3584        return reply({
3585          status: "error",
3586          action: ACTION,
3587          url: location.href,
3588          validationErrors: ["I agree to share my information with Optimizely and the provider of this listing: n
3588ot ticked"],
3589          confirmation: null,
3590          fieldsSent: fieldsSent,
3591          pageKey: pageKey,
3592          formKey: formKey,
3593          reason: "The consent control in form.opti-form is not ticked, and the request is not accepted without it.",
3594          nextStep: "Call optimizely-request-connector-contact-publisher with consent: true, then call this tool again.",
3595          summary: "Did not send: the consent control is not ticked."
3596        });
3597      }
3598
3599      var labelFor = function (el) {
3600        if (!el) { return "field"; }
3601        var id = el.getAttribute("id");
3602        if (id) {
3603          var lab = form.querySelector('label[for="' + id + '"]');
3604          if (lab && (lab.textContent || "").trim()) { return (lab.textContent || "").trim(); }
3605        }
3606        var aria = el.getAttribute("aria-label");
3607        if (aria) { return aria.trim(); }
3608        var ph = el.getAttribute("placeholder");
3609        if (ph) { return ph.trim(); }
3610        return el.getAttribute("name") || el.tagName.toLowerCase();
3611      };
3612
3613      var textOf = function (node) {
3614        if (!node) { return ""; }
3615        if (node.getAttribute && node.getAttribute("aria-hidden") === "true") { return ""; }
3616        return String(node.textContent || "").replace(/\s+/g, " ").trim();
3617      };
3618
3619      var IN_PROGRESS_TEXT = /^(submitting|loading)[.…]*$/i;
3620
3621      var readConfirmationText = function () {
3622        var scope = document.querySelector("#connector-vb") || document;
3623        var nodes = scope.querySelectorAll(".form-success-page, .form-confirmation");
3624        for (var i = 0; i < nodes.length; i++) {
3625          var t = textOf(nodes[i]);
3626          if (t && !IN_PROGRESS_TEXT.test(t)) { return t; }
3627        }
3628        return "";
3629      };
3630
3631      var readValidationErrors = function () {
3632        var out = [];
3633        var seen = {};
3634        var f = document.querySelector(FORM_SEL);
3635        if (!f) { return out; }
3636        var push = function (msg) {
3637          var m = String(msg || "").replace(/\s+/g, " ").trim();
3638          if (!m || m.length > 300 || seen[m]) { return; }
3639          seen[m] = true;
3640          out.push(m);
3641        };
3642        var nodes = f.querySelectorAll('[role="alert"], [class*="error"], [class*="Error"], [data-error]');
3643        for (var i = 0; i < nodes.length && out.length < 10; i++) { push(textOf(nodes[i])); }
3644        var invalids = f.querySelectorAll('[aria-invalid="true"]');
3645        for (var k = 0; k < invalids.length && out.length < 10; k++) {
3646          var el = invalids[k];
3647          var described = el.getAttribute("aria-describedby");
3648          var msg = "";
3649          if (described) {
3650            var ids = described.split(/\s+/);
3651            for (var d = 0; d < ids.length; d++) {
3652              var dn = document.getElementById(ids[d]);
3653              var dt = textOf(dn);
3654              if (dt) { msg = dt; break; }
3655            }
3656          }
3657          if (!msg && el.validationMessage) { msg = el.validationMessage; }
3658          push(labelFor(el) + ": " + (msg || "is not valid"));
3659        }
3660        var ctrls = f.querySelectorAll("input, textarea, select");
3661        for (var c = 0; c < ctrls.length && out.length < 10; c++) {
3662          var ctl = ctrls[c];
3663          if (ctl.willValidate && ctl.validity && !ctl.validity.valid && ctl.validationMessage) {
3664            push(labelFor(ctl) + ": " + ctl.validationMessage);
3665          }
3666        }
3667        return out;
3668      };
3669
3670      var submitBtn = document.querySelector('form.opti-form button[type="submit"]') || form.querySelector('button[type="submit"]');
3671      if (!submitBtn) {
3672        return reply({
3673          status: "error",
3674          action: ACTION,
3675          url: location.href,
3676          validationErrors: [],
3677          confirmation: null,
3678          fieldsSent: fieldsSent,
3679          pageKey: pageKey,
3680          formKey: formKey,
3681          reason: "The 'Contact publisher' submit button (form.opti-form button[type=\"submit\"]) was not found.",
3682          nextStep: "Scroll to the 'Request this connector' section and check the form rendered fully, then call this tool again.",
3683          summary: "Could not find the 'Contact publisher' button."
3684        });
3685      }
3686
3687      var urlBefore = location.href;
3688      var baseBefore = urlBefore.split("#")[0];
3689      var actionAttr = String(form.getAttribute("action") || "").trim();
3690      var isNativeSubmit = actionAttr !== "" && !/^javascript:/i.test(actionAttr) && actionAttr.charAt(0) !== "#";
3691
3692      if (isNativeSubmit) {
3693        var dest = actionAttr;
3694        try { dest = new URL(actionAttr, location.href).toString(); } catch (e1) { dest = actionAttr; }
3695        var navPayload = {
3696          status: "navigated",
3697          action: ACTION,
3698          url: dest,
3699          fieldsSent: fieldsSent,
3700          pageKey: pageKey,
3701          formKey: formKey,
3702          validationErrors: [],
3703          confirmation: null,
3704          nextStep: "The browser is now loading " + dest + "; read the confirmation on that page once it has settled.",
3705          summary: "Clicked 'Contact publisher' on a native submit form, so the browser is navigating to the form action URL."
3706        };
3707        submitBtn.click();
3708        return reply(navPayload);
3709      }
3710
3711      submitBtn.click();
3712
3713      var deadline = Date.now() + 15000;
3714      var confirmation = "";
3715      var errors = [];
3716      var formGone = false;
3717      var navigated = false;
3718      while (Date.now() < deadline) {
3719        await sleep(400);
3720        if (location.href.split("#")[0] !== baseBefore) { navigated = true; break; }
3721        confirmation = readConfirmationText();
3722        errors = readValidationErrors();
3723        formGone = !document.querySelector(FORM_SEL);
3724        if (confirmation || errors.length > 0) { break; }
3725      }
3726      if (!navigated && !confirmation && errors.length === 0) {
3727        await sleep(1000);
3728        confirmation = readConfirmationText();
3729        errors = readValidationErrors();
3730        formGone = !document.querySelector(FORM_SEL);
3731      }
3732
3733      if (navigated) {
3734        return reply({
3735          status: "navigated",
3736          action: ACTION,
3737          url: location.href,
3738          fieldsSent: fieldsSent,
3739          pageKey: pageKey,
3740          formKey: formKey,
3741          validationErrors: [],
3742          confirmation: null,
3743          nextStep: "The page changed after clicking 'Contact publisher'; read the confirmation on the page that is now loaded.",
3744          summary: "Clicking 'Contact publisher' moved the browser to " + location.href + "."
3745        });
3746      }
3747
3748      if (errors.length > 0) {
3749        return reply({
3750          status: "error",
3751          action: ACTION,
3752          url: location.href,
3753          validationErrors: errors,
3754          confirmation: confirmation || null,
3755          fieldsSent: fieldsSent,
3756          pageKey: pageKey,
3757          formKey: formKey,
3758          submitted: false,
3759          reason: "The form reported validation problems after clicking 'Contact publisher': " + errors.join("; "),
3760          nextStep: "Fix the flagged values with optimizely-request-connector-contact-publisher, then call this tool again.",
3761          summary: "The connector request was not accepted; the form shows " + errors.length + " validation message(s)."
3762        });
3763      }
3764
3765      if (confirmation) {
3766        return reply({
3767          status: "ok",
3768          action: ACTION,
3769          url: location.href,
3770          validationErrors: [],
3771          confirmation: confirmation,
3772          fieldsSent: fieldsSent,
3773          pageKey: pageKey,
3774          formKey: formKey,
3775          submitted: true,
3776          summary: "Sent the connector request; the page responded: " + confirmation
3777        });
3778      }
3779
3780      return reply({
3781        status: "partial",
3782        action: ACTION,
3783        url: location.href,
3784        validationErrors: [],
3785        confirmation: null,
3786        observed: false,
3787        submitted: null,
3788        formStillPresent: !formGone,
3789        fieldsSent: fieldsSent,
3790        pageKey: pageKey,
3791        formKey: formKey,
3792        reason: formGone
3793          ? "The form was removed from the page after clicking 'Contact publisher', but no success panel text could be read, so the confirmation wording is unknown."
3794          : "Clicked 'Contact publisher' but no success panel and no validation text appeared within about 16 seconds.",
3795        nextStep: "Look at the 'Request this connector' section on the page to see whether a confirmation appeared, and only resend if it clearly did not.",
3796        summary: "Clicked 'Contact publisher' but could not read an outcome from the page, so the request may or may not have gone through."
3797      });
3798    } catch (err) {
3799      return reply({
3800        status: "error",
3801        action: ACTION,
3802        url: location.href,
3803        validationErrors: [],
3804        confirmation: null,
3805        reason: "The tool threw while submitting the form: " + (err && err.message ? err.message : String(err)),
3806        nextStep: "Check the 'Request this connector' section on the page for a confirmation before trying again.",
3807        summary: "Could not complete the connector request submission."
3808      });
3809    }
3810  }
3811});
3812
3813  /* ---- optimizely-browse-field-notes-library, optimizely-browse-field-notes-library-results ---- */
3814mc.registerToolIf("/field-notes/**", {
3815  name: "optimizely-browse-field-notes-library",
3816  description: "Browse and filter the Field Notes resource library on the current listing page: applies a topic filter and/or a free-text keyword and can jump to a page number. Returns the resources rendered in the listing as objects with title, content type, publication date, reading time where shown, topic tags, excerpt, thumbnail image URL and absolute URL, together with the listing's own 'Showing X-Y of N results' total and the filter options that are actually on the page at call time.",
3817  inputSchema: {
3818    type: "object",
3819    properties: {
3820      topic: {
3821        type: "string",
3822        description: "Topic to filter by, spelled as it appears on the page (for example AI, Analytics, Commerce, Content management, Experimentation, Marketing, Personalization). The options are read off the live page at call time; an unrecognised value comes back with availableOptions so the call can be retried."
3823      },
3824      keyword: {
3825        type: "string",
3826        description: "Free-text keyword to search the listing for."
3827      },
3828      page: {
3829        type: "integer",
3830        minimum: 1,
3831        description: "Listing page number to show. Defaults to the page currently displayed."
3832      },
3833      limit: {
3834        type: "integer",
3835        minimum: 1,
3836        maximum: 50,
3837        description: "Maximum number of resources to return. Defaults to 12."
3838      }
3839    },
3840    required: []
3841  },
3842  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
3843  execute: async function (args) {
3844    trackWebMCPTool("optimizely-browse-field-notes-library");
3845
3846    var input = args || {};
3847    var limit = Math.max(1, Math.min(50, parseInt(input.limit, 10) || 12));
3848    var COUNT_SELECTOR = 'div:has(> p + nav) > p';
3849    var ITEM_SELECTOR = 'a[href^="/field-notes/"]';
3850    var SCOPE_ATTR = "data-webmcp-fieldnotes";
3851    var SCOPED_ITEMS = '[data-webmcp-fieldnotes="scope"] a[href^="/field-notes/"]';
3852    var COUNT_RE = /showing\s+[\d,]+\s*[-–]\s*[\d,]+\s+of\s+[\d,]+\s+results?/i;
3853    var DATE_RE = /^[A-Za-z]{3,9}\.?\s+\d{1,2},\s*\d{4}$/;
3854    var TIME_RE = /^\d+\s*(minute|minutes|min|mins|hour|hours|hr|hrs)\b/i;
3855    var FIELD_MAP = {
3856      url: "@href",
3857      title: "h3",
3858      excerpt: "p",
3859      thumbnail: "img @src",
3860      thumbnailAlt: "img @alt",
3861      contentType: "div > div > div:nth-child(2) > div:first-child > div > span:nth-child(2)",
3862      publicationDate: "div > div > div:nth-child(2) > div:first-child > span:nth-child(2)",
3863      readingTime: "div > div > div:nth-child(2) > div:first-child > span:nth-child(3)"
3864    };
3865
3866    var startUrl = location.href;
3867    var unloading = false;
3868    var markUnload = function () { unloading = true; };
3869    window.addEventListener("beforeunload", markUnload);
3870    window.addEventListener("pagehide", markUnload);
3871
3872    function done(payload) {
3873      try {
3874        window.removeEventListener("beforeunload", markUnload);
3875        window.removeEventListener("pagehide", markUnload);
3876      } catch (e) {}
3877      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
3878    }
3879
3880    function txt(el) {
3881      return el && el.textContent ? el.textContent.replace(/\s+/g, " ").trim() : "";
3882    }
3883    function clean(v) {
3884      return v == null ? "" : String(v).replace(/\s+/g, " ").trim();
3885    }
3886    function norm(v) {
3887      return clean(v).toLowerCase();
3888    }
3889    function sleep(ms) {
3890      return new Promise(function (r) { setTimeout(r, ms); });
3891    }
3892    function isVisible(el) {
3893      if (!el) return false;
3894      try {
3895        if (el.closest && el.closest('[aria-hidden="true"]')) return false;
3896        if (el.getAttribute && el.getAttribute("aria-hidden") === "true") return false;
3897        var r = el.getBoundingClientRect();
3898        return !!(r.width || r.height || el.getClientRects().length);
3899      } catch (e) { return true; }
3900    }
3901    function isIconText(t) {
3902      return /^[a-z0-9]+(_[a-z0-9]+)*$/.test(t || "");
3903    }
3904
3905    var cachedCount = null;
3906    function findCountEl() {
3907      if (cachedCount && cachedCount.isConnected && /showing/i.test(txt(cachedCount))) return cachedCount;
3908      var el = null;
3909      try { el = document.querySelector(COUNT_SELECTOR); } catch (e) { el = null; }
3910      if (el && /showing/i.test(txt(el))) { cachedCount = el; return el; }
3911      var nodes = document.querySelectorAll("p, span");
3912      for (var i = 0; i < nodes.length; i++) {
3913        if (nodes[i].children.length) continue;
3914        if (COUNT_RE.test(txt(nodes[i]))) { cachedCount = nodes[i]; return nodes[i]; }
3915      }
3916      cachedCount = null;
3917      return null;
3918    }
3919
3920    function collectCards(scope) {
3921      var out = [];
3922      var seen = {};
3923      var list = scope.querySelectorAll(ITEM_SELECTOR);
3924      for (var i = 0; i < list.length; i++) {
3925        var node = list[i];
3926        var href = node.getAttribute("href") || "";
3927        var parts = href.split("?")[0].split("#")[0].split("/").filter(Boolean);
3928        if (parts.length < 3) continue;
3929        if (!node.querySelector("h3")) continue;
3930        if (!isVisible(node)) continue;
3931        if (seen[href]) continue;
3932        seen[href] = true;
3933        out.push(node);
3934      }
3935      return out;
3936    }
3937
3938    function findListingScope(countEl) {
3939      var node = countEl ? countEl.parentElement : null;
3940      var hops = 0;
3941      while (node && hops < 14) {
3942        if (collectCards(node).length >= 2) return node;
3943        node = node.parentElement;
3944        hops++;
3945      }
3946      var main = document.querySelector("main");
3947      if (main && collectCards(main).length >= 2) return main;
3948      return document.body;
3949    }
3950
3951    function signature() {
3952      var c = findCountEl();
3953      var scope = findListingScope(c);
3954      var hrefs = collectCards(scope).map(function (x) { return x.getAttribute("href"); }).join("|");
3955      return (c ? txt(c) : "") + "||" + hrefs;
3956    }
3957
3958    async function settle(baseline, timeout) {
3959      var t0 = Date.now();
3960      while (Date.now() - t0 < timeout) {
3961        await sleep(250);
3962        if (unloading) return "unloading";
3963        if (signature() !== baseline) {
3964          var stable = signature();
3965          var stableSince = Date.now();
3966          while (Date.now() - t0 < timeout) {
3967            await sleep(200);
3968            if (unloading) return "unloading";
3969            var now = signature();
3970            if (now === stable) {
3971              if (Date.now() - stableSince > 400) return "changed";
3972            } else {
3973              stable = now;
3974              stableSince = Date.now();
3975            }
3976          }
3977          return "changed";
3978        }
3979      }
3980      return "unchanged";
3981    }
3982
3983    function groupOf(label) {
3984      try {
3985        var node = label;
3986        var hops = 0;
3987        while (node && node !== document.body && hops < 6) {
3988          var prev = node.previousElementSibling;
3989          while (prev) {
3990            var head = null;
3991            if (prev.matches && prev.matches("h2, h3, h4, h5")) head = prev;
3992            else if (prev.querySelector) head = prev.querySelector("h2, h3, h4, h5");
3993            if (head && txt(head)) return txt(head);
3994            prev = prev.previousElementSibling;
3995          }
3996          node = node.parentElement;
3997          hops++;
3998        }
3999      } catch (e) {}
4000      return "";
4001    }
4002
4003    function facetControls() {
4004      var out = [];
4005      var labels = document.querySelectorAll("aside label");
4006      for (var i = 0; i < labels.length; i++) {
4007        var lb = labels[i];
4008        if (!isVisible(lb)) continue;
4009        var name = "";
4010        var hidden = lb.querySelector("input[name]");
4011        if (hidden) name = clean(hidden.getAttribute("name"));
4012        if (!name) {
4013          var spans = lb.querySelectorAll("span");
4014          for (var s = spans.length - 1; s >= 0; s--) {
4015            if (spans[s].children.length) continue;
4016            var st = txt(spans[s]);
4017            if (st) { name = st; break; }
4018          }
4019        }
4020        if (!name) name = txt(lb);
4021        if (!name) continue;
4022        var control = lb.querySelector('button[role="checkbox"]') || lb.querySelector("button") || lb.querySelector('input[type="checkbox"]') || lb;
4023        out.push({ label: name, group: groupOf(lb) || "Filters", el: control, kind: "facet" });
4024      }
4025      return out;
4026    }
4027
4028    function chipControls(countEl) {
4029      var rows = [];
4030      var seenParents = [];
4031      var buttons = document.querySelectorAll("button");
4032      for (var i = 0; i < buttons.length; i++) {
4033        var parent = buttons[i].parentElement;
4034        if (!parent || seenParents.indexOf(parent) !== -1) continue;
4035        seenParents.push(parent);
4036        if (parent.closest("nav") || parent.closest("footer") || parent.closest("header") || parent.closest("dialog") || parent.closest("aside")) continue;
4037        var kids = [];
4038        var numeric = 0;
4039        for (var j = 0; j < parent.children.length; j++) {
4040          var child = parent.children[j];
4041          if (!child.tagName || child.tagName !== "BUTTON") continue;
4042          if (!isVisible(child)) continue;
4043          var t = txt(child);
4044          if (!t || t === "..." || t.length > 40) continue;
4045          if (/^\d+$/.test(t)) numeric++;
4046          kids.push({ label: t, el: child, kind: "chip" });
4047        }
4048        if (kids.length >= 3 && numeric < kids.length / 2) rows.push({ parent: parent, options: kids });
4049      }
4050      if (!rows.length) return [];
4051      if (countEl) {
4052        for (var r = 0; r < rows.length; r++) {
4053          var hops = 0;
4054          var node = countEl;
4055          while (node && !node.contains(rows[r].parent)) { node = node.parentElement; hops++; }
4056          rows[r].distance = node ? hops : 999;
4057        }
4058        rows.sort(function (x, y) { return (x.distance || 0) - (y.distance || 0); });
4059      }
4060      return rows[0].options.map(function (o) {
4061        o.group = "Topics";
4062        return o;
4063      });
4064    }
4065
4066    function filterOptions() {
4067      var facets = facetControls();
4068      if (facets.length) return facets;
4069      return chipControls(findCountEl());
4070    }
4071
4072    function revealButtonPresent() {
4073      var btns = document.querySelectorAll("aside button, main button");
4074      for (var i = 0; i < btns.length; i++) {
4075        if (txt(btns[i]) === "..." && isVisible(btns[i]) && !btns[i].closest("nav")) return true;
4076      }
4077      return false;
4078    }
4079
4080    async function revealHiddenOptions() {
4081      var clicked = 0;
4082      var btns = document.querySelectorAll("aside button, main button");
4083      for (var i = 0; i < btns.length && clicked < 5; i++) {
4084        if (txt(btns[i]) !== "..." || !isVisible(btns[i])) continue;
4085        if (btns[i].closest("nav")) continue;
4086        try { btns[i].click(); clicked++; } catch (e) {}
4087      }
4088      if (clicked) await sleep(450);
4089      return clicked;
4090    }
4091
4092    async function expandFacetGroups() {
4093      var aside = document.querySelector("aside");
4094      if (!aside) return;
4095      var collapsed = aside.querySelectorAll('button[aria-expanded="false"]');
4096      var clicked = 0;
4097      for (var i = 0; i < collapsed.length && clicked < 5; i++) {
4098        try { collapsed[i].click(); clicked++; } catch (e) {}
4099      }
4100      if (!clicked) {
4101        var heads = aside.querySelectorAll("h4, h3");
4102        for (var h = 0; h < heads.length && clicked < 5; h++) {
4103          var header = heads[h].parentElement;
4104          var group = header ? header.parentElement : null;
4105          if (!group || group.querySelector("label")) continue;
4106          var toggle = header.querySelector("button");
4107          if (!toggle) continue;
4108          try { toggle.click(); clicked++; } catch (e) {}
4109        }
4110      }
4111      if (clicked) await sleep(400);
4112    }
4113
4114    async function openFilterPanel() {
4115      var btns = document.querySelectorAll("button");
4116      for (var i = 0; i < btns.length; i++) {
4117        var b = btns[i];
4118        if (!isVisible(b)) continue;
4119        if (b.closest("nav") || b.closest("footer") || b.closest("header")) continue;
4120        if (/^filters?(\s*\(\d+\))?$/i.test(txt(b))) {
4121          try { b.click(); } catch (e) { return false; }
4122          await sleep(700);
4123          return true;
4124        }
4125      }
4126      return false;
4127    }
4128
4129    function findSearchSelector() {
4130      var candidates = ['aside form input[type="search"]', 'form input[type="search"]', 'input[type="search"]'];
4131      for (var i = 0; i < candidates.length; i++) {
4132        var el = document.querySelector(candidates[i]);
4133        if (el && isVisible(el)) return candidates[i];
4134      }
4135      return null;
4136    }
4137
4138    function findSearchTrigger() {
4139      var btns = document.querySelectorAll("button");
4140      for (var i = 0; i < btns.length; i++) {
4141        var b = btns[i];
4142        if (!isVisible(b)) continue;
4143        if (b.closest("nav") || b.closest("footer") || b.closest("header")) continue;
4144        var label = b.getAttribute("aria-label") || b.getAttribute("title") || "";
4145        if (norm(txt(b)) === "search" || /search/i.test(label)) return b;
4146      }
4147      return null;
4148    }
4149
4150    function paginationNavs() {
4151      var out = [];
4152      var labelled = document.querySelectorAll('nav[aria-label="Pagination"]');
4153      for (var i = 0; i < labelled.length; i++) out.push(labelled[i]);
4154      var c = findCountEl();
4155      if (c && c.parentElement) {
4156        var navs = c.parentElement.querySelectorAll("nav");
4157        for (var k = 0; k < navs.length; k++) if (out.indexOf(navs[k]) === -1) out.push(navs[k]);
4158        if (c.parentElement.parentElement) {
4159          var navs2 = c.parentElement.parentElement.querySelectorAll("nav");
4160          for (var m = 0; m < navs2.length; m++) if (out.indexOf(navs2[m]) === -1) out.push(navs2[m]);
4161        }
4162      }
4163      return out;
4164    }
4165
4166    function findPageButton(n) {
4167      var navs = paginationNavs();
4168      for (var i = 0; i < navs.length; i++) {
4169        var direct = navs[i].querySelector('button[aria-label="Page ' + n + '"]');
4170        if (direct && isVisible(direct)) return direct;
4171        var btns = navs[i].querySelectorAll("button");
4172        for (var j = 0; j < btns.length; j++) {
4173          if (txt(btns[j]) === String(n) && isVisible(btns[j])) return btns[j];
4174        }
4175      }
4176      return null;
4177    }
4178
4179    function parseRange(text) {
4180      var out = { start: null, end: null, total: null, page: null, totalPages: null };
4181      if (!text) return out;
4182      var t = String(text).replace(/,/g, "");
4183      var m = /showing\s+(\d+)\s*[-–]\s*(\d+)\s+of\s+(\d+)/i.exec(t);
4184      if (!m) {
4185        var m2 = /of\s+(\d+)\s+results?/i.exec(t);
4186        if (m2) out.total = parseInt(m2[1], 10);
4187        return out;
4188      }
4189      out.start = parseInt(m[1], 10);
4190      out.end = parseInt(m[2], 10);
4191      out.total = parseInt(m[3], 10);
4192      var size = out.end - out.start + 1;
4193      if (size > 0) {
4194        out.page = Math.floor((out.start - 1) / size) + 1;
4195        out.totalPages = Math.ceil(out.total / size);
4196      }
4197      return out;
4198    }
4199
4200    function typeFromHref(href) {
4201      var m = /^\/field-notes\/([^\/]+)\//.exec(href || "");
4202      if (!m) return "";
4203      var map = { articles: "Article", guides: "Guide", videos: "Video", "customer-stories": "Customer story", events: "Event" };
4204      return map[m[1]] || "";
4205    }
4206
4207    function leafSpans(card) {
4208      var out = [];
4209      var spans = card.querySelectorAll("span");
4210      for (var i = 0; i < spans.length; i++) {
4211        var s = spans[i];
4212        if (s.children.length) continue;
4213        if (s.getAttribute("aria-hidden") === "true") continue;
4214        if (s.closest('[aria-hidden="true"]')) continue;
4215        var t = txt(s);
4216        if (!t) continue;
4217        out.push({ el: s, text: t });
4218      }
4219      return out;
4220    }
4221
4222    function describeCard(card, base) {
4223      base = base || {};
4224      var href = card.getAttribute("href") || "";
4225      var absolute = href;
4226      try { absolute = new URL(href, location.origin).href; } catch (e) {}
4227
4228      var leaves = leafSpans(card);
4229      var typeText = "";
4230      var dateText = "";
4231      var timeText = "";
4232      var loose = [];
4233      for (var i = 0; i < leaves.length; i++) {
4234        var t = leaves[i].text;
4235        if (isIconText(t)) continue;
4236        if (!typeText && i > 0 && isIconText(leaves[i - 1].text)) { typeText = t; continue; }
4237        if (!dateText && DATE_RE.test(t)) { dateText = t; continue; }
4238        if (!timeText && TIME_RE.test(t)) { timeText = t; continue; }
4239        loose.push(t);
4240      }
4241
4242      var titleEl = card.querySelector("h3");
4243      var title = clean(base.title) || txt(titleEl);
4244      var excerptEl = card.querySelector("p");
4245      var excerpt = clean(base.excerpt) || txt(excerptEl);
4246      var img = card.querySelector("img");
4247      var thumbnail = clean(base.thumbnail) || (img ? clean(img.getAttribute("src")) : "");
4248      var thumbnailAlt = base.thumbnailAlt != null ? clean(base.thumbnailAlt) : (img ? clean(img.getAttribute("alt")) : "");
4249
4250      var contentType = clean(base.contentType);
4251      if (!contentType || isIconText(contentType) || DATE_RE.test(contentType) || TIME_RE.test(contentType)) contentType = typeText;
4252      if (!contentType || isIconText(contentType)) contentType = typeFromHref(href);
4253
4254      var publicationDate = clean(base.publicationDate);
4255      if (!DATE_RE.test(publicationDate)) publicationDate = DATE_RE.test(dateText) ? dateText : "";
4256
4257      var readingTime = clean(base.readingTime);
4258      if (!TIME_RE.test(readingTime)) readingTime = TIME_RE.test(timeText) ? timeText : "";
4259
4260      var topics = [];
4261      for (var k = 0; k < loose.length; k++) {
4262        var tag = loose[k];
4263        if (!tag || tag.length > 60) continue;
4264        if (tag === contentType || tag === publicationDate || tag === readingTime) continue;
4265        if (title && tag === title) continue;
4266        if (excerpt && tag === excerpt) continue;
4267        if (DATE_RE.test(tag) || TIME_RE.test(tag)) continue;
4268        if (topics.indexOf(tag) === -1) topics.push(tag);
4269      }
4270
4271      return {
4272        title: title || null,
4273        contentType: contentType || null,
4274        publicationDate: publicationDate || null,
4275        readingTime: readingTime || null,
4276        topics: topics,
4277        excerpt: excerpt || null,
4278        thumbnail: thumbnail || null,
4279        thumbnailAlt: thumbnailAlt || null,
4280        url: absolute || null
4281      };
4282    }
4283
4284    async function buildResults() {
4285      var countEl = findCountEl();
4286      var scope = findListingScope(countEl);
4287      var scoped = scope !== document.body;
4288      var extracted = null;
4289      try {
4290        scope.setAttribute(SCOPE_ATTR, "scope");
4291        extracted = await extractItems(SCOPED_ITEMS, FIELD_MAP, limit * 4);
4292      } catch (e) {
4293        extracted = null;
4294      }
4295      var byUrl = {};
4296      if (extracted && extracted.results) {
4297        for (var i = 0; i < extracted.results.length; i++) {
4298          var row = extracted.results[i];
4299          if (row && row.url) byUrl[String(row.url)] = row;
4300        }
4301      }
4302      var cards = collectCards(scope);
4303      var results = [];
4304      for (var c = 0; c < cards.length && results.length < limit; c++) {
4305        var href = cards[c].getAttribute("href") || "";
4306        results.push(describeCard(cards[c], byUrl[href]));
4307      }
4308      try { scope.removeAttribute(SCOPE_ATTR); } catch (e) {}
4309      return { countEl: countEl, scope: scope, scoped: scoped, results: results, available: cards.length };
4310    }
4311
4312    function highlight(el) {
4313      if (!el || !el.style) return;
4314      try {
4315        var prevOutline = el.style.outline;
4316        var prevOffset = el.style.outlineOffset;
4317        el.style.outline = "3px solid #0037ff";
4318        el.style.outlineOffset = "2px";
4319        el.scrollIntoView({ behavior: "smooth", block: "center" });
4320        setTimeout(function () {
4321          try {
4322            el.style.outline = prevOutline;
4323            el.style.outlineOffset = prevOffset;
4324          } catch (e) {}
4325        }, 4000);
4326      } catch (e) {}
4327    }
4328
4329    function navigatedPayload(actionText) {
4330      return {
4331        status: "navigated",
4332        action: actionText,
4333        url: location.href,
4334        nextStep: "The browser is loading another page; call optimizely-browse-field-notes-library-results once it has settled to read the resources and the total.",
4335        summary: "The page started navigating, so the listing could not be read from here."
4336      };
4337    }
4338
4339    function navigateWithUrl(reason) {
4340      var isHub = /^\/field-notes\/?$/.test(location.pathname);
4341      var wantsFilter = !!(input.topic || input.keyword);
4342      var base = isHub && wantsFilter ? "/field-notes/articles" : location.pathname.replace(/\/+$/, "");
4343      var params = [];
4344      var values = {};
4345      if (input.topic) { params.push("topic={topic}"); values.topic = String(input.topic); }
4346      if (input.keyword) { params.push("search={keyword}"); values.keyword = String(input.keyword); }
4347      if (input.page) { params.push("page={page}"); values.page = String(parseInt(input.page, 10)); }
4348      var target = base + (params.length ? "?" + params.join("&") : "");
4349      var built = target;
4350      try {
4351        built = params.length ? buildUrl(target, values) : base;
4352      } catch (e) {
4353        built = base;
4354      }
4355      var absolute = built;
4356      try { absolute = new URL(built, location.origin).href; } catch (e) {}
4357      var payload = {
4358        status: "navigated",
4359        action: "load the Field Notes listing with the requested filters in the URL",
4360        url: absolute,
4361        appliedFilters: {
4362          topic: input.topic ? String(input.topic) : null,
4363          keyword: input.keyword ? String(input.keyword) : null,
4364          page: input.page ? parseInt(input.page, 10) : null
4365        },
4366        reason: "Fell back to URL parameters because " + reason + "." + (isHub && wantsFilter ? " Topic and keyword parameters take effect on the article listing at /field-notes/articles." : ""),
4367        nextStep: "The listing is loading at " + absolute + "; call optimizely-browse-field-notes-library-results to read the resources and the total once it has settled.",
4368        summary: "Loading the Field Notes listing with the requested filters applied through the URL."
4369      };
4370      var out = done(payload);
4371      location.assign(absolute);
4372      return out;
4373    }
4374
4375    try {
4376      var beforeCountEl = findCountEl();
4377      var countBefore = beforeCountEl ? txt(beforeCountEl) : "";
4378      var baseline = signature();
4379      var applied = { topic: null, keyword: null, page: null };
4380      var interacted = false;
4381      var settleOutcome = "not-needed";
4382      var wantTopic = clean(input.topic);
4383      var wantKeyword = clean(input.keyword);
4384      var wantPage = input.page != null && String(input.page) !== "" ? parseInt(input.page, 10) : null;
4385      if (isNaN(wantPage)) wantPage = null;
4386
4387      if (wantTopic) {
4388        await expandFacetGroups();
4389        await revealHiddenOptions();
4390        var options = filterOptions();
4391        if (!options.length) {
4392          var opened = await openFilterPanel();
4393          if (opened) {
4394            await expandFacetGroups();
4395            await revealHiddenOptions();
4396            options = filterOptions();
4397          }
4398        }
4399        if (!options.length) {
4400          return navigateWithUrl("no topic filter control could be found on this page");
4401        }
4402        var target = norm(wantTopic);
4403        var hit = null;
4404        for (var oi = 0; oi < options.length; oi++) {
4405          if (norm(options[oi].label) === target) { hit = options[oi]; break; }
4406        }
4407        if (!hit) {
4408          for (var oj = 0; oj < options.length; oj++) {
4409            if (norm(options[oj].label).indexOf(target) === 0) { hit = options[oj]; break; }
4410          }
4411        }
4412        if (!hit) {
4413          var labels = options.map(function (o) { return o.label; });
4414          var grouped = options.map(function (o) { return { label: o.label, group: o.group || null }; });
4415          return done({
4416            status: "error",
4417            action: "apply a topic filter to the Field Notes listing",
4418            url: location.href,
4419            reason: "The value '" + wantTopic + "' is not one of the filter options currently rendered on this listing.",
4420            availableOptions: labels,
4421            availableOptionsByGroup: grouped,
4422            optionsComplete: !revealButtonPresent(),
4423            nextStep: "Call the tool again with one of the values in availableOptions, or omit topic to browse the unfiltered listing.",
4424            summary: "Could not apply the topic '" + wantTopic + "' because the listing offers a different set of filter values."
4425          });
4426        }
4427        try {
4428          hit.el.click();
4429          interacted = true;
4430          applied.topic = hit.label;
4431        } catch (e) {
4432          return navigateWithUrl("the topic control '" + hit.label + "' could not be clicked");
4433        }
4434        await sleep(300);
4435        if (unloading) return done(navigatedPayload("apply the topic filter '" + hit.label + "'"));
4436      }
4437
4438      if (wantKeyword) {
4439        var searchSelector = findSearchSelector();
4440        if (!searchSelector) {
4441          var trigger = findSearchTrigger();
4442          if (trigger) {
4443            try { trigger.click(); } catch (e) {}
4444            try { await waitForElement('input[type="search"]', 5000); } catch (e) {}
4445            searchSelector = findSearchSelector();
4446          }
4447        }
4448        if (!searchSelector) {
4449          return navigateWithUrl("the keyword search input could not be opened on this page");
4450        }
4451        var inputEl = document.querySelector(searchSelector);
4452        var form = inputEl && inputEl.closest ? inputEl.closest("form") : null;
4453        if (form && form.getAttribute("action")) {
4454          return navigateWithUrl("the search form submits natively to " + form.getAttribute("action"));
4455        }
4456        try {
4457          await fillInput(searchSelector, wantKeyword);
4458          applied.keyword = wantKeyword;
4459          interacted = true;
4460        } catch (e) {
4461          return navigateWithUrl("the keyword could not be typed into the search input");
4462        }
4463        try {
4464          ["keydown", "keyup"].forEach(function (type) {
4465            inputEl.dispatchEvent(new KeyboardEvent(type, { key: "Enter", code: "Enter", keyCode: 13, which: 13, bubbles: true }));
4466          });
4467        } catch (e) {}
4468        await sleep(300);
4469        if (unloading) return done(navigatedPayload("search the Field Notes listing for '" + wantKeyword + "'"));
4470      }
4471
4472      if (interacted) {
4473        settleOutcome = await settle(baseline, 9000);
4474        if (unloading) return done(navigatedPayload("apply the requested Field Notes filters"));
4475      }
4476
4477      if (wantPage) {
4478        var nowRange = parseRange(findCountEl() ? txt(findCountEl()) : "");
4479        if (nowRange.page !== wantPage) {
4480          var pageBtn = findPageButton(wantPage);
4481          if (!pageBtn) {
4482            return navigateWithUrl("the pagination does not expose a button for page " + wantPage);
4483          }
4484          var pageBaseline = signature();
4485          try {
4486            pageBtn.click();
4487            interacted = true;
4488            applied.page = wantPage;
4489          } catch (e) {
4490            return navigateWithUrl("the button for page " + wantPage + " could not be clicked");
4491          }
4492          await sleep(300);
4493          if (unloading) return done(navigatedPayload("open page " + wantPage + " of the Field Notes listing"));
4494          settleOutcome = await settle(pageBaseline, 9000);
4495          if (unloading) return done(navigatedPayload("open page " + wantPage + " of the Field Notes listing"));
4496        } else {
4497          applied.page = wantPage;
4498        }
4499      }
4500
4501      var built = await buildResults();
4502      var countEl = built.countEl;
4503      var countText = countEl ? txt(countEl) : "";
4504      var range = parseRange(countText);
4505      var resultCount = range.total;
4506      var countSource = resultCount != null ? "listing-count-text" : "none";
4507      if (resultCount == null) {
4508        try {
4509          var rc = readResultCount(COUNT_SELECTOR, null);
4510          if (typeof rc === "number" && !isNaN(rc) && rc > 0 && countText) {
4511            resultCount = rc;
4512            countSource = "listing-count-text";
4513          }
4514        } catch (e) {}
4515      }
4516      if (resultCount == null && built.results.length) {
4517        resultCount = built.results.length;
4518        countSource = "rendered-items";
4519      }
4520
4521      highlight(built.scope);
4522
4523      var liveOptions = filterOptions();
4524      var observed = !!(countText || built.results.length);
4525      var filterConfirmed = null;
4526      if (applied.topic || applied.keyword || applied.page) {
4527        filterConfirmed = settleOutcome === "changed" || (countText && countText !== countBefore) || location.href !== startUrl;
4528      }
4529
4530      var actionBits = [];
4531      if (applied.topic) actionBits.push("topic " + applied.topic);
4532      if (applied.keyword) actionBits.push("keyword '" + applied.keyword + "'");
4533      if (applied.page) actionBits.push("page " + applied.page);
4534      var actionText = actionBits.length
4535        ? "filter the Field Notes listing by " + actionBits.join(" and ")
4536        : "read the Field Notes listing";
4537
4538      var payload = {
4539        status: observed ? "ok" : "partial",
4540        action: actionText,
4541        url: location.href,
4542        listingPath: location.pathname,
4543        observed: observed,
4544        appliedFilters: {
4545          topic: applied.topic,
4546          keyword: applied.keyword,
4547          page: applied.page
4548        },
4549        filterConfirmed: filterConfirmed,
4550        countText: countText || null,
4551        resultCount: resultCount == null ? null : resultCount,
4552        countSource: countSource,
4553        rangeStart: range.start,
4554        rangeEnd: range.end,
4555        page: range.page != null ? range.page : applied.page,
4556        totalPages: range.totalPages,
4557        renderedCount: built.results.length,
4558        truncated: built.available > built.results.length,
4559        scopedToListing: built.scoped,
4560        results: built.results,
4561        filterOptions: liveOptions.map(function (o) { return { label: o.label, group: o.group || null }; }),
4562        filterOptionsComplete: !revealButtonPresent(),
4563        summary: ""
4564      };
4565
4566      if (!observed) {
4567        payload.reason = "Neither the 'Showing X-Y of N results' element nor any resource card could be read after the interaction settled.";
4568        payload.nextStep = "Reload " + location.pathname + " and call the tool again, or call it with a page number to force the listing to re-render.";
4569        payload.summary = "Could not read the Field Notes listing: no count element and no resource cards were present after the page settled.";
4570      } else {
4571        var bits = [];
4572        bits.push("Read " + built.results.length + " Field Notes resource" + (built.results.length === 1 ? "" : "s") + " from " + location.pathname);
4573        if (countText) bits.push("(" + countText + ")");
4574        if (applied.topic) bits.push("with topic " + applied.topic);
4575        if (applied.keyword) bits.push("matching '" + applied.keyword + "'");
4576        if (applied.page) bits.push("on page " + applied.page);
4577        var sentence = bits.join(" ") + ".";
4578        if (filterConfirmed === false) {
4579          sentence += " The listing did not visibly change after the filter was applied, so the numbers may still be the unfiltered ones.";
4580          payload.nextStep = "Call the tool again, or open the listing with topic and search query parameters, to confirm the filter took effect.";
4581        }
4582        payload.summary = sentence;
4583      }
4584
4585      return done(payload);
4586    } catch (err) {
4587      return done({
4588        status: "error",
4589        action: "browse the Field Notes listing",
4590        url: location.href,
4591        reason: "The tool hit an unexpected page error: " + (err && err.message ? err.message : String(err)),
4592        nextStep: "Reload the Field Notes listing page and call the tool again.",
4593        summary: "Could not browse the Field Notes listing because of an unexpected page error."
4594      });
4595    }
4596  }
4597});
4598
4599mc.registerToolIf("/field-notes/**", {
4600  name: "optimizely-browse-field-notes-library-results",
4601  description: "Read the Field Notes listing that is currently loaded and return the resources it shows as objects with title, content type, publication date, reading time where shown, topic tags, excerpt, thumbnail image URL and absolute URL, plus the listing's own 'Showing X-Y of N results' total, the current page and the filter options rendered on the page. Use this after a call that loaded a filtered listing URL.",
4602  inputSchema: {
4603    type: "object",
4604    properties: {
4605      limit: {
4606        type: "integer",
4607        minimum: 1,
4608        maximum: 50,
4609        description: "Maximum number of resources to return. Defaults to 12."
4610      }
4611    },
4612    required: []
4613  },
4614  annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
4615  execute: async function (args) {
4616    trackWebMCPTool("optimizely-browse-field-notes-library-results");
4617
4618    var input = args || {};
4619    var limit = Math.max(1, Math.min(50, parseInt(input.limit, 10) || 12));
4620    var COUNT_SELECTOR = 'div:has(> p + nav) > p';
4621    var ITEM_SELECTOR = 'a[href^="/field-notes/"]';
4622    var SCOPE_ATTR = "data-webmcp-fieldnotes";
4623    var SCOPED_ITEMS = '[data-webmcp-fieldnotes="scope"] a[href^="/field-notes/"]';
4624    var COUNT_RE = /showing\s+[\d,]+\s*[-–]\s*[\d,]+\s+of\s+[\d,]+\s+results?/i;
4625    var DATE_RE = /^[A-Za-z]{3,9}\.?\s+\d{1,2},\s*\d{4}$/;
4626    var TIME_RE = /^\d+\s*(minute|minutes|min|mins|hour|hours|hr|hrs)\b/i;
4627    var FIELD_MAP = {
4628      url: "@href",
4629      title: "h3",
4630      excerpt: "p",
4631      thumbnail: "img @src",
4632      thumbnailAlt: "img @alt",
4633      contentType: "div > div > div:nth-child(2) > div:first-child > div > span:nth-child(2)",
4634      publicationDate: "div > div > div:nth-child(2) > div:first-child > span:nth-child(2)",
4635      readingTime: "div > div > div:nth-child(2) > div:first-child > span:nth-child(3)"
4636    };
4637
4638    function wrap(payload) {
4639      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
4640    }
4641    function txt(el) {
4642      return el && el.textContent ? el.textContent.replace(/\s+/g, " ").trim() : "";
4643    }
4644    function clean(v) {
4645      return v == null ? "" : String(v).replace(/\s+/g, " ").trim();
4646    }
4647    function sleep(ms) {
4648      return new Promise(function (r) { setTimeout(r, ms); });
4649    }
4650    function isVisible(el) {
4651      if (!el) return false;
4652      try {
4653        if (el.closest && el.closest('[aria-hidden="true"]')) return false;
4654        if (el.getAttribute && el.getAttribute("aria-hidden") === "true") return false;
4655        var r = el.getBoundingClientRect();
4656        return !!(r.width || r.height || el.getClientRects().length);
4657      } catch (e) { return true; }
4658    }
4659    function isIconText(t) {
4660      return /^[a-z0-9]+(_[a-z0-9]+)*$/.test(t || "");
4661    }
4662    function findCountEl() {
4663      var el = null;
4664      try { el = document.querySelector(COUNT_SELECTOR); } catch (e) { el = null; }
4665      if (el && /showing/i.test(txt(el))) return el;
4666      var nodes = document.querySelectorAll("p, span");
4667      for (var i = 0; i < nodes.length; i++) {
4668        if (nodes[i].children.length) continue;
4669        if (COUNT_RE.test(txt(nodes[i]))) return nodes[i];
4670      }
4671      return null;
4672    }
4673    function collectCards(scope) {
4674      var out = [];
4675      var seen = {};
4676      var list = scope.querySelectorAll(ITEM_SELECTOR);
4677      for (var i = 0; i < list.length; i++) {
4678        var node = list[i];
4679        var href = node.getAttribute("href") || "";
4680        var parts = href.split("?")[0].split("#")[0].split("/").filter(Boolean);
4681        if (parts.length < 3) continue;
4682        if (!node.querySelector("h3")) continue;
4683        if (!isVisible(node)) continue;
4684        if (seen[href]) continue;
4685        seen[href] = true;
4686        out.push(node);
4687      }
4688      return out;
4689    }
4690    function findListingScope(countEl) {
4691      var node = countEl ? countEl.parentElement : null;
4692      var hops = 0;
4693      while (node && hops < 14) {
4694        if (collectCards(node).length >= 2) return node;
4695        node = node.parentElement;
4696        hops++;
4697      }
4698      var main = document.querySelector("main");
4699      if (main && collectCards(main).length >= 2) return main;
4700      return document.body;
4701    }
4702    function parseRange(text) {
4703      var out = { start: null, end: null, total: null, page: null, totalPages: null };
4704      if (!text) return out;
4705      var t = String(text).replace(/,/g, "");
4706      var m = /showing\s+(\d+)\s*[-–]\s*(\d+)\s+of\s+(\d+)/i.exec(t);
4707      if (!m) {
4708        var m2 = /of\s+(\d+)\s+results?/i.exec(t);
4709        if (m2) out.total = parseInt(m2[1], 10);
4710        return out;
4711      }
4712      out.start = parseInt(m[1], 10);
4713      out.end = parseInt(m[2], 10);
4714      out.total = parseInt(m[3], 10);
4715      var size = out.end - out.start + 1;
4716      if (size > 0) {
4717        out.page = Math.floor((out.start - 1) / size) + 1;
4718        out.totalPages = Math.ceil(out.total / size);
4719      }
4720      return out;
4721    }
4722    function typeFromHref(href) {
4723      var m = /^\/field-notes\/([^\/]+)\//.exec(href || "");
4724      if (!m) return "";
4725      var map = { articles: "Article", guides: "Guide", videos: "Video", "customer-stories": "Customer story", events: "Event" };
4726      return map[m[1]] || "";
4727    }
4728    function leafSpans(card) {
4729      var out = [];
4730      var spans = card.querySelectorAll("span");
4731      for (var i = 0; i < spans.length; i++) {
4732        var s = spans[i];
4733        if (s.children.length) continue;
4734        if (s.getAttribute("aria-hidden") === "true") continue;
4735        if (s.closest('[aria-hidden="true"]')) continue;
4736        var t = txt(s);
4737        if (!t) continue;
4738        out.push({ el: s, text: t });
4739      }
4740      return out;
4741    }
4742    function describeCard(card, base) {
4743      base = base || {};
4744      var href = card.getAttribute("href") || "";
4745      var absolute = href;
4746      try { absolute = new URL(href, location.origin).href; } catch (e) {}
4747      var leaves = leafSpans(card);
4748      var typeText = "";
4749      var dateText = "";
4750      var timeText = "";
4751      var loose = [];
4752      for (var i = 0; i < leaves.length; i++) {
4753        var t = leaves[i].text;
4754        if (isIconText(t)) continue;
4755        if (!typeText && i > 0 && isIconText(leaves[i - 1].text)) { typeText = t; continue; }
4756        if (!dateText && DATE_RE.test(t)) { dateText = t; continue; }
4757        if (!timeText && TIME_RE.test(t)) { timeText = t; continue; }
4758        loose.push(t);
4759      }
4760      var title = clean(base.title) || txt(card.querySelector("h3"));
4761      var excerpt = clean(base.excerpt) || txt(card.querySelector("p"));
4762      var img = card.querySelector("img");
4763      var thumbnail = clean(base.thumbnail) || (img ? clean(img.getAttribute("src")) : "");
4764      var thumbnailAlt = base.thumbnailAlt != null ? clean(base.thumbnailAlt) : (img ? clean(img.getAttribute("alt")) : "");
4765      var contentType = clean(base.contentType);
4766      if (!contentType || isIconText(contentType) || DATE_RE.test(contentType) || TIME_RE.test(contentType)) contentType = typeText;
4767      if (!contentType || isIconText(contentType)) contentType = typeFromHref(href);
4768      var publicationDate = clean(base.publicationDate);
4769      if (!DATE_RE.test(publicationDate)) publicationDate = DATE_RE.test(dateText) ? dateText : "";
4770      var readingTime = clean(base.readingTime);
4771      if (!TIME_RE.test(readingTime)) readingTime = TIME_RE.test(timeText) ? timeText : "";
4772      var topics = [];
4773      for (var k = 0; k < loose.length; k++) {
4774        var tag = loose[k];
4775        if (!tag || tag.length > 60) continue;
4776        if (tag === contentType || tag === publicationDate || tag === readingTime) continue;
4777        if (title && tag === title) continue;
4778        if (excerpt && tag === excerpt) continue;
4779        if (DATE_RE.test(tag) || TIME_RE.test(tag)) continue;
4780        if (topics.indexOf(tag) === -1) topics.push(tag);
4781      }
4782      return {
4783        title: title || null,
4784        contentType: contentType || null,
4785        publicationDate: publicationDate || null,
4786        readingTime: readingTime || null,
4787        topics: topics,
4788        excerpt: excerpt || null,
4789        thumbnail: thumbnail || null,
4790        thumbnailAlt: thumbnailAlt || null,
4791        url: absolute || null
4792      };
4793    }
4794    function highlight(el) {
4795      if (!el || !el.style) return;
4796      try {
4797        var prevOutline = el.style.outline;
4798        var prevOffset = el.style.outlineOffset;
4799        el.style.outline = "3px solid #0037ff";
4800        el.style.outlineOffset = "2px";
4801        el.scrollIntoView({ behavior: "smooth", block: "center" });
4802        setTimeout(function () {
4803          try {
4804            el.style.outline = prevOutline;
4805            el.style.outlineOffset = prevOffset;
4806          } catch (e) {}
4807        }, 4000);
4808      } catch (e) {}
4809    }
4810
4811    try {
4812      try { await waitForElement(COUNT_SELECTOR, 9000); } catch (e) {}
4813      await sleep(400);
4814
4815      var countEl = findCountEl();
4816      var scope = findListingScope(countEl);
4817      var extracted = null;
4818      try {
4819        scope.setAttribute(SCOPE_ATTR, "scope");
4820        extracted = await extractItems(SCOPED_ITEMS, FIELD_MAP, limit * 4);
4821      } catch (e) {
4822        extracted = null;
4823      }
4824      var byUrl = {};
4825      if (extracted && extracted.results) {
4826        for (var i = 0; i < extracted.results.length; i++) {
4827          var row = extracted.results[i];
4828          if (row && row.url) byUrl[String(row.url)] = row;
4829        }
4830      }
4831      var cards = collectCards(scope);
4832      var results = [];
4833      for (var c = 0; c < cards.length && results.length < limit; c++) {
4834        results.push(describeCard(cards[c], byUrl[cards[c].getAttribute("href") || ""]));
4835      }
4836      try { scope.removeAttribute(SCOPE_ATTR); } catch (e) {}
4837
4838      var countText = countEl ? txt(countEl) : "";
4839      var range = parseRange(countText);
4840      var resultCount = range.total;
4841      var countSource = resultCount != null ? "listing-count-text" : "none";
4842      if (resultCount == null && results.length) {
4843        resultCount = results.length;
4844        countSource = "rendered-items";
4845      }
4846      highlight(scope);
4847
4848      var observed = !!(countText || results.length);
4849      var payload = {
4850        status: observed ? "ok" : "partial",
4851        action: "read the Field Notes listing currently on screen",
4852        url: location.href,
4853        listingPath: location.pathname,
4854        observed: observed,
4855        countText: countText || null,
4856        resultCount: resultCount == null ? null : resultCount,
4857        countSource: countSource,
4858        rangeStart: range.start,
4859        rangeEnd: range.end,
4860        page: range.page,
4861        totalPages: range.totalPages,
4862        renderedCount: results.length,
4863        truncated: cards.length > results.length,
4864        scopedToListing: scope !== document.body,
4865        results: results,
4866        summary: ""
4867      };
4868      if (!observed) {
4869        payload.reason = "The listing count element never appeared and no resource cards with a heading were present on this page.";
4870        payload.nextStep = "Reload " + location.pathname + " and call this tool again once the listing has rendered.";
4871        payload.summary = "Could not read the Field Notes listing: nothing had rendered by the time the tool looked.";
4872      } else {
4873        payload.summary = "Read " + results.length + " Field Notes resource" + (results.length === 1 ? "" : "s") + " from " + location.pathname + (countText ? " (" + countText + ")" : "") + ".";
4874      }
4875      return wrap(payload);
4876    } catch (err) {
4877      return wrap({
4878        status: "error",
4879        action: "read the Field Notes listing currently on screen",
4880        url: location.href,
4881        reason: "The tool hit an unexpected page error: " + (err && err.message ? err.message : String(err)),
4882        nextStep: "Reload the Field Notes listing page and call the tool again.",
4883        summary: "Could not read the Field Notes listing because of an unexpected page error."
4884      });
4885    }
4886  }
4887});
4888
4889  /* ---- optimizely-find-optimizely-events, optimizely-find-optimizely-events-results, optimizely-find-optimizely-events-open ---- */
4890mc.registerToolIf("/field-notes/events/**", {
4891  name: "optimizely-find-optimizely-events",
4892  description: "Search, filter and page through the Optimizely events and webinars listing at /field-notes/events. Applies a free-text query, a topic and a page number to the listing and returns the events as objects with name, date, format ('On-location event' or 'Online event'), topic tags, description, card image and event-page URL, plus the page's own 'Showing X-Y of N results' counter. When the requested query, topic and page are the ones already showing, it reads and returns them directly; otherwise it opens the matching listing URL and names the tool that reads the loaded page.",
4893  inputSchema: {
4894    type: "object",
4895    properties: {
4896      search: {
4897        type: "string",
4898        description: "Free-text query to apply to the listing, for example 'opticon' or 'webinar'. Omit to keep the query already applied; pass an empty string to clear it."
4899      },
4900      topic: {
4901        type: "string",
4902        description: "Topic to filter by, for example 'AI'. The available topics are read from the live page at call time; a value that is not offered comes back with the list that is actually available."
4903      },
4904      page: {
4905        type: "integer",
4906        minimum: 1,
4907        description: "1-based page of the card grid (6 events per page). Omit to keep the current page, or to start at page 1 when the query or topic changes."
4908      },
4909      limit: {
4910        type: "integer",
4911        minimum: 1,
4912        maximum: 60,
4913        description: "Maximum number of events to return when the listing is read in place. Defaults to 24."
4914      }
4915    },
4916    required: []
4917  },
4918  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
4919  execute: async function (args) {
4920    trackWebMCPTool("optimizely-find-optimizely-events");
4921    var a = args || {};
4922    var LISTING_PATH = "/field-notes/events";
4923    var ITEM_SELECTOR = 'main a[href^="/field-notes/events/"]';
4924    var COUNT_SELECTOR = "main div:has(> p):has(> nav) > p";
4925    var FIELD_MAP = {
4926      url: "@href",
4927      name: "h3",
4928      date: "div > div > div:nth-child(2) > div:nth-child(1) > span",
4929      format: "div > div > div:nth-child(2) > div:nth-child(1) > div > span:nth-child(2)",
4930      description: "p",
4931      topics: "div > div > div:nth-child(2) > div:last-child > span",
4932      image: "img @src"
4933    };
4934    var startUrl = location.href;
4935
4936    function wrap(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
4937    function txt(el) { return el && el.textContent ? el.textContent.replace(/\s+/g, " ").trim() : ""; }
4938    function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
4939    function cards() {
4940      try { return Array.prototype.slice.call(document.querySelectorAll(ITEM_SELECTOR)); } catch (e) { return []; }
4941    }
4942    function highlight(el) {
4943      if (!el) return;
4944      try {
4945        el.scrollIntoView({ behavior: "smooth", block: "center" });
4946        el.style.outline = "3px solid #0037ff";
4947        el.style.outlineOffset = "2px";
4948        setTimeout(function () { try { el.style.outline = ""; el.style.outlineOffset = ""; } catch (e) {} }, 6000);
4949      } catch (e) {}
4950    }
4951    function regionFor(list) {
4952      if (!list.length) return document.querySelector("main");
4953      var node = list[0].parentElement, best = list[0], hops = 0;
4954      while (node && hops < 6) {
4955        if (node.querySelectorAll('a[href^="/field-notes/events/"]').length >= list.length) { best = node; break; }
4956        node = node.parentElement;
4957        hops++;
4958      }
4959      return best;
4960    }
4961    function counterElement() {
4962      var el = null;
4963      try { el = document.querySelector(COUNT_SELECTOR); } catch (e) { el = null; }
4964      if (el) return el;
4965      var ps = Array.prototype.slice.call(document.querySelectorAll("main p"));
4966      for (var i = 0; i < ps.length; i++) {
4967        if (/showing\s+[\d,]+\s*-\s*[\d,]+\s+of\s+[\d,]+/i.test(txt(ps[i]))) return ps[i];
4968      }
4969      return null;
4970    }
4971    function emptyStateText() {
4972      var nodes = Array.prototype.slice.call(document.querySelectorAll("main p, main h2, main h3, main div"));
4973      for (var i = 0; i < nodes.length; i++) {
4974        var t = txt(nodes[i]);
4975        if (t && t.length < 200 && /no results found/i.test(t)) return t;
4976      }
4977      return "";
4978    }
4979    async function liveTopics() {
4980      var out = [], seen = {};
4981      function add(t) {
4982        t = (t || "").replace(/\s+/g, " ").trim();
4983        if (!t || t.length > 48) return;
4984        if (/^(filter|filters|search|clear|clear all|apply|reset|reset filters|close|all|all topics|sort|previous|next)$/i.test(t)) return;
4985        var k = t.toLowerCase();
4986        if (seen[k]) return;
4987        seen[k] = 1;
4988        out.push(t);
4989      }
4990      var panelBtn = null;
4991      var btns = Array.prototype.slice.call(document.querySelectorAll("main button"));
4992      for (var i = 0; i < btns.length; i++) {
4993        var lbl = txt(btns[i]) || btns[i].getAttribute("aria-label") || btns[i].getAttribute("title") || "";
4994        if (/^filters?$/i.test(lbl.trim())) { panelBtn = btns[i]; break; }
4995      }
4996      var opened = false;
4997      if (panelBtn) {
4998        try { panelBtn.click(); opened = true; await sleep(800); } catch (e) {}
4999      }
5000      Array.prototype.slice.call(document.querySelectorAll("main select option")).forEach(function (o) {
5001        if (o.getAttribute("value") !== "") add(txt(o));
5002      });
5003      Array.prototype.slice.call(document.querySelectorAll("main label")).forEach(function (l) { add(txt(l)); });
5004      Array.prototype.slice.call(document.querySelectorAll('main input[type="checkbox"], main input[type="radio"]')).forEach(function (inp) {
5005        add(inp.getAttribute("aria-label") || inp.getAttribute("value") || "");
5006      });
5007      Array.prototype.slice.call(document.querySelectorAll('main [role="option"], main [role="menuitemcheckbox"], main [role="checkbox"]')).forEach(function (n) {
5008        add(n.getAttribute("aria-label") || txt(n));
5009      });
5010      if (opened) { try { panelBtn.click(); await sleep(200); } catch (e) {} }
5011      cards().forEach(function (c) {
5012        Array.prototype.slice.call(c.querySelectorAll(FIELD_MAP.topics)).forEach(function (s) {
5013          if (s.getAttribute("aria-hidden") === "true") return;
5014          add(txt(s));
5015        });
5016      });
5017      return out;
5018    }
5019    async function readListing(limit) {
5020      try { await waitForElement(ITEM_SELECTOR, 8000); } catch (e) {}
5021      try { await waitForElement(COUNT_SELECTOR, 2500); } catch (e) {}
5022      await sleep(350);
5023      var list = cards();
5024      highlight(regionFor(list));
5025      var data = null;
5026      try { data = await extractItems(ITEM_SELECTOR, FIELD_MAP, limit); } catch (e) { data = null; }
5027      var results = (data && data.results) || [];
5028      var truncated = !!(data && data.truncated);
5029      var counterText = txt(counterElement());
5030      var total = null;
5031      var m = counterText.match(/of\s+([\d,]+)/i);
5032      if (m) total = parseInt(m[1].replace(/,/g, ""), 10);
5033      if (total === null || isNaN(total)) {
5034        total = null;
5035        var parsed = null;
5036        try { parsed = await readResultCount(COUNT_SELECTOR, null); } catch (e) { parsed = null; }
5037        if (typeof parsed === "number" && !isNaN(parsed)) total = parsed;
5038      }
5039      var empty = emptyStateText();
5040      if (total === null && empty) total = 0;
5041      if (total === null && results.length) total = results.length;
5042      return {
5043        results: results,
5044        truncated: truncated,
5045        counterText: counterText,
5046        total: total,
5047        empty: empty,
5048        shown: results.length
5049      };
5050    }
5051
5052    try {
5053      var onListing = location.pathname.replace(/\/+$/, "") === LISTING_PATH;
5054      var params = new URLSearchParams(location.search);
5055      var curSearch = onListing ? (params.get("search") || "").trim() : "";
5056      var curTopic = onListing ? (params.get("topic") || "").trim() : "";
5057      var curPage = onListing ? ((params.get("page") || "1").trim() || "1") : "1";
5058
5059      var limit = parseInt(a.limit, 10);
5060      if (isNaN(limit) || limit < 1) limit = 24;
5061      if (limit > 60) limit = 60;
5062
5063      var tgtSearch = typeof a.search === "string" ? a.search.trim() : curSearch;
5064      var tgtTopic = typeof a.topic === "string" ? a.topic.trim() : curTopic;
5065
5066      if (tgtTopic && onListing && tgtTopic.toLowerCase() !== curTopic.toLowerCase()) {
5067        var options = await liveTopics();
5068        var exact = null, partial = null;
5069        for (var i = 0; i < options.length; i++) {
5070          if (options[i].toLowerCase() === tgtTopic.toLowerCase()) { exact = options[i]; break; }
5071          if (!partial && options[i].toLowerCase().indexOf(tgtTopic.toLowerCase()) !== -1) partial = options[i];
5072        }
5073        if (exact) {
5074          tgtTopic = exact;
5075        } else if (partial) {
5076          tgtTopic = partial;
5077        } else if (options.length >= 3) {
5078          return wrap({
5079            status: "error",
5080            action: "check the requested topic against the topics offered on the events listing",
5081            url: location.href,
5082            requestedTopic: tgtTopic,
5083            availableOptions: options,
5084            reason: "The topic '" + tgtTopic + "' does not match any topic read from the listing's Filter control or from the topic tags on the event cards at call time.",
5085            nextStep: "Call optimizely-find-optimizely-events again with one of availableOptions as topic, or drop topic and pass a search query instead.",
5086            summary: "Topic '" + tgtTopic + "' was not among the topics available on the events listing."
5087          });
5088        }
5089      }
5090
5091      var reqPage = parseInt(a.page, 10);
5092      var tgtPage;
5093      if (!isNaN(reqPage) && reqPage >= 1) tgtPage = String(reqPage);
5094      else if (tgtSearch !== curSearch || tgtTopic !== curTopic) tgtPage = "1";
5095      else tgtPage = curPage;
5096
5097      if (onListing && tgtSearch === curSearch && tgtTopic === curTopic && tgtPage === curPage) {
5098        var read = await readListing(limit);
5099        var applied = { search: tgtSearch, topic: tgtTopic, page: parseInt(tgtPage, 10) };
5100        if (read.results.length) {
5101          return wrap({
5102            status: "ok",
5103            action: "read the events listing already showing the requested query, topic and page",
5104            url: location.href,
5105            appliedFilters: applied,
5106            observed: true,
5107            resultCount: read.total,
5108            shownOnPage: read.shown,
5109            truncated: read.truncated,
5110            counterText: read.counterText,
5111            results: read.results,
5112            summary: "Read " + read.results.length + " event(s) from the listing" + (read.total !== null ? "; the page counter reports " + read.total + " matching result(s)" : "") + "."
5113          });
5114        }
5115        if (read.empty) {
5116          var topicsNow = await liveTopics();
5117          return wrap({
5118            status: "ok",
5119            action: "read the events listing already showing the requested query, topic and page",
5120            url: location.href,
5121            appliedFilters: applied,
5122            observed: true,
5123            resultCount: 0,
5124            shownOnPage: 0,
5125            truncated: false,
5126            counterText: read.counterText,
5127            emptyState: read.empty,
5128            results: [],
5129            availableOptions: topicsNow,
5130            nextStep: "Call optimizely-find-optimizely-events again with a broader search, a topic from availableOptions, or page 1.",
5131            summary: "The listing reported no results for this query, topic and page."
5132          });
5133        }
5134        return wrap({
5135          status: "partial",
5136          action: "read the events listing already showing the requested query, topic and page",
5137          url: location.href,
5138          appliedFilters: applied,
5139          observed: false,
5140          resultCount: null,
5141          shownOnPage: 0,
5142          truncated: false,
5143          counterText: read.counterText,
5144          results: [],
5145          reason: "No event cards matched " + ITEM_SELECTOR + " and no 'No results found' message was present, so neither the events nor the result count could be read from this page.",
5146          nextStep: "Wait for the listing to finish rendering and call optimizely-find-optimizely-events-results, or reload /field-notes/events and try again.",
5147          summary: "The events grid could not be read on this page, so nothing is being reported as found."
5148        });
5149      }
5150
5151      var target = buildUrl("/field-notes/events?search={search}&topic={topic}&page={page}", {
5152        search: tgtSearch,
5153        topic: tgtTopic,
5154        page: tgtPage === "1" ? "" : tgtPage
5155      });
5156      var targetHref = target;
5157      try { targetHref = new URL(target, location.href).href; } catch (e) {}
5158      var payload = {
5159        status: "navigated",
5160        action: "open the events listing with the requested search, topic and page",
5161        url: targetHref,
5162        fromUrl: startUrl,
5163        appliedFilters: { search: tgtSearch, topic: tgtTopic, page: parseInt(tgtPage, 10) },
5164        nextStep: "The events listing is loading at that URL. Call optimizely-find-optimizely-events-results to read the events and the 'Showing X-Y of N results' counter from the loaded page.",
5165        summary: "Opening the events listing" + (tgtSearch ? " for '" + tgtSearch + "'" : "") + (tgtTopic ? " with topic '" + tgtTopic + "'" : "") + " at page " + tgtPage + "."
5166      };
5167      location.assign(target);
5168      return wrap(payload);
5169    } catch (err) {
5170      return wrap({
5171        status: "error",
5172        action: "apply the requested search, topic and page to the events listing",
5173        url: location.href,
5174        reason: "The tool failed while working with the page: " + (err && err.message ? err.message : String(err)),
5175        nextStep: "Reload /field-notes/events and call optimizely-find-optimizely-events again with simpler arguments.",
5176        summary: "The events listing could not be searched or filtered because of an unexpected page error."
5177      });
5178    }
5179  }
5180});
5181
5182mc.registerToolIf("/field-notes/events/**", {
5183  name: "optimizely-find-optimizely-events-results",
5184  description: "Read the Optimizely events and webinars listing as it currently stands at /field-notes/events and return each event as an object with name, date, format ('On-location event' or 'Online event'), topic tags, description, card image and event-page URL, together with the page's own 'Showing X-Y of N results' counter and the search, topic and page currently in the URL. Scrolls to and highlights the results grid the data was read from.",
5185  inputSchema: {
5186    type: "object",
5187    properties: {
5188      limit: {
5189        type: "integer",
5190        minimum: 1,
5191        maximum: 60,
5192        description: "Maximum number of events to return. Defaults to 24."
5193      }
5194    },
5195    required: []
5196  },
5197  annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
5198  execute: async function (args) {
5199    trackWebMCPTool("optimizely-find-optimizely-events-results");
5200    var a = args || {};
5201    var ITEM_SELECTOR = 'main a[href^="/field-notes/events/"]';
5202    var COUNT_SELECTOR = "main div:has(> p):has(> nav) > p";
5203    var FIELD_MAP = {
5204      url: "@href",
5205      name: "h3",
5206      date: "div > div > div:nth-child(2) > div:nth-child(1) > span",
5207      format: "div > div > div:nth-child(2) > div:nth-child(1) > div > span:nth-child(2)",
5208      description: "p",
5209      topics: "div > div > div:nth-child(2) > div:last-child > span",
5210      image: "img @src"
5211    };
5212
5213    function wrap(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
5214    function txt(el) { return el && el.textContent ? el.textContent.replace(/\s+/g, " ").trim() : ""; }
5215    function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
5216    function cards() {
5217      try { return Array.prototype.slice.call(document.querySelectorAll(ITEM_SELECTOR)); } catch (e) { return []; }
5218    }
5219    function highlight(el) {
5220      if (!el) return;
5221      try {
5222        el.scrollIntoView({ behavior: "smooth", block: "center" });
5223        el.style.outline = "3px solid #0037ff";
5224        el.style.outlineOffset = "2px";
5225        setTimeout(function () { try { el.style.outline = ""; el.style.outlineOffset = ""; } catch (e) {} }, 6000);
5226      } catch (e) {}
5227    }
5228    function regionFor(list) {
5229      if (!list.length) return document.querySelector("main");
5230      var node = list[0].parentElement, best = list[0], hops = 0;
5231      while (node && hops < 6) {
5232        if (node.querySelectorAll('a[href^="/field-notes/events/"]').length >= list.length) { best = node; break; }
5233        node = node.parentElement;
5234        hops++;
5235      }
5236      return best;
5237    }
5238    function counterElement() {
5239      var el = null;
5240      try { el = document.querySelector(COUNT_SELECTOR); } catch (e) { el = null; }
5241      if (el) return el;
5242      var ps = Array.prototype.slice.call(document.querySelectorAll("main p"));
5243      for (var i = 0; i < ps.length; i++) {
5244        if (/showing\s+[\d,]+\s*-\s*[\d,]+\s+of\s+[\d,]+/i.test(txt(ps[i]))) return ps[i];
5245      }
5246      return null;
5247    }
5248    function emptyStateText() {
5249      var nodes = Array.prototype.slice.call(document.querySelectorAll("main p, main h2, main h3, main div"));
5250      for (var i = 0; i < nodes.length; i++) {
5251        var t = txt(nodes[i]);
5252        if (t && t.length < 200 && /no results found/i.test(t)) return t;
5253      }
5254      return "";
5255    }
5256    async function liveTopics() {
5257      var out = [], seen = {};
5258      function add(t) {
5259        t = (t || "").replace(/\s+/g, " ").trim();
5260        if (!t || t.length > 48) return;
5261        if (/^(filter|filters|search|clear|clear all|apply|reset|reset filters|close|all|all topics|sort|previous|next)$/i.test(t)) return;
5262        var k = t.toLowerCase();
5263        if (seen[k]) return;
5264        seen[k] = 1;
5265        out.push(t);
5266      }
5267      var panelBtn = null;
5268      var btns = Array.prototype.slice.call(document.querySelectorAll("main button"));
5269      for (var i = 0; i < btns.length; i++) {
5270        var lbl = txt(btns[i]) || btns[i].getAttribute("aria-label") || btns[i].getAttribute("title") || "";
5271        if (/^filters?$/i.test(lbl.trim())) { panelBtn = btns[i]; break; }
5272      }
5273      var opened = false;
5274      if (panelBtn) {
5275        try { panelBtn.click(); opened = true; await sleep(800); } catch (e) {}
5276      }
5277      Array.prototype.slice.call(document.querySelectorAll("main select option")).forEach(function (o) {
5278        if (o.getAttribute("value") !== "") add(txt(o));
5279      });
5280      Array.prototype.slice.call(document.querySelectorAll("main label")).forEach(function (l) { add(txt(l)); });
5281      Array.prototype.slice.call(document.querySelectorAll('main input[type="checkbox"], main input[type="radio"]')).forEach(function (inp) {
5282        add(inp.getAttribute("aria-label") || inp.getAttribute("value") || "");
5283      });
5284      Array.prototype.slice.call(document.querySelectorAll('main [role="option"], main [role="menuitemcheckbox"], main [role="checkbox"]')).forEach(function (n) {
5285        add(n.getAttribute("aria-label") || txt(n));
5286      });
5287      if (opened) { try { panelBtn.click(); await sleep(200); } catch (e) {} }
5288      cards().forEach(function (c) {
5289        Array.prototype.slice.call(c.querySelectorAll(FIELD_MAP.topics)).forEach(function (s) {
5290          if (s.getAttribute("aria-hidden") === "true") return;
5291          add(txt(s));
5292        });
5293      });
5294      return out;
5295    }
5296
5297    try {
5298      var limit = parseInt(a.limit, 10);
5299      if (isNaN(limit) || limit < 1) limit = 24;
5300      if (limit > 60) limit = 60;
5301
5302      var params = new URLSearchParams(location.search);
5303      var applied = {
5304        search: (params.get("search") || "").trim(),
5305        topic: (params.get("topic") || "").trim(),
5306        page: parseInt((params.get("page") || "1").trim() || "1", 10)
5307      };
5308
5309      try { await waitForElement(ITEM_SELECTOR, 8000); } catch (e) {}
5310      try { await waitForElement(COUNT_SELECTOR, 2500); } catch (e) {}
5311      await sleep(350);
5312
5313      var list = cards();
5314      highlight(regionFor(list));
5315
5316      var data = null;
5317      try { data = await extractItems(ITEM_SELECTOR, FIELD_MAP, limit); } catch (e) { data = null; }
5318      var results = (data && data.results) || [];
5319      var truncated = !!(data && data.truncated);
5320      var counterText = txt(counterElement());
5321      var total = null;
5322      var m = counterText.match(/of\s+([\d,]+)/i);
5323      if (m) total = parseInt(m[1].replace(/,/g, ""), 10);
5324      if (total === null || isNaN(total)) {
5325        total = null;
5326        var parsed = null;
5327        try { parsed = await readResultCount(COUNT_SELECTOR, null); } catch (e) { parsed = null; }
5328        if (typeof parsed === "number" && !isNaN(parsed)) total = parsed;
5329      }
5330      var empty = emptyStateText();
5331
5332      if (results.length) {
5333        if (total === null) total = results.length;
5334        return wrap({
5335          status: "ok",
5336          action: "read the events listing on the loaded page",
5337          url: location.href,
5338          appliedFilters: applied,
5339          observed: true,
5340          resultCount: total,
5341          shownOnPage: results.length,
5342          truncated: truncated,
5343          counterText: counterText,
5344          results: results,
5345          nextStep: "Call optimizely-find-optimizely-events-open with the url of the event you want, or optimizely-find-optimizely-events with a page number to see more.",
5346          summary: "Read " + results.length + " event(s) from the listing; the page counter reports " + total + " matching result(s)."
5347        });
5348      }
5349
5350      if (empty) {
5351        var topicsNow = await liveTopics();
5352        return wrap({
5353          status: "ok",
5354          action: "read the events listing on the loaded page",
5355          url: location.href,
5356          appliedFilters: applied,
5357          observed: true,
5358          resultCount: 0,
5359          shownOnPage: 0,
5360          truncated: false,
5361          counterText: counterText,
5362          emptyState: empty,
5363          results: [],
5364          availableOptions: topicsNow,
5365          nextStep: "Call optimizely-find-optimizely-events again with a broader search, a topic from availableOptions, or page 1.",
5366          summary: "The listing reported no results for the search, topic and page currently applied."
5367        });
5368      }
5369
5370      return wrap({
5371        status: "partial",
5372        action: "read the events listing on the loaded page",
5373        url: location.href,
5374        appliedFilters: applied,
5375        observed: false,
5376        resultCount: null,
5377        shownOnPage: 0,
5378        truncated: false,
5379        counterText: counterText,
5380        results: [],
5381        reason: "No event cards matched " + ITEM_SELECTOR + " and no 'No results found' message was present, so neither the events nor the result count could be read.",
5382        nextStep: "Wait for the listing to finish rendering and call optimizely-find-optimizely-events-results again, or reload /field-notes/events.",
5383        summary: "The events grid could not be read, so no events and no count are being reported."
5384      });
5385    } catch (err) {
5386      return wrap({
5387        status: "error",
5388        action: "read the events listing on the loaded page",
5389        url: location.href,
5390        reason: "The tool failed while reading the page: " + (err && err.message ? err.message : 
5390String(err)),
5391        nextStep: "Reload /field-notes/events and call optimizely-find-optimizely-events-results again.",
5392        summary: "The events listing could not be read because of an unexpected page error."
5393      });
5394    }
5395  }
5396});
5397
5398mc.registerToolIf("/field-notes/events/**", {
5399  name: "optimizely-find-optimizely-events-open",
5400  description: "Open a specific Optimizely event page from the events listing, or follow the listing's 'Register now' CTA to the Camp Opticon hub at /field-notes/opticon/, so the visitor lands on the registration step. Takes the event-page URL returned by the listing tools. Returns the destination that was opened, how it was opened and the event names and URLs currently listed on the page.",
5401  inputSchema: {
5402    type: "object",
5403    properties: {
5404      eventUrl: {
5405        type: "string",
5406        description: "Path or full URL of the event page to open, exactly as returned in the 'url' field of a listing result, for example /field-notes/events/opticon-new-york/."
5407      },
5408      openRegistration: {
5409        type: "boolean",
5410        description: "Set true to follow the hero 'Register now' CTA to the Camp Opticon registration hub at /field-notes/opticon/ instead of opening a single event page."
5411      }
5412    },
5413    required: []
5414  },
5415  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
5416  execute: async function (args) {
5417    trackWebMCPTool("optimizely-find-optimizely-events-open");
5418    var a = args || {};
5419    var ITEM_SELECTOR = 'main a[href^="/field-notes/events/"]';
5420    var HUB_PATH = "/field-notes/opticon/";
5421    var startUrl = location.href;
5422
5423    function wrap(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
5424    function txt(el) { return el && el.textContent ? el.textContent.replace(/\s+/g, " ").trim() : ""; }
5425    function collapseRepeat(s) { return String(s || "").replace(/(.{4,}?)\1{2,}/g, "$1").replace(/\s+/g, " ").trim(); }
5426    function trimSlash(s) { return String(s || "").replace(/\/+$/, ""); }
5427    function highlight(el) {
5428      if (!el) return;
5429      try {
5430        el.scrollIntoView({ behavior: "smooth", block: "center" });
5431        el.style.outline = "3px solid #0037ff";
5432        el.style.outlineOffset = "2px";
5433        setTimeout(function () { try { el.style.outline = ""; el.style.outlineOffset = ""; } catch (e) {} }, 6000);
5434      } catch (e) {}
5435    }
5436    function listedEvents() {
5437      var out = [], seen = {};
5438      var list = [];
5439      try { list = Array.prototype.slice.call(document.querySelectorAll(ITEM_SELECTOR)); } catch (e) { list = []; }
5440      list.forEach(function (c) {
5441        var href = c.getAttribute("href");
5442        if (!href || seen[href]) return;
5443        seen[href] = 1;
5444        out.push({ name: txt(c.querySelector("h3")), url: href });
5445      });
5446      return out;
5447    }
5448    function anchorFor(dest) {
5449      var anchors = Array.prototype.slice.call(document.querySelectorAll("main a[href]"));
5450      for (var i = 0; i < anchors.length; i++) {
5451        var h = anchors[i].getAttribute("href");
5452        if (!h) continue;
5453        if (h === dest || trimSlash(h) === trimSlash(dest)) return anchors[i];
5454      }
5455      return null;
5456    }
5457
5458    try {
5459      var raw = typeof a.eventUrl === "string" ? a.eventUrl.trim() : "";
5460      var wantRegistration = a.openRegistration === true || /^register(\s*now)?$/i.test(raw);
5461      if (wantRegistration && !/^\/field-notes\//.test(raw) && !/^https?:\/\//i.test(raw)) raw = "";
5462
5463      var available = listedEvents();
5464      var dest = "";
5465
5466      if (raw) {
5467        var validPath = /^\/field-notes\//.test(raw);
5468        var validAbsolute = /^https:\/\/(www\.optimizely\.com|events\.optimizely\.com)\//i.test(raw);
5469        if (!validPath && !validAbsolute) {
5470          return wrap({
5471            status: "error",
5472            action: "open the requested event page",
5473            url: location.href,
5474            requestedUrl: raw,
5475            availableOptions: available.map(function (e) { return e.url; }),
5476            availableEvents: available,
5477            reason: "'" + raw + "' is not an Optimizely event destination; expected a /field-notes/... path or an optimizely.com URL taken from a listing result.",
5478            nextStep: "Call optimizely-find-optimizely-events-results to list the events, then call this tool again with one of the returned url values.",
5479            summary: "The requested destination was not a recognised Optimizely event URL."
5480          });
5481        }
5482        dest = raw;
5483      } else if (wantRegistration) {
5484        dest = HUB_PATH;
5485      } else {
5486        return wrap({
5487          status: "error",
5488          action: "open an event page",
5489          url: location.href,
5490          availableOptions: available.map(function (e) { return e.url; }),
5491          availableEvents: available,
5492          reason: "No eventUrl was supplied and openRegistration was not set, so there was no destination to open.",
5493          nextStep: "Call this tool again with eventUrl set to one of availableOptions, or with openRegistration true to reach the Camp Opticon registration hub.",
5494          summary: "No event destination was requested, so nothing was opened."
5495        });
5496      }
5497
5498      var destHref = dest;
5499      try { destHref = new URL(dest, location.href).href; } catch (e) {}
5500
5501      if (trimSlash(destHref.split("#")[0]) === trimSlash(location.href.split("#")[0])) {
5502        var heading = document.querySelector("main h1");
5503        var name = collapseRepeat(txt(heading));
5504        if (name) {
5505          highlight(heading);
5506          return wrap({
5507            status: "ok",
5508            action: "confirm the requested event page is already open",
5509            url: location.href,
5510            observed: true,
5511            eventName: name,
5512            eventUrl: location.pathname,
5513            nextStep: "Read this page for its registration link or form and follow it to complete registration.",
5514            summary: "Already on the event page '" + name + "'."
5515          });
5516        }
5517        return wrap({
5518          status: "partial",
5519          action: "confirm the requested event page is already open",
5520          url: location.href,
5521          observed: false,
5522          eventName: null,
5523          eventUrl: location.pathname,
5524          reason: "The browser is already at the requested URL but no main h1 heading could be read to confirm which event this page is.",
5525          nextStep: "Wait for the page to finish rendering and read it again.",
5526          summary: "The requested URL is already open but the event name could not be read."
5527        });
5528      }
5529
5530      var anchor = anchorFor(dest);
5531      var known = null;
5532      for (var i = 0; i < available.length; i++) {
5533        if (trimSlash(available[i].url) === trimSlash(dest)) { known = available[i]; break; }
5534      }
5535
5536      var payload = {
5537        status: "navigated",
5538        action: wantRegistration && dest === HUB_PATH ? "follow the 'Register now' CTA to the Camp Opticon registration hub" : "open the requested event page",
5539        url: destHref,
5540        fromUrl: startUrl,
5541        opened: { name: known ? known.name : null, url: dest },
5542        method: anchor ? "clicked the matching link on the page" : "opened the URL directly",
5543        listedEvents: available,
5544        nextStep: wantRegistration && dest === HUB_PATH
5545          ? "The Camp Opticon hub is loading; choose the New York, London or Online registration destination on that page."
5546          : "The event page is loading; read it for its registration link or form and follow that to complete registration.",
5547        summary: wantRegistration && dest === HUB_PATH
5548          ? "Opening the Camp Opticon registration hub at " + HUB_PATH + "."
5549          : "Opening the event page " + dest + (known && known.name ? " ('" + known.name + "')" : "") + "."
5550      };
5551
5552      if (anchor) {
5553        anchor.click();
5554        return wrap(payload);
5555      }
5556      location.assign(dest);
5557      return wrap(payload);
5558    } catch (err) {
5559      return wrap({
5560        status: "error",
5561        action: "open the requested event page",
5562        url: location.href,
5563        reason: "The tool failed while opening the destination: " + (err && err.message ? err.message : String(err)),
5564        nextStep: "Call optimizely-find-optimizely-events-results to re-read the listing, then retry with a url from the results.",
5565        summary: "The event destination could not be opened because of an unexpected page error."
5566      });
5567    }
5568  }
5569});
5570
5571  /* ---- optimizely-search-opal-agent-directory, optimizely-search-opal-agent-directory-results ---- */
5572mc.registerToolIf("/agents/**", {
5573  name: "optimizely-search-opal-agent-directory",
5574  description: "Searches and filters the Optimizely Opal AI agent directory on /agents/. Accepts a free-text keyword, a Product facet value, a 'Built By' publisher (name or key) and a page number, reads the Product and Built By options the page offers at call time and, when those options can be read, checks the requested values against them, then applies the state through the directory's own ?search=, ?product=, ?builtBy= and ?page= URL parameters. When the directory is already showing exactly the requested state it returns the listing itself: the directory's own result total plus, for each agent, its name, short description, any card badge such as NEW, the publisher name and logo, the card thumbnail image when the card carries one, and the absolute detail page URL. When the state has to change it navigates the directory to that URL and returns status navigated together with the name of the results tool that reads the settled page. When a requested facet value is not among the options read from the page it returns status error with the available options.",
5575  inputSchema: {
5576    type: "object",
5577    properties: {
5578      search: {
5579        type: "string",
5580        description: "Free-text keyword to search the agent directory for, for example 'blog' or 'variation'."
5581      },
5582      product: {
5583        type: "string",
5584        description: "Product facet value to filter by, for example 'Opal' or 'Web Experimentation'. Checked against the Product options read from the page at call time; an unknown value comes back as an error listing the options that are really there."
5585      },
5586      builtBy: {
5587        type: "string",
5588        description: "Built By publisher to filter by. Accepts the publisher display name, for example 'Niteco', or the directory's builtBy key. Checked against the Built By options read from the page at call time."
5589      },
5590      page: {
5591        type: "integer",
5592        minimum: 1,
5593        description: "Result page of the directory to open. Page 1 is the first page."
5594      },
5595      limit: {
5596        type: "integer",
5597        minimum: 1,
5598        maximum: 50,
5599        description: "Maximum number of agent cards to return when the directory is already showing the requested state. Defaults to 12."
5600      }
5601    },
5602    additionalProperties: false
5603  },
5604  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
5605  execute: async function (args) {
5606    trackWebMCPTool("optimizely-search-opal-agent-directory");
5607    var a = args || {};
5608    var RESULTS_TOOL = "optimizely-search-opal-agent-directory-results";
5609    var LIST_SELECTOR = "#ListingResults";
5610    var ITEM_SELECTOR = "#ListingResults a.group";
5611    var COUNT_SELECTOR = "#ListingResults p.text-body-xxs";
5612    var TEMPLATE = "/agents/?search={search}&product={product}&builtBy={builtBy}&page={page}";
5613    var FIELD_MAP = {
5614      url: "@href",
5615      name: "h3",
5616      description: "p.line-clamp-3",
5617      badge: "span.font-overline",
5618      thumbnail: "img.object-cover @src",
5619      publisher: "div.pr-6 > span",
5620      publisherLogo: "img.object-contain @src",
5621      publisherLogoAlt: "img.object-contain @alt"
5622    };
5623
5624    function respond(payload) {
5625      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
5626    }
5627    function txt(el) {
5628      return el && el.textContent ? el.textContent.replace(/\s+/g, " ").trim() : "";
5629    }
5630    function num(s) {
5631      var n = parseInt(String(s).replace(/[^\d]/g, ""), 10);
5632      return isNaN(n) ? null : n;
5633    }
5634    function absolute(u) {
5635      if (!u) return null;
5636      try { return new URL(u, location.origin).href; } catch (e) { return u; }
5637    }
5638    function delay(ms) {
5639      return new Promise(function (r) { setTimeout(r, ms); });
5640    }
5641    function highlight(el) {
5642      try {
5643        if (!el) return;
5644        el.scrollIntoView({ behavior: "smooth", block: "center" });
5645        var prev = el.style.outline;
5646        el.style.outline = "3px solid #0037ff";
5647        setTimeout(function () { try { el.style.outline = prev; } catch (e) {} }, 5000);
5648      } catch (e) {}
5649    }
5650
5651    function sliceBalanced(text, start) {
5652      var depth = 0, inStr = false, esc = false;
5653      var max = Math.min(text.length, start + 400000);
5654      for (var i = start; i < max; i++) {
5655        var c = text.charAt(i);
5656        if (inStr) {
5657          if (esc) { esc = false; }
5658          else if (c === "\\") { esc = true; }
5659          else if (c === '"') { inStr = false; }
5660          continue;
5661        }
5662        if (c === '"') { inStr = true; continue; }
5663        if (c === "{") { depth++; }
5664        else if (c === "}") { depth--; if (depth === 0) return text.slice(start, i + 1); }
5665      }
5666      return null;
5667    }
5668    function pushOption(bucket, value, label, count) {
5669      var v = String(value == null ? label : value).trim();
5670      var l = String(label == null ? value : label).trim();
5671      if (!v) return;
5672      for (var i = 0; i < bucket.length; i++) {
5673        if (bucket[i].value.toLowerCase() === v.toLowerCase()) return;
5674      }
5675      bucket.push({ value: v, label: l || v, count: (typeof count === "number" && isFinite(count)) ? count : null });
5676    }
5677    function harvestFacets(obj, out) {
5678      if (!obj || typeof obj !== "object") return;
5679      Object.keys(obj).forEach(function (key) {
5680        var list = obj[key];
5681        if (!Array.isArray(list)) return;
5682        var bucket = /^product$/i.test(key) ? out.product : (/^builtby$/i.test(key) ? out.builtBy : null);
5683        if (!bucket) return;
5684        list.forEach(function (entry) {
5685          if (!entry || typeof entry !== "object") return;
5686          var value = entry.name != null ? entry.name : (entry.value != null ? entry.value : entry.label);
5687          var label = entry.label != null ? entry.label : value;
5688          if (value == null) return;
5689          pushOption(bucket, value, label, entry.count);
5690        });
5691      });
5692    }
5693    function facetsFromPayload() {
5694      var out = { product: [], builtBy: [] };
5695      try {
5696        var scripts = document.querySelectorAll("script");
5697        for (var i = 0; i < scripts.length; i++) {
5698          var raw = scripts[i].textContent || "";
5699          if (raw.indexOf("initialFacets") === -1) continue;
5700          var variants = [raw, raw.replace(/\\"/g, '"')];
5701          for (var v = 0; v < variants.length; v++) {
5702            var text = variants[v];
5703            var idx = text.indexOf('"initialFacets"');
5704            var guard = 0;
5705            while (idx !== -1 && guard < 5) {
5706              guard++;
5707              var braceStart = text.indexOf("{", idx);
5708              if (braceStart === -1) break;
5709              var json = sliceBalanced(text, braceStart);
5710              if (json) {
5711                try { harvestFacets(JSON.parse(json), out); } catch (e) {}
5712              }
5713              idx = text.indexOf('"initialFacets"', idx + 1);
5714            }
5715            if (out.product.length || out.builtBy.length) break;
5716          }
5717          if (out.product.length || out.builtBy.length) break;
5718        }
5719      } catch (e) {}
5720      return out;
5721    }
5722    function facetsFromDom() {
5723      var out = { product: [], builtBy: [] };
5724      function labelFor(input) {
5725        var lab = "";
5726        try {
5727          if (input.id) {
5728            var byFor = document.querySelector('label[for="' + input.id.replace(/"/g, '\\"') + '"]');
5729            if (byFor) lab = txt(byFor);
5730          }
5731          if (!lab && input.closest) {
5732            var parentLabel = input.closest("label");
5733            if (parentLabel) lab = txt(parentLabel);
5734          }
5735          if (!lab && input.parentElement) lab = txt(input.parentElement);
5736          if (!lab) lab = input.getAttribute("aria-label") || "";
5737        } catch (e) {}
5738        return lab.replace(/\s*\(\s*\d+\s*\)\s*$/, "").trim();
5739      }
5740      function countFor(input) {
5741        try {
5742          var host = (input.closest && input.closest("label")) || input.parentElement;
5743          var m = txt(host).match(/\((\d+)\)\s*$/);
5744          return m ? parseInt(m[1], 10) : null;
5745        } catch (e) { return null; }
5746      }
5747      try {
5748        var inputs = document.querySelectorAll('input[type="checkbox"], input[type="radio"]');
5749        for (var i = 0; i < inputs.length; i++) {
5750          var el = inputs[i];
5751          var nm = (el.getAttribute("name") || "").trim().toLowerCase();
5752          var bucket = nm === "product" ? out.product : (nm === "builtby" ? out.builtBy : null);
5753          if (!bucket) continue;
5754          var lbl = labelFor(el);
5755          pushOption(bucket, el.getAttribute("value") || lbl, lbl, countFor(el));
5756        }
5757        var links = document.querySelectorAll('a[href*="product="], a[href*="builtBy="], a[href*="builtby="]');
5758        for (var j = 0; j < links.length; j++) {
5759          try {
5760            var u = new URL(links[j].getAttribute("href"), location.origin);
5761            var pv = u.searchParams.get("product");
5762            var bv = u.searchParams.get("builtBy") || u.searchParams.get("builtby");
5763            var lt = txt(links[j]).replace(/\s*\(\s*\d+\s*\)\s*$/, "").trim();
5764            if (pv) pushOption(out.product, pv, lt || pv, null);
5765            if (bv) pushOption(out.builtBy, bv, lt || bv, null);
5766          } catch (e) {}
5767        }
5768      } catch (e) {}
5769      return out;
5770    }
5771    function findFilterButton() {
5772      var scoped = document.querySelector("div.z-39 > button");
5773      if (scoped && /filter/i.test(txt(scoped) + " " + (scoped.getAttribute("aria-label") || ""))) return scoped;
5774      var btns = document.querySelectorAll("button");
5775      for (var i = 0; i < btns.length; i++) {
5776        var t = txt(btns[i]);
5777        var al = (btns[i].getAttribute("aria-label") || "").trim();
5778        if (/^filters?\b/i.test(t) || /^filters?$/i.test(al)) return btns[i];
5779      }
5780      return scoped || null;
5781    }
5782    async function readLiveFacets() {
5783      var facets = facetsFromDom();
5784      var source = (facets.product.length || facets.builtBy.length) ? "facet controls on the page" : null;
5785      if (!source) {
5786        var btn = findFilterButton();
5787        if (btn) {
5788          try { btn.click(); } catch (e) {}
5789          for (var t = 0; t < 12; t++) {
5790            await delay(200);
5791            facets = facetsFromDom();
5792            if (facets.product.length || facets.builtBy.length) { source = "Filter panel"; break; }
5793          }
5794          try { btn.click(); } catch (e) {}
5795        }
5796      }
5797      if (!source) {
5798        var fromPayload = facetsFromPayload();
5799        if (fromPayload.product.length || fromPayload.builtBy.length) {
5800          facets = fromPayload;
5801          source = "listing configuration payload in the page";
5802        }
5803      }
5804      return { facets: facets, source: source };
5805    }
5806    function matchOption(list, wanted) {
5807      var w = String(wanted).trim().toLowerCase();
5808      var i;
5809      for (i = 0; i < list.length; i++) {
5810        if (String(list[i].value).toLowerCase() === w) return list[i];
5811      }
5812      for (i = 0; i < list.length; i++) {
5813        if (String(list[i].label).toLowerCase() === w) return list[i];
5814      }
5815      if (w.length >= 3) {
5816        for (i = 0; i < list.length; i++) {
5817          var lab = String(list[i].label).toLowerCase();
5818          if (lab && (lab.indexOf(w) !== -1 || w.indexOf(lab) !== -1)) return list[i];
5819        }
5820      }
5821      return null;
5822    }
5823    async function readListing(limit) {
5824      var out = {
5825        countText: "",
5826        totalResults: null,
5827        showingFrom: null,
5828        showingTo: null,
5829        cardsFound: 0,
5830        truncated: false,
5831        results: []
5832      };
5833      var countEl = document.querySelector(COUNT_SELECTOR);
5834      out.countText = txt(countEl);
5835      var m = out.countText.match(/showing\s+([\d,]+)\s*[-–]\s*([\d,]+)\s+of\s+([\d,]+)/i);
5836      if (m) {
5837        out.showingFrom = num(m[1]);
5838        out.showingTo = num(m[2]);
5839        out.totalResults = num(m[3]);
5840      } else {
5841        var alt = out.countText.match(/of\s+([\d,]+)/i);
5842        if (alt) out.totalResults = num(alt[1]);
5843      }
5844      if (out.totalResults === null) {
5845        try {
5846          var rc = await readResultCount(COUNT_SELECTOR, null);
5847          if (typeof rc === "number" && isFinite(rc)) out.totalResults = rc;
5848        } catch (e) {}
5849      }
5850      var items = null;
5851      try { items = await extractItems(ITEM_SELECTOR, FIELD_MAP, limit); } catch (e) { items = null; }
5852      if (items && items.results) {
5853        out.cardsFound = typeof items.resultCount === "number" ? items.resultCount : items.results.length;
5854        out.truncated = !!items.truncated;
5855        out.results = items.results.map(function (r) {
5856          return {
5857            name: r.name || null,
5858            description: r.description || null,
5859            badge: r.badge || null,
5860            publisher: r.publisher || r.publisherLogoAlt || null,
5861            publisherLogo: absolute(r.publisherLogo),
5862            thumbnail: absolute(r.thumbnail),
5863            url: absolute(r.url)
5864          };
5865        });
5866      }
5867      return out;
5868    }
5869    function stateOf(href) {
5870      var u = new URL(href, location.origin);
5871      var p = u.searchParams;
5872      return {
5873        path: u.pathname.replace(/\/+$/, "").toLowerCase(),
5874        search: (p.get("search") || "").trim().toLowerCase(),
5875        product: (p.get("product") || "").trim().toLowerCase(),
5876        builtBy: (p.get("builtBy") || p.get("builtby") || "").trim().toLowerCase(),
5877        page: String(parseInt(p.get("page") || "1", 10) || 1)
5878      };
5879    }
5880
5881    try {
5882      var startUrl = location.href;
5883      var limit = Math.max(1, Math.min(50, parseInt(a.limit, 10) || 12));
5884      var wantSearch = a.search != null ? String(a.search).trim() : "";
5885      var wantProduct = a.product != null ? String(a.product).trim() : "";
5886      var wantBuiltBy = a.builtBy != null ? String(a.builtBy).trim() : "";
5887      var wantPage = a.page != null ? Math.max(1, parseInt(a.page, 10) || 1) : 1;
5888
5889      var live = { facets: { product: [], builtBy: [] }, source: null };
5890      if (wantProduct || wantBuiltBy) {
5891        live = await readLiveFacets();
5892      }
5893      var resolvedProduct = wantProduct;
5894      var resolvedBuiltBy = wantBuiltBy;
5895
5896      if (wantProduct && live.facets.product.length) {
5897        var hitProduct = matchOption(live.facets.product, wantProduct);
5898        if (!hitProduct) {
5899          return respond({
5900            status: "error",
5901            action: "apply a Product filter on the Opal agent directory",
5902            url: location.href,
5903            invalidArgument: "product",
5904            requestedValue: wantProduct,
5905            reason: "The Product facet value '" + wantProduct + "' is not among the Product options currently offered by the directory (read from the " + live.source + ").",
5906            availableOptions: live.facets.product,
5907            nextStep: "Retry with one of the entries in availableOptions, passing its value or its label as the product argument.",
5908            summary: "No Product option on the directory matches '" + wantProduct + "'; " + live.facets.product.length + " Product options are available."
5909          });
5910        }
5911        resolvedProduct = hitProduct.value;
5912      }
5913      if (wantBuiltBy && live.facets.builtBy.length) {
5914        var hitBuiltBy = matchOption(live.facets.builtBy, wantBuiltBy);
5915        if (!hitBuiltBy) {
5916          return respond({
5917            status: "error",
5918            action: "apply a Built By filter on the Opal agent directory",
5919            url: location.href,
5920            invalidArgument: "builtBy",
5921            requestedValue: wantBuiltBy,
5922            reason: "The Built By value '" + wantBuiltBy + "' is not among the publisher options currently offered by the directory (read from the " + live.source + ").",
5923            availableOptions: live.facets.builtBy,
5924            nextStep: "Retry with one of the entries in availableOptions, passing its value (the builtBy key) or its label (the publisher name) as the builtBy argument.",
5925            summary: "No Built By option on the directory matches '" + wantBuiltBy + "'; " + live.facets.builtBy.length + " publisher options are available."
5926          });
5927        }
5928        resolvedBuiltBy = hitBuiltBy.value;
5929      }
5930
5931      var appliedFilters = {
5932        search: wantSearch || null,
5933        product: resolvedProduct || null,
5934        builtBy: resolvedBuiltBy || null,
5935        page: wantPage
5936      };
5937
5938      var target = buildUrl(TEMPLATE, {
5939        search: wantSearch,
5940        product: resolvedProduct,
5941        builtBy: resolvedBuiltBy,
5942        page: wantPage > 1 ? String(wantPage) : ""
5943      });
5944      if (!target) target = "/agents/";
5945      target = String(target).replace(/[?&]+$/, "");
5946      var targetAbs = absolute(target);
5947
5948      var current = stateOf(location.href);
5949      var wanted = stateOf(targetAbs);
5950      var sameState =
5951        current.path === wanted.path &&
5952        current.search === wanted.search &&
5953        current.product === wanted.product &&
5954        current.builtBy === wanted.builtBy &&
5955        current.page === wanted.page;
5956
5957      if (sameState) {
5958        var listRegion = null;
5959        try { listRegion = await waitForElement(LIST_SELECTOR, 8000); } catch (e) { listRegion = null; }
5960        if (!listRegion) listRegion = document.querySelector(LIST_SELECTOR);
5961        if (!listRegion) {
5962          return respond({
5963            status: "partial",
5964            action: "read the Opal agent directory listing",
5965            url: location.href,
5966            observed: false,
5967            resultCount: null,
5968            results: [],
5969            appliedFilters: appliedFilters,
5970            reason: "The directory results region (" + LIST_SELECTOR + ") did not appear on this page, so no agent cards or result total could be read.",
5971            nextStep: "Open " + location.origin + "/agents/ and call this tool again, or call " + RESULTS_TOOL + " on the directory page.",
5972            summary: "The requested directory state is already in the URL but the results region was not found."
5973          });
5974        }
5975        highlight(listRegion);
5976        await delay(400);
5977        var listing = await readListing(limit);
5978        var resultCount = listing.totalResults !== null ? listing.totalResults : (listing.cardsFound || null);
5979        var observed = (listing.countText !== "") || listing.results.length > 0;
5980        var status = "ok";
5981        var reason = null;
5982        var nextStep = null;
5983        if (!observed || (resultCount === null && listing.results.length === 0)) {
5984          status = "partial";
5985          observed = false;
5986          reason = "Neither the directory's own result total nor any agent card could be read out of the settled listing.";
5987          nextStep = "Reload " + targetAbs + " and call " + RESULTS_TOOL + " once the cards are visible.";
5988        } else if (listing.results.length === 0 && resultCount !== null && resultCount > 0) {
5989          status = "partial";
5990          reason = "The directory reports " + resultCount + " results but no agent card could be read from the listing.";
5991          nextStep = "Call " + RESULTS_TOOL + " again once the cards have rendered.";
5992        }
5993        var payload = {
5994          status: status,
5995          action: "read the Opal agent directory listing for the requested search and filters",
5996          url: location.href,
5997          observed: observed,
5998          appliedFilters: appliedFilters,
5999          resultCount: resultCount,
6000          returnedCount: listing.results.length,
6001          truncated: listing.truncated,
6002          countText: listing.countText || null,
6003          showingFrom: listing.showingFrom,
6004          showingTo: listing.showingTo,
6005          results: listing.results,
6006          resultsToolName: RESULTS_TOOL,
6007          summary: status === "ok"
6008            ? ("The directory is already showing this state and reports " + (resultCount === null ? "an unread number of" : resultCount) + " results; returned " + listing.results.length + " agent card(s).")
6009            : "The directory is already showing this state but the listing could not be fully read."
6010        };
6011        if (live.source) {
6012          payload.facetOptions = live.facets;
6013          payload.facetOptionsSource = live.source;
6014        }
6015        if (reason) payload.reason = reason;
6016        if (nextStep) payload.nextStep = nextStep;
6017        return respond(payload);
6018      }
6019
6020      var navPayload = respond({
6021        status: "navigated",
6022        action: "open the Opal agent directory with the requested search and filters",
6023        url: targetAbs,
6024        fromUrl: startUrl,
6025        appliedFilters: appliedFilters,
6026        resultsToolName: RESULTS_TOOL,
6027        nextStep: "The directory is now loading " + targetAbs + ". Call " + RESULTS_TOOL + " on that page to read the result total and the agent cards, and pass a higher page argument to this tool to walk further pages.",
6028        summary: "Opening the Opal agent directory at " + targetAbs + "."
6029      });
6030      var pushed = false;
6031      try {
6032        if (window.next && window.next.router && typeof window.next.router.push === "function") {
6033          window.next.router.push(target);
6034          pushed = true;
6035        }
6036      } catch (e) {
6037        pushed = false;
6038      }
6039      if (!pushed) location.assign(target);
6040      return navPayload;
6041    } catch (err) {
6042      return respond({
6043        status: "error",
6044        action: "search the Opal agent directory",
6045        url: location.href,
6046        reason: "The tool hit an unexpected error: " + (err && err.message ? err.message : String(err)),
6047        nextStep: "Reload " + location.origin + "/agents/ and try again with simpler arguments.",
6048        summary: "The Opal agent directory search could not be completed."
6049      });
6050    }
6051  }
6052});
6053
6054mc.registerToolIf("/agents/**", {
6055  name: "optimizely-search-opal-agent-directory-results",
6056  description: "Reads the Opal AI agent directory listing that is currently on screen at /agents/ and returns the directory's own result total plus one object per agent card: the agent name, its short description, any card badge such as NEW, the publisher name and logo, the card thumbnail image when the card carries one, and the absolute agent detail page URL. Also returns the search and facet values in the current URL, the range shown, the current page number and the page numbers the pagination offers, and it scrolls to and highlights the results region so a human can see where the data came from.",
6057  inputSchema: {
6058    type: "object",
6059    properties: {
6060      limit: {
6061        type: "integer",
6062        minimum: 1,
6063        maximum: 50,
6064        description: "Maximum number of agent cards to return. Defaults to 12."
6065      }
6066    },
6067    additionalProperties: false
6068  },
6069  annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
6070  execute: async function (args) {
6071    trackWebMCPTool("optimizely-search-opal-agent-directory-results");
6072    var a = args || {};
6073    var LIST_SELECTOR = "#ListingResults";
6074    var ITEM_SELECTOR = "#ListingResults a.group";
6075    var COUNT_SELECTOR = "#ListingResults p.text-body-xxs";
6076    var PAGINATION_SELECTOR = '#ListingResults nav[aria-label="Pagination"]';
6077    var FIELD_MAP = {
6078      url: "@href",
6079      name: "h3",
6080      description: "p.line-clamp-3",
6081      badge: "span.font-overline",
6082      thumbnail: "img.object-cover @src",
6083      publisher: "div.pr-6 > span",
6084      publisherLogo: "img.object-contain @src",
6085      publisherLogoAlt: "img.object-contain @alt"
6086    };
6087
6088    function respond(payload) {
6089      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
6090    }
6091    function txt(el) {
6092      return el && el.textContent ? el.textContent.replace(/\s+/g, " ").trim() : "";
6093    }
6094    function num(s) {
6095      var n = parseInt(String(s).replace(/[^\d]/g, ""), 10);
6096      return isNaN(n) ? null : n;
6097    }
6098    function absolute(u) {
6099      if (!u) return null;
6100      try { return new URL(u, location.origin).href; } catch (e) { return u; }
6101    }
6102    function delay(ms) {
6103      return new Promise(function (r) { setTimeout(r, ms); });
6104    }
6105    function highlight(el) {
6106      try {
6107        if (!el) return;
6108        el.scrollIntoView({ behavior: "smooth", block: "center" });
6109        var prev = el.style.outline;
6110        el.style.outline = "3px solid #0037ff";
6111        setTimeout(function () { try { el.style.outline = prev; } catch (e) {} }, 5000);
6112      } catch (e) {}
6113    }
6114
6115    try {
6116      var limit = Math.max(1, Math.min(50, parseInt(a.limit, 10) || 12));
6117      var params = new URL(location.href).searchParams;
6118      var activeFilters = {
6119        search: params.get("search") || null,
6120        product: params.get("product") || null,
6121        builtBy: params.get("builtBy") || params.get("builtby") || null,
6122        page: parseInt(params.get("page") || "1", 10) || 1
6123      };
6124
6125      var region = null;
6126      try { region = await waitForElement(LIST_SELECTOR, 8000); } catch (e) { region = null; }
6127      if (!region) region = document.querySelector(LIST_SELECTOR);
6128      if (!region) {
6129        return respond({
6130          status: "partial",
6131          action: "read the Opal agent directory listing",
6132          url: location.href,
6133          observed: false,
6134          resultCount: null,
6135          results: [],
6136          activeFilters: activeFilters,
6137          reason: "The directory results region (" + LIST_SELECTOR + ") is not present on this page, so no result total and no agent cards could be read.",
6138          nextStep: "Open " + location.origin + "/agents/ (optionally with ?search=, ?product=, ?builtBy= or ?page=) and call this tool again.",
6139          summary: "No Opal agent directory listing was found on this page."
6140        });
6141      }
6142      highlight(region);
6143      await delay(400);
6144
6145      // The count line updates a beat before the card grid re-renders, so a
6146      // read taken right after a filter change can pair the new total with the
6147      // previous result set's cards. Wait for the two to agree, and say so in
6148      // the payload when they never do.
6149      var countEl = null;
6150      var countText = "";
6151      var totalResults = null, showingFrom = null, showingTo = null;
6152      var expectedCards = null, renderedCards = 0, cardsSettled = false;
6153      var settleDeadline = Date.now() + 6000;
6154      while (true) {
6155        countEl = document.querySelector(COUNT_SELECTOR);
6156        countText = txt(countEl);
6157        totalResults = null;
6158        showingFrom = null;
6159        showingTo = null;
6160        var m = countText.match(/showing\s+([\d,]+)\s*[-–]\s*([\d,]+)\s+of\s+([\d,]+)/i);
6161        if (m) {
6162          showingFrom = num(m[1]);
6163          showingTo = num(m[2]);
6164          totalResults = num(m[3]);
6165        } else {
6166          var alt = countText.match(/of\s+([\d,]+)/i);
6167          if (alt) totalResults = num(alt[1]);
6168        }
6169        expectedCards = (showingFrom !== null && showingTo !== null) ? (showingTo - showingFrom + 1) : null;
6170        renderedCards = document.querySelectorAll(ITEM_SELECTOR).length;
6171        cardsSettled = (expectedCards === null) || (renderedCards === expectedCards);
6172        if (cardsSettled || Date.now() > settleDeadline) break;
6173        await delay(250);
6174      }
6175      if (totalResults === null) {
6176        try {
6177          var rc = await readResultCount(COUNT_SELECTOR, null);
6178          if (typeof rc === "number" && isFinite(rc)) totalResults = rc;
6179        } catch (e) {}
6180      }
6181
6182      var items = null;
6183      try { items = await extractItems(ITEM_SELECTOR, FIELD_MAP, limit); } catch (e) { items = null; }
6184      var cardsFound = items && typeof items.resultCount === "number" ? items.resultCount : ((items && items.results) ? items.results.length : 0);
6185      var truncated = !!(items && items.truncated);
6186      var results = (items && items.results ? items.results : []).map(function (r) {
6187        return {
6188          name: r.name || null,
6189          description: r.description || null,
6190          badge: r.badge || null,
6191          publisher: r.publisher || r.publisherLogoAlt || null,
6192          publisherLogo: absolute(r.publisherLogo),
6193          thumbnail: absolute(r.thumbnail),
6194          url: absolute(r.url)
6195        };
6196      });
6197
6198      var page = null;
6199      var pagesOffered = [];
6200      var pagination = document.querySelector(PAGINATION_SELECTOR);
6201      if (pagination) {
6202        var currentBtn = pagination.querySelector('[aria-current="page"]');
6203        if (currentBtn) {
6204          var cm = (currentBtn.getAttribute("aria-label") || "").match(/(\d+)/);
6205          page = cm ? parseInt(cm[1], 10) : num(txt(currentBtn));
6206        }
6207        var pageBtns = pagination.querySelectorAll("button[aria-label], a[aria-label]");
6208        for (var i = 0; i < pageBtns.length; i++) {
6209          var lm = (pageBtns[i].getAttribute("aria-label") || "").match(/page\s+(\d+)/i);
6210          if (lm) {
6211            var pn = parseInt(lm[1], 10);
6212            if (pagesOffered.indexOf(pn) === -1) pagesOffered.push(pn);
6213          }
6214        }
6215        pagesOffered.sort(function (x, y) { return x - y; });
6216      }
6217      if (page === null) page = activeFilters.page;
6218      var lastPageOffered = pagesOffered.length ? pagesOffered[pagesOffered.length - 1] : null;
6219      var hasNextPage = (showingTo !== null && totalResults !== null) ? (showingTo < totalResults) : null;
6220
6221      var resultCount = totalResults !== null ? totalResults : (cardsFound || null);
6222      var observed = (countText !== "") || results.length > 0;
6223      var status = "ok";
6224      var reason = null;
6225      var nextStep = null;
6226      if (!observed || (resultCount === null && results.length === 0)) {
6227        status = "partial";
6228        observed = false;
6229        reason = "Neither the directory's own result total nor any agent card could be read out of the results region.";
6230        nextStep = "Reload " + location.href + " and call this tool again once the agent cards are visible.";
6231      } else if (results.length === 0 && resultCount !== null && resultCount > 0) {
6232        status = "partial";
6233        reason = "The directory reports " + resultCount + " results but no agent card could be read from the listing.";
6234        nextStep = "Call this tool again once the cards have rendered.";
6235      } else if (!cardsSettled) {
6236        status = "partial";
6237        reason = "The count line reads \"" + countText + "\" but " + renderedCards + " card(s) are rendered, so the listing had not finished re-rendering. The cards returned may still be the previous result set.";
6238        nextStep = "Call this tool again in a moment to read the settled listing.";
6239      }
6240
6241      var payload = {
6242        status: status,
6243        action: "read the Opal agent directory listing on this page",
6244        url: location.href,
6245        observed: observed,
6246        activeFilters: activeFilters,
6247        resultCount: resultCount,
6248        returnedCount: results.length,
6249        cardsOnPage: cardsFound,
6250        truncated: truncated,
6251        countText: countText || null,
6252        showingFrom: showingFrom,
6253        showingTo: showingTo,
6254        page: page,
6255        pagesOffered: pagesOffered,
6256        lastPageOffered: lastPageOffered,
6257        hasNextPage: hasNextPage,
6258        results: results,
6259        summary: status === "ok"
6260          ? ("Read " + results.length + " agent card(s) from the directory, which reports " + (resultCount === null ? "an unread number of" : resultCount) + " results" + (page ? " on page " + page : "") + ".")
6261          : "The Opal agent directory listing on this page could not be fully read."
6262      };
6263      if (reason) payload.reason = reason;
6264      if (nextStep) payload.nextStep = nextStep;
6265      return respond(payload);
6266    } catch (err) {
6267      return respond({
6268        status: "error",
6269        action: "read the Opal agent directory listing",
6270        url: location.href,
6271        reason: "The tool hit an unexpected error: " + (err && err.message ? err.message : String(err)),
6272        nextStep: "Reload the directory page and call this tool again.",
6273        summary: "The Opal agent directory listing could not be read."
6274      });
6275    }
6276  }
6277});
6278
6279  /* ---- optimizely-find-implementation-partner ---- */
6280mc.registerToolIf("/partners", {
6281  name: "optimizely-find-implementation-partner",
6282  description: "Searches the Optimizely partner directory on /partners. Types a keyword into the partner search input, opens the Countries, Specializations and Industry Expertises filter groups and toggles the options asked for (option labels are read off the live page at call time and matched by their visible text), can move to a given results page and can clear every active filter first. Returns the matching partners as objects with name, description, tier (for example Premier Platinum or Platinum), number of global certifications, number of product specializations, the full badge list and the partner website URL, together with the page's own 'Showing 1-8 of 183 results' text so the caller gets the total and the current page position, plus the filter options the tool toggled.",
6283  inputSchema: {
6284    type: "object",
6285    properties: {
6286      keyword: {
6287        type: "string",
6288        description: "Text to type into the partner search input, for example an agency name, a city or a technology. Omit to leave the current search term untouched."
6289      },
6290      countries: {
6291        type: "array",
6292        items: { type: "string" },
6293        description: "Country filter labels exactly as shown in the Countries group, for example '
6293United States' or 'Sweden'. Matched against the options read from the live page at call time; the hidden countries behind the 'more' button are revealed automatically. Unknown values return status 'error' with the list of options that are actually available."
6294      },
6295      specializations: {
6296        type: "array",
6297        items: { type: "string" },
6298        description: "Specialization filter labels as shown in the Specializations group, for example 'CMS' or 'Commerce'. The group is opened and its options are enumerated at call time; unknown values return status 'error' with the available options."
6299      },
6300      industryExpertises: {
6301        type: "array",
6302        items: { type: "string" },
6303        description: "Industry Expertises filter labels as shown in that group, for example 'Retail' or 'Financial Services'. The group is opened and its options are enumerated at call time; unknown values return status 'error' with the available options."
6304      },
6305      page: {
6306        type: "integer",
6307        description: "Results page to land on, 1 being the first page. Applied after the keyword and the filters, using the page's own pagination controls."
6308      },
6309      clearFilters: {
6310        type: "boolean",
6311        description: "Set true to press the Clear control next to the Filters heading before applying anything else."
6312      },
6313      limit: {
6314        type: "integer",
6315        description: "Maximum number of partner cards to return from the page currently shown. Defaults to 8, which is one full page."
6316      }
6317    }
6318  },
6319  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
6320  execute: async function (args) {
6321    trackWebMCPTool("optimizely-find-implementation-partner");
6322
6323    var FORM = 'form:has(input[type="search"])';
6324    var SEARCH_INPUT = FORM + ' input[type="search"]';
6325    var SEARCH_BUTTON = FORM + ' button';
6326    var COUNT_SEL = "div:has(> nav) > p";
6327    var ITEM_SEL = "a:has(ul li)";
6328    var GROUPS = {
6329      countries: {
6330        label: "Countries",
6331        toggle: FORM + " + div > button",
6332        panel: FORM + " + div > div",
6333        more: FORM + " + div > div > button"
6334      },
6335      specializations: {
6336        label: "Specializations",
6337        toggle: FORM + " + div + div > button",
6338        panel: FORM + " + div + div > div",
6339        more: FORM + " + div + div > div > button"
6340      },
6341      industryExpertises: {
6342        label: "Industry Expertises",
6343        toggle: FORM + " + div + div + div > button",
6344        panel: FORM + " + div + div + div > div",
6345        more: FORM + " + div + div + div > div > button"
6346      }
6347    };
6348
6349    function done(payload) {
6350      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
6351    }
6352    function sleep(ms) {
6353      return new Promise(function (r) { setTimeout(r, ms); });
6354    }
6355    function txt(el) {
6356      return el ? (el.textContent || "").replace(/\s+/g, " ").trim() : "";
6357    }
6358    function norm(s) {
6359      return String(s == null ? "" : s).replace(/\s+/g, " ").trim().toLowerCase();
6360    }
6361    function toList(v) {
6362      if (v == null) return [];
6363      var raw = Array.isArray(v) ? v : (typeof v === "string" ? v.split(",") : [v]);
6364      var out = [];
6365      for (var i = 0; i < raw.length; i++) {
6366        var s = String(raw[i] == null ? "" : raw[i]).trim();
6367        if (s) out.push(s);
6368      }
6369      return out;
6370    }
6371    function countElement() {
6372      return document.querySelector(COUNT_SEL);
6373    }
6374    function countText() {
6375      var e = countElement();
6376      return e ? txt(e) : null;
6377    }
6378    // The pagination nav sits beside the results count paragraph. Resolving it from
6379    // that paragraph keeps it away from the site header, which is also a div holding
6380    // a direct child nav and comes first in document order.
6381    function pagerNode() {
6382      var countEl = countElement();
6383      var scope = countEl ? countEl.parentElement : null;
6384      if (!scope) return null;
6385      var navs = scope.querySelectorAll("nav");
6386      for (var i = 0; i < navs.length; i++) {
6387        var bs = navs[i].querySelectorAll("button");
6388        for (var j = 0; j < bs.length; j++) {
6389          if (/^\d+$/.test(txt(bs[j]))) return navs[i];
6390        }
6391      }
6392      return navs.length ? navs[0] : null;
6393    }
6394    function parseCount(t, perHint) {
6395      var info = { rangeStart: null, rangeEnd: null, total: null, page: null, pageCount: null, perPage: null };
6396      if (!t) return info;
6397      var m = t.match(/(\d[\d,]*)\s*[-–—]\s*(\d[\d,]*)\s+of\s+(\d[\d,]*)/i);
6398      if (m) {
6399        info.rangeStart = parseInt(m[1].replace(/,/g, ""), 10);
6400        info.rangeEnd = parseInt(m[2].replace(/,/g, ""), 10);
6401        info.total = parseInt(m[3].replace(/,/g, ""), 10);
6402        var span = info.rangeEnd - info.rangeStart + 1;
6403        var per = (info.rangeEnd < info.total || !perHint) ? span : perHint;
6404        if (per > 0) {
6405          info.perPage = per;
6406          info.page = Math.floor((info.rangeStart - 1) / per) + 1;
6407          info.pageCount = Math.ceil(info.total / per);
6408        }
6409        return info;
6410      }
6411      var one = t.match(/of\s+(\d[\d,]*)/i);
6412      if (one) info.total = parseInt(one[1].replace(/,/g, ""), 10);
6413      return info;
6414    }
6415    function highlight(el) {
6416      if (!el || !el.style) return;
6417      try {
6418        el.style.outline = "3px solid #0037ff";
6419        el.style.outlineOffset = "2px";
6420        setTimeout(function () {
6421          try { el.style.outline = ""; el.style.outlineOffset = ""; } catch (e) {}
6422        }, 6000);
6423      } catch (e) {}
6424    }
6425    function scrollTo(el) {
6426      if (!el || !el.scrollIntoView) return;
6427      try { el.scrollIntoView({ behavior: "smooth", block: "center" }); } catch (e) { try { el.scrollIntoView(); } catch (e2) {} }
6428    }
6429    async function settle(prevText, timeout) {
6430      var deadline = Date.now() + (timeout || 10000);
6431      var last = countText();
6432      var lastChange = Date.now();
6433      var changed = last !== null && last !== prevText;
6434      while (Date.now() < deadline) {
6435        await sleep(200);
6436        var now = countText();
6437        if (now !== last) {
6438          last = now;
6439          lastChange = Date.now();
6440          if (now !== null && now !== prevText) changed = true;
6441          continue;
6442        }
6443        var quiet = Date.now() - lastChange;
6444        if (changed && quiet > 700) break;
6445        if (!changed && quiet > 2200) break;
6446      }
6447      return countText();
6448    }
6449    function filtersHeading() {
6450      var hs = document.querySelectorAll("h2");
6451      for (var i = 0; i < hs.length; i++) {
6452        if (/^filters\b/i.test(txt(hs[i]))) return hs[i];
6453      }
6454      return null;
6455    }
6456    function activeFilterCount() {
6457      var h = filtersHeading();
6458      if (!h) return null;
6459      var m = txt(h).match(/\((\d+)\)/);
6460      return m ? parseInt(m[1], 10) : 0;
6461    }
6462    function optionLabelText(labelEl) {
6463      var nodes = labelEl.querySelectorAll("span, p, div");
6464      for (var i = 0; i < nodes.length; i++) {
6465        var n = nodes[i];
6466        if (n.getAttribute && n.getAttribute("aria-hidden") === "true") continue;
6467        if (n.querySelector && n.querySelector("button")) continue;
6468        var t = txt(n);
6469        if (t) return t;
6470      }
6471      return txt(labelEl);
6472    }
6473    function listOptions(panelSel) {
6474      var labels = document.querySelectorAll(panelSel + " label");
6475      var out = [];
6476      for (var i = 0; i < labels.length; i++) {
6477        var t = optionLabelText(labels[i]);
6478        if (t) out.push({ el: labels[i], text: t });
6479      }
6480      return out;
6481    }
6482    function optionTexts(opts) {
6483      var out = [];
6484      for (var i = 0; i < opts.length; i++) out.push(opts[i].text);
6485      return out;
6486    }
6487    function findOption(opts, want) {
6488      var w = norm(want);
6489      var i;
6490      for (i = 0; i < opts.length; i++) if (norm(opts[i].text) === w) return opts[i];
6491      var partial = [];
6492      for (i = 0; i < opts.length; i++) {
6493        var o = norm(opts[i].text);
6494        if (o.indexOf(w) !== -1 || w.indexOf(o) !== -1) partial.push(opts[i]);
6495      }
6496      return partial.length === 1 ? partial[0] : null;
6497    }
6498    function isChecked(labelEl) {
6499      var input = labelEl.querySelector('input[type="checkbox"], input[type="radio"]');
6500      if (input) return !!input.checked;
6501      var btn = labelEl.querySelector("button");
6502      if (btn) {
6503        var a = btn.getAttribute("aria-checked") || btn.getAttribute("aria-pressed") || btn.getAttribute("data-state");
6504        if (a === "true" || a === "checked" || a === "on") return true;
6505        if (a === "false" || a === "unchecked" || a === "off") return false;
6506      }
6507      return null;
6508    }
6509    function leftPage() {
6510      return location.origin + location.pathname !== startOrigin + startPath;
6511    }
6512
6513    var startOrigin = location.origin;
6514    var startPath = location.pathname;
6515    var a = args || {};
6516    var keyword = (a.keyword == null) ? null : String(a.keyword);
6517    var wantCountries = toList(a.countries);
6518    var wantSpecializations = toList(a.specializations);
6519    var wantIndustries = toList(a.industryExpertises);
6520    var wantPage = parseInt(a.page, 10);
6521    if (!(wantPage > 0)) wantPage = null;
6522    var limit = parseInt(a.limit, 10);
6523    if (!(limit > 0)) limit = 8;
6524    if (limit > 50) limit = 50;
6525    var applied = { keyword: null, countries: [], specializations: [], industryExpertises: [], cleared: false, page: null };
6526    var optionsSeen = {};
6527    var notes = [];
6528
6529    try {
6530      var formNode = document.querySelector(FORM);
6531      if (!formNode) {
6532        try { await waitForElement(FORM, 8000); } catch (e) {}
6533        formNode = document.querySelector(FORM);
6534      }
6535      if (!formNode) {
6536        return done({
6537          status: "error",
6538          action: "search the Optimizely partner directory",
6539          url: location.href,
6540          reason: "The partner search form was not found on this page.",
6541          nextStep: "Open https://www.optimizely.com/partners and call this tool again once the directory has loaded.",
6542          summary: "The partner directory search form is not present, so nothing could be searched."
6543        });
6544      }
6545
6546      // Runtime navigation check: a real native submit would take the document away,
6547      // so build the destination first and hand the caller a navigated payload.
6548      if (keyword !== null && formNode.getAttribute("action")) {
6549        var dest = buildUrl(location.pathname + "?q={keyword}&countries={countries}&specializations={specializations}&page={page}", {
6550          keyword: keyword,
6551          countries: wantCountries.join(","),
6552          specializations: wantSpecializations.join(","),
6553          page: wantPage ? String(wantPage) : ""
6554        });
6555        var navPayload = {
6556          status: "navigated",
6557          action: "submit the partner directory search",
6558          url: dest,
6559          nextStep: "The partner directory is loading with the search applied. Call optimizely-find-implementation-partner again with 
6559no keyword to read the settled results.",
6560          summary: "The partner search form submits natively, so the directory was reloaded with the search applied."
6561        };
6562        location.assign(dest);
6563        return done(navPayload);
6564      }
6565
6566      scrollTo(formNode);
6567      highlight(formNode);
6568
6569      try { await waitForElement(COUNT_SEL, 10000); } catch (e) { notes.push("The results count element did not appear within 10 seconds."); }
6570      var firstInfo = parseCount(countText(), null);
6571      var perPageHint = (firstInfo.rangeEnd !== null && firstInfo.total !== null && firstInfo.rangeEnd < firstInfo.total) ? firstInfo.perPage : null;
6572
6573      // 1. Clear existing filters when asked.
6574      if (a.clearFilters === true) {
6575        var h2 = filtersHeading();
6576        var clearBtn = null;
6577        if (h2) {
6578          var sib = h2.nextElementSibling;
6579          if (sib && sib.tagName === "BUTTON") clearBtn = sib;
6580          if (!clearBtn && h2.parentElement) {
6581            var cands = h2.parentElement.querySelectorAll("button");
6582            for (var ci = 0; ci < cands.length; ci++) {
6583              if (/^clear\b/i.test(txt(cands[ci]))) { clearBtn = cands[ci]; break; }
6584            }
6585          }
6586        }
6587        if (clearBtn) {
6588          var beforeClear = countText();
6589          clearBtn.click();
6590          applied.cleared = true;
6591          await settle(beforeClear, 9000);
6592        } else {
6593          notes.push("The Clear control next to the Filters heading was not found, so existing filters were left in place.");
6594        }
6595      }
6596
6597      // 2. Keyword.
6598      if (keyword !== null) {
6599        try { await fillInput(SEARCH_INPUT, keyword); } catch (e) { notes.push("The search input could not be filled."); }
6600        var searchBtn = document.querySelector(SEARCH_BUTTON);
6601        var beforeSearch = countText();
6602        if (searchBtn) {
6603          searchBtn.click();
6604        } else {
6605          var inputEl = document.querySelector(SEARCH_INPUT);
6606          if (inputEl) {
6607            inputEl.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", code: "Enter", keyCode: 13, which: 13, bubbles: true }));
6608          } else {
6609            notes.push("Neither the search button nor the search input could be located.");
6610          }
6611        }
6612        await settle(beforeSearch, 10000);
6613        var readBack = document.querySelector(SEARCH_INPUT);
6614        applied.keyword = readBack ? readBack.value : null;
6615        if (leftPage()) {
6616          return done({
6617            status: "navigated",
6618            action: "submit the partner directory search",
6619            url: location.href,
6620            nextStep: "A new page is loading. Call optimizely-find-implementation-partner again once it has settled to read the partner results.",
6621            summary: "Searching the directory moved the browser to a different page, so no results were read here."
6622          });
6623        }
6624      }
6625
6626      // 3. Filter groups - options are enumerated from the live page.
6627      var groupOrder = [
6628        { key: "countries", wanted: wantCountries },
6629        { key: "specializations", wanted: wantSpecializations },
6630        { key: "industryExpertises", wanted: wantIndustries }
6631      ];
6632      for (var gi = 0; gi < groupOrder.length; gi++) {
6633        var key = groupOrder[gi].key;
6634        var wanted = groupOrder[gi].wanted;
6635        if (!wanted.length) continue;
6636        var g = GROUPS[key];
6637        var opts = listOptions(g.panel);
6638        if (!opts.length) {
6639          var toggleEl = document.querySelector(g.toggle);
6640          if (toggleEl) {
6641            toggleEl.click();
6642            try { await waitForElement(g.panel + " label", 6000); } catch (e) {}
6643            await sleep(350);
6644            opts = listOptions(g.panel);
6645          }
6646        }
6647        if (!opts.length) {
6648          return done({
6649            status: "error",
6650            action: "open the " + g.label + " filter group",
6651            url: location.href,
6652            reason: "The " + g.label + " filter group did not expose any options after its toggle was clicked.",
6653            availableOptions: [],
6654            group: g.label,
6655            appliedFilters: applied,
6656            nextStep: "Retry once the page has finished loading, or filter with a keyword instead.",
6657            summary: "No " + g.label + " options were readable on the page."
6658          });
6659        }
6660        var missing = [];
6661        for (var wi = 0; wi < wanted.length; wi++) {
6662          var want = wanted[wi];
6663          var hit = findOption(opts, want);
6664          if (!hit) {
6665            var moreBtn = document.querySelector(g.more);
6666            if (moreBtn && !(moreBtn.closest && moreBtn.closest("label"))) {
6667              moreBtn.click();
6668              await sleep(500);
6669              opts = listOptions(g.panel);
6670              hit = findOption(opts, want);
6671            }
6672          }
6673          if (!hit) { missing.push(want); continue; }
6674          var state = isChecked(hit.el);
6675          if (state === true) {
6676            applied[key].push(hit.text);
6677            continue;
6678          }
6679          var clickTarget = hit.el.querySelector("button") || hit.el.querySelector('input[type="checkbox"]') || hit.el;
6680          var beforeFilter = countText();
6681          clickTarget.click();
6682          applied[key].push(hit.text);
6683          await settle(beforeFilter, 10000);
6684          opts = listOptions(g.panel);
6685        }
6686        optionsSeen[key] = optionTexts(opts);
6687        if (missing.length) {
6688          return done({
6689            status: "error",
6690            action: "apply " + g.label + " filters on the partner directory",
6691            url: location.href,
6692            reason: "These " + g.label + " values are not among the options on the page: " + missing.join(", ") + ".",
6693            availableOptions: optionsSeen[key],
6694            group: g.label,
6695            appliedFilters: applied,
6696            activeFilterCount: activeFilterCount(),
6697            nextStep: "Call this tool again using a value from availableOptions for " + key + ".",
6698            summary: "Could not apply " + missing.join(", ") + " because the " + g.label + " group does not offer those options."
6699          });
6700        }
6701      }
6702
6703      // 4. Pagination.
6704      if (wantPage) {
6705        var pageNote = null;
6706        for (var guard = 0; guard < 30; guard++) {
6707          var nowInfo = parseCount(countText(), perPageHint);
6708          if (nowInfo.perPage) perPageHint = nowInfo.rangeEnd !== null && nowInfo.total 
6708!== null && nowInfo.rangeEnd < nowInfo.total ? nowInfo.perPage : perPageHint;
6709          if (nowInfo.page === wantPage) break;
6710          var pager = pagerNode();
6711          if (!pager) { pageNote = "The pagination controls were not found, so the page position was left unchanged."; break; }
6712          var buttons = pager.querySelectorAll("button");
6713          if (!buttons.length) { pageNote = "The pagination controls carried no buttons."; break; }
6714          var direct = null, nextBtn = null, prevBtn = null, bi;
6715          for (bi = 0; bi < buttons.length; bi++) {
6716            var bt = txt(buttons[bi]);
6717            if (bt === String(wantPage) && !buttons[bi].disabled) direct = buttons[bi];
6718            if (/chevron_right|next/i.test(bt)) nextBtn = buttons[bi];
6719            if (/chevron_left|previous|prev/i.test(bt)) prevBtn = buttons[bi];
6720          }
6721          if (!nextBtn) nextBtn = buttons[buttons.length - 1];
6722          if (!prevBtn) prevBtn = buttons[0];
6723          var stepBtn = direct;
6724          if (!stepBtn) stepBtn = (nowInfo.page !== null && wantPage < nowInfo.page) ? prevBtn : nextBtn;
6725          if (!stepBtn || stepBtn.disabled) { pageNote = "Page " + wantPage + " could not be reached with the pagination controls."; break; }
6726          var beforePage = countText();
6727          stepBtn.click();
6728          await settle(beforePage, 10000);
6729          var afterInfo = parseCount(countText(), perPageHint);
6730          if (afterInfo.page === nowInfo.page) { pageNote = "The pagination controls stopped at page " + (afterInfo.page === null ? "unknown" : afterInfo.page) + "."; break; }
6731        }
6732        if (pageNote) notes.push(pageNote);
6733        applied.page = wantPage;
6734      }
6735
6736      if (leftPage()) {
6737        return done({
6738          status: "navigated",
6739          action: "filter the partner directory",
6740          url: location.href,
6741          nextStep: "A new page is loading. Call optimizely-find-implementation-partner again once it has settled to read the partner results.",
6742          summary: "The interaction moved the browser to a different page, so no results were read here."
6743        });
6744      }
6745
6746      // 5. Read the settled results.
6747      try { await waitForElement(ITEM_SEL, 8000); } catch (e) { notes.push("No partner cards were present after waiting 8 seconds."); }
6748      await settle(countText(), 3000);
6749
6750      var extracted = { resultCount: null, truncated: false, results: [] };
6751      try {
6752        var got = await extractItems(ITEM_SEL, {
6753          websiteUrl: "@href",
6754          name: "div > div > div:nth-child(2) > div > span",
6755          description: "div > div > div:nth-child(1)",
6756          tier: "ul li:nth-child(1)"
6757        }, limit);
6758        if (got) extracted = got;
6759      } catch (e) {
6760        notes.push("The partner cards could not be extracted: " + (e && e.message ? e.message : "unknown error") + ".");
6761      }
6762
6763      var cards = document.querySelectorAll(ITEM_SEL);
6764      var badgeLists = [];
6765      for (var ni = 0; ni < cards.length; ni++) {
6766        var lis = cards[ni].querySelectorAll("ul li");
6767        var badges = [];
6768        for (var li = 0; li < lis.length; li++) {
6769          if (lis[li].getAttribute && lis[li].getAttribute("aria-hidden") === "true") continue;
6770          var bt = txt(lis[li]);
6771          if (bt) badges.push(bt);
6772        }
6773        badgeLists.push(badges);
6774      }
6775
6776      var rows = extracted.results || [];
6777      var results = [];
6778      for (var ri = 0; ri < rows.length; ri++) {
6779        var row = rows[ri] || {};
6780        var badgeList = badgeLists[ri] || [];
6781        var certs = null, specs = null, tier = null, other = [];
6782        for (var bj = 0; bj < badgeList.length; bj++) {
6783          var b = badgeList[bj];
6784          var mc1 = b.match(/(\d[\d,]*)\s*global certification/i);
6785          var ms1 = b.match(/(\d[\d,]*)\s*product specialization/i);
6786          if (mc1) { certs = parseInt(mc1[1].replace(/,/g, ""), 10); continue; }
6787          if (ms1) { specs = parseInt(ms1[1].replace(/,/g, ""), 10); continue; }
6788          if (tier === null) tier = b; else other.push(b);
6789        }
6790        if (tier === null && row.tier && !/global certification|product specialization/i.test(row.tier)) tier = row.tier;
6791        var name = (row.name || "").trim();
6792        var desc = (row.description || "").replace(/\s+/g, " ").trim();
6793        for (var bk = 0; bk < badgeList.length; bk++) {
6794          desc = desc.split(badgeList[bk]).join(" ");
6795        }
6796        desc = desc.replace(/\s+/g, " ").trim();
6797        if (name && norm(desc) === norm(name)) desc = "";
6798        var href = (row.websiteUrl || "").trim();
6799        if (href && href.indexOf("//") === -1 && href.charAt(0) === "/") href = location.origin + href;
6800        results.push({
6801          name: name || null,
6802          description: desc || null,
6803          tier: tier || null,
6804          globalCertifications: certs,
6805          productSpecializations: specs,
6806          badges: badgeList,
6807          websiteUrl: href || null
6808        });
6809      }
6810
6811      var finalText = countText();
6812      var info = parseCount(finalText, perPageHint);
6813      var total = info.total;
6814      if (total === null) {
6815        try {
6816          var rc = await readResultCount(COUNT_SEL, null);
6817          if (typeof rc === "number" && !isNaN(rc)) total = rc;
6818        } catch (e) {}
6819      }
6820
6821      var region = cards.length ? (cards[0].parentElement || cards[0]) : null;
6822      if (region) { scrollTo(region); highlight(region); }
6823
6824      var observed = (total !== null) || results.length > 0 || finalText !== null;
6825      var activeCount = activeFilterCount();
6826      var searchNow = document.querySelector(SEARCH_INPUT);
6827      var currentKeyword = searchNow ? searchNow.value : null;
6828
6829      var payload = {
6830        status: observed ? "ok" : "partial",
6831        action: "search and filter the Optimizely partner directory",
6832        url: location.href,
6833        observed: observed,
6834        keyword: currentKeyword,
6835        appliedFilters: applied,
6836        activeFilterCount: activeCount,
6837        countText: finalText,
6838        resultCount: total !== null ? total : (results.length ? results.length : null),
6839        returnedCount: results.length,
6840        rangeStart: info.rangeStart,
6841        rangeEnd: info.rangeEnd,
6842        page: info.page,
6843        pageCount: info.pageCount,
6844        truncated: !!extracted.truncated,
6845        results: results,
6846        availableOptions: optionsSeen,
6847        summary: ""
6848      };
6849      if (notes.length) payload.notes = notes;
6850
6851      if (!observed) {
6852        payload.status = "partial";
6853        payload.reason = "The results count element and the partner cards were both absent after the interaction, so nothing could be read back from the page.";
6854        payload.nextStep = "Wait for the directory to finish loading and call optimizely-find-implementation-partner again.";
6855        payload.summary = "No partner results could be read from the page.";
6856      } else if (!results.length) {
6857        payload.status = total === 0 ? "ok" : "partial";
6858        payload.reason = total === 0 ? undefined : "The count element was read but no partner cards were extracted from the grid.";
6859        if (total !== 0) payload.nextStep = "Call the tool again after the grid has rendered, or clear the filters and retry.";
6860        payload.summary = total === 0
6861          ? "No partners match the current search and filters."
6862          : "Read the count text '" + finalText + "' but could not extract any partner cards.";
6863      } else {
6864        payload.summary = "Read " + results.length + " partner" + (results.length === 1 ? "" : "s") +
6865          (total !== null ? " out of " + total : "") +
6866          (info.page !== null ? " on page " + info.page + (info.pageCount !== null ? " of " + info.pageCount : "") : "") +
6867          (activeCount ? " with " + activeCount + " filter" + (activeCount === 1 ? "" : "s") + " active" : "") + ".";
6868      }
6869      return done(payload);
6870    } catch (err) {
6871      return done({
6872        status: "error",
6873        action: "search and filter the Optimizely partner directory",
6874        url: location.href,
6875        reason: "The tool failed while driving the directory: " + (err && err.message ? err.message : 
6875String(err)) + ".",
6876        appliedFilters: applied,
6877        nextStep: "Reload https://www.optimizely.com/partners and call optimizely-find-implementation-partner again with fewer filters.",
6878        summary: "The partner directory search could not be completed."
6879      });
6880    }
6881  }
6882});
6883
6884  /* ---- optimizely-apply-become-solution-partner, optimizely-apply-become-solution-partner-submit ---- */
6885mc.registerToolIf("/partners", {
6886  name: "optimizely-apply-become-solution-partner",
6887  description: "Fills the 'Become a solution partner' application form in the Become a partner section of the Optimizely partners page: first name, last name, company, job title, work email, website URL, country, number of company employees, prior Optimizely product experience, whether a current client is interested, product interest, interest in building an Optimizely delivery practice, and the free-text answer to 'What interests you about partnering with Optimizely?'. Scrolls the form into view, highlights it, writes each value through React-aware setters, then reads the values back out of the form and returns them together with the fields it treats as required that are still empty, a ready flag, the name of the companion submit tool that commits the application, and the path of the separate technology-partner application form.",
6888  inputSchema: {
6889    type: "object",
6890    properties: {
6891      firstName: { type: "string", description: "First name of the person applying." },
6892      lastName: { type: "string", description: "Last name of the person applying." },
6893      company: { type: "string", description: "Company or agency name for the 'Your Company' field." },
6894      jobTitle: { type: "string", description: "Job title of the person applying." },
6895      workEmail: { type: "string", description: "Work email address." },
6896      websiteUrl: { type: "string", description: "Company website URL, for example https://example.com." },
6897      country: { type: "string", description: "Country name to resolve in the Country typeahead, for example United States. The tool types the name, picks the matching suggestion the page renders and verifies the resolved value; if nothing matches it returns the suggestions it actually saw." },
6898      numberOfCompanyEmployees: { type: "string", description: "Company size band as offered by the page's 'Number of Company Employees' select, for example Under 50 or 1000-2000. The live options are read at call time and returned when the value does not match." },
6899      optimizelyProductExperience: { type: "string", description: "Answer to 'Do you have experience working with Optimizely products', normally Yes or No; matched against the live select options." },
6900      currentClientInterested: { type: "string", description: "Answer to 'Do you have a current client interested in one of our products?', normally Yes or No; matched against the live select options." },
6901      deliveryPracticeInterest: { type: "string", description: "Answer to 'Are you interested in building an Optimizely delivery practice as part of your agency services offering?', normally Yes or No; matched against the live select options." },
6902      productInterest: { type: "string", description: "Free text for the 'Product interest' box, for example which Optimizely products the partnership would cover." },
6903      partneringInterest: { type: "string", description: "Free text answer to 'What interests you about partnering with Optimizely?'." }
6904    },
6905    required: ["firstName", "lastName", "company", "jobTitle", "workEmail", "websiteUrl"]
6906  },
6907  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
6908  execute: async function (args) {
6909    trackWebMCPTool("optimizely-apply-become-solution-partner");
6910    var TOOL = "optimizely-apply-become-solution-partner";
6911    var SUBMIT_TOOL = "optimizely-apply-become-solution-partner-submit";
6912    var TECH_PATH = "/partners/become-a-partner/tech/";
6913    var ACTION = "fill the Become a solution partner application form";
6914    function out(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
6915    try {
6916      var a = args || {};
6917      function norm(s) { return (s === null || s === undefined) ? "" : String(s).replace(/\s+/g, " ").trim(); }
6918      function low(s) { return norm(s).toLowerCase(); }
6919      function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
6920      function visible(el) {
6921        if (!el) return false;
6922        if (el.getAttribute && el.getAttribute("aria-hidden") === "true") return false;
6923        var cs = null;
6924        try { cs = window.getComputedStyle(el); } catch (e) { cs = null; }
6925        if (cs && (cs.display === "none" || cs.visibility === "hidden")) return false;
6926        var r = el.getBoundingClientRect();
6927        return !!(r.width || r.height || el.offsetParent);
6928      }
6929      function nativeSet(el, value) {
6930        var proto = window.HTMLInputElement.prototype;
6931        if (el.tagName === "TEXTAREA") proto = window.HTMLTextAreaElement.prototype;
6932        else if (el.tagName === "SELECT") proto = window.HTMLSelectElement.prototype;
6933        var d = Object.getOwnPropertyDescriptor(proto, "value");
6934        if (d && d.set) d.set.call(el, value); else el.value = value;
6935        el.dispatchEvent(new Event("input", { bubbles: true }));
6936        el.dispatchEvent(new Event("change", { bubbles: true }));
6937      }
6938
6939      var FORM_SELECTOR = 'main form:has(fieldset)';
6940      var form = null;
6941      try { form = document.querySelector(FORM_SELECTOR); } catch (e) { form = null; }
6942      if (!form) {
6943        var allForms = document.querySelectorAll("main form, form");
6944        for (var fi = 0; fi < allForms.length; fi++) {
6945          if (allForms[fi].querySelector("fieldset") && low(allForms[fi].textContent).indexOf("become a solution partner") !== -1) { form = allForms[fi]; break; }
6946        }
6947        if (!form) {
6948          for (var fj = 0; fj < allForms.length; fj++) { if (allForms[fj].querySelector("fieldset")) { form = allForms[fj]; break; } }
6949        }
6950      }
6951      if (!form) {
6952        return out({
6953          status: "error",
6954          action: ACTION,
6955          url: location.href,
6956          reason: "The Become a solution partner form could not be found in the page.",
6957          nextStep: "Open https://www.optimizely.com/partners, follow the 'Become a partner' link to the application section, then call " + TOOL + " again.",
6958          summary: "Could not find the solution partner application form on this page."
6959        });
6960      }
6961
6962      try {
6963        form.style.outline = "3px solid #0037ff";
6964        form.style.outlineOffset = "4px";
6965        form.scrollIntoView({ behavior: "smooth", block: "center" });
6966      } catch (e) { }
6967
6968      var TEXT_TAGS = 'input[type="text"], input[type="email"], input[type="url"], input[type="tel"], input:not([type])';
6969      var FIELDS = [
6970        { key: "firstName", label: "First name", kind: "text", tags: TEXT_TAGS, required: true, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(1) > div:nth-child(1) > div > div > input[type="text"]' },
6971        { key: "lastName", label: "Last name", kind: "text", tags: TEXT_TAGS, required: true, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(1) > div:nth-child(2) > div > div > input[type="text"]' },
6972        { key: "company", label: "Your Company", kind: "text", tags: TEXT_TAGS, required: true, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(1) > div:nth-child(3) > div > div > input[type="text"]' },
6973        { key: "jobTitle", label: "Job title", kind: "text", tags: TEXT_TAGS, required: true, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(1) > div:nth-child(4) > div > div > input[type="text"]' },
6974        { key: "workEmail", label: "Work email", kind: "text", tags: TEXT_TAGS, required: true, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(1) > div:nth-child(5) > div > div > input[type="text"]' },
6975        { key: "websiteUrl", label: "Website URL", kind: "text", tags: TEXT_TAGS, required: true, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(1) > div:nth-child(6) > div > div > input[type="text"]' },
6976        { key: "numberOfCompanyEmployees", label: "Number of Company Employees", kind: "select", tags: "select", required: false, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(1) > div:nth-child(7) > div > select' },
6977        { key: "partneringInterest", label: "What interests you about partnering with Optimizely?", kind: "textarea", tags: "textarea", required: false, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(2) > div > div > div > textarea' },
6978        { key: "optimizelyProductExperience", label: "Do you have experience working with Optimizely products", kind: "select", tags: "select", required: false, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(3) > div > div > select' },
6979        { key: "currentClientInterested", label: "Do you have a current client interested in one of our products?", kind: "select", tags: "select", required: false, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(4) > div > div > select' },
6980        { key: "productInterest", label: "Product interest", kind: "textarea", tags: "textarea", required: false, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(5) > div > div > div > textarea' },
6981        { key: "deliveryPracticeInterest", label: "Are you interested in building an Optimizely delivery practice", kind: "select", tags: "select", required: false, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(6) > div > div > select' }
6982      ];
6983      var COUNTRY = { key: "country", label: "Country", kind: "country", tags: 'input[type="text"], input
6983:not([type]), input[type="search"]', required: false, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(1) > div:nth-child(8) > div > div > div > div > input' };
6984      var COUNTRY_HIDDEN_SELECTOR = 'main form:has(fieldset) fieldset > div > div:nth-child(1) > div:nth-child(8) > div > input[type="hidden"]';
6985
6986      function labelFor(el) {
6987        var parts = [];
6988        if (el.getAttribute) {
6989          if (el.getAttribute("aria-label")) parts.push(el.getAttribute("aria-label"));
6990          if (el.getAttribute("placeholder")) parts.push(el.getAttribute("placeholder"));
6991          if (el.getAttribute("name")) parts.push(el.getAttribute("name"));
6992          if (el.getAttribute("title")) parts.push(el.getAttribute("title"));
6993        }
6994        if (el.id) {
6995          try {
6996            var esc = (window.CSS && CSS.escape) ? CSS.escape(el.id) : el.id;
6997            var lf = document.querySelector('label[for="' + esc + '"]');
6998            if (lf) parts.push(lf.textContent);
6999          } catch (e) { }
7000        }
7001        if (!parts.length && el.tagName === "SELECT") {
7002          var sibBtn = el.previousElementSibling;
7003          if (sibBtn && sibBtn.tagName === "BUTTON") {
7004            var sibSpan = sibBtn.querySelector("span");
7005            parts.push((sibSpan || sibBtn).textContent);
7006          }
7007        }
7008        var own = el.closest ? el.closest("label") : null;
7009        if (own) parts.push(own.textContent);
7010        if (!parts.length) {
7011          var node = el.parentElement, hops = 0;
7012          while (node && hops < 5 && node !== form) {
7013            var labs = node.querySelectorAll("label, legend");
7014            if (labs.length === 1) { parts.push(labs[0].textContent); break; }
7015            if (labs.length > 1) break;
7016            node = node.parentElement; hops++;
7017          }
7018        }
7019        if (!parts.length) {
7020          var p = el.parentElement, h = 0;
7021          while (p && h < 4 && !parts.length) {
7022            var prev = p.previousElementSibling;
7023            if (prev && !prev.querySelector("input, select, textarea")) {
7024              var pt = norm(prev.textContent);
7025              if (pt && pt.length < 140) parts.push(pt);
7026            }
7027            p = p.parentElement; h++;
7028          }
7029        }
7030        return low(parts.join(" | "));
7031      }
7032
7033      function locate(spec) {
7034        var structural = null;
7035        try { structural = document.querySelector(spec.selector); } catch (e) { structural = null; }
7036        if (structural && form.contains(structural)) {
7037          var lt = labelFor(structural);
7038          if (!lt || lt.indexOf(low(spec.label)) !== -1) return structural;
7039        }
7040        var want = low(spec.label);
7041        var candidates = form.querySelectorAll(spec.tags);
7042        for (var i = 0; i < candidates.length; i++) {
7043          if (labelFor(candidates[i]).indexOf(want) !== -1) return candidates[i];
7044        }
7045        if (want.length > 24) {
7046          var shortWant = want.slice(0, 24);
7047          for (var k = 0; k < candidates.length; k++) {
7048            if (labelFor(candidates[k]).indexOf(shortWant) !== -1) return candidates[k];
7049          }
7050        }
7051        if (structural && form.contains(structural)) return structural;
7052        return null;
7053      }
7054
7055      function optionsOf(sel) {
7056        var res = [];
7057        for (var i = 0; i < sel.options.length; i++) {
7058          var o = sel.options[i];
7059          var lbl = norm(o.textContent) || norm(o.value);
7060          if (!lbl || lbl === "-- choose --") continue;
7061          res.push({ value: o.value, label: lbl });
7062        }
7063        return res;
7064      }
7065      function matchOption(sel, wanted) {
7066        var w = low(wanted);
7067        var opts = optionsOf(sel);
7068        for (var i = 0; i < opts.length; i++) { if (low(opts[i].value) === w || low(opts[i].label) === w) return opts[i]; }
7069        for (var j = 0; j < opts.length; j++) { if (low(opts[j].label).indexOf(w) === 0 || low(opts[j].value).indexOf(w) === 0) return opts[j]; }
7070        return null;
7071      }
7072
7073      var located = {};
7074      for (var s = 0; s < FIELDS.length; s++) { located[FIELDS[s].key] = locate(FIELDS[s]); }
7075      var countryInput = locate(COUNTRY);
7076
7077      var invalid = [];
7078      var selectPlan = [];
7079      for (var v = 0; v < FIELDS.length; v++) {
7080        var spec = FIELDS[v];
7081        if (spec.kind !== "select") continue;
7082        var raw = a[spec.key];
7083        if (raw === undefined || raw === null || norm(raw) === "") continue;
7084        var selEl = located[spec.key];
7085        if (!selEl) continue;
7086        var m = matchOption(selEl, raw);
7087        if (!m) {
7088          invalid.push({
7089            field: spec.key,
7090            label: spec.label,
7091            requested: norm(raw),
7092            availableOptions: optionsOf(selEl).map(function (o) { return o.label; })
7093          });
7094        } else {
7095          selectPlan.push({ spec: spec, el: selEl, option: m });
7096        }
7097      }
7098      if (invalid.length) {
7099        return out({
7100          status: "error",
7101          action: ACTION,
7102          url: location.href,
7103          reason: "The value requested for " + invalid[0].field + " (" + invalid[0].requested + ") is not one of the options this page currently offers for '" + invalid[0].label + "'. Nothing was written to the form.",
7104          invalidFields: invalid,
7105          availableOptions: invalid[0].availableOptions,
7106          nextStep: "Call " + TOOL + " again using one of the values listed in availableOptions for " + invalid[0].field + ".",
7107          summary: "Did not fill the form: " + invalid[0].requested + " is not an option offered for " + invalid[0].label + "."
7108        });
7109      }
7110
7111      var notWritten = [];
7112      var skippedHidden = [];
7113      var notFound = [];
7114
7115      async function writeValue(spec, el, value) {
7116        var target = String(value);
7117        if (spec.kind === "select") {
7118          var usedHelper = false;
7119          try {
7120            if (typeof fillSelect === "function" && document.querySelector(spec.selector) === el) {
7121              await fillSelect(spec.selector, target);
7122              usedHelper = true;
7123            }
7124          } catch (e) { usedHelper = false; }
7125          if (low(el.value) !== low(target)) nativeSet(el, target);
7126          return usedHelper;
7127        }
7128        try {
7129          if (typeof fillInput === "function" && document.querySelector(spec.selector) === el) {
7130            await fillInput(spec.selector, target);
7131          }
7132        } catch (e) { }
7133        if (norm(el.value) !== norm(target)) nativeSet(el, target);
7134        try { el.dispatchEvent(new FocusEvent("focusout", { bubbles: true })); } catch (e) { }
7135        return true;
7136      }
7137
7138      for (var w = 0; w < FIELDS.length; w++) {
7139        var fs = FIELDS[w];
7140        if (fs.kind === "select") continue;
7141        var val = a[fs.key];
7142        if (val === undefined || val === null || norm(val) === "") continue;
7143        var el = located[fs.key];
7144        if (!el) { notFound.push(fs.key); continue; }
7145        if (!visible(el) && norm(el.value)) { skippedHidden.push({ field: fs.key, existingValue: norm(el.value) }); continue; }
7146        await writeValue(fs, el, norm(val));
7147      }
7148      for (var p2 = 0; p2 < selectPlan.length; p2++) {
7149        await writeValue(selectPlan[p2].spec, selectPlan[p2].el, selectPlan[p2].option.value);
7150      }
7151      for (var nf = 0; nf < FIELDS.length; nf++) {
7152        var fk = FIELDS[nf];
7153        if (a[fk.key] !== undefined && a[fk.key] !== null && norm(a[fk.key]) !== "" && !located[fk.key] && notFound.indexOf(fk.key) === -1) notFound.push(fk.key);
7154      }
7155
7156      var countryReport = null;
7157      if (a.country !== undefined && a.country !== null && norm(a.country) !== "") {
7158        if (!countryInput) {
7159          notFound.push("country");
7160          countryReport = { requested: norm(a.country), resolved: "", shown: "", suggestionsSeen: [] };
7161        } else {
7162          var hidden = null;
7163          try { hidden = document.querySelector(COUNTRY_HIDDEN_SELECTOR); } catch (e) { hidden = null; }
7164          var group = countryInput.parentElement, gh = 0;
7165          while (group && gh < 5 && group !== form) {
7166            if (group.querySelector('input[type="hidden"]')) break;
7167            group = group.parentElement; gh++;
7168          }
7169          if (!group || group === form) group = countryInput.closest("div") || form;
7170          if (!hidden || !group.contains(hidden)) {
7171            var h2 = group.querySelector('input[type="hidden"]');
7172            if (h2) hidden = h2;
7173          }
7174          try { countryInput.focus(); } catch (e) { }
7175          nativeSet(countryInput, norm(a.country));
7176          try { countryInput.dispatchEvent(new KeyboardEvent("keyup", { bubbles: true, key: norm(a.country).slice(-1) })); } catch (e) { }
7177          var picked = null;
7178          var seen = [];
7179          for (var t = 0; t < 20 && !picked; t++) {
7180            await sleep(150);
7181            var nodes = group.querySelectorAll('[role="option"], li, ul button, ul div, [role="listbox"] div');
7182            seen = [];
7183            for (var n = 0; n < nodes.length; n++) {
7184              var nd = nodes[n];
7185              if (!visible(nd)) continue;
7186              if (nd.querySelector && nd.querySelector('[role="option"], li')) continue;
7187              var txt = norm(nd.textContent);
7188              if (!txt || txt.length > 60) continue;
7189              if (seen.indexOf(txt) === -1) seen.push(txt);
7190              if (!picked && low(txt) === low(a.country)) picked = nd;
7191            }
7192            if (!picked) {
7193              for (var q = 0; q < nodes.length; q++) {
7194                var nd2 = nodes[q];
7195                if (!visible(nd2)) continue;
7196                var t2 = norm(nd2.textContent);
7197                if (t2 && t2.length <= 60 && low(t2).indexOf(low(a.country)) === 0) { picked = nd2; break; }
7198              }
7199            }
7200          }
7201          if (picked) {
7202            try { picked.dispatchEvent(new MouseEvent("mousedown", { bubbles: true })); } catch (e) { }
7203            try { picked.click(); } catch (e) { }
7204          } else {
7205            try { countryInput.dispatchEvent(new KeyboardEvent("keydown", { bubbles: true, key: "Enter", code: "Enter", keyCode: 13, which: 13 })); } catch (e) { }
7206          }
7207          await sleep(500);
7208          countryReport = {
7209            requested: norm(a.country),
7210            resolved: hidden ? norm(hidden.value) : "",
7211            shown: norm(countryInput.value),
7212            suggestionsSeen: seen.slice(0, 40),
7213            chosenSuggestion: picked ? norm(picked.textContent) : null
7214          };
7215          if (!countryReport.resolved && low(countryReport.shown) !== low(a.country)) notWritten.push("country");
7216        }
7217      }
7218
7219      await sleep(600);
7220
7221      var fields = {};
7222      for (var r = 0; r < FIELDS.length; r++) {
7223        var rs = FIELDS[r];
7224        var rel = located[rs.key] && located[rs.key].isConnected ? located[rs.key] : locate(rs);
7225        located[rs.key] = rel;
7226        if (!rel) continue;
7227        fields[rs.key] = norm(rel.value);
7228      }
7229      if (countryInput) {
7230        var hid = null;
7231        try { hid = document.querySelector(COUNTRY_HIDDEN_SELECTOR); } catch (e) { hid = null; }
7232        fields.country = norm(countryInput.value) || (hid ? norm(hid.value) : "");
7233        fields.countryResolvedValue = hid ? norm(hid.value) : "";
7234      }
7235
7236      for (var c = 0; c < FIELDS.length; c++) {
7237        var cs2 = FIELDS[c];
7238        var want2 = a[cs2.key];
7239        if (want2 === undefined || want2 === null || norm(want2) === "") continue;
7240        var got = fields[cs2.key];
7241        if (got === undefined) continue;
7242        var okWrite = low(got) === low(want2) || (cs2.kind === "select" && low(got).indexOf(low(want2)) === 0) || low(got).indexOf(low(want2)) !== -1;
7243        if (!okWrite && notWritten.indexOf(cs2.key) === -1) notWritten.push(cs2.key);
7244      }
7245
7246      var missing = [];
7247      for (var mq = 0; mq < FIELDS.length; mq++) {
7248        var ms = FIELDS[mq];
7249        if (!ms.required) continue;
7250        if (!located[ms.key]) { missing.push(ms.key); continue; }
7251        if (!norm(located[ms.key].value)) missing.push(ms.key);
7252      }
7253
7254      var readAnything = Object.keys(fields).length > 0;
7255      var ready = missing.length === 0;
7256      var status = (readAnything && ready && notWritten.length === 0 && notFound.length === 0) ? "ok" : (readAnything ? "partial" : "partial");
7257      var payload = {
7258        status: status,
7259        observed: readAnything,
7260        action: ACTION,
7261        fields: fields,
7262        missing: missing,
7263        notWritten: notWritten,
7264        fieldsNotFound: notFound,
7265        skippedPrefilledHiddenFields: skippedHidden,
7266        country: countryReport,
7267        ready: ready && notWritten.length === 0,
7268        submitToolName: SUBMIT_TOOL,
7269        techPartnerApplicationPath: TECH_PATH,
7270        url: location.href,
7271        summary: ready && notWritten.length === 0 && notFound.length === 0
7272          ? "Filled the Become a solution partner form and read every value back; call " + SUBMIT_TOOL + " to send the application."
7273          : "Filled what could be written into the Become a solution partner form; some values are still missing or did not stick."
7274      };
7275      if (payload.status !== "ok") {
7276        var bits = [];
7277        if (missing.length) bits.push("required fields still empty: " + missing.join(", "));
7278        if (notWritten.length) bits.push("values that did not stick in the DOM: " + notWritten.join(", "));
7279        if (notFound.length) bits.push("controls not found on the page: " + notFound.join(", "));
7280        if (!readAnything) bits.push("no field value could be read back out of the form");
7281        payload.reason = bits.join("; ") + ".";
7282        payload.nextStep = "Supply or correct the listed values and call " + TOOL + " again; only call " + SUBMIT_TOOL + " once ready is true.";
7283      }
7284      return out(payload);
7285    } catch (err) {
7286      return out({
7287        status: "error",
7288        action: ACTION,
7289        url: location.href,
7290        reason: "The tool hit an unexpected error: " + (err && err.message ? err.message : 
7290String(err)),
7291        nextStep: "Reload https://www.optimizely.com/partners, scroll to the Become a partner section, then call " + TOOL + " again.",
7292        summary: "Could not fill the solution partner form because of an unexpected error."
7293      });
7294    }
7295  }
7296});
7297
7298mc.registerToolIf("/partners", {
7299  name: "optimizely-apply-become-solution-partner-submit",
7300  description: "Clicks the 'Become a solution partner' button in the Become a partner section of the Optimizely partners page to send the application that optimizely-apply-become-solution-partner filled in, waits for the page to settle, and returns what it observes: the validation messages the form renders, the confirmation text if one appears, any other new message text in the form area, and the URL after the click. If the click triggers a native form navigation instead, it returns that the page navigated and the destination URL.",
7301  inputSchema: { type: "object", properties: {} },
7302  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
7303  execute: async function (args) {
7304    trackWebMCPTool("optimizely-apply-become-solution-partner-submit");
7305    var FILL_TOOL = "optimizely-apply-become-solution-partner";
7306    var ACTION = "submit the Become a solution partner application form";
7307    function out(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
7308    try {
7309      function norm(s) { return (s === null || s === undefined) ? "" : String(s).replace(/\s+/g, " ").trim(); }
7310      function low(s) { return norm(s).toLowerCase(); }
7311      function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
7312      function visible(el) {
7313        if (!el) return false;
7314        if (el.getAttribute && el.getAttribute("aria-hidden") === "true") return false;
7315        var cs = null;
7316        try { cs = window.getComputedStyle(el); } catch (e) { cs = null; }
7317        if (cs && (cs.display === "none" || cs.visibility === "hidden")) return false;
7318        var r = el.getBoundingClientRect();
7319        return !!(r.width || r.height || el.offsetParent);
7320      }
7321
7322      var form = null;
7323      try { form = document.querySelector('main form:has(fieldset)'); } catch (e) { form = null; }
7324      if (!form) {
7325        var allForms = document.querySelectorAll("main form, form");
7326        for (var fi = 0; fi < allForms.length; fi++) {
7327          if (allForms[fi].querySelector("fieldset") && low(allForms[fi].textContent).indexOf("become a solution partner") !== -1) { form = allForms[fi]; break; }
7328        }
7329        if (!form) { for (var fj = 0; fj < allForms.length; fj++) { if (allForms[fj].querySelector("fieldset")) { form = allForms[fj]; break; } } }
7330      }
7331      if (!form) {
7332        return out({
7333          status: "error",
7334          action: ACTION,
7335          url: location.href,
7336          validationErrors: [],
7337          confirmation: null,
7338          reason: "The Become a solution partner form is not present on this page, so there is nothing to submit.",
7339          nextStep: "Open https://www.optimizely.com/partners, call " + FILL_TOOL + " to fill the application, then call this tool again.",
7340          summary: "Could not submit: the solution partner form was not found."
7341        });
7342      }
7343
7344      var btn = null;
7345      try { btn = document.querySelector('main form:has(fieldset) fieldset > div > div:nth-child(10) > div > div:nth-child(1) > button'); } catch (e) { btn = null; }
7346      if (!btn || !form.contains(btn) || low(btn.textContent).indexOf("become a solution partner") === -1) {
7347        var byText = null;
7348        var buttons = form.querySelectorAll("button");
7349        for (var b = 0; b < buttons.length; b++) { if (low(buttons[b].textContent) === "become a solution partner") { byText = buttons[b]; break; } }
7350        if (!byText) { for (var b2 = 0; b2 < buttons.length; b2++) { if (low(buttons[b2].textContent).indexOf("become a solution partner") !== -1) { byText = buttons[b2]; break; } } }
7351        if (byText) btn = byText;
7352      }
7353      if (!btn || !form.contains(btn)) {
7354        return out({
7355          status: "error",
7356          action: ACTION,
7357          url: location.href,
7358          validationErrors: [],
7359          confirmation: null,
7360          reason: "The 'Become a solution partner' submit button could not be located inside the form.",
7361          nextStep: "Scroll to the Become a partner section, confirm the application form is rendered, then call this tool again.",
7362          summary: "Could not submit: the submit button was not found in the form."
7363        });
7364      }
7365
7366      var startUrl = location.href;
7367      var btnType = low(btn.getAttribute("type") || "submit");
7368      var formAction = form.getAttribute("action");
7369      if (btnType === "submit" && formAction) {
7370        var dest = startUrl;
7371        try { dest = new URL(formAction, location.href).toString(); } catch (e) { dest = startUrl; }
7372        var navPayload = {
7373          status: "navigated",
7374          action: ACTION,
7375          url: dest,
7376          nextStep: "The browser is leaving this page for the post-submission destination; read the confirmation on the page that loads.",
7377          summary: "Submitted the solution partner application; the browser is navigating to " + dest + "."
7378        };
7379        btn.click();
7380        return out(navPayload);
7381      }
7382
7383      var region = form.closest("section") || form.parentElement || form;
7384      function leafTexts(root) {
7385        var set = {};
7386        if (!root) return set;
7387        var all = root.querySelectorAll("*");
7388        for (var i = 0; i < all.length; i++) {
7389          var el = all[i];
7390          if (el.children && el.children.length) continue;
7391          var tn = el.tagName;
7392          if (tn === "SCRIPT" || tn === "STYLE" || tn === "OPTION" || tn === "svg" || tn === "PATH") continue;
7393          if (el.getAttribute && el.getAttribute("aria-hidden") === "true") continue;
7394          var t = norm(el.textContent);
7395          if (!t || t.length > 240) continue;
7396          if (!visible(el)) continue;
7397          set[t] = true;
7398        }
7399        return set;
7400      }
7401      var before = leafTexts(region);
7402
7403      var unloading = false;
7404      var onHide = function () { unloading = true; };
7405      try {
7406        window.addEventListener("pagehide", onHide);
7407        window.addEventListener("beforeunload", onHide);
7408      } catch (e) { }
7409
7410      try { btn.click(); } catch (clickErr) {
7411        try { window.removeEventListener("pagehide", onHide); window.removeEventListener("beforeunload", onHide); } catch (e) { }
7412        return out({
7413          status: "error",
7414          action: ACTION,
7415          url: location.href,
7416          validationErrors: [],
7417          confirmation: null,
7418          reason: "Clicking the submit button threw: " + (clickErr && clickErr.message ? clickErr.message : String(clickErr)),
7419          nextStep: "Reload the partners page, refill the form with " + FILL_TOOL + ", then call this tool again.",
7420          summary: "The submit button could not be clicked."
7421        });
7422      }
7423
7424      var ERROR_RE = /(required|invalid|please\s|must\s|enter a|valid email|cannot be|not a valid|is missing|fill (in|out))/i;
7425      var CONFIRM_RE = /(thank you|thanks|we have received|received your|success|submitted|be in touch|get back to you|shortly|soon as possible|our team will)/i;
7426      var validationErrors = [];
7427      var otherMessages = [];
7428      var confirmation = null;
7429      var navigated = false;
7430      var deadline = Date.now() + 12000;
7431
7432      while (Date.now() < deadline) {
7433        await sleep(400);
7434        if (unloading) { navigated = true; break; }
7435        if (location.href !== startUrl) { navigated = true; break; }
7436        var scope = (region && region.isConnected) ? region : (document.querySelector("main") || document.body);
7437        var now = leafTexts(scope);
7438        validationErrors = [];
7439        otherMessages = [];
7440        confirmation = null;
7441        for (var key in now) {
7442          if (!Object.prototype.hasOwnProperty.call(now, key)) continue;
7443          if (before[key]) continue;
7444          if (ERROR_RE.test(key) && key.length <= 200) {
7445            if (validationErrors.indexOf(key) === -1) validationErrors.push(key);
7446          } else if (CONFIRM_RE.test(key)) {
7447            if (!confirmation || key.length > confirmation.length) confirmation = key;
7448          } else if (otherMessages.indexOf(key) === -1) {
7449            otherMessages.push(key);
7450          }
7451        }
7452        if (!confirmation && !form.isConnected && otherMessages.length) {
7453          var longest = "";
7454          for (var om = 0; om < otherMessages.length; om++) { if (otherMessages[om].length > longest.length) longest = otherMessages[om]; }
7455          if (longest) confirmation = longest;
7456        }
7457        if (validationErrors.length || confirmation) break;
7458      }
7459
7460      try { window.removeEventListener("pagehide", onHide); window.removeEventListener("beforeunload", onHide); } catch (e) { }
7461
7462      if (navigated) {
7463        return out({
7464          status: "navigated",
7465          action: ACTION,
7466          url: location.href,
7467          nextStep: "The page moved to the post-submission destination; read the confirmation text there rather than on the form page.",
7468          summary: "Clicked the submit button and the page navigated away from the application form."
7469        });
7470      }
7471
7472      var gate = null;
7473      try { gate = document.querySelector('iframe[src*="recaptcha"], iframe[src*="hcaptcha"], iframe[title*="captcha" i], #onetrust-banner-sdk'); } catch (e) { gate = null; }
7474      var gateNote = (gate && visible(gate)) ? " A consent or captcha layer is visible and may be blocking the submission." : "";
7475
7476      if (validationErrors.length) {
7477        return out({
7478          status: "error",
7479          action: ACTION,
7480          url: location.href,
7481          validationErrors: validationErrors.slice(0, 20),
7482          confirmation: null,
7483          otherMessages: otherMessages.slice(0, 10),
7484          reason: "The form rendered validation messages instead of a confirmation." + gateNote,
7485          nextStep: "Correct the listed fields with " + FILL_TOOL + ", then call this tool again.",
7486          summary: "The application was not accepted: " + validationErrors.length + " validation message(s) appeared on the form."
7487        });
7488      }
7489      if (confirmation) {
7490        return out({
7491          status: "ok",
7492          observed: true,
7493          action: ACTION,
7494          validationErrors: [],
7495          confirmation: confirmation,
7496          otherMessages: otherMessages.slice(0, 10),
7497          url: location.href,
7498          summary: "Submitted the solution partner application; the page now shows: " + confirmation
7499        });
7500      }
7501      return out({
7502        status: "partial",
7503        observed: false,
7504        action: ACTION,
7505        validationErrors: [],
7506        confirmation: null,
7507        otherMessages: otherMessages.slice(0, 10),
7508        url: location.href,
7509        reason: "The submit button was clicked but neither a confirmation nor a validation message appeared in the form area within 12 seconds, and the URL did not change." + gateNote,
7510        nextStep: "Look at the Become a partner section in the browser to see whether the application was accepted before submitting again; re-running this tool may send a duplicate application.",
7511        summary: "Clicked submit but could not read any outcome text from the page."
7512      });
7513    } catch (err) {
7514      return out({
7515        status: "error",
7516        action: ACTION,
7517        url: location.href,
7518        validationErrors: [],
7519        confirmation: null,
7520        reason: "The tool hit an unexpected error: " + (err && err.message ? err.message : 
7520String(err)),
7521        nextStep: "Check the form in the browser before retrying, so the application is not sent twice.",
7522        summary: "Could not report the submission outcome because of an unexpected error."
7523      });
7524    }
7525  }
7526});
7527
7528  /* ---- optimizely-open-sales-chat ---- */
7529mc.registerToolIf("/products/**", {
7530  name: "optimizely-open-sales-chat",
7531  description: "Opens the Optimizely live sales chat (the Qualified messenger mounted on this page as an app.qualified.com iframe) so the visitor can talk to a sales representative. Optionally attaches known visitor details (email, name, company) to the chat session first, and can push an opening message onto the chat command queue on the visitor's behalf; the conversation itself is rendered inside the app.qualified.com frame, so the tool reports whether that command was accepted rather than confirming delivery. Returns the messenger state read back after the call: whether the messenger widget is present, whether its panel opened, its measured size before and after, which visitor details were attached, whether the opening message command was accepted, the active cookie consent groups, and the page URL.",
7532  inputSchema: {
7533    type: "object",
7534    properties: {
7535      email: {
7536        type: "string",
7537        description: "Work email of the visitor, attached to the chat session before the messenger opens. Optional."
7538      },
7539      name: {
7540        type: "string",
7541        description: "Full name of the visitor, attached to the chat session before the messenger opens. Optional."
7542      },
7543      company: {
7544        type: "string",
7545        description: "Company name of the visitor, attached to the chat session before the messenger opens. Optional."
7546      },
7547      message: {
7548        type: "string",
7549        description: "Opening message to send to sales on the visitor's behalf. Optional; when omitted the messenger is only opened."
7550      }
7551    },
7552    required: []
7553  },
7554  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
7555  execute: async function (args) {
7556    trackWebMCPTool("optimizely-open-sales-chat");
7557    var action = "open the Qualified sales chat messenger";
7558    var urlBefore = location.href;
7559    var reply = function (payload) {
7560      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
7561    };
7562    try {
7563      var a = args || {};
7564      var sleep = function (ms) { return new Promise(function (r) { setTimeout(r, ms); }); };
7565      var str = function (v) { return typeof v === "string" && v.trim() ? v.trim() : null; };
7566
7567      var findFrame = function () {
7568        return document.querySelector('iframe[src*="app.qualified.com"]');
7569      };
7570      var frameState = function (el) {
7571        if (!el) { return { present: false, width: 0, height: 0, visible: false }; }
7572        var r = el.getBoundingClientRect();
7573        var cs = window.getComputedStyle(el);
7574        return {
7575          present: true,
7576          width: Math.round(r.width),
7577          height: Math.round(r.height),
7578          visible: r.width > 1 && r.height > 1 && cs.display !== "none" && cs.visibility !== "hidden" && parseFloat(cs.opacity || "1") > 0.05
7579        };
7580      };
7581
7582      var consentGroups = typeof window.OnetrustActiveGroups === "string" ? window.OnetrustActiveGroups : null;
7583      var before = frameState(findFrame());
7584
7585      // The Qualified snippet may initialise late; poll briefly for the command queue.
7586      var api = null;
7587      for (var i = 0; i < 25; i++) {
7588        if (typeof window.qualified === "function") { api = window.qualified; break; }
7589        await sleep(200);
7590      }
7591
7592      var details = {};
7593      if (str(a.email)) { details.email = str(a.email); }
7594      if (str(a.name)) { details.name = str(a.name); }
7595      if (str(a.company)) { details.company = str(a.company); }
7596      var wantedMessage = str(a.message);
7597
7598      var apiUsed = null;
7599      var identify = { requested: Object.keys(details).length > 0, sent: false, fields: details, error: null };
7600      var openingMessage = wantedMessage
7601        ? { text: wantedMessage, commandAttempted: false, commandAccepted: false, deliveryConfirmed: false, error: null }
7602        : null;
7603      var openError = null;
7604
7605      if (api) {
7606        apiUsed = "qualified-command-queue";
7607        if (identify.requested) {
7608          try { api("identify", details); identify.sent = true; }
7609          catch (e1) { identify.error = String((e1 && e1.message) || e1); }
7610        }
7611        try { api("showChat"); }
7612        catch (e2) { openError = String((e2 && e2.message) || e2); }
7613        if (openingMessage) {
7614          openingMessage.commandAttempted = true;
7615          try { api("sendMessage", wantedMessage); openingMessage.commandAccepted = true; }
7616          catch (e3) { openingMessage.error = String((e3 && e3.message) || e3); }
7617        }
7618      } else {
7619        // No global: look for a host-side launcher by its accessible name before giving up.
7620        var launcher = document.querySelector(
7621          'button[aria-label*="chat" i], button[title*="chat" i], a[aria-label*="chat" i], [role="button"][aria-label*="chat" i], button[aria-label*="messenger" i], [role="button"][aria-label*="messenger" i], button[aria-label*="qualified" i]'
7622        );
7623        if (launcher) {
7624          apiUsed = "launcher-click-fallback";
7625          try { launcher.click(); }
7626          catch (e4) { openError = String((e4 && e4.message) || e4); }
7627        } else {
7628          var noApiState = frameState(findFrame());
7629          return reply({
7630            status: "error",
7631            action: action,
7632            url: location.href,
7633            observed: noApiState.present,
7634            messengerPresent: noApiState.present,
7635            messengerOpen: null,
7636            qualifiedApi: "absent",
7637            consentGroups: consentGroups,
7638            reason: "window.qualified was not defined after waiting about 5 seconds, and no chat launcher control with a chat or messenger accessible name was found in the document.",
7639            nextStep: "Cookie consent may be blocking the chat widget: read window.OnetrustActiveGroups, allow the relevant group (OneTrust.ToggleInfoDisplay() opens the preference centre, OneTrust.AllowAll() accepts all), then call this tool again. Otherwise use the 'Book a demo' link at /get-started/ or the contact form on this page.",
7640            summary: "The live sales chat could not be opened because the Qualified messenger API and launcher were both unavailable on this page."
7641          });
7642        }
7643      }
7644
7645      if (location.href !== urlBefore) {
7646        return reply({
7647          status: "navigated",
7648          action: action,
7649          url: location.href,
7650          nextStep: "The page changed while the chat was being opened; wait for the new page to load and call optimizely-open-sales-chat again there.",
7651          summary: "The page navigated while opening the sales chat, so the messenger state was not read."
7652        });
7653      }
7654
7655      // Let the messenger settle, watching for the panel to appear or expand.
7656      var after = frameState(findFrame());
7657      for (var j = 0; j < 25; j++) {
7658        await sleep(200);
7659        after = frameState(findFrame());
7660        var grew = after.visible && (after.width * after.height) > (before.width * before.height) + 1000;
7661        if (grew || (after.visible && after.width * after.height > 90000)) { break; }
7662      }
7663
7664      var frameEl = findFrame();
7665      if (frameEl) {
7666        try {
7667          var prevOutline = frameEl.style.outline;
7668          frameEl.style.outline = "3px solid #0037ff";
7669          setTimeout(function () { frameEl.style.outline = prevOutline; }, 5000);
7670        } catch (e5) { /* highlight is cosmetic only */ }
7671      }
7672
7673      var beforeArea = before.width * before.height;
7674      var afterArea = after.width * after.height;
7675      var expanded = afterArea > beforeArea + 1000;
7676      var messengerOpen = after.present && after.visible && (expanded || afterArea > 90000);
7677
7678      var payload = {
7679        status: "ok",
7680        action: action,
7681        url: location.href,
7682        observed: after.present,
7683        messengerPresent: after.present,
7684        messengerOpen: after.present ? messengerOpen : null,
7685        messengerSize: {
7686          before: { width: before.width, height: before.height, visible: before.visible },
7687          after: { width: after.width, height: after.height, visible: after.visible }
7688        },
7689        qualifiedApi: apiUsed,
7690        identify: identify,
7691        openingMessage: openingMessage,
7692        consentGroups: consentGroups,
7693        summary: ""
7694      };
7695
7696      if (!after.present) {
7697        payload.status = "partial";
7698        payload.observed = false;
7699        payload.reason = "The Qualified messenger element (iframe[src*=app.qualified.com]) was not in the document after the open command, so no messenger state could be read.";
7700        payload.nextStep = "Wait a few seconds for the chat widget to load, check window.OnetrustActiveGroups for c
7700onsent, then call this tool again; or use the 'Book a demo' link at /get-started/.";
7701        payload.summary = "The open command was issued but no messenger widget could be found on the page afterwards.";
7702        return reply(payload);
7703      }
7704
7705      if (openError) {
7706        payload.status = "partial";
7707        payload.reason = "The open command reported an error: " + openError;
7708        payload.nextStep = "Ask the visitor to click the chat launcher on the page, or use the 'Book a demo' link at /get-started/.";
7709        payload.summary = "The messenger widget is on the page but the open command reported an error.";
7710        return reply(payload);
7711      }
7712
7713      if (!messengerOpen) {
7714        payload.status = "partial";
7715        payload.reason = "The messenger widget is present but its measured size after the open command (" + after.width + "x" + after.height + ") did not grow, so an open panel could not be confirmed.";
7716        payload.nextStep = "Ask the visitor to click the chat launcher on the page and retry, or use the 'Book a demo' link at /get-started/.";
7717        payload.summary = "The sales chat widget is present but the panel was not observed to open.";
7718        return reply(payload);
7719      }
7720
7721      if (openingMessage) {
7722        var messageState = openingMessage.commandAccepted
7723          ? "accepted"
7724          : (openingMessage.commandAttempted ? "rejected" : "not attempted because the chat command queue was unavailable");
7725        payload.status = "partial";
7726        payload.reason = "The conversation itself is rendered inside the app.qualified.com frame, so delivery of the opening message could not be read back from this page.";
7727        payload.nextStep = "Ask the visitor to check the open messenger panel and re-type the opening message if it is not shown there.";
7728        payload.summary = "The sales chat messenger opened (" + after.width + "x" + after.height + "); the opening message command was " + messageState + " and its delivery was not confirmed.";
7729        return reply(payload);
7730      }
7731
7732      payload.summary = "The Optimizely sales chat messenger is open (" + after.width + "x" + after.height + ")" + (identify.sent ? " with the visitor details attached." : ".");
7733      return reply(payload);
7734    } catch (err) {
7735      return reply({
7736        status: "error",
7737        action: action,
7738        url: location.href,
7739        observed: false,
7740        reason: "Unexpected error while opening the sales chat: " + String((err && err.message) || err),
7741        nextStep: "Call this tool again, or use the 'Book a demo' link at /get-started/.",
7742        summary: "The sales chat could not be opened because of an unexpected error."
7743      });
7744    }
7745  }
7746});
7747
7748  /* ---- optimizely-read-product-page-details ---- */
7749mc.registerToolIf("/products/*", {
7750  name: "optimizely-read-product-page-details",
7751  description: "Reads the product page the browser is on and returns its content as structured JSON for side-by-side vendor comparison. It expands each 'Questions you might have' accordion item and returns question and answer pairs, steps the customer-proof carousel and the 'Meet the agents' slider through their slides to collect the quotes (audience, quote, name, role, company) or linked customer stories (headline, attribution line, company, link) and every agent slide (name, link, image) together with the slider's own stated total, and returns the feature blocks (eyebrow, heading, description, image, CTA target), the capability columns with their bullet lists, the highlighted metrics, the in-page link targets, the section headings, and whether the inline demo form is present with its field labels. It restores the accordion and the sliders to their starting state, highlights the regions it read, and only reads: it never fills or submits the form. Returns url, title, faqs, quotes, stories, agents, agentTotal, featureBlocks, capabilityColumns, stats, ctas, pageSections, demoFormPresent, demoFormSelector, counts and a per-section note of what was actually observed.",
7752  inputSchema: {
7753    type: "object",
7754    properties: {
7755      sections: {
7756        type: "array",
7757        items: { type: "string" },
7758        description: "Optional list of sections to read. Known values: faqs, quotes, stories, agents, features, stats, capabilities, ctas, form. Omit to read every section; an unknown value is reported back together with the list that is available."
7759      },
7760      maxItemsPerSection: {
7761        type: "number",
7762        description: "Maximum number of items returned per section. Defaults to 40."
7763      },
7764      restoreState: {
7765        type: "boolean",
7766        description: "Collapse the accordion items and return the sliders to their first slide after reading. Defaults to true."
7767      }
7768    }
7769  },
7770  annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
7771  execute: async function (args) {
7772    trackWebMCPTool("optimizely-read-product-page-details");
7773
7774    var out = function (payload) {
7775      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
7776    };
7777    var ACTION = "expand and read product page content";
7778    var KNOWN = ["faqs", "quotes", "stories", "agents", "features", "stats", "capabilities", "ctas", "form"];
7779    var startUrl = location.href;
7780    var base = startUrl.split("#")[0];
7781
7782    try {
7783      var a = args || {};
7784      var wanted = KNOWN.slice();
7785
7786      if (a.sections !== undefined && a.sections !== null && a.sections !== "") {
7787        var requested = Array.isArray(a.sections) ? a.sections : [a.sections];
7788        var bad = [];
7789        var good = [];
7790        for (var si = 0; si < requested.length; si++) {
7791          var val = String(requested[si] === null || requested[si] === undefined ? "" : requested[si]).trim().toLowerCase();
7792          if (!val) continue;
7793          if (KNOWN.indexOf(val) === -1) bad.push(String(requested[si]));
7794          else if (good.indexOf(val) === -1) good.push(val);
7795        }
7796        if (bad.length) {
7797          return out({
7798            status: "error",
7799            action: ACTION,
7800            url: location.href,
7801            reason: "Unknown section name(s) requested: " + bad.join(", "),
7802            availableOptions: KNOWN,
7803            nextStep: "Call again with sections drawn from availableOptions, or omit the sections argument to read every section.",
7804            summary: "Did not read the page because the requested section name(s) are not ones this tool knows."
7805          });
7806        }
7807        if (good.length) wanted = good;
7808      }
7809
7810      var want = function (name) { return wanted.indexOf(name) !== -1; };
7811      var limit = (typeof a.maxItemsPerSection === "number" && a.maxItemsPerSection > 0) ? Math.floor(a.maxItemsPerSection) : 40;
7812      var restore = a.restoreState === false ? false : true;
7813
7814      var sleep = function (ms) { return new Promise(function (r) { setTimeout(r, ms); }); };
7815
7816      var clean = function (raw) {
7817        var t = (raw === null || raw === undefined) ? "" : String(raw);
7818        t = t.replace(/\s+/g, " ").trim();
7819        if (!t) return "";
7820        for (var n = 6; n >= 2; n--) {
7821          if (t.length % n !== 0) continue;
7822          var unit = t.slice(0, t.length / n);
7823          if (unit.length < 2) continue;
7824          var rebuilt = "";
7825          for (var k = 0; k < n; k++) rebuilt += unit;
7826          if (rebuilt === t) return unit.trim();
7827        }
7828        return t;
7829      };
7830
7831      var visText = function (el) {
7832        if (!el) return "";
7833        if (el.getAttribute && el.getAttribute("aria-hidden") === "true") return "";
7834        var parts = [];
7835        var kids = el.childNodes || [];
7836        for (var i = 0; i < kids.length; i++) {
7837          var node = kids[i];
7838          if (node.nodeType === 3) parts.push(node.nodeValue || "");
7839          else if (node.nodeType === 1) parts.push(visText(node));
7840        }
7841        return clean(parts.join(" "));
7842      };
7843
7844      var highlighted = [];
7845      var scrolledOnce = false;
7846      var mark = function (el) {
7847        if (!el || highlighted.indexOf(el) !== -1) return;
7848        try {
7849          el.style.outline = "3px solid #0037ff";
7850          el.style.outlineOffset = "2px";
7851          highlighted.push(el);
7852        } catch (e) {}
7853        if (!scrolledOnce) {
7854          scrolledOnce = true;
7855          try { el.scrollIntoView({ behavior: "smooth", block: "center" }); }
7856          catch (e2) { try { el.scrollIntoView(); } catch (e3) {} }
7857        }
7858      };
7859
7860      var navigatedAway = function () { return location.href.split("#")[0] !== base; };
7861      var navPayload = function (phase) {
7862        return {
7863          status: "navigated",
7864          action: ACTION,
7865          url: location.href,
7866          nextStep: "The browser left the product page while reading the " + phase + " section. Wait for the new page to finish loading, then call optimizely-read-product-page-details again if it is another product page.",
7867          summary: "The page navigated away while reading the " + phase + " section, so no content was returned."
7868        };
7869      };
7870
7871      var safeExtract = async function (itemSelector, fieldMap, cap) {
7872        try {
7873          var r = await extractItems(itemSelector, fieldMap, cap);
7874          if (!r || !Array.isArray(r.results)) return { resultCount: 0, truncated: false, results: [] };
7875          var cleaned = [];
7876          for (var i = 0; i < r.results.length; i++) {
7877            var item = r.results[i];
7878            var obj = {};
7879            for (var key in item) {
7880              if (!Object.prototype.hasOwnProperty.call(item, key)) continue;
7881              var v = item[key];
7882              obj[key] = (typeof v === "string") ? clean(v) : v;
7883            }
7884            cleaned.push(obj);
7885          }
7886          return { resultCount: r.resultCount, truncated: !!r.truncated, results: cleaned };
7887        } catch (e) {
7888          return { resultCount: 0, truncated: false, results: [] };
7889        }
7890      };
7891
7892      var findControl = function (root, names) {
7893        if (!root) return null;
7894        var nodes = root.querySelectorAll('button, div[role="button"], [role="button"]');
7895        for (var i = 0; i < nodes.length; i++) {
7896          var el = nodes[i];
7897          var label = (el.getAttribute("aria-label") || el.getAttribute("title") || visText(el) || "").toLowerCase().trim();
7898          if (!label) continue;
7899          for (var j = 0; j < names.length; j++) {
7900            if (label === names[j] || label.indexOf(names[j]) === 0) return el;
7901          }
7902        }
7903        return null;
7904      };
7905
7906      var notes = {};
7907      var truncatedAny = false;
7908
7909      /* ---------------- FAQ accordion ---------------- */
7910      var faqs = [];
7911      notes.faqs = "notFoundOnThisPage";
7912      if (want("faqs")) {
7913        var accRoot = document.querySelector(".accordionblock");
7914        var faqItems = document.querySelectorAll('.accordionblock div.overflow-clip');
7915        if (accRoot && faqItems.length) {
7916          mark(accRoot);
7917          var faqMax = Math.min(faqItems.length, limit);
7918          if (faqItems.length > faqMax) truncatedAny = true;
7919          for (var fi = 0; fi < faqMax; fi++) {
7920            var item = faqItems[fi];
7921            var trigger = item.querySelector('h3 > button');
7922            if (!trigger) {
7923              faqs.push({ index: fi, question: "", answer: "", status: "trigger-not-found" });
7924              continue;
7925            }
7926            var question = visText(trigger);
7927            var wasOpen = trigger.getAttribute("aria-expanded") === "true";
7928            var panelId = trigger.getAttribute("aria-controls") || "";
7929            if (!wasOpen) { try { trigger.click(); } catch (eClick) {} }
7930
7931            var answer = "";
7932            var deadline = Date.now() + 3000;
7933            while (Date.now() < deadline) {
7934              await sleep(120);
7935              var panel = item.querySelector('div[role="region"]');
7936              if (!panel && panelId) panel = document.getElementById(panelId);
7937              var isOpen = trigger.getAttribute("aria-expanded") === "true";
7938              var panelText = panel ? visText(panel) : "";
7939              if (isOpen && panelText) { answer = panelText; break; }
7940            }
7941
7942            var state = answer ? "expanded" : (trigger.getAttribute("aria-expanded") === "true" ? "expanded-but-no-text-within-3s" : "did-not-expand");
7943            faqs.push({
7944              index: fi,
7945              question: question,
7946              answer: answer,
7947              status: state,
7948              triggerId: trigger.getAttribute("id") || "",
7949              panelId: panelId
7950            });
7951
7952            if (restore && !wasOpen && trigger.getAttribute("aria-expanded") === "true") {
7953              try { trigger.click(); } catch (eRestore) {}
7954              await sleep(90);
7955            }
7956            if (navigatedAway()) return out(navPayload("FAQ accordion"));
7957          }
7958          var answered = 0;
7959          for (var fa = 0; fa < faqs.length; fa++) if (faqs[fa].answer) answered++;
7960          notes.faqs = "expanded " + faqs.length + " of " + faqItems.length + " accordion items; " + answered + " returned answer text";
7961        }
7962      }
7963
7964      /* ---------------- Portrait quote carousel ---------------- */
7965      var quotes = [];
7966      notes.quotes = "notFoundOnThisPage";
7967      if (want("quotes")) {
7968        var cards = document.querySelectorAll('div[role="button"][aria-label="Next card"]');
7969        if (cards.length) {
7970          var quoteRegion = document.querySelector('div[aria-roledescription="carousel"][aria-label="Portrait card carousel"]') || cards[0].parentElement;
7971          mark(quoteRegion);
7972          var seenQuote = {};
7973          var collectQuotes = async function () {
7974            var r = await safeExtract('div[role="button"][aria-label="Next card"]', {
7975              audience: 'span.font-mono',
7976              quote: 'blockquote',
7977              name: 'div.font-body > span:nth-child(1)',
7978              role: 'div.font-body > span:nth-child(2)',
7979              company: 'img @alt',
7980              image: 'img @src'
7981            }, limit);
7982            if (r.truncated) truncatedAny = true;
7983            for (var qi = 0; qi < r.results.length; qi++) {
7984              var q = r.results[qi];
7985              if (!q.quote && !q.name) continue;
7986              /* some cards print the company on the first attribution line and the person on the second */
7987              if (q.name && q.company && q.name === q.company && q.role) {
7988                q.name = q.role;
7989                q.role = "";
7990              }
7991              var key = (q.quote || "") + "|" + (q.company || "") + "|" + (q.name || "");
7992              if (seenQuote[key]) continue;
7993              seenQuote[key] = true;
7994              quotes.push(q);
7995            }
7996          };
7997          await collectQuotes();
7998          var quoteRounds = Math.min(cards.length, 6);
7999          var advanced = 0;
8000          for (var qr = 0; qr < quoteRounds; qr++) {
8001            var activeCard = document.querySelector('div[role="button"][aria-label="Next card"][tabindex="0"]') ||
8002                             document.querySelector('div[role="button"][aria-label="Next card"]');
8003            if (!activeCard) break;
8004            try { activeCard.click(); } catch (eQ) {}
8005            advanced++;
8006            await sleep(420);
8007            if (navigatedAway()) return out(navPayload("customer quote carousel"));
8008            await collectQuotes();
8009          }
8010          notes.quotes = "read the portrait quote carousel; advanced it " + advanced + " time(s) and collected " + quotes.length + " unique card(s) from " + cards.length + " card node(s)";
8011        }
8012      }
8013
8014      /* ---------------- Stories of success carousel ---------------- */
8015      var stories = [];
8016      notes.stories = "notFoundOnThisPage";
8017      if (want("stories")) {
8018        /* the same data-carousel-item attribute is also used by the brand logo marquee;
8019           the story cards are the ones that also carry data-carousel-set */
8020        var storySelector = 'div[data-carousel-item][data-carousel-set]';
8021        var storyNodes = document.querySelectorAll(storySelector);
8022        if (!storyNodes.length) {
8023          storySelector = 'div[data-carousel-item]';
8024          storyNodes = document.querySelectorAll(storySelector);
8025        }
8026        if (storyNodes.length) {
8027          var storyRegion = document.querySelector('div[role="region"][aria-roledescription="carousel"]');
8028          if (!storyRegion) {
8029            storyRegion = storyNodes[0].closest ? (storyNodes[0].closest('section') || storyNodes[0].parentElement) : storyNodes[0].parentElement;
8030          }
8031          mark(storyRegion);
8032          var seenStory = {};
8033          var collectStories = async function () {
8034            var r = await safeExtract(storySelector, {
8035              storyUrl: 'a @href',
8036              headline: 'h3',
8037              attribution: 'div.font-body p',
8038              company: 'span[role="img"] @aria-label',
8039              image: 'img @src',
8040              imageAlt: 'img @alt',
8041              hidden: '@aria-hidden',
8042              carouselSet: '@data-carousel-set'
8043            }, 200);
8044            if (r.truncated) truncatedAny = true;
8045            var visible = [];
8046            for (var vi = 0; vi < r.results.length; vi++) {
8047              if (r.results[vi].hidden !== "true") visible.push(r.results[vi]);
8048            }
8049            var pool = visible.length ? visible : r.results;
8050            var added = 0;
8051            for (var pi = 0; pi < pool.length; pi++) {
8052              var s = pool[pi];
8053              var skey = s.storyUrl || s.headline || "";
8054              if (!skey) continue;
8055              if (seenStory[skey]) continue;
8056              seenStory[skey] = true;
8057              stories.push({
8058                storyUrl: s.storyUrl || "",
8059                headline: s.headline || "",
8060                attribution: s.attribution || "",
8061                company: s.company || "",
8062                image: s.image || "",
8063                imageAlt: s.imageAlt || ""
8064              });
8065              added++;
8066              if (stories.length >= limit) { truncatedAny = true; break; }
8067            }
8068            return added;
8069          };
8070          await collectStories();
8071          var storyNext = findControl(storyRegion, ["next"]);
8072          var storySteps = 0;
8073          var quiet = 0;
8074          while (storyNext && storySteps < 6 && quiet < 2 && stories.length < limit) {
8075            try { storyNext.click(); } catch (eS) {}
8076            storySteps++;
8077            await sleep(450);
8078            if (navigatedAway()) return out(navPayload("stories of success carousel"));
8079            var gained = await collectStories();
8080            quiet = gained > 0 ? 0 : quiet + 1;
8081          }
8082          notes.stories = "read the stories carousel; " + storyNodes.length + " carousel node(s) matching '" + storySelector + "' collapsed to " + stories.length + " unique story/stories after " + storySteps + " advance click(s)" + (storyNext ? "" : " (no advance control found by accessible name, read the nodes directly)");
8083        }
8084      }
8085
8086      /* ---------------- Meet the agents slider ---------------- */
8087      var agents = [];
8088      var agentTotal = null;
8089      notes.agents = "notFoundOnThisPage";
8090      if (want("agents")) {
8091        var showcase = document.querySelector('section[aria-label="Agent showcase"]');
8092        if (showcase) {
8093          mark(showcase);
8094          var liveSelector = 'section[aria-label="Agent showcase"] div[aria-live="polite"][aria-atomic="true"]';
8095          var liveText = function () {
8096            var el = document.querySelector(liveSelector);
8097            return el ? visText(el) : "";
8098          };
8099          var parseTotal = function () {
8100            var m = liveText().match(/of\s+(\d+)/i);
8101            return m ? parseInt(m[1], 10) : null;
8102          };
8103          agentTotal = parseTotal();
8104          var dots = showcase.querySelectorAll('div[role="tablist"][aria-label="Select agent"] button[role="tab"]');
8105          if (agentTotal === null && dots.length) agentTotal = dots.length;
8106          if (agentTotal === null) {
8107            try {
8108              var stated = await readResultCount(liveSelector, null);
8109              if (typeof stated === "number" && stated > 0) agentTotal = stated;
8110            } catch (eCount) {}
8111          }
8112
8113          var seenAgent = {};
8114          var collectAgents = async function () {
8115            var r = await safeExtract('section[aria-label="Agent showcase"] div[aria-roledescription="slide"]', {
8116              slideLabel: '@aria-label',
8117              agentName: 'h3',
8118              agentUrl: 'a @href',
8119              agentTitle: 'a @title',
8120              image: 'img @src',
8121              imageAlt: 'img @alt'
8122            }, 40);
8123            if (r.truncated) truncatedAny = true;
8124            for (var ai = 0; ai < r.results.length; ai++) {
8125              var ag = r.results[ai];
8126              var akey = ag.agentUrl || ag.agentName || ag.slideLabel || "";
8127              if (!akey) continue;
8128              if (seenAgent[akey]) continue;
8129              if (!ag.agentName && !ag.agentUrl) continue;
8130              seenAgent[akey] = true;
8131              agents.push(ag);
8132            }
8133          };
8134
8135          await collectAgents();
8136          var agentSteps = 0;
8137          if (dots.length) {
8138            var dotMax = Math.min(dots.length, 12);
8139            for (var di = 0; di < dotMax; di++) {
8140              try { dots[di].click(); } catch (eD) {}
8141              agentSteps++;
8142              await sleep(520);
8143              if (navigatedAway()) return out(navPayload("agent slider"));
8144              await collectAgents();
8145            }
8146            if (restore && dots[0]) {
8147              try { dots[0].click(); } catch (eD0) {}
8148              await sleep(200);
8149            }
8150          } else {
8151            var nextAgent = showcase.querySelector('button[aria-label="Next agent"]') || document.querySelector('button[aria-label="Next agent"]');
8152            if (nextAgent) {
8153              var stepMax = Math.min(agentTotal || 6, 12);
8154              for (var ns = 0; ns < stepMax; ns++) {
8155                try { nextAgent.click(); } catch (eN) {}
8156                agentSteps++;
8157                await sleep(520);
8158                if (navigatedAway()) return out(navPayload("agent slider"));
8159                await collectAgents();
8160              }
8161              if (restore) {
8162                var prevAgent = document.querySelector('button[aria-label="Previous agent"]');
8163                var guard = 0;
8164                while (prevAgent && guard < 12 && !/agent\s+1\s+of/i.test(liveText())) {
8165                  try { prevAgent.click(); } catch (eP) {}
8166                  guard++;
8167                  await sleep(260);
8168                }
8169              }
8170            }
8171          }
8172          if (agentTotal === null) agentTotal = parseTotal();
8173          notes.agents = "stepped the agent slider " + agentSteps + " time(s) and collected " + agents.length + " unique slide(s)" +
8174            (agentTotal ? "; the slider states a total of " + agentTotal : "; the slider stated no total, so the total is the number collected");
8175        }
8176      }
8177
8178      /* ---------------- Feature blocks ---------------- */
8179      var featureBlocks = [];
8180      notes.features = "notFoundOnThisPage";
8181      if (want("features")) {
8182        var featureNotes = [];
8183        var itcNode = document.querySelector('div.image-text-container-block');
8184        if (itcNode) {
8185          mark(itcNode);
8186          var itc = await safeExtract('div.image-text-container-block', {
8187            eyebrow: 'p.font-body',
8188            heading: 'h3',
8189            description: 'div[data-epi-edit="Description"] p',
8190            ctaLabel: 'a > div:nth-child(2) > span > span:first-child',
8191            ctaTitle: 'a @title',
8192            ctaUrl: 'a @href',
8193            image: 'img @src',
8194            imageAlt: 'img @alt'
8195          }, limit);
8196          if (itc.truncated) truncatedAny = true;
8197          for (var ii = 0; ii < itc.results.length; ii++) {
8198            var b = itc.results[ii];
8199            if (!b.heading && !b.description) continue;
8200            b.blockType = "image-text-container-block";
8201            featureBlocks.push(b);
8202          }
8203          featureNotes.push(itc.results.length + " image-text block(s)");
8204        }
8205        var zzNode = document.querySelector('div.zigzagblock article');
8206        if (zzNode) {
8207          mark(zzNode.closest ? (zzNode.closest('div.zigzagblock') || zzNode) : zzNode);
8208          var zz = await safeExtract('div.zigzagblock article', {
8209            heading: 'h3',
8210            description: 'p',
8211            image: 'img @src',
8212            imageAlt: 'img @alt'
8213          }, limit);
8214          if (zz.truncated) truncatedAny = true;
8215          for (var zi = 0; zi < zz.results.length; zi++) {
8216            var z = zz.results[zi];
8217            if (!z.heading && !z.description) continue;
8218            z.blockType = "zigzag";
8219            featureBlocks.push(z);
8220          }
8221          featureNotes.push(zz.results.length + " zigzag block(s)");
8222        }
8223        if (featureNotes.length) notes.features = "read " + featureNotes.join(" and ");
8224      }
8225
8226      /* ---------------- Stats ---------------- */
8227      var stats = [];
8228      notes.stats = "notFoundOnThisPage";
8229      if (want("stats")) {
8230        var statProbe = document.querySelector('div.rounded-3xl');
8231        if (statProbe) {
8232          var stx = await safeExtract('div.rounded-3xl', {
8233            metric: 'span[aria-hidden="false"]',
8234            label: 'p.font-body'
8235          }, Math.min(limit, 30));
8236          for (var sti = 0; sti < stx.results.length; sti++) {
8237            var s2 = stx.results[sti];
8238            if (!s2.metric || !s2.label) continue;
8239            stats.push({ metric: s2.metric, label: s2.label });
8240          }
8241          if (stats.length) {
8242            mark(statProbe);
8243            notes.stats = "read " + stats.length + " metric block(s) from the leaf span[aria-hidden=\"false\"] nodes";
8244          } else {
8245            notes.stats = "matched " + stx.results.length + " candidate node(s) but none carried both a metric and a label";
8246          }
8247        }
8248      }
8249
8250      /* ---------------- Capability columns ---------------- */
8251      var capabilityColumns = [];
8252      notes.capabilities = "notFoundOnThisPage";
8253      if (want("capabilities")) {
8254        var scope = null;
8255        var featuresHeading = document.getElementById("features");
8256        if (featuresHeading && featuresHeading.closest) {
8257          scope = featuresHeading.closest("section") || featuresHeading.closest("div") || featuresHeading.parentElement;
8258        }
8259        var columns = (scope || document).querySelectorAll('div.bg-tertiary-2');
8260        if (!columns.length) columns = document.querySelectorAll('div.bg-tertiary-2');
8261        for (var ci = 0; ci < columns.length && capabilityColumns.length < limit; ci++) {
8262          var col = columns[ci];
8263          var colHeading = col.querySelector('h3 > span');
8264          var bullets = col.querySelectorAll('div[data-epi-edit="MainBody"] ul > li');
8265          if (!colHeading || !bullets.length) continue;
8266          var list = [];
8267          for (var bi = 0; bi < bullets.length; bi++) {
8268            var bt = visText(bullets[bi]);
8269            if (bt) list.push(bt);
8270          }
8271          var ch = visText(colHeading);
8272          if (!ch && !list.length) continue;
8273          capabilityColumns.push({ heading: ch, capabilities: list });
8274          mark(col);
8275        }
8276        if (capabilityColumns.length) {
8277          notes.capabilities = "read " + capabilityColumns.length + " capability column(s)" + (scope ? " scoped to the features section" : " (features heading anchor not found, scanned the document and kept only columns carrying a heading and a bullet list)");
8278        }
8279      }
8280
8281      /* ---------------- CTA targets ---------------- */
8282      var ctas = [];
8283      notes.ctas = "notFoundOnThisPage";
8284      if (want("ctas")) {
8285        var mainEl = document.querySelector("main") || document.body;
8286        var anchors = mainEl ? mainEl.querySelectorAll("a[href]") : [];
8287        var seenCta = {};
8288        var ctaCap = Math.min(limit, 60);
8289        for (var ci2 = 0; ci2 < anchors.length && ctas.length < ctaCap; ci2++) {
8290          var an = anchors[ci2];
8291          if (an.closest && (an.closest("nav") || an.closest("footer"))) continue;
8292          var href = an.getAttribute("href") || "";
8293          if (!href || href.toLowerCase().indexOf("javascript:") === 0) continue;
8294          var label = clean(an.getAttribute("aria-label") || "");
8295          if (!label) label = visText(an);
8296          if (!label) label = clean(an.getAttribute("title") || "");
8297          if (!label) continue;
8298          if (label.length > 120) label = label.slice(0, 120);
8299          var ckey = href + "|" + label;
8300          if (seenCta[ckey]) continue;
8301          seenCta[ckey] = true;
8302          ctas.push({ label: label, url: href, title: clean(an.getAttribute("title") || "") });
8303        }
8304        if (anchors.length > ctas.length) truncatedAny = truncatedAny || (ctas.length >= ctaCap);
8305        if (ctas.length) notes.ctas = "collected " + ctas.length + " in-page link target(s) outside the site navigation and footer";
8306      }
8307
8308      /* ---------------- Inline demo form (report only) ---------------- */
8309      var demoFormPresent = false;
8310      var demoFormSelector = null;
8311      var demoFormSubmitSelector = null;
8312      var demoFormFields = [];
8313      notes.form = "notFoundOnThisPage";
8314      if (want("form")) {
8315        var demoForm = document.querySelector("form.opti-form");
8316        if (demoForm) {
8317          demoFormPresent = true;
8318          demoFormSelector = "form.opti-form";
8319          if (demoForm.querySelector('button[type="submit"]')) demoFormSubmitSelector = 'form.opti-form button[type="submit"]';
8320          var controls = demoForm.querySelectorAll("input, textarea, select");
8321          for (var fci = 0; fci < controls.length; fci++) {
8322            var ctl = controls[fci];
8323            var ctype = (ctl.getAttribute("type") || ctl.tagName || "").toLowerCase();
8324            if (ctype === "hidden") continue;
8325            var flabel = clean(ctl.getAttribute("aria-label") || "");
8326            if (!flabel && ctl.labels && ctl.labels.length) flabel = visText(ctl.labels[0]);
8327            if (!flabel && ctl.id) {
8328              var lab = document.querySelector('label[for="' + ctl.id + '"]');
8329              if (lab) flabel = visText(lab);
8330            }
8331            if (!flabel) flabel = clean(ctl.getAttribute("placeholder") || "");
8332            if (!flabel) flabel = clean(ctl.getAttribute("name") || "");
8333            if (!flabel) continue;
8334            demoFormFields.push({
8335              label: flabel,
8336              name: ctl.getAttribute("name") || "",
8337              type: ctype,
8338              required: !!ctl.required
8339            });
8340          }
8341          mark(demoForm);
8342          notes.form = "the inline demo form is present with " + demoFormFields.length + " visible field(s); this tool reported it only and did not fill or submit it";
8343        }
8344      }
8345
8346      /* ---------------- Page section headings (unrecognised blocks) ---------------- */
8347      var pageSections = [];
8348      var mainScope = document.querySelector("main") || document.body;
8349      if (mainScope) {
8350        var heads = mainScope.querySelectorAll("h2");
8351        for (var hi = 0; hi < heads.length && pageSections.length < 25; hi++) {
8352          var ht = visText(heads[hi]);
8353          if (ht && pageSections.indexOf(ht) === -1) pageSections.push(ht);
8354        }
8355      }
8356
8357      if (navigatedAway()) return out(navPayload("final read"));
8358
8359      var counts = {
8360        faqs: faqs.length,
8361        quotes: quotes.length,
8362        stories: stories.length,
8363        agents: agents.length,
8364        featureBlocks: featureBlocks.length,
8365        capabilityColumns: capabilityColumns.length,
8366        stats: stats.length,
8367        ctas: ctas.length
8368      };
8369      var total = counts.faqs + counts.quotes + counts.stories + counts.agents + counts.featureBlocks + counts.capabilityColumns + counts.stats + counts.ctas;
8370      var faqsAnswered = 0;
8371      for (var fq = 0; fq < faqs.length; fq++) if (faqs[fq].answer) faqsAnswered++;
8372
8373      var observed = total > 0;
8374      var payload = {
8375        status: observed ? "ok" : "partial",
8376        observed: observed,
8377        action: ACTION,
8378        url: location.href,
8379        title: clean(document.title),
8380        sectionsRead: wanted,
8381        resultCount: total,
8382        counts: counts,
8383        faqs: faqs,
8384        faqsAnswered: faqsAnswered,
8385        quotes: quotes,
8386        stories: stories,
8387        agents: agents,
8388        agentTotal: agentTotal,
8389        featureBlocks: featureBlocks,
8390        capabilityColumns: capabilityColumns,
8391        stats: stats,
8392        ctas: ctas,
8393        pageSections: pageSections,
8394        demoFormPresent: demoFormPresent,
8395        demoFormSelector: demoFormSelector,
8396        demoFormSubmitSelector: demoFormSubmitSelector,
8397        demoFormFields: demoFormFields,
8398        truncated: truncatedAny,
8399        notes: notes,
8400        summary: observed
8401          ? ("Read " + counts.faqs + " FAQ item(s) (" + faqsAnswered + " with answer text), " + counts.quotes + " quote(s), " + counts.stories + " customer story/stories, " + counts.agents + " agent slide(s), " + counts.featureBlocks + " feature block(s), " + counts.capabilityColumns + " capability column(s), " + counts.stats + " metric(s) and " + counts.ctas + " link target(s) from this product page.")
8402          : "Interacted with the page but could not read any content out of the requested sections."
8403      };
8404
8405      if (!observed) {
8406        payload.reason = "None of the requested sections yielded readable content: no accordion items, carousel cards, agent slides, feature blocks, metrics or link targets were found in the settled DOM.";
8407        payload.nextStep = "Confirm the page has finished loading (this page hydrates client-side) and call optimizely-read-product-page-details again, or request a narrower sections list.";
8408      } else if (demoFormPresent) {
8409        payload.nextStep = "The inline demo form is on this page; hand off to the demo request tool to fill and submit it.";
8410      }
8411
8412      return out(payload);
8413    } catch (err) {
8414      return out({
8415        status: "error",
8416        action: ACTION,
8417        url: location.href,
8418        reason: "The read failed with an unexpected error: " + String((err && err.message) ? err.message : err),
8419        nextStep: "Retry once the page has finished loading, or call again with a narrower sections list such as ['faqs'] to isolate the failing section.",
8420        summary: "Could not read the product page content because of an unexpected error."
8421      });
8422    }
8423  }
8424});
8425
8426  /* ---- optimizely-list-optimizely-products-and-plans ---- */
8427mc.registerToolIf("/p*", {
8428  name: "optimizely-list-optimizely-products-and-plans",
8429  description: "Read the Optimizely product catalogue and plan packages from the page currently open. On the products listing it returns each product card as name, one-line positioning description, absolute URL and card image. On the plans page it returns each plan card with its name, one-line description, the complete feature bullet list and its Read more URL, plus the additional marketer tool cards with name, description and URL, and the page's own pricing statement flagged as quote-only when that statement is present. Also returns which page layout was read, the observed card count, where the data was read from, and the page URL. Read-only: it scrolls to and highlights the card region it read.",
8430  inputSchema: {
8431    type: "object",
8432    properties: {
8433      limit: {
8434        type: "integer",
8435        description: "Maximum number of cards to return per section. Defaults to 50."
8436      }
8437    },
8438    required: []
8439  },
8440  annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
8441  execute: async function (args) {
8442    trackWebMCPTool("optimizely-list-optimizely-products-and-plans");
8443
8444    var startUrl = location.href;
8445
8446    function pack(payload) {
8447      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
8448    }
8449
8450    try {
8451      var a = args || {};
8452      var limit = parseInt(a.limit, 10);
8453      if (!isFinite(limit) || limit <= 0) { limit = 50; }
8454      if (limit > 100) { limit = 100; }
8455
8456      function q(sel) {
8457        try { return Array.prototype.slice.call(document.querySelectorAll(sel)); } catch (e) { return []; }
8458      }
8459      function qIn(root, sel) {
8460        try { return Array.prototype.slice.call(root.querySelectorAll(sel)); } catch (e) { return []; }
8461      }
8462      function txt(node) {
8463        if (!node) { return ""; }
8464        try {
8465          if (node.getAttribute && node.getAttribute("aria-hidden") === "true") { return ""; }
8466        } catch (e) {}
8467        return (node.textContent || "").replace(/\s+/g, " ").trim();
8468      }
8469      function leaf(root, sel) {
8470        try { return txt(root.querySelector(sel)); } catch (e) { return ""; }
8471      }
8472      function abs(href) {
8473        if (!href) { return null; }
8474        try { return new URL(href, location.origin).href; } catch (e) { return href; }
8475      }
8476      function imgSrc(root) {
8477        try {
8478          var im = root.querySelector("img");
8479          return im ? (im.getAttribute("src") || im.src || null) : null;
8480        } catch (e) { return null; }
8481      }
8482      function highlight(els) {
8483        if (!els || !els.length) { return false; }
8484        var anc = els[0];
8485        var last = els[els.length - 1];
8486        var guard = 0;
8487        while (anc && anc.parentElement && !anc.contains(last) && guard < 25) {
8488          anc = anc.parentElement;
8489          guard++;
8490        }
8491        var target = anc || els[0];
8492        try {
8493          target.style.outline = "3px solid #0037ff";
8494          target.style.outlineOffset = "4px";
8495          target.scrollIntoView({ behavior: "smooth", block: "center" });
8496          return true;
8497        } catch (e) { return false; }
8498      }
8499
8500      // Attempt the Next.js data island first, as a fallback source of card content.
8501      function islandProps() {
8502        try {
8503          var el = document.getElementById("__NEXT_DATA__");
8504          if (!el) { return null; }
8505          var data = JSON.parse(el.textContent || "{}");
8506          return (data && data.props && data.props.pageProps) || null;
8507        } catch (e) { return null; }
8508      }
8509      function cardsFromIsland(pp) {
8510        var out = [];
8511        var seen = {};
8512        var visits = 0;
8513        function walk(node, depth) {
8514          if (!node || depth > 8 || visits > 6000) { return; }
8515          visits++;
8516          if (Array.isArray(node)) {
8517            for (var i = 0; i < node.length; i++) { walk(node[i], depth + 1); }
8518            return;
8519          }
8520          if (typeof node !== "object") { return; }
8521          var href = null;
8522          ["url", "href", "link", "path", "slug"].forEach(function (k) {
8523            if (!href && typeof node[k] === "string" && node[k].indexOf("/products/") === 0) { href = node[k]; }
8524          });
8525          var name = null;
8526          ["title", "name", "heading", "label", "eyebrow"].forEach(function (k) {
8527            if (!name && typeof node[k] === "string" && node[k].trim() && node[k].length < 120) { name = node[k].trim(); }
8528          });
8529          var desc = null;
8530          ["description", "subtitle", "summary", "intro", "text"].forEach(function (k) {
8531            if (!desc && typeof node[k] === "string" && node[k].trim() && node[k].length < 400) { desc = node[k].trim(); }
8532          });
8533          if (href && name && !seen[href]) {
8534            seen[href] = 1;
8535            out.push({ name: name, description: desc || null, url: abs(href), image: null });
8536          }
8537          for (var k2 in node) {
8538            if (Object.prototype.hasOwnProperty.call(node, k2)) { walk(node[k2], depth + 1); }
8539          }
8540        }
8541        walk(pp, 0);
8542        return out;
8543      }
8544      var islandCards = cardsFromIsland(islandProps());
8545
8546      // Let the page settle before reading it.
8547      try { await waitForElement('main a[href^="/products/"]', 8000); } catch (e) {}
8548      await new Promise(function (r) { setTimeout(r, 350); });
8549
8550      if (location.href !== startUrl) {
8551        return pack({
8552          status: "navigated",
8553          action: "read the Optimizely product and plan cards",
8554          url: location.href,
8555          nextStep: "The page changed while the tool was reading. Call this tool again once the new page has loaded.",
8556          summary: "The page navigated before the cards could be read."
8557        });
8558      }
8559
8560      var allAnchors = q('main a[href^="/products/"]');
8561
8562      var planEls = q('main a[href^="/products/"]:has(ul)');
8563      if (!planEls.length) {
8564        planEls = allAnchors.filter(function (el) { return !!el.querySelector("ul li"); });
8565      }
8566      var toolEls = q('main a[href^="/products/"]:has(video)');
8567      if (!toolEls.length) {
8568        toolEls = allAnchors.filter(function (el) { return !!el.querySelector("video") && !el.querySelector("ul li"); });
8569      }
8570
8571      var path = location.pathname.replace(/\/+$/, "") || "/";
8572      // Only the products listing itself carries the product card section; other
8573      // paths matching this pattern may link to /products/ pages without being it.
8574      var onProductsListing = /(^|\/)products$/.test(path) ||
8575        q("main h2").some(function (h) { return /one platform, every function/i.test(txt(h)); });
8576      var layout;
8577      if (planEls.length) {
8578        layout = "plans";
8579      } else if (path === "/plans") {
8580        layout = "plans";
8581      } else if (allAnchors.length && onProductsListing) {
8582        layout = "products";
8583      } else {
8584        layout = "unknown";
8585      }
8586
8587      if (layout === "plans" && !toolEls.length && allAnchors.length) {
8588        var planHrefs = {};
8589        planEls.forEach(function (el) { planHrefs[el.getAttribute("href")] = 1; });
8590        toolEls = allAnchors.filter(function (el) { return !planHrefs[el.getAttribute("href")]; });
8591      }
8592
8593      // Pricing statement, read from the page's own copy.
8594      var pricingStatement = null;
8595      q("main p, main li, main h2, main h3").some(function (n) {
8596        var t = txt(n);
8597        if (t && /individually packaged/i.test(t)) { pricingStatement = t; return true; }
8598        return false;
8599      });
8600      var pricing = {
8601        statementObserved: !!pricingStatement,
8602        statement: pricingStatement,
8603        model: pricingStatement ? "quote-only" : null
8604      };
8605
8606      if (layout === "plans") {
8607        var plans = [];
8608        planEls.slice(0, limit).forEach(function (el) {
8609          var bullets = [];
8610          qIn(el, "ul li").forEach(function (li) {
8611            var t = txt(li);
8612            if (t) { bullets.push(t); }
8613          });
8614          plans.push({
8615            name: leaf(el, "h3") || null,
8616            description: leaf(el, "p:nth-of-type(1)") || leaf(el, "p") || null,
8617            features: bullets,
8618            featureCount: bullets.length,
8619            readMoreUrl: abs(el.getAttribute("href")),
8620            image: imgSrc(el)
8621          });
8622        });
8623
8624        var seenTool = {};
8625        var tools = [];
8626        toolEls.forEach(function (el) {
8627          if (tools.length >= limit) { return; }
8628          var u = abs(el.getAttribute("href"));
8629          if (!u || seenTool[u]) { return; }
8630          seenTool[u] = 1;
8631          tools.push({
8632            name: leaf(el, "h3") || null,
8633            description: leaf(el, "p") || null,
8634            url: u
8635          });
8636        });
8637
8638        var readable = plans.some(function (p) { return !!p.name || !!p.description; }) ||
8639                       tools.some(function (t) { return !!t.name || !!t.description; });
8640        var highlighted = highlight(planEls.concat(toolEls));
8641
8642        if (!readable) {
8643          return pack({
8644            status: "partial",
8645            observed: false,
8646            action: "read the Optimizely plan packages",
8647            pageLayout: "plans",
8648            url: location.href,
8649            source: "dom",
8650            resultCount: null,
8651            plans: [],
8652            additionalTools: [],
8653            pricing: pricing,
8654            reason: "No plan cards with readable text were found under main on this page.",
8655            nextStep: "Confirm the plans page has finished loading, then call this tool again.",
8656            summary: "The plans page was open but no plan card text could be read."
8657          });
8658        }
8659
8660        return pack({
8661          status: "ok",
8662          observed: true,
8663          action: "read the Optimizely plan packages",
8664          pageLayout: "plans",
8665          url: location.href,
8666          source: "dom",
8667          highlighted: highlighted,
8668          resultCount: plans.length + tools.length,
8669          planCount: plans.length,
8670          additionalToolCount: tools.length,
8671          truncated: (planEls.length > plans.length) || (toolEls.length > tools.length),
8672          plans: plans,
8673          additionalTools: tools,
8674          pricing: pricing,
8675          summary: "Read " + plans.length + " plan card(s) with their feature bullets and " + tools.length + " additional tool card(s) from the plans page" + (pricingStatement ? ", plus the quote-only pricing statement." : ".")
8676        });
8677      }
8678
8679      if (layout === "products") {
8680        var raw = [];
8681        var truncatedFlag = false;
8682        var source = "dom";
8683        try {
8684          var ex = await extractItems('main a[href^="/products/"]', { name: "p", description: "h3", url: "@href", image: "img @src" }, limit);
8685          if (ex && ex.results && ex.results.length) {
8686            raw = ex.results;
8687            truncatedFlag = !!ex.truncated;
8688          }
8689        } catch (e) {}
8690        if (!raw.length) {
8691          allAnchors.slice(0, limit).forEach(function (el) {
8692            raw.push({
8693              name: leaf(el, "p"),
8694              description: leaf(el, "h3"),
8695              url: el.getAttribute("href"),
8696              image: imgSrc(el)
8697            });
8698          });
8699          truncatedFlag = allAnchors.length > raw.length;
8700        }
8701
8702        var seenP = {};
8703        var products = [];
8704        raw.forEach(function (it) {
8705          var u = abs(it.url);
8706          if (!u || seenP[u]) { return; }
8707          seenP[u] = 1;
8708          products.push({
8709            name: (it.name || "").trim() || null,
8710            description: (it.description || "").trim() || null,
8711            url: u,
8712            image: it.image || null
8713          });
8714        });
8715
8716        var okProducts = products.some(function (p) { return !!p.name || !!p.description; });
8717
8718        if (!okProducts && islandCards.length) {
8719          return pack({
8720            status: "partial",
8721            observed: false,
8722            action: "read the Optimizely product catalogue",
8723            pageLayout: "products",
8724            url: location.href,
8725            source: "next-data",
8726            highlighted: false,
8727            resultCount: islandCards.length,
8728            truncated: islandCards.length > limit,
8729            products: islandCards.slice(0, limit),
8730            pricing: pricing,
8731            reason: "The rendered product cards could not be read, so these entries come from the page's embedded data payload and were not checked against the visible cards.",
8732            nextStep: "Confirm the products listing has finished rendering, then call this tool again to read the visible cards.",
8733            summary: "Returned " + Math.min(islandCards.length, limit) + " product entries from the page data payload because the rendered cards could not be read."
8734          });
8735        }
8736
8737        if (!okProducts) {
8738          return pack({
8739            status: "partial",
8740            observed: false,
8741            action: "read the Optimizely product catalogue",
8742            pageLayout: "products",
8743            url: location.href,
8744            source: "dom",
8745            resultCount: null,
8746            products: [],
8747            pricing: pricing,
8748            reason: "No product cards with readable name or description text were found under main on this page.",
8749            nextStep: "Confirm the products listing has finished loading, then call this tool again.",
8750            summary: "The products page was open but no product card text could be read."
8751          });
8752        }
8753
8754        var hl = highlight(allAnchors);
8755        return pack({
8756          status: "ok",
8757          observed: true,
8758          action: "read the Optimizely product catalogue",
8759          pageLayout: "products",
8760          url: location.href,
8761          source: source,
8762          highlighted: hl,
8763          resultCount: products.length,
8764          truncated: truncatedFlag,
8765          products: products,
8766          pricing: pricing,
8767          nextStep: "Open the plans page and call this tool again to read the plan packages, their feature bullets and the pricing statement.",
8768          summary: "Read " + products.length + " product card(s) with name, description and URL from the products listing."
8769        });
8770      }
8771
8772      return pack({
8773        status: "partial",
8774        observed: false,
8775        action: "read the Optimizely product and plan cards",
8776        pageLayout: "unknown",
8777        url: location.href,
8778        source: "dom",
8779        resultCount: null,
8780        products: [],
8781        plans: [],
8782        additionalTools: [],
8783        pricing: pricing,
8784        reason: allAnchors.length
8785          ? "This page links to product pages but carries neither the product card section nor any plan cards, so it is not the products listing or the plans page."
8786          : "This page carries no product cards (no links to /products/ inside main) and no plan cards.",
8787        nextStep: "Open the products listing or the plans page and call this tool again.",
8788        summary: "No product or plan card section was found on this page."
8789      });
8790    } catch (err) {
8791      return pack({
8792        status: "error",
8793        action: "read the Optimizely product and plan cards",
8794        url: location.href,
8795        reason: "Unexpected failure while reading the page: " + ((err && err.message) ? err.message : 
8795String(err)),
8796        nextStep: "Reload the page and call this tool again.",
8797        summary: "The catalogue could not be read because of an unexpected error."
8798      });
8799    }
8800  }
8801});
8802
8803  /* ---- optimizely-developer-hub-entry-points, optimizely-developer-hub-entry-points-results ---- */
8804mc.registerToolIf("/developers/**", {
8805  name: "optimizely-developer-hub-entry-points",
8806  description: "Lists the developer entry points linked from the current Optimizely developer page and opens one on request. Returns each entry point with its title, URL, path, group (role, product, beta, community destination or other), the section heading it sits under and, where the page carries them, its description and call-to-action text, all read from the live page. Pass destination with an entry point title, section, path or URL to follow that link, and the tool reports the destination it is opening.",
8807  inputSchema: {
8808    type: "object",
8809    properties: {
8810      destination: {
8811        type: "string",
8812        description: "Optional. Title, section heading, path or full URL of the entry point to open, for example 'Front-end devs', '/developers/cms/', 'Betas', or an Academy or community URL. It is matched against the entry points read from the page at call time. Omit it to list the entry points without opening one."
8813      }
8814    },
8815    required: [],
8816    additionalProperties: false
8817  },
8818  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
8819  execute: async function (args) {
8820    trackWebMCPTool("optimizely-developer-hub-entry-points");
8821    var startUrl = location.href;
8822    try {
8823      var params = args || {};
8824      var requested = typeof params.destination === "string" ? params.destination.trim() : "";
8825
8826      try { await waitForElement("main", 5000); } catch (e) {}
8827
8828      var ICONS = /\b(arrow_forward|arrow_outward|arrow_right_alt|open_in_new|north_east|chevron_right|chevron_left|keyboard_arrow_down|keyboard_arrow_up|keyboard_arrow_right|rocket_launch)\b/g;
8829      function clean(s) { return String(s == null ? "" : s).replace(ICONS, " ").replace(/\s+/g, " ").trim(); }
8830      function qsa(sel, root) {
8831        try { return Array.prototype.slice.call((root || document).querySelectorAll(sel)); } catch (e) { return []; }
8832      }
8833      function leafText(el) {
8834        if (!el) return "";
8835        if (el.getAttribute && el.getAttribute("aria-hidden") === "true") return "";
8836        return clean(el.textContent);
8837      }
8838      function headingText(el) {
8839        if (!el) return "";
8840        var parts = [];
8841        (function walk(n) {
8842          if (!n) return;
8843          if (n.nodeType === 3) { var t = clean(n.nodeValue); if (t) parts.push(t); return; }
8844          if (n.nodeType !== 1) return;
8845          if (n.getAttribute && n.getAttribute("aria-hidden") === "true") return;
8846          for (var i = 0; i < n.childNodes.length; i++) { walk(n.childNodes[i]); }
8847        })(el);
8848        var out = [];
8849        for (var j = 0; j < parts.length; j++) {
8850          if (!out.length || out[out.length - 1].toLowerCase() !== parts[j].toLowerCase()) { out.push(parts[j]); }
8851        }
8852        return clean(out.join(" "));
8853      }
8854      function labelOf(a) {
8855        if (!a) return "";
8856        var al = a.getAttribute && (a.getAttribute("aria-label") || a.getAttribute("title"));
8857        if (al) return clean(al);
8858        var h = a.querySelector ? a.querySelector("h1, h2, h3, h4") : null;
8859        if (h && headingText(h)) return headingText(h);
8860        return headingText(a);
8861      }
8862      function contextOf(a) {
8863        var res = { section: null, description: null };
8864        var el = a && a.parentElement;
8865        for (var d = 0; d < 5 && el; d++) {
8866          var tag = el.tagName;
8867          if (tag === "MAIN" || tag === "BODY" || tag === "FOOTER" || tag === "HTML") break;
8868          var h = null;
8869          try { h = el.querySelector("h2, h3, h4, h5"); } catch (e) { h = null; }
8870          if (h && !(h.closest && h.closest("a")) && headingText(h)) {
8871            res.section = headingText(h);
8872            var ps = qsa("p", el);
8873            for (var i = 0; i < ps.length; i++) {
8874              if (ps[i].querySelector("a")) continue;
8875              var t = leafText(ps[i]);
8876              if (t) { res.description = t; break; }
8877            }
8878            break;
8879          }
8880          el = el.parentElement;
8881        }
8882        return res;
8883      }
8884      function abs(href) {
8885        try { return new URL(href, location.href).href; } catch (e) { return String(href == null ? "" : href); }
8886      }
8887      function pathOf(u) {
8888        try { var x = new URL(u); return x.origin === location.origin ? (x.pathname + x.search + x.hash) : null; } catch (e) { return null; }
8889      }
8890
8891      var entries = [];
8892      var seen = {};
8893      var firstNode = null;
8894      function add(group, title, href, description, cta, node, section) {
8895        if (!href) return;
8896        var url = abs(href);
8897        if (!url || /^(javascript|mailto|tel):/i.test(url)) return;
8898        if (seen[url]) return;
8899        seen[url] = true;
8900        entries.push({
8901          group: group,
8902          title: clean(title) || url,
8903          url: url,
8904          path: pathOf(url),
8905          section: clean(section) || null,
8906          description: clean(description) || null,
8907          cta: clean(cta) || null,
8908          external: pathOf(url) === null
8909        });
8910        if (!firstNode && node) firstNode = node;
8911      }
8912
8913      qsa('main p > a[href^="/developers/"]').forEach(function (a) {
8914        add("role", labelOf(a), a.getAttribute("href"), null, null, a, null);
8915      });
8916
8917      var cardData = null;
8918      try {
8919        cardData = await extractItems('main a[href^="/developers/"]:has(h3)', { url: "@href", title: "h3", description: "p", cta: "div > span > span:nth-child(1)" }, 25);
8920      } catch (e) { cardData = null; }
8921      var cardsTruncated = !!(cardData && cardData.truncated);
8922      if (cardData && cardData.results && cardData.results.length) {
8923        cardData.results.forEach(function (r) { add("product", r.title, r.url, r.description, r.cta, null, null); });
8924      } else {
8925        qsa('main a[href^="/developers/"]').forEach(function (a) {
8926          if (!a.querySelector || !a.querySelector("h3")) return;
8927          add("product", headingText(a.querySelector("h3")), a.getAttribute("href"), leafText(a.querySelector("p")), null, a, null);
8928        });
8929      }
8930
8931      qsa('main a[href^="/developers/optimizely-one"]').forEach(function (a) {
8932        var c = contextOf(a);
8933        add("product", c.section || labelOf(a), a.getAttribute("href"), c.description, labelOf(a), a, c.section);
8934      });
8935
8936      qsa('main a[href^="/beta"]').forEach(function (a) {
8937        var c = contextOf(a);
8938        add("beta", labelOf(a) || "Betas", a.getAttribute("href"), c.description, labelOf(a), a, c.section);
8939      });
8940
8941      [
8942        ['main a[href^="https://world.optimizely.com/"]', "Optimizely World community forum"],
8943        ['main a[href^="https://academy.optimizely.com/"]', "Academy course catalog"],
8944        ['main a[href^="https://join.slack.com/"]', "Community Slack invite"],
8945        ['a[href="https://support.optimizely.com/"], a[href="https://support.optimizely.com"]', "Support"]
8946      ].forEach(function (pair) {
8947        qsa(pair[0]).forEach(function (a) {
8948          if (a.querySelector && a.querySelector("h3")) return;
8949          var c = contextOf(a);
8950          add("community", labelOf(a) || pair[1], a.getAttribute("href"), c.description, labelOf(a), a, c.section);
8951        });
8952      });
8953
8954      qsa('main a[href^="/developers/"]').forEach(function (a) {
8955        var t = labelOf(a);
8956        if (!t) return;
8957        add("other", t, a.getAttribute("href"), null, null, a, null);
8958      });
8959
8960      var grouped = { roles: [], products: [], beta: [], community: [], other: [] };
8961      var groupKey = { role: "roles", product: "products", beta: "beta", community: "community", other: "other" };
8962      entries.forEach(function (e) { (grouped[groupKey[e.group]] || grouped.other).push(e); });
8963
8964      function norm(s) { return String(s == null ? "" : s).toLowerCase().replace(/[^a-z0-9]+/g, " ").trim(); }
8965      function normPath(s) { return String(s == null ? "" : s).toLowerCase().replace(/\/+$/, ""); }
8966
8967      if (requested) {
8968        var wantedUrl = /^https?:\/\//i.test(requested) ? requested : (requested.charAt(0) === "/" ? abs(requested) : "");
8969        var target = null;
8970        var tiers = [
8971          function (e) { return wantedUrl && normPath(e.url) === normPath(wantedUrl); },
8972          function (e) { return e.path && normPath(e.path) === normPath(requested); },
8973          function (e) { return e.title && norm(e.title) === norm(requested); },
8974          function (e) { return e.section && norm(e.section) === norm(requested); },
8975          function (e) { return e.cta && norm(e.cta) === norm(requested); },
8976          function (e) { return e.title && norm(requested).length > 2 && norm(e.title).indexOf(norm(requested)) !== -1; },
8977          function (e) { return e.section && norm(requested).length > 2 && norm(e.section).indexOf(norm(requested)) !== -1; },
8978          function (e) { return e.cta && norm(requested).length > 2 && norm(e.cta).indexOf(norm(requested)) !== -1; },
8979          function (e) { return norm(requested).length > 2 && normPath(e.url).indexOf(normPath(requested)) !== -1; }
8980        ];
8981        for (var t = 0; t < tiers.length && !target; t++) {
8982          for (var i = 0; i < entries.length; i++) {
8983            if (tiers[t](entries[i])) { target = entries[i]; break; }
8984          }
8985        }
8986
8987        if (!target) {
8988          return { content: [{ type: "text", text: JSON.stringify({
8989            status: "error",
8990            action: "open a developer entry point",
8991            url: location.href,
8992            reason: entries.length
8993              ? ("No entry point on this page matches \"" + requested + "\".")
8994              : ("No entry point links could be read from this page, so \"" + requested + "\" could not be matched."),
8995            availableOptions: entries.map(function (e) { return { title: e.title, section: e.section, url: e.url, group: e.group }; }),
8996            nextStep: entries.length
8997              ? "Call this tool again with destination set to one of the titles or URLs in availableOptions."
8998              : "Wait for the developer page to finish rendering, then call this tool again with no destination to list what is on it.",
8999            summary: "Could not match \"" + requested + "\" to a link on this page."
9000          }) }] };
9001        }
9002
9003        var destUrl = target.url;
9004        try {
9005          var built = buildUrl("{destinationUrl}", { destinationUrl: target.url });
9006          if (built && /^https?:\/\//i.test(built) && built.indexOf("%3A") === -1) { destUrl = built; }
9007        } catch (e) {}
9008
9009        var sameOrigin = !target.external;
9010        var readable = sameOrigin && target.path && /^\/developers(\/|$)/.test(target.path);
9011        var payload = {
9012          status: "navigated",
9013          action: "open the " + target.title + " developer entry point",
9014          url: destUrl,
9015          fromUrl: startUrl,
9016          destination: {
9017            title: target.title,
9018            url: destUrl,
9019            path: target.path,
9020            group: target.group,
9021            section: target.section,
9022            description: target.description,
9023            cta: target.cta,
9024            external: target.external
9025          },
9026          nextStep: readable
9027            ? ("The browser is loading " + destUrl + ". Once it has settled, call optimizely-developer-hub-entry-points-results to read that page.")
9028            : (sameOrigin
9029              ? ("The browser is loading " + destUrl + ", which is outside the developer section, so the tools registered there do not read it.")
9030              : ("The browser is loading " + destUrl + ", which is on another domain, so the tools registered on this site do not read it.")),
9031          summary: "Opening " + target.title + " at " + destUrl + "."
9032        };
9033
9034        var moved = false;
9035        if (sameOrigin && window.next && window.next.router && typeof window.next.router.push === "function") {
9036          try { window.next.router.push(target.path || destUrl); moved = true; } catch (e) { moved = false; }
9037        }
9038        if (!moved) { location.assign(destUrl); }
9039        return { content: [{ type: "text", text: JSON.stringify(payload) }] };
9040      }
9041
9042      var region = null;
9043      if (firstNode && firstNode.closest) { region = firstNode.closest("section") || firstNode.parentElement; }
9044      if (!region) { region = document.querySelector("main"); }
9045      if (region && region.style) {
9046        var previousOutline = region.style.outline;
9047        region.style.outline = "3px solid #0037ff";
9048        try { region.scrollIntoView({ behavior: "smooth", block: "center" }); } catch (e) { region.scrollIntoView(); }
9049        setTimeout(function () { region.style.outline = previousOutline; }, 6000);
9050      }
9051
9052      if (!entries.length) {
9053        return { content: [{ type: "text", text: JSON.stringify({
9054          status: "partial",
9055          action: "list the developer entry points on this page",
9056          url: location.href,
9057          observed: false,
9058          resultCount: null,
9059          entryPoints: grouped,
9060          results: [],
9061          reason: "No entry point anchors were present in the DOM when the page was read, so nothing could be confirmed.",
9062          nextStep: "Wait for the page to finish rendering and call this tool again, or open /developers directly.",
9063          summary: "The developer entry point links could not be read from this page."
9064        }) }] };
9065      }
9066
9067      return { content: [{ type: "text", text: JSON.stringify({
9068        status: "ok",
9069        action: "list the developer entry points on this page",
9070        url: location.href,
9071        observed: true,
9072        resultCount: entries.length,
9073        truncated: cardsTruncated,
9074        counts: {
9075          roles: grouped.roles.length,
9076          products: grouped.products.length,
9077          beta: grouped.beta.length,
9078          community: grouped.community.length,
9079          other: grouped.other.length
9080        },
9081        entryPoints: grouped,
9082        results: entries,
9083        nextStep: "Call this tool again with destination set to one of the returned titles or URLs to open that entry point.",
9084        summary: "Read " + entries.length + " developer entry points from this page: " + grouped.roles.length + " by role, " + grouped.products.length + " by product, " + grouped.community.length + " community destinations."
9085      }) }] };
9086    } catch (err) {
9087      return { content: [{ type: "text", text: JSON.stringify({
9088        status: "error",
9089        action: "read or open a developer entry point",
9090        url: location.href,
9091        reason: "The tool failed while working with the page: " + (err && err.message ? err.message : 
9091String(err)),
9092        nextStep: "Load the developer page again and call the tool again.",
9093        summary: "Could not read the developer entry points on this page."
9094      }) }] };
9095    }
9096  }
9097});
9098
9099mc.registerToolIf("/developers/**", {
9100  name: "optimizely-developer-hub-entry-points-results",
9101  description: "Reads the Optimizely developer page the browser has landed on and returns its main heading plus the links and cards listed in its main content, each with title, URL, the section heading it sits under and the description and call-to-action text where the page carries them, along with how many were found. Scrolls to and highlights the region the links were read from.",
9102  inputSchema: {
9103    type: "object",
9104    properties: {
9105      limit: {
9106        type: "integer",
9107        description: "Optional maximum number of links to return. Defaults to 40."
9108      }
9109    },
9110    required: [],
9111    additionalProperties: false
9112  },
9113  annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
9114  execute: async function (args) {
9115    trackWebMCPTool("optimizely-developer-hub-entry-points-results");
9116    try {
9117      var limit = (args && typeof args.limit === "number" && args.limit > 0) ? Math.floor(args.limit) : 40;
9118      try { await waitForElement("main", 6000); } catch (e) {}
9119
9120      var ICONS = /\b(arrow_forward|arrow_outward|arrow_right_alt|open_in_new|north_east|chevron_right|chevron_left|keyboard_arrow_down|keyboard_arrow_up|keyboard_arrow_right|rocket_launch)\b/g;
9121      function clean(s) { return String(s == null ? "" : s).replace(ICONS, " ").replace(/\s+/g, " ").trim(); }
9122      function qsa(sel, root) {
9123        try { return Array.prototype.slice.call((root || document).querySelectorAll(sel)); } catch (e) { return []; }
9124      }
9125      function leafText(el) {
9126        if (!el) return "";
9127        if (el.getAttribute && el.getAttribute("aria-hidden") === "true") return "";
9128        return clean(el.textContent);
9129      }
9130      function headingText(el) {
9131        if (!el) return "";
9132        var parts = [];
9133        (function walk(n) {
9134          if (!n) return;
9135          if (n.nodeType === 3) { var t = clean(n.nodeValue); if (t) parts.push(t); return; }
9136          if (n.nodeType !== 1) return;
9137          if (n.getAttribute && n.getAttribute("aria-hidden") === "true") return;
9138          for (var i = 0; i < n.childNodes.length; i++) { walk(n.childNodes[i]); }
9139        })(el);
9140        var out = [];
9141        for (var j = 0; j < parts.length; j++) {
9142          if (!out.length || out[out.length - 1].toLowerCase() !== parts[j].toLowerCase()) { out.push(parts[j]); }
9143        }
9144        return clean(out.join(" "));
9145      }
9146      function labelOf(a) {
9147        if (!a) return "";
9148        var al = a.getAttribute && (a.getAttribute("aria-label") || a.getAttribute("title"));
9149        if (al) return clean(al);
9150        var h = a.querySelector ? a.querySelector("h1, h2, h3, h4") : null;
9151        if (h && headingText(h)) return headingText(h);
9152        return headingText(a);
9153      }
9154      function contextOf(a) {
9155        var res = { section: null, description: null };
9156        var el = a && a.parentElement;
9157        for (var d = 0; d < 5 && el; d++) {
9158          var tag = el.tagName;
9159          if (tag === "MAIN" || tag === "BODY" || tag === "FOOTER" || tag === "HTML") break;
9160          var h = null;
9161          try { h = el.querySelector("h2, h3, h4, h5"); } catch (e) { h = null; }
9162          if (h && !(h.closest && h.closest("a")) && headingText(h)) {
9163            res.section = headingText(h);
9164            var ps = qsa("p", el);
9165            for (var i = 0; i < ps.length; i++) {
9166              if (ps[i].querySelector("a")) continue;
9167              var t = leafText(ps[i]);
9168              if (t) { res.description = t; break; }
9169            }
9170            break;
9171          }
9172          el = el.parentElement;
9173        }
9174        return res;
9175      }
9176      function abs(href) {
9177        try { return new URL(href, location.href).href; } catch (e) { return String(href == null ? "" : href); }
9178      }
9179
9180      var results = [];
9181      var seen = {};
9182      var firstNode = null;
9183      function add(title, href, description, cta, node, section) {
9184        if (!href) return;
9185        var url = abs(href);
9186        if (!url || /^(javascript|mailto|tel):/i.test(url) || seen[url]) return;
9187        seen[url] = true;
9188        results.push({
9189          title: clean(title) || url,
9190          url: url,
9191          section: clean(section) || null,
9192          description: clean(description) || null,
9193          cta: clean(cta) || null
9194        });
9195        if (!firstNode && node) firstNode = node;
9196      }
9197
9198      var cardData = null;
9199      try {
9200        cardData = await extractItems('main a[href^="/developers/"]:has(h3)', { url: "@href", title: "h3", description: "p", cta: "div > span > span:nth-child(1)" }, limit);
9201      } catch (e) { cardData = null; }
9202      if (cardData && cardData.results) {
9203        cardData.results.forEach(function (r) { add(r.title, r.url, r.description, r.cta, null, null); });
9204      }
9205
9206      qsa('main p > a[href^="/developers/"]').forEach(function (a) { add(labelOf(a), a.getAttribute("href"), null, null, a, null); });
9207      qsa('main a[href^="/developers/"]').forEach(function (a) {
9208        var h3 = a.querySelector ? a.querySelector("h3") : null;
9209        var c = h3 ? { section: null, description: null } : contextOf(a);
9210        add(labelOf(a), a.getAttribute("href"), h3 ? leafText(a.querySelector("p")) : c.description, null, a, c.section);
9211      });
9212      qsa('main a[href^="/beta"]').forEach(function (a) {
9213        var c = contextOf(a);
9214        add(labelOf(a) || "Betas", a.getAttribute("href"), c.description, labelOf(a), a, c.section);
9215      });
9216      [
9217        'main a[href^="https://world.optimizely.com/"]',
9218        'main a[href^="https://academy.optimizely.com/"]',
9219        'main a[href^="https://join.slack.com/"]',
9220        'a[href="https://support.optimizely.com/"], a[href="https://support.optimizely.com"]'
9221      ].forEach(function (sel) {
9222        qsa(sel).forEach(function (a) {
9223          if (a.querySelector && a.querySelector("h3")) return;
9224          var c = contextOf(a);
9225          add(labelOf(a), a.getAttribute("href"), c.description, labelOf(a), a, c.section);
9226        });
9227      });
9228      qsa("main p > a[href]").forEach(function (a) {
9229        var c = contextOf(a);
9230        add(labelOf(a), a.getAttribute("href"), c.description, labelOf(a), a, c.section);
9231      });
9232
9233      var truncated = results.length > limit || !!(cardData && cardData.truncated);
9234      if (results.length > limit) { results = results.slice(0, limit); }
9235
9236      var heading = headingText(document.querySelector("main h1")) || clean(document.title) || null;
9237
9238      var region = null;
9239      if (firstNode && firstNode.closest) { region = firstNode.closest("section") || firstNode.parentElement; }
9240      if (!region) { region = document.querySelector("main"); }
9241      if (region && region.style) {
9242        var previousOutline = region.style.outline;
9243        region.style.outline = "3px solid #0037ff";
9244        try { region.scrollIntoView({ behavior: "smooth", block: "center" }); } catch (e) { region.scrollIntoView(); }
9245        setTimeout(function () { region.style.outline = previousOutline; }, 6000);
9246      }
9247
9248      if (!results.length) {
9249        return { content: [{ type: "text", text: JSON.stringify({
9250          status: "partial",
9251          action: "read the developer page this browser landed on",
9252          url: location.href,
9253          observed: false,
9254          heading: heading,
9255          resultCount: null,
9256          results: [],
9257          truncated: false,
9258          reason: "No links were present in the main content when the page was read.",
9259          nextStep: "Wait for the page to finish rendering and call this tool again.",
9260          summary: "Landed on " + (heading || location.href) + " but no links could be read from it yet."
9261        }) }] };
9262      }
9263
9264      return { content: [{ type: "text", text: JSON.stringify({
9265        status: "ok",
9266        action: "read the developer page this browser landed on",
9267        url: location.href,
9268        observed: true,
9269        heading: heading,
9270        resultCount: results.length,
9271        results: results,
9272        truncated: truncated,
9273        summary: "Read " + results.length + " links from " + (heading || location.href) + "."
9274      }) }] };
9275    } catch (err) {
9276      return { content: [{ type: "text", text: JSON.stringify({
9277        status: "error",
9278        action: "read the developer page this browser landed on",
9279        url: location.href,
9280        reason: "The tool failed while reading the page: " + (err && err.message ? err.message : String(err)),
9281        nextStep: "Load the page again and call this tool again.",
9282        summary: "Could not read this developer page."
9283      }) }] };
9284    }
9285  }
9286});
9287
9288  /* ---- optimizely-manage-cookie-consent ---- */
9289mc.registerTool({
9290  name: "optimizely-manage-cookie-consent",
9291  description: "Manages cookie consent on this page through the OneTrust consent SDK: reads the current consent state, opens or closes the cookie preference centre, allows every category, rejects the optional categories, or updates named consent groups. Returns the action performed, the mechanism it used, the raw OnetrustActiveGroups value, the parsed list of active consent group IDs, any consent groups and their labels that can be read from the preference centre while it is open, whether the preference centre is open, which consent-gated globals (chat, analytics) are present afterwards, and the 'Do not sell or share my personal information' privacy portal URL read from the page footer.",
9292  inputSchema: {
9293    type: "object",
9294    properties: {
9295      action: {
9296        type: "string",
9297        description: "What to do: status (read only, the default), open, close, accept-all, reject-all, or update-groups. The value is validated at call time and an unsupported one comes back as an error listing availableOptions."
9298      },
9299      allowGroups: {
9300        type: "array",
9301        items: { type: "string" },
9302        description: "Consent group IDs to allow, used with action update-groups. The IDs are read off the preference centre at call time, so an unknown one comes back as an error listing the IDs that actually exist."
9303      },
9304      denyGroups: {
9305        type: "array",
9306        items: { type: "string" },
9307        description: "Consent group IDs to withdraw consent from, used with action update-groups. Validated against the same live list of group IDs."
9308      }
9309    },
9310    required: []
9311  },
9312  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false },
9313  execute: async function (args) {
9314    trackWebMCPTool("optimizely-manage-cookie-consent");
9315
9316    var SUPPORTED = ["status", "open", "close", "accept-all", "reject-all", "update-groups"];
9317    var DSAR_SELECTOR = 'footer a[href="https://privacyportal-cdn.onetrust.com/dsarwebform/1b599339-d045-4e2a-bde8-37ce346ab896/f6b0dc19-39c9-4553-bae5-b0306087d29e.html"]';
9318    var startUrl = location.href;
9319
9320    function reply(payload) {
9321      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
9322    }
9323    function sleep(ms) {
9324      return new Promise(function (resolve) { setTimeout(resolve, ms); });
9325    }
9326    function clean(s) {
9327      return (s || "").replace(/\s+/g, " ").trim();
9328    }
9329    function isVisible(el) {
9330      if (!el) return false;
9331      try {
9332        var r = el.getBoundingClientRect();
9333        if (!r.width || !r.height) return false;
9334        var s = window.getComputedStyle(el);
9335        return s.display !== "none" && s.visibility !== "hidden" && s.opacity !== "0";
9336      } catch (e) {
9337        return false;
9338      }
9339    }
9340    function getApi() {
9341      try {
9342        var o = window.OneTrust;
9343        if (o && (typeof o.ToggleInfoDisplay === "function" || typeof o.AllowAll === "function" || typeof o.RejectAll === "function")) {
9344          return { api: o, name: "window.OneTrust" };
9345        }
9346        var l = window.Optanon;
9347        if (l && (typeof l.ToggleInfoDisplay === "function" || typeof l.AllowAll === "function" || typeof l.RejectAll === "function")) {
9348          return { api: l, name: "window.Optanon" };
9349        }
9350      } catch (e) {}
9351      return null;
9352    }
9353    async function waitForApi(timeout) {
9354      var deadline = Date.now() + timeout;
9355      var f = getApi();
9356      while (!f && Date.now() < deadline) {
9357        await sleep(200);
9358        f = getApi();
9359      }
9360      return f;
9361    }
9362    function readActiveGroups() {
9363      var raw = null;
9364      try {
9365        if (typeof window.OnetrustActiveGroups === "string") raw = window.OnetrustActiveGroups;
9366        else if (typeof window.OptanonActiveGroups === "string") raw = window.OptanonActiveGroups;
9367      } catch (e) {
9368        raw = null;
9369      }
9370      var list = [];
9371      if (raw) {
9372        var parts = raw.split(",");
9373        for (var i = 0; i < parts.length; i++) {
9374          var p = clean(parts[i]);
9375          if (p && list.indexOf(p) === -1) list.push(p);
9376        }
9377      }
9378      return { raw: raw, list: list };
9379    }
9380    function findModal() {
9381      var pc = document.getElementById("onetrust-pc-sdk");
9382      if (pc && isVisible(pc)) return pc;
9383      var nodes = document.querySelectorAll('[role="dialog"], [role="alertdialog"]');
9384      for (var i = 0; i < nodes.length; i++) {
9385        var n = nodes[i];
9386        if (!isVisible(n)) continue;
9387        if (n.id === "onetrust-banner-sdk") continue;
9388        if (n.closest && n.closest("#onetrust-banner-sdk")) continue;
9389        var t = clean(n.textContent).toLowerCase();
9390        if (t.indexOf("cookie") > -1 || t.indexOf("consent") > -1 || t.indexOf("privacy preference") > -1) return n;
9391      }
9392      return null;
9393    }
9394    async function waitForModal(timeout) {
9395      var deadline = Date.now() + timeout;
9396      var m = findModal();
9397      while (!m && Date.now() < deadline) {
9398        await sleep(150);
9399        m = findModal();
9400      }
9401      return m;
9402    }
9403    async function waitForModalGone(timeout) {
9404      var deadline = Date.now() + timeout;
9405      while (findModal() && Date.now() < deadline) {
9406        await sleep(150);
9407      }
9408      return !findModal();
9409    }
9410    function labelFor(el) {
9411      var txt = "";
9412      try {
9413        var aria = clean(el.getAttribute("aria-label"));
9414        if (aria) return aria.slice(0, 120);
9415        var labelledBy = el.getAttribute("aria-labelledby");
9416        if (labelledBy) {
9417          var ids = labelledBy.split(/\s+/);
9418          for (var i = 0; i < ids.length; i++) {
9419            var ref = document.getElementById(ids[i]);
9420            if (ref && ref.getAttribute("aria-hidden") !== "true") txt += " " + clean(ref.textContent);
9421          }
9422          txt = clean(txt);
9423          if (txt) return txt.slice(0, 120);
9424        }
9425        var id = el.getAttribute("id");
9426        if (id) {
9427          var sel = 'label[for="' + (window.CSS && CSS.escape ? CSS.escape(id) : id) + '"]';
9428          var lab = document.querySelector(sel);
9429          if (lab && lab.getAttribute("aria-hidden") !== "true") {
9430            txt = clean(lab.textContent);
9431            if (txt) return txt.slice(0, 120);
9432          }
9433        }
9434        var wrap = el.closest ? el.closest("label") : null;
9435        if (wrap && wrap.getAttribute("aria-hidden") !== "true") {
9436          txt = clean(wrap.textContent);
9437          if (txt) return txt.slice(0, 120);
9438        }
9439        var title = clean(el.getAttribute("title"));
9440        if (title) return title.slice(0, 120);
9441        var host = el.closest ? el.closest("li, div") : null;
9442        if (host) {
9443          var head = host.querySelector("h2, h3, h4, h5");
9444          if (head && head.getAttribute("aria-hidden") !== "true") {
9445            txt = clean(head.textContent);
9446            if (txt) return txt.slice(0, 120);
9447          }
9448        }
9449      } catch (e) {}
9450      return null;
9451    }
9452    function groupIdFor(box) {
9453      try {
9454        var id = box.getAttribute("id") || "";
9455        var m = id.match(/ot-group-id-(.+)$/);
9456        if (m && m[1]) return m[1];
9457        var direct = box.getAttribute("data-optanongroupid");
9458        if (direct) return direct;
9459        var host = box.closest ? box.closest("[data-optanongroupid]") : null;
9460        if (host) return host.getAttribute("data-optanongroupid");
9461        var named = box.getAttribute("name") || "";
9462        var n = named.match(/^(C\d{4}|BG\w+|STACK\d+)$/i);
9463        if (n) return n[1];
9464      } catch (e) {}
9465      return null;
9466    }
9467    function discoverGroups(scopeEl) {
9468      var out = [];
9469      var seen = {};
9470      var scope = scopeEl || document;
9471      var boxes = [];
9472      try {
9473        boxes = scope.querySelectorAll('input[type="checkbox"], [role="switch"], [role="checkbox"]');
9474      } catch (e) {
9475        boxes = [];
9476      }
9477      for (var i = 0; i < boxes.length; i++) {
9478        var box = boxes[i];
9479        var gid = groupIdFor(box);
9480        if (!gid) continue;
9481        var key = gid.toUpperCase();
9482        if (seen[key]) continue;
9483        seen[key] = true;
9484        var checked = false;
9485        try {
9486          checked = box.checked === true || box.getAttribute("aria-checked") === "true";
9487        } catch (e) {}
9488        out.push({ id: gid, label: labelFor(box), allowed: checked });
9489      }
9490      return out;
9491    }
9492    function findCookieSettingsLink() {
9493      var anchors = document.querySelectorAll("footer a");
9494      for (var i = 0; i < anchors.length; i++) {
9495        var t = clean(anchors[i].textContent).toLowerCase();
9496        if (t === "cookie settings" || t.indexOf("cookie setting") > -1 || t.indexOf("cookie preference") > -1) return anchors[i];
9497      }
9498      return null;
9499    }
9500    function findDsarLink() {
9501      var el = document.querySelector(DSAR_SELECTOR);
9502      if (el) return el;
9503      var anchors = document.querySelectorAll("footer a");
9504      for (var i = 0; i < anchors.length; i++) {
9505        var href = anchors[i].getAttribute("href") || "";
9506        var t = clean(anchors[i].textContent).toLowerCase();
9507        if (href.indexOf("dsarwebform") > -1 || href.indexOf("privacyportal") > -1) return anchors[i];
9508        if (t.indexOf("do not sell") > -1) return anchors[i];
9509      }
9510      return null;
9511    }
9512    function highlight(el, scroll) {
9513      if (!el) return false;
9514      try {
9515        if (scroll && el.scrollIntoView) el.scrollIntoView({ behavior: "smooth", block: "center" });
9516        var prevOutline = el.style.outline;
9517        var prevOffset = el.style.outlineOffset;
9518        el.style.outline = "3px solid #0037ff";
9519        el.style.outlineOffset = "2px";
9520        setTimeout(function () {
9521          try {
9522            el.style.outline = prevOutline;
9523            el.style.outlineOffset = prevOffset;
9524          } catch (e) {}
9525        }, 6000);
9526        return true;
9527      } catch (e) {
9528        return false;
9529      }
9530    }
9531    function gatedGlobals() {
9532      var out = { qualifiedChat: false, googleAnalytics: false, microsoftUet: false, optimizelyWeb: false };
9533      try { out.qualifiedChat = typeof window.qualified !== "undefined"; } catch (e) {}
9534      try { out.googleAnalytics = typeof window.gtag === "function" || typeof window.dataLayer !== "undefined"; } catch (e) {}
9535      try { out.microsoftUet = typeof window.uetq !== "undefined"; } catch (e) {}
9536      try { out.optimizelyWeb = typeof window.optimizely !== "undefined"; } catch (e) {}
9537      return out;
9538    }
9539    function navigatedPayload(actionLabel) {
9540      return reply({
9541        status: "navigated",
9542        action: actionLabel,
9543        url: location.href,
9544        nextStep: "The page is loading a new document. Call optimizely-manage-cookie-consent again once it has settled to read the consent state.",
9545        summary: "The interaction navigated the browser, so the consent state was not read on this page."
9546      });
9547    }
9548
9549    try {
9550      var a = args || {};
9551      var requested = clean(a.action || "status").toLowerCase();
9552      var actionLabels = {
9553        "status": "read the cookie consent state",
9554        "open": "open the cookie preference centre",
9555        "close": "close the cookie preference centre",
9556        "accept-all": "accept all cookie categories",
9557        "reject-all": "reject the optional cookie categories",
9558        "update-groups": "update individual cookie consent groups"
9559      };
9560
9561      if (SUPPORTED.indexOf(requested) === -1) {
9562        return reply({
9563          status: "error",
9564          action: "validate the requested consent action",
9565          requestedAction: a.action === undefined || a.action === null ? null : String(a.action),
9566          url: location.href,
9567          reason: "The action '" + requested + "' is not one this tool performs.",
9568          availableOptions: SUPPORTED,
9569          nextStep: "Call again with one of the actions listed in availableOptions, for example status or accept-all.",
9570          summary: "Unsupported action; the supported actions are listed in availableOptions."
9571        });
9572      }
9573
9574      var actionLabel = actionLabels[requested];
9575      var before = readActiveGroups();
9576      var found = await waitForApi(requested === "status" ? 1500 : 4000);
9577      var api = found ? found.api : null;
9578      var apiName = found ? found.name : null;
9579
9580      var mechanism = "none";
9581      var performed = false;
9582      var failure = null;
9583
9584      if (requested === "status") {
9585        performed = true;
9586        mechanism = "read window.OnetrustActiveGroups";
9587      } else if (requested === "open") {
9588        var openModal = findModal();
9589        if (openModal) {
9590          performed = true;
9591          mechanism = "preference centre was already open";
9592        } else if (api && typeof api.ToggleInfoDisplay === "function") {
9593          api.ToggleInfoDisplay();
9594          mechanism = apiName + ".ToggleInfoDisplay()";
9595          await waitForModal(3000);
9596          performed = !!findModal();
9597          if (!performed) {
9598            failure = {
9599              reason: "ToggleInfoDisplay() was called but no visible cookie preference dialog appeared within 3 seconds.",
9600              nextStep: "Retry once the consent SDK has finished loading, or hand the visitor the privacyPortalUrl."
9601            };
9602          }
9603        } else {
9604          var link = findCookieSettingsLink();
9605          if (link) {
9606            var guard = function (ev) { ev.preventDefault(); };
9607            document.addEventListener("click", guard, true);
9608            link.addEventListener("click", guard, { once: true });
9609            link.click();
9610            try { document.removeEventListener("click", guard, true); } catch (e) {}
9611            try { link.removeEventListener("click", guard); } catch (e) {}
9612            mechanism = "clicked the footer Cookie Settings link with default navigation cancelled";
9613            if (location.href !== startUrl) return navigatedPayload(actionLabel);
9614            await waitForModal(3000);
9615            performed = !!findModal();
9616            if (!performed) {
9617              failure = {
9618                reason: "Neither window.OneTrust nor window.Optanon resolved, and clicking the footer Cookie Settings link did not open a visible cookie preference dialog.",
9619                nextStep: "Retry once the consent SDK has loaded, or hand the visitor the privacyPortalUrl to manage their data directly."
9620              };
9621            }
9622          } else {
9623            failure = {
9624              reason: "The OneTrust consent SDK globals did not resolve and no footer link labelled Cookie Settings could be found.",
9625              nextStep: "Retry once the consent SDK has loaded, or hand the visitor the privacyPortalUrl."
9626            };
9627          }
9628        }
9629      } else if (requested === "close") {
9630        var toClose = findModal();
9631        if (!toClose) {
9632          performed = true;
9633          mechanism = "preference centre was already closed";
9634        } else if (api && typeof api.ToggleInfoDisplay === "function") {
9635          api.ToggleInfoDisplay();
9636          mechanism = apiName + ".ToggleInfoDisplay()";
9637          await waitForModalGone(2500);
9638          performed = !findModal();
9639          if (!performed && api && typeof api.Close === "function") {
9640            api.Close();
9641            mechanism = apiName + ".ToggleInfoDisplay() then " + apiName + ".Close()";
9642            await waitForModalGone(1500);
9643            performed = !findModal();
9644          }
9645          if (!performed) {
9646            failure = {
9647              reason: "The consent SDK close call ran but a cookie preference dialog is still visible.",
9648              nextStep: "Ask the visitor to close the dialog, or call this tool again with action close."
9649            };
9650          }
9651        } else {
9652          var closeBtn = null;
9653          var btns = toClose.querySelectorAll('button, [role="button"]');
9654          for (var ci = 0; ci < btns.length; ci++) {
9655            var lbl = clean(btns[ci].getAttribute("aria-label") || btns[ci].getAttribute("title") || btns[ci].textContent).toLowerCase();
9656            if (lbl === "close" || lbl.indexOf("close") > -1) { closeBtn = btns[ci]; break; }
9657          }
9658          if (closeBtn) {
9659            closeBtn.click();
9660            mechanism = "clicked the dialog close control";
9661            await waitForModalGone(2000);
9662            performed = !findModal();
9663          }
9664          if (!performed) {
9665            failure = {
9666              reason: "No consent SDK global resolved and no close control could be located inside the visible cookie dialog.",
9667              nextStep: "Ask the visitor to close the dialog manually."
9668            };
9669          }
9670        }
9671      } else if (requested === "accept-all" || requested === "reject-all") {
9672        var fnName = requested === "accept-all" ? "AllowAll" : "RejectAll";
9673        if (api && typeof api[fnName] === "function") {
9674          api[fnName]();
9675          mechanism = apiName + "." + fnName + "()";
9676          performed = true;
9677          var deadlineA = Date.now() + 2500;
9678          while (Date.now() < deadlineA) {
9679            await sleep(200);
9680            if (readActiveGroups().raw !== before.raw) break;
9681          }
9682          await sleep(400);
9683        } else {
9684          failure = {
9685            reason: "The consent SDK function " + fnName + "() is not available on window.OneTrust or window.Optanon at call time.",
9686            nextStep: "Call this tool with action open to show the preference centre so the visitor can set the categories, or hand them the privacyPortalUrl."
9687          };
9688        }
9689      } else if (requested === "update-groups") {
9690        var allow = Array.isArray(a.allowGroups) ? a.allowGroups.map(function (g) { return clean(String(g)); }
9690).filter(Boolean) : [];
9691        var deny = Array.isArray(a.denyGroups) ? a.denyGroups.map(function (g) { return clean(String(g)); }).filter(Boolean) : [];
9692        if (!allow.length && !deny.length) {
9693          return reply({
9694            status: "error",
9695            action: actionLabel,
9696            requestedAction: requested,
9697            url: location.href,
9698            reason: "action update-groups was requested but neither allowGroups nor denyGroups contained a consent group ID.",
9699            availableOptions: discoverGroups(findModal()).map(function (g) { return g.id; }),
9700            nextStep: "Call with action open first to enumerate the consent groups, then call update-groups with the IDs you want, or use accept-all / reject-all.",
9701            summary: "No consent group IDs were supplied for update-groups."
9702          });
9703        }
9704        var modalForGroups = findModal();
9705        var known = discoverGroups(modalForGroups);
9706        if (!known.length && api && typeof api.ToggleInfoDisplay === "function") {
9707          api.ToggleInfoDisplay();
9708          modalForGroups = await waitForModal(3000);
9709          await sleep(300);
9710          known = discoverGroups(modalForGroups);
9711        }
9712        if (!known.length) {
9713          return reply({
9714            status: "error",
9715            action: actionLabel,
9716            requestedAction: requested,
9717            mechanism: "none",
9718            observed: before.raw !== null,
9719            preferenceCentreOpen: !!findModal(),
9720            activeGroupsRaw: before.raw,
9721            activeGroups: before.list,
9722            groups: [],
9723            consentGatedGlobals: gatedGlobals(),
9724            privacyPortalUrl: (function () { var d = findDsarLink(); return d ? d.href : null; })(),
9725            url: location.href,
9726            reason: "The individual consent groups could not be read from the preference centre at call time, so the requested group IDs cannot be checked or toggled.",
9727            availableOptions: before.list,
9728            nextStep: "Use action accept-all or reject-all, or call action open and let the visitor set the categories.",
9729            summary: "Per-group consent could not be applied because the preference centre groups were not readable."
9730          });
9731        }
9732        var knownIds = known.map(function (g) { return g.id; });
9733        var knownUpper = knownIds.map(function (g) { return g.toUpperCase(); });
9734        var unknown = [];
9735        var wanted = allow.concat(deny);
9736        for (var wi = 0; wi < wanted.length; wi++) {
9737          if (knownUpper.indexOf(wanted[wi].toUpperCase()) === -1) unknown.push(wanted[wi]);
9738        }
9739        if (unknown.length) {
9740          return reply({
9741            status: "error",
9742            action: actionLabel,
9743            requestedAction: requested,
9744            mechanism: "none",
9745            observed: true,
9746            preferenceCentreOpen: !!findModal(),
9747            activeGroupsRaw: before.raw,
9748            activeGroups: before.list,
9749            groups: known,
9750            consentGatedGlobals: gatedGlobals(),
9751            privacyPortalUrl: (function () { var d = findDsarLink(); return d ? d.href : null; })(),
9752            url: location.href,
9753            reason: "These consent group IDs are not on this page: " + unknown.join(", ") + ".",
9754            availableOptions: knownIds,
9755            nextStep: "Call again using the group IDs listed in availableOptions, or use accept-all / reject-all.",
9756            summary: "The requested consent group IDs do not exist here; the live IDs are in availableOptions."
9757          });
9758        }
9759        var pairs = [];
9760        for (var ai = 0; ai < allow.length; ai++) pairs.push(allow[ai] + ":1");
9761        for (var di = 0; di < deny.length; di++) pairs.push(deny[di] + ":0");
9762        if (api && typeof api.UpdateConsent === "function") {
9763          api.UpdateConsent("Category", pairs.join(","));
9764          mechanism = apiName + ".UpdateConsent('Category', '" + pairs.join(",") + "')";
9765          performed = true;
9766          await sleep(900);
9767        } else {
9768          var toggled = 0;
9769          var boxesAll = (findModal() || document).querySelectorAll('input[type="checkbox"], [role="switch"], [role="checkbox"]');
9770          for (var bi = 0; bi < boxesAll.length; bi++) {
9771            var bx = boxesAll[bi];
9772            var bid = groupIdFor(bx);
9773            if (!bid) continue;
9774            var up = bid.toUpperCase();
9775            var wantOn = allow.some(function (g) { return g.toUpperCase() === up; });
9776            var wantOff = deny.some(function (g) { return g.toUpperCase() === up; });
9777            if (!wantOn && !wantOff) continue;
9778            var isOn = bx.checked === true || bx.getAttribute("aria-checked") === "true";
9779            if ((wantOn && !isOn) || (wantOff && isOn)) {
9780              bx.click();
9781              toggled++;
9782              await sleep(120);
9783            }
9784          }
9785          var saveBtn = null;
9786          var saveScope = findModal() || document;
9787          var sbtns = saveScope.querySelectorAll('button, [role="button"]');
9788          for (var si = 0; si < sbtns.length; si++) {
9789            var stxt = clean(sbtns[si].getAttribute("aria-label") || sbtns[si].textContent).toLowerCase();
9790            if (/confirm my choices|save (and )?(exit|close)?|save my choices|save settings|save changes/.test(stxt)) { saveBtn = sbtns[si]; break; }
9791          }
9792          if (saveBtn) {
9793            saveBtn.click();
9794            mechanism = "toggled " + toggled + " preference centre group control(s) and clicked the save control";
9795            performed = true;
9796            await sleep(1000);
9797          } else if (toggled > 0) {
9798            mechanism = "toggled " + toggled + " preference centre group control(s)";
9799            performed = true;
9800            failure = {
9801              reason: "The group toggles were set but no save or confirm control could be located in the preference centre, so the choices may not be persisted.",
9802              nextStep: "Ask the visitor to confirm their choices in the open dialog, or use accept-all / reject-all."
9803            };
9804            await sleep(500);
9805          } else {
9806            failure = {
9807              reason: "No granular update API was exposed and no matching group control could be toggled in the preference centre.",
9808              nextStep: "Use action accept-all or reject-all, or call action open and let the visitor set the categories."
9809            };
9810          }
9811        }
9812        if (location.href !== startUrl) return navigatedPayload(actionLabel);
9813      }
9814
9815      if (location.href !== startUrl) return navigatedPayload(actionLabel);
9816
9817      if (performed && requested !== "status") {
9818        var deadlineB = Date.now() + 1200;
9819        while (Date.now() < deadlineB) {
9820          if (typeof window.qualified !== "undefined") break;
9821          await sleep(200);
9822        }
9823      }
9824
9825      var after = readActiveGroups();
9826      var modalNow = findModal();
9827      var groupsNow = discoverGroups(modalNow);
9828      var dsarEl = findDsarLink();
9829      var dsarUrl = dsarEl ? dsarEl.href : null;
9830      var highlighted = false;
9831      if (dsarEl) highlighted = highlight(dsarEl.parentElement || dsarEl, !modalNow);
9832
9833      var observed = after.raw !== null || groupsNow.length > 0 || (requested === "open" && !!modalNow);
9834
9835      var status;
9836      if (failure) status = "error";
9837      else if (!observed) status = "partial";
9838      else status = "ok";
9839
9840      var summary;
9841      if (status === "error") {
9842        summary = "Could not " + actionLabel + "; see reason and nextStep.";
9843      } else if (status === "partial") {
9844        summary = "Ran " + mechanism + " but the consent state could not be read back from this page.";
9845      } else if (requested === "status") {
9846        summary = "Consent state read: " + (after.list.length ? after.list.length + " active group(s) - " + after.list.join(", ") : "no active consent groups reported") + ".";
9847      } else {
9848        summary = "Performed " + actionLabel + "; active consent groups are now " + (after.list.length ? after.list.join(", ") : "none reported") + ".";
9849      }
9850
9851      var payload = {
9852        status: status,
9853        action: actionLabel,
9854        requestedAction: requested,
9855        mechanism: mechanism,
9856        consentSdk: apiName,
9857        observed: observed,
9858        preferenceCentreOpen: !!modalNow,
9859        activeGroupsRaw: after.raw,
9860        activeGroups: after.list,
9861        activeGroupsBefore: before.list,
9862        groups: groupsNow,
9863        consentGatedGlobals: gatedGlobals(),
9864        privacyPortalUrl: dsarUrl,
9865        privacyPortalLabel: dsarEl ? clean(dsarEl.textContent) : null,
9866        highlightedFooterPrivacyLinks: highlighted,
9867        url: location.href,
9868        summary: summary
9869      };
9870
9871      if (status === "error") {
9872        payload.reason = failure.reason;
9873        payload.nextStep = failure.nextStep;
9874        if (failure.availableOptions) payload.availableOptions = failure.availableOptions;
9875      } else if (status === "partial") {
9876        payload.reason = "Neither window.OnetrustActiveGroups nor any preference centre group control could be read after the action, so the consent state is unknown.";
9877        payload.nextStep = "Retry once the consent SDK has loaded, or hand the visitor the privacyPortalUrl to manage their data directly.";
9878      }
9879
9880      return reply(payload);
9881    } catch (err) {
9882      return reply({
9883        status: "error",
9884        action: "manage cookie consent",
9885        url: location.href,
9886        reason: "The consent tool threw while running: " + (err && err.message ? err.message : String(err)),
9887        nextStep: "Retry with action status to read the consent state before attempting a change.",
9888        summary: "Cookie consent could not be managed because of a runtime error."
9889      });
9890    }
9891  }
9892});
9893
9894  /* ---- optimizely-discover-site-capabilities ----
9895     Registered on every page. WebMCP registrations live and die with the
9896     document, so page-scoped tools only exist on their own pages; this tool
9897     gives an agent the full site capability map from anywhere and can carry
9898     it to the right page, where the snippet re-registers that page's tools. */
9899mc.registerTool({
9900  name: "optimizely-discover-site-capabilities",
9901  description: "Lists every visitor capability the Optimizely WebMCP toolset offers across the whole site - not just the tools registered on the current page - as a map of intent, the page it lives on, and the tool names that become available there. Tools register per page, so a capability's tools only appear once its page is loaded; pass goTo with a capability id (or a site path) and this tool navigates the browser there, and the toolset for that page registers when the new document loads. Use this from any page to answer 'what can I do on this site?' and to reach the page where a capability's tools live.",
9902  inputSchema: {
9903    type: "object",
9904    properties: {
9905      goTo: {
9906        type: "string",
9907        description: "Optional capability id from the returned map (for example site-search, find-partner, register-beta-interest) or a site path (for example /connectors). When given, the browser navigates to that capability's page; call the capability's tools once the page has loaded and its toolset has registered. Omit to just read the map."
9908      }
9909    },
9910    required: []
9911  },
9912  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
9913  execute: async function (args) {
9914    trackWebMCPTool("optimizely-discover-site-capabilities");
9915    var a = args || {};
9916    function out(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
9917    var CAPABILITIES = [
9918      { id: "request-demo", intent: "Request a demo, pricing or a custom plan from the sales team", path: "/get-started/", tools: ["optimizely-request-demo-lead-form", "optimizely-request-demo-lead-form-submit"], availableEverywhere: true, note: "The lead form tools are registered on every page; the form itself lives on /get-started/ and other pages carrying the contact form." },
9919      { id: "site-search", intent: "Search the site for articles, guides, videos and customer stories", path: "/search", tools: ["optimizely-search-optimizely-site", "optimizely-search-optimizely-site-results", "optimizely-filter-search-facets"] },
9920      { id: "connectors-directory", intent: "Search and filter the integrations/connectors directory", path: "/connectors", tools: ["optimizely-search-connectors-directory", "optimizely-extract-connector-listing"] },
9921      { id: "connector-detail", intent: "Read a connector's detail page and contact its publisher", path: "/connectors", tools: ["optimizely-extract-connector-listing", "optimizely-request-connector-contact-publisher", "optimizely-request-connector-contact-publisher-submit"], note: "The contact-publisher tools register on individual connector pages under /connectors/<slug>." },
9922      { id: "field-notes", intent: "Filter and page through the Field Notes resource hub", path: "/field-notes", tools: ["optimizely-browse-field-notes-library", "optimizely-browse-field-notes-library-results"] },
9923      { id: "events", intent: "Find Optimizely events and webinars", path: "/field-notes/events", tools: ["optimizely-find-optimizely-events", "optimizely-find-optimizely-events-results", "optimizely-find-optimizely-events-open"] },
9924      { id: "opticon", intent: "Explore and register for the Opticon 26 conference", path: "/field-notes/opticon", tools: ["optimizely-get-opticon-overview", "optimizely-open-opticon-registration", "optimizely-register-opticon-online", "optimizely-register-opticon-online-submit"], note: "Online registration is drivable end to end; New York and London hand off to an external Cvent wizard." },
9925      { id: "agents-directory", intent: "Search the Opal AI agent directory", path: "/agents/", tools: ["optimizely-search-opal-agent-directory", "optimizely-search-opal-agent-directory-results"] },
9926      { id: "find-partner", intent: "Find an implementation partner by country, specialization and industry", path: "/partners", tools: ["optimizely-find-implementation-partner"] },
9927      { id: "become-partner", intent: "Apply to become an Optimizely solution partner", path: "/partners", tools: ["optimizely-apply-become-solution-partner", "optimizely-apply-become-solution-partner-submit"] },
9928      { id: "products", intent: "Read the product catalogue and product page details, or open the sales chat", path: "/products", tools: ["optimizely-list-optimizely-products-and-plans", "optimizely-read-product-page-details", "optimizely-open-sales-chat"], note: "The product-detail and sales-chat tools register on individual product pages under /products/<slug>; the catalogue/plans tool registers on /products and /plans." },
9929      { id: "plans", intent: "Read the plan packages and pricing statement", path: "/plans", tools: ["optimizely-list-optimizely-products-and-plans"] },
9930      { id: "developers", intent: "Browse the developer hub entry points", path: "/developers", tools: ["optimizely-developer-hub-entry-points", "optimizely-developer-hub-entry-points-results"] },
9931      { id: "register-beta-interest", intent: "Register interest in Optimizely beta programmes", path: "/beta", tools: ["optimizely-list-beta-programs", "optimizely-register-beta-interest", "optimizely-register-beta-interest-submit"] },
9932      { id: "cookie-consent", intent: "Read or change cookie consent", path: null, tools: ["optimizely-manage-cookie-consent"], availableEverywhere: true }
9933    ];
9934    try {
9935      var wanted = typeof a.goTo === "string" ? a.goTo.trim() : "";
9936      if (!wanted) {
9937        return out({
9938          status: "ok",
9939          action: "list the site's WebMCP capabilities",
9940          url: location.href,
9941          currentPath: location.pathname,
9942          registrationModel: "Tools register per page when the snippet runs on that page's document; a capability's page-scoped tools exist only while its page is loaded. Use goTo to reach a capability's page.",
9943          capabilities: CAPABILITIES,
9944          summary: "The site offers " + CAPABILITIES.length + " capabilities; page-scoped toolsets register on their own pages, and goTo navigates there."
9945        });
9946      }
9947      var lowWant = wanted.toLowerCase();
9948      var hit = null;
9949      var i;
9950      for (i = 0; i < CAPABILITIES.length && !hit; i++) {
9951        if (CAPABILITIES[i].id === lowWant) hit = CAPABILITIES[i];
9952      }
9953      if (!hit && wanted.charAt(0) === "/") {
9954        var wantPath = wanted.replace(/\/+$/, "").toLowerCase();
9955        for (i = 0; i < CAPABILITIES.length && !hit; i++) {
9956          var p = CAPABILITIES[i].path ? CAPABILITIES[i].path.replace(/\/+$/, "").toLowerCase() : null;
9957          if (p && p === wantPath) hit = CAPABILITIES[i];
9958        }
9959        if (!hit) hit = { id: null, intent: "open the requested path", path: wanted, tools: [] };
9960      }
9961      if (!hit) {
9962        for (i = 0; i < CAPABILITIES.length && !hit; i++) {
9963          if (CAPABILITIES[i].id.indexOf(lowWant) !== -1 || CAPABILITIES[i].intent.toLowerCase().indexOf(lowWant) !== -1 || CAPABILITIES[i].tools.join(" ").indexOf(lowWant) !== -1) hit = CAPABILITIES[i];
9964        }
9965      }
9966      if (!hit) {
9967        return out({
9968          status: "error",
9969          action: "open a capability's page",
9970          url: location.href,
9971          reason: "'" + wanted + "' does not match any capability id, intent or path.",
9972          availableOptions: CAPABILITIES.map(function (c) { return c.id; }),
9973          nextStep: "Call this tool again with goTo set to one of availableOptions, or to a site path starting with /.",
9974          summary: "The requested capability was not recognised."
9975        });
9976      }
9977      if (!hit.path) {
9978        return out({
9979          status: "ok",
9980          action: "open a capability's page",
9981          url: location.href,
9982          capability: hit,
9983          nextStep: "This capability's tools are registered on every page - call them directly, no navigation needed.",
9984          summary: "The '" + hit.id + "' tools are available on every page, including this one."
9985        });
9986      }
9987      var destAbs = hit.path;
9988      try { destAbs = new URL(hit.path, location.origin).href; } catch (e0) {}
9989      if (location.pathname.replace(/\/+$/, "") === hit.path.replace(/\/+$/, "")) {
9990        return out({
9991          status: "ok",
9992          action: "open a capability's page",
9993          url: location.href,
9994          capability: hit,
9995          nextStep: "Already on this capability's page; its tools (" + hit.tools.join(", ") + ") are registered here - call them directly.",
9996          summary: "Already on " + hit.path + "; the capability's tools are available now."
9997        });
9998      }
9999      var payload = {
10000        status: "navigated",
10001        action: "open the page for the '" + (hit.id || wanted) + "' capability",
10002        url: destAbs,
10003        capability: hit,
10004        nextStep: "The page is loading; once it settles the snippet registers that page's toolset" + (hit.tools.length ? " (" + hit.tools.join(", ") + ")" : "") + " - call those tools there.",
10005        summary: "Navigating to " + hit.path + (hit.id ? " for the '" + hit.id + "' capability" : "") + "."
10006      };
10007      location.assign(hit.path);
10008      return out(payload);
10009    } catch (err) {
10010      return out({
10011        status: "error",
10012        action: "discover the site's WebMCP capabilities",
10013        url: location.href,
10014        reason: "The discovery tool threw: " + (err && err.message ? err.message : String(err)),
10015        nextStep: "Call this tool again with no arguments to read the capability map.",
10016        summary: "The capability map could not be produced."
10017      });
10018    }
10019  }
10020});
10021
10022  /* ---- optimizely-list-beta-programs, optimizely-register-beta-interest, optimizely-register-beta-interest-submit ---- */
10023mc.registerToolIf("/beta", {
10024  name: "optimizely-list-beta-programs",
10025  description: "Reads the Optimizely beta hub at /beta and returns the beta programmes it lists, grouped the way the page groups them: Open betas, Closed betas, and the 'Other features you can try out right now' list. Each entry is the accordion card title read from the live page. Pass expandBeta with a programme name to open that card's accordion and return its description text as well; the card is collapsed again afterwards unless it was already open. Also returns the anchor of the shared 'Register interest for betas' form and the name of the tool that fills it.",
10026  inputSchema: {
10027    type: "object",
10028    properties: {
10029      expandBeta: {
10030        type: "string",
10031        description: "Name of one listed programme whose accordion should be opened so its description can be read, matched case-insensitively against the card titles on the page at call time. An unmatched name comes back as an error listing the titles that are actually there."
10032      }
10033    },
10034    required: []
10035  },
10036  annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
10037  execute: async function (args) {
10038    trackWebMCPTool("optimizely-list-beta-programs");
10039    var a = args || {};
10040    function out(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
10041    function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
10042    function collapseRepeat(s) { return String(s || "").replace(/(.{4,}?)\1{2,}/g, "$1"); }
10043    function cleanText(el) {
10044      var t = "";
10045      (function walk(n) {
10046        if (!n) return;
10047        if (n.nodeType === 3) { t += n.nodeValue; return; }
10048        if (n.nodeType !== 1) return;
10049        if (n.getAttribute && n.getAttribute("aria-hidden") === "true") return;
10050        if (/material-symbols/.test(String(n.className || ""))) return;
10051        for (var i = 0; i < n.childNodes.length; i++) walk(n.childNodes[i]);
10052      })(el);
10053      return collapseRepeat(t.replace(/\s+/g, " ").trim()).trim();
10054    }
10055    try {
10056      try { await waitForElement("main h3 button", 10000); } catch (e) {}
10057      await sleep(300);
10058      var main = document.querySelector("main") || document.body;
10059      var sections = { openBetas: [], closedBetas: [], otherAvailableFeatures: [] };
10060      var cards = [];
10061      var current = null;
10062      var heads = main.querySelectorAll("h1, h2, h3");
10063      for (var i = 0; i < heads.length; i++) {
10064        var h = heads[i];
10065        var trigger = h.querySelector("button");
10066        if (!trigger) {
10067          var t = cleanText(h).toLowerCase();
10068          if (t.indexOf("open betas") === 0) current = "openBetas";
10069          else if (t.indexOf("closed betas") === 0) current = "closedBetas";
10070          else if (t.indexOf("other features") === 0) current = "otherAvailableFeatures";
10071          else if (t.indexOf("register interest") === 0 || t.indexOf("currently available") === 0) current = current;
10072          continue;
10073        }
10074        if (!current) continue;
10075        var name = cleanText(trigger);
10076        if (!name) continue;
10077        sections[current].push(name);
10078        cards.push({ name: name, heading: h, trigger: trigger, section: current });
10079      }
10080
10081      if (!cards.length) {
10082        return out({
10083          status: "partial",
10084          observed: false,
10085          action: "list the beta programmes on the beta hub",
10086          url: location.href,
10087          resultCount: null,
10088          openBetas: [],
10089          closedBetas: [],
10090          otherAvailableFeatures: [],
10091          reason: "No accordion cards (main h3 > button) were readable after waiting for the page to settle, so no programmes could be listed.",
10092          nextStep: "Let https://www.optimizely.com/beta finish hydrating and call this tool again.",
10093          summary: "The beta hub was open but no programme cards could be read from it."
10094        });
10095      }
10096
10097      var expanded = null;
10098      if (typeof a.expandBeta === "string" && a.expandBeta.trim()) {
10099        var want = a.expandBeta.replace(/\s+/g, " ").trim().toLowerCase();
10100        var hit = null;
10101        for (var ci = 0; ci < cards.length; ci++) {
10102          if (cards[ci].name.toLowerCase() === want) { hit = cards[ci]; break; }
10103        }
10104        if (!hit) {
10105          var partial = [];
10106          for (var cj = 0; cj < cards.length; cj++) {
10107            if (cards[cj].name.toLowerCase().indexOf(want) !== -1) partial.push(cards[cj]);
10108          }
10109          if (partial.length === 1) hit = partial[0];
10110        }
10111        if (!hit) {
10112          return out({
10113            status: "error",
10114            action: "expand a beta programme card",
10115            url: location.href,
10116            reason: "'" + a.expandBeta + "' does not match any programme card title on the page right now.",
10117            availableOptions: cards.map(function (c) { return c.name; }),
10118            nextStep: "Call this tool again with expandBeta set to one of the titles in availableOptions.",
10119            summary: "The requested programme name was not found among the cards on the beta hub."
10120          });
10121        }
10122        var wasOpen = hit.trigger.getAttribute("aria-expanded") === "true";
10123        if (!wasOpen) { try { hit.trigger.click(); } catch (e1) {} }
10124        var region = null;
10125        var deadline = Date.now() + 3500;
10126        while (Date.now() < deadline) {
10127          await sleep(150);
10128          var wrap = hit.heading.parentElement;
10129          region = wrap ? wrap.querySelector('div[role="region"]') : null;
10130          if (region && region.getAttribute("data-state") === "open" && cleanText(region)) break;
10131        }
10132        var description = region ? cleanText(region) : "";
10133        expanded = { name: hit.name, section: hit.section, description: description || null };
10134        try {
10135          hit.heading.scrollIntoView({ behavior: "smooth", block: "center" });
10136          hit.heading.style.outline = "3px solid #0037ff";
10137          setTimeout(function () { try { hit.heading.style.outline = ""; } catch (e2) {} }, 5000);
10138        } catch (e3) {}
10139        if (!wasOpen && hit.trigger.getAttribute("aria-expanded") === "true") {
10140          try { hit.trigger.click(); } catch (e4) {}
10141        }
10142      } else {
10143        try {
10144          var first = cards[0].heading;
10145          first.scrollIntoView({ behavior: "smooth", block: "center" });
10146        } catch (e5) {}
10147      }
10148
10149      var total = cards.length;
10150      var payload = {
10151        status: "ok",
10152        observed: true,
10153        action: "list the beta programmes on the beta hub",
10154        url: location.href,
10155        resultCount: total,
10156        openBetas: sections.openBetas,
10157        closedBetas: sections.closedBetas,
10158        otherAvailableFeatures: sections.otherAvailableFeatures,
10159        expanded: expanded,
10160        registerFormAnchor: "/beta/#register-interest",
10161        registerToolName: "optimizely-register-beta-interest",
10162        summary: "Read " + total + " cards from the beta hub: " + sections.openBetas.length + " open beta(s), " + sections.closedBetas.length + " closed beta(s) and " + sections.otherAvailableFeatures.length + " already-available feature(s)" + (expanded ? "; expanded '" + expanded.name + "'" + (expanded.description ? " and read its description" : " but its description text did not render") : "") + "."
10163      };
10164      if (expanded && !expanded.description) {
10165        payload.status = "partial";
10166        payload.reason = "The card '" + expanded.name + "' was toggled but its accordion body showed no readable text within 3.5 seconds.";
10167        payload.nextStep = "Call this tool again with the same expandBeta value once the page has settled.";
10168      }
10169      return out(payload);
10170    } catch (err) {
10171      return out({
10172        status: "error",
10173        action: "list the beta programmes on the beta hub",
10174        url: location.href,
10175        reason: "The read failed: " + (err && err.message ? err.message : String(err)),
10176        nextStep: "Reload https://www.optimizely.com/beta and call this tool again.",
10177        summary: "The beta programme list could not be read."
10178      });
10179    }
10180  }
10181});
10182
10183mc.registerToolIf("/beta", {
10184  name: "optimizely-register-beta-interest",
10185  description: "Fills the shared 'Register interest for betas' form on the Optimizely beta hub (
10185/beta, anchor #register-interest). Ticks the checkbox for each requested beta programme in the 'Which betas are you interested in?' group (names are matched against the checkbox labels on the page at call time), writes Full name, Work email, Company name and the optional Partner name, sets the Job role through the form's own option list, ticks any 'Which Optimizely products do you currently use?' checkboxes requested, writes the Additional information note, and sets the design-feedback opt-in checkbox (which the page defaults to ticked). All requested option names are validated against the live page before anything is written; an unknown name comes back as an error carrying availableOptions. Returns the values read back out of the form, the betas and products actually ticked, the required items still outstanding, a ready flag and the name of the companion submit tool. Nothing is sent until that submit tool is called. Note the page's static disclaimer: submitting the form itself constitutes agreement to the beta terms (/beta/beta-terms2/) - there is no separate consent checkbox.",
10186  inputSchema: {
10187    type: "object",
10188    properties: {
10189      betas: {
10190        type: "array",
10191        items: { type: "string" },
10192        description: "One or more beta programme names to tick in the 'Which betas are you interested in?' group, written as they read on the page, for example 'Outlier management in Experimentation'. Matched case-insensitively against the checkbox labels present at call time."
10193      },
10194      fullName: { type: "string", description: "Value for the Full name field." },
10195      email: { type: "string", description: "Value for the Work email field." },
10196      company: { type: "string", description: "Value for the Company name field." },
10197      partnerName: { type: "string", description: "Optional value for the 'Partner name (optional)' field." },
10198      jobRole: { type: "string", description: "Job role to choose, matched against the options the form offers at call time (for example Developer, Product Manager, Executive). An unknown value comes back with the live option list." },
10199      productsUsed: {
10200        type: "array",
10201        items: { type: "string" },
10202        description: "Optional list of names to tick in the 'Which Optimizely products do you currently use?' group, matched against the checkbox labels present at call time."
10203      },
10204      additionalInformation: { type: "string", description: "Optional free text for the 'Additional information' box, for example what the beta will be used for." },
10205      designFeedbackOptIn: { type: "boolean", description: "Whether the 'Yes, you can ask me for design feedback' checkbox should be ticked. The page defaults it to ticked; pass false to untick it." }
10206    },
10207    required: ["betas", "fullName", "email", "company"]
10208  },
10209  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
10210  execute: async function (args) {
10211    trackWebMCPTool("optimizely-register-beta-interest");
10212    var a = args || {};
10213    var ACTION = "fill the beta register-interest form";
10214    var FORM_SEL = "form.opti-form";
10215    function out(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
10216    function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
10217    function norm(s) { return String(s == null ? "" : s).replace(/\s+/g, " ").trim(); }
10218    function low(s) { return norm(s).toLowerCase(); }
10219    function labelName(l) {
10220      var t = "";
10221      (function walk(n) {
10222        if (!n) return;
10223        if (n.nodeType === 3) { t += n.nodeValue; return; }
10224        if (n.nodeType !== 1) return;
10225        if (n.getAttribute && n.getAttribute("aria-hidden") === "true") return;
10226        if (/material-symbols/.test(String(n.className || ""))) return;
10227        for (var i = 0; i < n.childNodes.length; i++) walk(n.childNodes[i]);
10228      })(l);
10229      return norm(t);
10230    }
10231    function nativeSet(el, value) {
10232      var proto = window.HTMLInputElement.prototype;
10233      if (el.tagName === "TEXTAREA") proto = window.HTMLTextAreaElement.prototype;
10234      else if (el.tagName === "SELECT") proto = window.HTMLSelectElement.prototype;
10235      var d = Object.getOwnPropertyDescriptor(proto, "value");
10236      if (d && d.set) { d.set.call(el, value); } else { el.value = value; }
10237      el.dispatchEvent(new Event("input", { bubbles: true }));
10238      el.dispatchEvent(new Event("change", { bubbles: true }));
10239    }
10240    function toList(v) {
10241      if (Array.isArray(v)) return v.map(norm).filter(Boolean);
10242      if (typeof v === "string" && v.trim()) return [norm(v)];
10243      return [];
10244    }
10245    /* The two checkbox groups share one markup pattern; each option's clickable
10246       element is the button[role="checkbox"], not the shadow input. Group
10247       membership is decided by the last group legend seen in document order. */
10248    function readGroups(form) {
10249      var res = { betas: [], products: [], feedback: null };
10250      var all = form.querySelectorAll("*");
10251      var cur = null;
10252      var seen = [];
10253      for (var i = 0; i < all.length; i++) {
10254        var el = all[i];
10255        if (!el.children.length) {
10256          var t = low(el.textContent || "");
10257          if (t.indexOf("which betas are you interested") !== -1) cur = "betas";
10258          else if (t.indexOf("which optimizely products") !== -1) cur = "products";
10259        }
10260        if (el.tagName !== "LABEL") continue;
10261        var btn = el.querySelector('button[role="checkbox"]');
10262        if (!btn || seen.indexOf(btn) !== -1) continue;
10263        seen.push(btn);
10264        var name = labelName(el);
10265        if (!name) continue;
10266        if (/design feedback/i.test(name)) { res.feedback = { name: name, btn: btn }; continue; }
10267        if (cur === "betas") res.betas.push({ name: name, btn: btn });
10268        else if (cur === "products") res.products.push({ name: name, btn: btn });
10269      }
10270      return res;
10271    }
10272    function isChecked(btn) { return btn.getAttribute("aria-checked") === "true"; }
10273    function matchEntry(list, wanted) {
10274      var w = low(wanted);
10275      var i;
10276      for (i = 0; i < list.length; i++) { if (low(list[i].name) === w) return list[i]; }
10277      var partial = [];
10278      for (i = 0; i < list.length; i++) { if (low(list[i].name).indexOf(w) !== -1) partial.push(list[i]); }
10279      return partial.length === 1 ? partial[0] : null;
10280    }
10281    function names(list) { return list.map(function (e) { return e.name; }); }
10282    function checkedNames(list) {
10283      return list.filter(function (e) { return isChecked(e.btn); }).map(function (e) { return e.name; });
10284    }
10285    async function setCheckbox(btn, desired) {
10286      if (isChecked(btn) === desired) return true;
10287      try { btn.click(); } catch (e) {}
10288      await sleep(120);
10289      return isChecked(btn) === desired;
10290    }
10291
10292    try {
10293      var form = null;
10294      try { form = await waitForElement(FORM_SEL, 12000); } catch (e0) { form = document.querySelector(FORM_SEL); }
10295      if (!form) {
10296        return out({
10297          status: "error",
10298          action: ACTION,
10299          url: location.href,
10300          reason: "No form matching " + FORM_SEL + " is present on this page.",
10301          nextStep: "Open https://www.optimizely.com/beta and call this tool again once the register-interest form has rendered.",
10302          summary: "The beta register-interest form was not found on this page."
10303        });
10304      }
10305      try {
10306        form.scrollIntoView({ behavior: "smooth", block: "center" });
10307        form.style.outline = "3px solid #0037ff";
10308        form.style.outlineOffset = "4px";
10309      } catch (e1) {}
10310      await sleep(300);
10311
10312      var groups = readGroups(form);
10313      if (!groups.betas.length) {
10314        return out({
10315          status: "error",
10316          action: ACTION,
10317          url: location.href,
10318          reason: "The 'Which betas are you interested in?' checkbox group could not be read from the form, so the requested betas cannot be matched.",
10319          nextStep: "Let the page finish hydrating and call this tool again.",
10320          summary: "The beta checkbox group was not readable, so nothing was written."
10321        });
10322      }
10323
10324      var wantBetas = toList(a.betas);
10325      var wantProducts = toList(a.productsUsed);
10326      if (!wantBetas.length) {
10327        return out({
10328          status: "error",
10329          action: ACTION,
10330          url: location.href,
10331          reason: "The betas argument was empty; at least one beta programme must be named.",
10332          availableOptions: names(groups.betas),
10333          nextStep: "Call this tool again with betas set to one or more names from availableOptions.",
10334          summary: "No beta programme was requested, so nothing was written."
10335        });
10336      }
10337
10338      /* Validate every requested option before writing anything. */
10339      var unknownBetas = [];
10340      var betaHits = [];
10341      var i;
10342      for (i = 0; i < wantBetas.length; i++) {
10343        var bHit = matchEntry(groups.betas, wantBetas[i]);
10344        if (bHit) betaHits.push(bHit); else unknownBetas.push(wantBetas[i]);
10345      }
10346      if (unknownBetas.length) {
10347        return out({
10348          status: "error",
10349          action: ACTION,
10350          url: location.href,
10351          reason: "These beta names are not among the checkboxes on the page right now: " + unknownBetas.join(", ") + ". Nothing was written to the form.",
10352          availableOptions: names(groups.betas),
10353          nextStep: "Call this tool again using names from availableOptions.",
10354          summary: "One or more requested betas do not exist on the form, so nothing was written."
10355        });
10356      }
10357      var unknownProducts = [];
10358      var productHits = [];
10359      for (i = 0; i < wantProducts.length; i++) {
10360        var pHit = matchEntry(groups.products, wantProducts[i]);
10361        if (pHit) productHits.push(pHit); else unknownProducts.push(wantProducts[i]);
10362      }
10363      if (unknownProducts.length) {
10364        return out({
10365          status: "error",
10366          action: ACTION,
10367          url: location.href,
10368          reason: "These product names are not among the 'Which Optimizely products do you currently use?' checkboxes: " + unknownProducts.join(", ") + ". Nothing was written to the form.",
10369          availableOptions: names(groups.products),
10370          nextStep: "Call this tool again using names from availableOptions, or omit productsUsed.",
10371          summary: "One or more requested products do not exist on the form, so nothing was written."
10372        });
10373      }
10374      var roleSelect = form.querySelector('select[name="job_role_specifics"]');
10375      var roleChoice = null;
10376      if (a.jobRole !== undefined && a.jobRole !== null && norm(a.jobRole) !== "") {
10377        if (!roleSelect) {
10378          return out({
10379            status: "error",
10380            action: ACTION,
10381            url: location.href,
10382            reason: "A jobRole was requested but the Job role select (select[name=\"job_role_specifics\"]) is not in the form.",
10383            nextStep: "Let the page finish hydrating and call this tool again, or omit jobRole.",
10384            summary: "The Job role control was not found, so nothing was written."
10385          });
10386        }
10387        var opts = [];
10388        for (i = 0; i < roleSelect.options.length; i++) {
10389          var lbl = norm(roleSelect.options[i].textContent) || norm(roleSelect.options[i].value);
10390          if (lbl) opts.push({ value: roleSelect.options[i].value, label: lbl });
10391        }
10392        var wantRole = low(a.jobRole);
10393        for (i = 0; i < opts.length; i++) { if (low(opts[i].label) === wantRole || low(opts[i].value) === wantRole) { roleChoice = opts[i]; break; } }
10394        if (!roleChoice) {
10395          for (i = 0; i < opts.length; i++) { if (low(opts[i].label).indexOf(wantRole) === 0) { roleChoice = opts[i]; break; } }
10396        }
10397        if (!roleChoice) {
10398          return out({
10399            status: "error",
10400            action: ACTION,
10401            url: location.href,
10402            reason: "The job role '" + a.jobRole + "' is not one of the options the form offers. Nothing was written to the form.",
10403            availableOptions: opts.map(function (o) { return o.label; }),
10404            nextStep: "Call this tool again with jobRole set to one of the values in availableOptions.",
10405            summary: "The requested job role does not exist on the form, so nothing was written."
10406          });
10407        }
10408      }
10409
10410      /* Everything validated - now write. Text fields target stable name
10411         attributes; the Radix-generated ids regenerate every load. */
10412      var notes = [];
10413      var textPlan = [
10414        { key: "fullName", sel: 'form.opti-form input[name="firstName"]', value: a.fullName, required: true, label: "Full name" },
10415        { key: "email", sel: 'form.opti-form input[name="email"]', value: a.email, required: true, label: "Work email" },
10416        { key: "company", sel: 'form.opti-form input[name="company"]', value: a.company, required: true, label: "Company name" },
10417        { key: "partnerName", sel: 'form.opti-form input[name="partner_name"]', value: a.partnerName, required: false, label: "Partner name" },
10418        { key: "additionalInformation", sel: 'form.opti-form textarea[name="Other_Additional_Information__c"]', value: a.additionalInformation, required: false, label: "Additional information" }
10419      ];
10420      var fields = {};
10421      var missing = [];
10422      for (i = 0; i < textPlan.length; i++) {
10423        var spec = textPlan[i];
10424        var el = document.querySelector(spec.sel);
10425        if (!el) {
10426          fields[spec.key] = null;
10427          if (spec.required) missing.push(spec.label);
10428          notes.push("The '" + spec.label + "' control was not found in the form.");
10429          continue;
10430        }
10431        if (spec.value !== undefined && spec.value !== null && String(spec.value) !== "") {
10432          var wanted = String(spec.value);
10433          var filled = false;
10434          try { await fillInput(spec.sel, wanted); filled = true; } catch (e2) { filled = false; }
10435          if (!filled || norm(el.value) !== norm(wanted)) nativeSet(el, wanted);
10436          await sleep(80);
10437        }
10438        fields[spec.key] = norm(el.value);
10439        if (spec.required && !fields[spec.key]) missing.push(spec.label);
10440      }
10441
10442      if (roleChoice && roleSelect) {
10443        nativeSet(roleSelect, roleChoice.value);
10444        await sleep(250);
10445        /* Radix's hidden native select listens for change (autofill support);
10446           verify the visible combobox trigger picked the value up, and drive
10447           the trigger UI as a fallback if it did not. */
10448        var trigger = form.querySelector('button[role="combobox"]');
10449        var triggerOk = trigger ? low(trigger.textContent || "").indexOf(low(roleChoice.label)) !== -1 : false;
10450        if (roleSelect.value !== roleChoice.value || (trigger && !triggerOk)) {
10451          if (trigger) {
10452            try { trigger.click(); } catch (e3) {}
10453            var opt = null;
10454            var deadline = Date.now() + 3000;
10455            while (Date.now() < deadline && !opt) {
10456              await sleep(150);
10457              var rendered = document.querySelectorAll('[role="option"]');
10458              for (var oi = 0; oi < rendered.length; oi++) {
10459                if (low(rendered[oi].textContent) === low(roleChoice.label)) { opt = rendered[oi]; break; }
10460              }
10461            }
10462            if (opt) {
10463              try { opt.dispatchEvent(new MouseEvent("mousedown", { bubbles: true })); } catch (e4) {}
10464              try { opt.click(); } catch (e5) {}
10465              await sleep(250);
10466            } else {
10467              try { trigger.click(); } catch (e6) {}
10468              notes.push("The Job role option list did not render, so the role may not be applied; check fields.jobRole.");
10469            }
10470          }
10471        }
10472        fields.jobRole = roleSelect ? norm(roleSelect.value) : null;
10473      } else {
10474        fields.jobRole = roleSelect ? norm(roleSelect.value) : null;
10475      }
10476
10477      for (i = 0; i < betaHits.length; i++) {
10478        var okB = await setCheckbox(betaHits[i].btn, true);
10479        if (!okB) notes.push("The checkbox for '" + betaHits[i].name + "' did not report aria-checked=\"true\" after clicking.");
10480      }
10481      for (i = 0; i < productHits.length; i++) {
10482        var okP = await setCheckbox(productHits[i].btn, true);
10483        if (!okP) notes.push("The checkbox for '" + productHits[i].name + "' did not report aria-checked=\"true\" after clicking.");
10484      }
10485      var feedbackState = null;
10486      if (groups.feedback) {
10487        if (typeof a.designFeedbackOptIn === "boolean") {
10488          var okF = await setCheckbox(groups.feedback.btn, a.designFeedbackOptIn);
10489          if (!okF) notes.push("The design-feedback checkbox did not settle into the requested state.");
10490        }
10491        feedbackState = isChecked(groups.feedback.btn);
10492      }
10493
10494      await sleep(300);
10495      var liveGroups = readGroups(form);
10496      var betasChecked = checkedNames(liveGroups.betas);
10497      var productsChecked = checkedNames(liveGroups.products);
10498      if (!betasChecked.length) missing.push("Which betas are you interested in");
10499      fields.betasChecked = betasChecked;
10500      fields.productsChecked = productsChecked;
10501      fields.designFeedbackOptIn = feedbackState;
10502
10503      function hiddenVal(name) {
10504        var h = form.querySelector('input[name="' + name + '"]');
10505        return h ? String(h.value || "") : null;
10506      }
10507      var ready = missing.length === 0;
10508      var payload = {
10509        status: ready && !notes.length ? "ok" : "partial",
10510        observed: true,
10511        action: ACTION,
10512        url: location.href,
10513        fields: fields,
10514        missing: missing,
10515        ready: ready,
10516        trackingKeysPresent: { pageKey: !!hiddenVal("page_key"), formKey: !!hiddenVal("form_key") },
10517        termsNote: "Submitting this form constitutes agreement to the beta terms at /beta/beta-terms2/ per the page's own disclaimer; there is no separate consent checkbox.",
10518        submitToolName: "optimizely-register-beta-interest-submit",
10519        notes: notes,
10520        summary: ""
10521      };
10522      if (ready && !notes.length) {
10523        payload.nextStep = "Review the values, then call optimizely-register-beta-interest-submit to send the registration.";
10524        payload.summary = "Filled the beta register-interest form for " + (fields.email || "the supplied contact") + " with " + betasChecked.length + " beta(s) ticked; it is ready to submit.";
10525      } else if (!ready) {
10526        payload.reason = "These required items are still outstanding: " + missing.join(", ") + ".";
10527        payload.nextStep = "Supply the missing values and call this tool again before submitting.";
10528        payload.summary = "Filled the form but " + missing.length + " required item(s) are still outstanding.";
10529      } else {
10530        payload.reason = notes.join(" ");
10531        payload.nextStep = "Check the flagged items in notes, re-run this tool if needed, then call optimizely-register-beta-interest-submit.";
10532        payload.summary = "Filled the form with " + betasChecked.length + " beta(s) ticked, but some controls could not be fully verified.";
10533      }
10534      return out(payload);
10535    } catch (err) {
10536      return out({
10537        status: "error",
10538        action: ACTION,
10539        url: location.href,
10540        reason: "The fill attempt threw an error: " + (err && err.message ? err.message : String(err)),
10541        nextStep: "Let the form finish loading and call this tool again.",
10542        summary: "Could not complete filling the beta register-interest form."
10543      });
10544    }
10545  }
10546});
10547
10548mc.registerToolIf("/beta", {
10549  name: "optimizely-register-beta-interest-submit",
10550  description: "Clicks the Submit button on the beta register-interest form that optimizely-register-beta-interest filled, waits for the page to respond, and reports what it observes: the confirmation text if the registration was accepted (including anything announced in the form's aria-live status region), any validation messages the form renders, and the URL after the click. The form submits via JavaScript, so the page normally stays put. It refuses to click when no beta checkbox is ticked or the Work email field is empty, since the submission would be pointless. This sends the registration to Optimizely - fill and review the values first.",
10551  inputSchema: {
10552    type: "object",
10553    properties: {
10554      confirm: { type: "boolean", description: "Set true to confirm the reviewed form should be sent. Defaults to true; set false to abort without clicking." }
10555    },
10556    required: []
10557  },
10558  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
10559  execute: async function (args) {
10560    trackWebMCPTool("optimizely-register-beta-interest-submit");
10561    var a = args || {};
10562    var ACTION = "submit the beta register-interest form";
10563    var FORM_SEL = "form.opti-form";
10564    function out(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
10565    function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
10566    function norm(s) { return String(s == null ? "" : s).replace(/\s+/g, " ").trim(); }
10567    function visible(el) {
10568      if (!el) return false;
10569      try { if (el.closest && el.closest('[aria-hidden="true"]')) return false; } catch (e) {}
10570      var r = el.getBoundingClientRect ? el.getBoundingClientRect() : { width: 0, height: 0 };
10571      return !!(el.offsetParent || r.width || r.height);
10572    }
10573    function leafTexts(root) {
10574      var set = {};
10575      if (!root) return set;
10576      var all = root.querySelectorAll("*");
10577      for (var i = 0; i < all.length; i++) {
10578        var el = all[i];
10579        if (el.children && el.children.length) continue;
10580        var tag = el.tagName;
10581        if (tag === "SCRIPT" || tag === "STYLE" || tag === "OPTION") continue;
10582        if (el.getAttribute && el.getAttribute("aria-hidden") === "true") continue;
10583        var t = norm(el.textContent);
10584        if (!t || t.length > 240) continue;
10585        if (!visible(el)) continue;
10586        set[t] = true;
10587      }
10588      return set;
10589    }
10590    try {
10591      if (a.confirm === false) {
10592        return out({
10593          status: "error",
10594          action: ACTION,
10595          url: location.href,
10596          validationErrors: [],
10597          confirmation: null,
10598          reason: "confirm was false, so the form was not clicked and nothing was sent.",
10599          nextStep: "Call this tool again with confirm set to true when the reviewed values should be sent.",
10600          summary: "Submission aborted at the caller's request; nothing was sent."
10601        });
10602      }
10603      var form = document.querySelector(FORM_SEL);
10604      if (!form) {
10605        return out({
10606          status: "error",
10607          action: ACTION,
10608          url: location.href,
10609          validationErrors: [],
10610          confirmation: null,
10611          reason: "No form matching " + FORM_SEL + " is present on this page, so there is nothing to submit.",
10612          nextStep: "Open /beta, fill the form with optimizely-register-beta-interest, then call this tool.",
10613          summary: "The beta register-interest form is not on this page."
10614        });
10615      }
10616      var emailEl = form.querySelector('input[name="email"]');
10617      var anyBeta = false;
10618      var boxes = form.querySelectorAll('button[role="checkbox"][aria-checked="true"]');
10619      for (var bi = 0; bi < boxes.length; bi++) {
10620        var wrap = boxes[bi].closest ? boxes[bi].closest("label") : null;
10621        var t = wrap ? norm(wrap.textContent) : "";
10622        if (t && !/design feedback/i.test(t)) { anyBeta = true; break; }
10623      }
10624      if (!anyBeta || !emailEl || !norm(emailEl.value)) {
10625        return out({
10626          status: "error",
10627          action: ACTION,
10628          url: location.href,
10629          validationErrors: [],
10630          confirmation: null,
10631          reason: !anyBeta
10632            ? "No beta checkbox is ticked, so the submission would carry no programme selection."
10633            : "The Work email field is empty.",
10634          nextStep: "Call optimizely-register-beta-interest to complete the form, then call this tool again.",
10635          summary: "Did not submit: the form is not complete."
10636        });
10637      }
10638      var btn = form.querySelector('button[type="submit"]');
10639      if (!btn) {
10640        return out({
10641          status: "error",
10642          action: ACTION,
10643          url: location.href,
10644          validationErrors: [],
10645          confirmation: null,
10646          reason: "The form's Submit button (form.opti-form button[type=\"submit\"]) was not found.",
10647          nextStep: "Let the form finish rendering and call this tool again.",
10648          summary: "Could not find the Submit button on the form."
10649        });
10650      }
10651
10652      var container = form.parentElement || document.querySelector("main") || document.body;
10653      var before = leafTexts(container);
10654      var liveRegion = document.querySelector('div[aria-live]');
10655      var liveBefore = liveRegion ? norm(liveRegion.textContent) : "";
10656      var urlBefore = location.href;
10657
10658      try { btn.scrollIntoView({ behavior: "smooth", block: "center" }); } catch (e0) {}
10659      btn.click();
10660
10661      var ERROR_RE = /(required|invalid|valid email|please\s|must\s|enter a|cannot be|is missing|fill (in|out)|try again)/i;
10662      var CONFIRM_RE = /(thank|thanks|received|success|submitted|in touch|be notified|we will|confirmation)/i;
10663      var validationErrors = [];
10664      var confirmation = null;
10665      var navigated = false;
10666      var formGone = false;
10667      var deadline = Date.now() + 15000;
10668      while (Date.now() < deadline) {
10669        await sleep(400);
10670        if (location.href !== urlBefore) { navigated = true; break; }
10671        var liveNow = liveRegion && liveRegion.isConnected ? norm(liveRegion.textContent) : "";
10672        if (liveNow && liveNow !== liveBefore) {
10673          if (ERROR_RE.test(liveNow)) validationErrors.push(liveNow);
10674          else confirmation = liveNow;
10675        }
10676        var scope = (container && container.isConnected) ? container : (document.querySelector("main") || document.body);
10677        var now = leafTexts(scope);
10678        for (var key in now) {
10679          if (!Object.prototype.hasOwnProperty.call(now, key)) continue;
10680          if (before[key]) continue;
10681          if (ERROR_RE.test(key)) {
10682            if (validationErrors.indexOf(key) === -1) validationErrors.push(key);
10683          } else if (CONFIRM_RE.test(key)) {
10684            if (!confirmation || key.length > confirmation.length) confirmation = key;
10685          }
10686        }
10687        formGone = !document.querySelector(FORM_SEL);
10688        if (formGone && !confirmation) {
10689          for (var k2 in now) {
10690            if (Object.prototype.hasOwnProperty.call(now, k2) && !before[k2] && (!confirmation || k2.length > confirmation.length)) confirmation = k2;
10691          }
10692        }
10693        if (validationErrors.length || confirmation) break;
10694      }
10695
10696      if (navigated) {
10697        return out({
10698          status: "navigated",
10699          action: ACTION,
10700          url: location.href,
10701          validationErrors: [],
10702          confirmation: null,
10703          nextStep: "The page moved after the click; read the confirmation on the page that is now loaded.",
10704          summary: "Clicked Submit and the browser navigated to " + location.href + "."
10705        });
10706      }
10707      if (validationErrors.length) {
10708        return out({
10709          status: "error",
10710          action: ACTION,
10711          url: location.href,
10712          validationErrors: validationErrors.slice(0, 15),
10713          confirmation: null,
10714          submitted: false,
10715          reason: "The form rendered validation messages instead of a confirmation.",
10716          nextStep: "Correct the flagged values with optimizely-register-beta-interest, then call this tool again.",
10717          summary: "The registration was not accepted; the form shows " + validationErrors.length + " validation message(s)."
10718        });
10719      }
10720      if (confirmation) {
10721        try { form.style.outline = ""; form.style.outlineOffset = ""; } catch (e1) {}
10722        return out({
10723          status: "ok",
10724          action: ACTION,
10725          url: location.href,
10726          validationErrors: [],
10727          confirmation: confirmation,
10728          submitted: true,
10729          summary: "Submitted the beta registration; the page responded: " + confirmation
10730        });
10731      }
10732      return out({
10733        status: "partial",
10734        action: ACTION,
10735        url: location.href,
10736        observed: false,
10737        validationErrors: [],
10738        confirmation: null,
10739        submitted: null,
10740        formStillPresent: !formGone,
10741        reason: formGone
10742          ? "The form left the page after the click but no confirmation text could be read, so the outcome wording is unknown."
10743          : "No confirmation and no validation message appeared within 15 seconds of the click, so the outcome could not be read.",
10744        nextStep: "Look at the register-interest section on the page; only resend if the form is clearly still showing unsent values, since re-clicking may register the interest twice.",
10745        summary: "Clicked Submit but could not read a confirmation or an error from the page."
10746      });
10747    } catch (err) {
10748      return out({
10749        status: "error",
10750        action: ACTION,
10751        url: location.href,
10752        validationErrors: [],
10753        confirmation: null,
10754        reason: "The submit attempt threw an error: " + (err && err.message ? err.message : 
10754String(err)),
10755        nextStep: "Check the page for a confirmation before retrying, so the interest is not registered twice.",
10756        summary: "Could not complete submitting the beta register-interest form."
10757      });
10758    }
10759  }
10760});
10761
10762  /* ---- optimizely-get-opticon-overview, optimizely-open-opticon-registration,
10763          optimizely-register-opticon-online, optimizely-register-opticon-online-submit ---- */
10764mc.registerToolIf("/field-notes/opticon**", {
10765  name: "optimizely-get-opticon-overview",
10766  description: "Reads the Camp Opticon 26 conference hub at /field-notes/opticon and returns the three registration destinations (New York, London, Online) with their dates and registration URLs, the venue and date shown on the location tab that is currently active, that tab's agenda rows (time and session title), the unique speaker cards from the speaker carousel, and the FAQ question list. Pass tab to switch the hub to another location tab (the tabs are URL-driven, so this loads ?tab=new-york, ?tab=london or ?tab=online). Pass expandFaq with a question's wording to open that FAQ accordion item and return its answer text - pricing lives in the 'What's the cost?' item. Note the accordion only holds one item open at a time, so expanding one closes whichever was open.",
10767  inputSchema: {
10768    type: "object",
10769    properties: {
10770      tab: {
10771        type: "string",
10772        description: "Location tab to show: new-york, london or online. When it differs from the active tab the tool loads the hub with that ?tab= parameter and returns status navigated; call it again on the loaded page to read that tab's venue and agenda."
10773      },
10774      expandFaq: {
10775        type: "string",
10776        description: "Wording of one FAQ question to expand and read, matched case-insensitively against the questions on the page at call time, for example 'What's the cost?'. An unmatched value comes back with the questions that are actually there."
10777      }
10778    },
10779    required: []
10780  },
10781  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
10782  execute: async function (args) {
10783    trackWebMCPTool("optimizely-get-opticon-overview");
10784    var a = args || {};
10785    var ACTION = "read the Camp Opticon hub";
10786    function out(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
10787    function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
10788    /* The hub's marquee headings repeat their text (block-level x5, and
10789       per-letter on some tabs) purely for visual effect - collapse both. */
10790    function collapseRepeat(s) {
10791      var t = String(s || "").replace(/\s+/g, " ").trim();
10792      for (var guard = 0; guard < 4; guard++) {
10793        var next = t.replace(/(.{4,}?)\1{1,}/g, "$1");
10794        if (next === t) break;
10795        t = next;
10796      }
10797      if (t.length >= 6 && t.length % 2 === 0) {
10798        var halves = true;
10799        for (var i = 0; i < t.length; i += 2) { if (t.charAt(i) !== t.charAt(i + 1)) { halves = false; break; } }
10800        if (halves) {
10801          var un = "";
10802          for (var j = 0; j < t.length; j += 2) un += t.charAt(j);
10803          t = un;
10804        }
10805      }
10806      return t.trim();
10807    }
10808    function txt(el) { return el && el.textContent ? collapseRepeat(el.textContent) : ""; }
10809    function stripIcon(s) { return String(s || "").replace(/\b(keyboard_arrow_(down|up)|arrow_forward|arrow_outward)\b/g, "").replace(/\s+/g, " ").trim(); }
10810    var DATE_RE = /\b\d{1,2}\s+(January|February|March|April|May|June|July|August|September|October|November|December)\s+\d{4}\b/i;
10811    try {
10812      try { await waitForElement('[role="tablist"] [role="tab"]', 10000); } catch (e0) {}
10813      await sleep(300);
10814
10815      var tabs = Array.prototype.slice.call(document.querySelectorAll('[role="tablist"] [role="tab"]'));
10816      var tabNames = tabs.map(function (t) { return txt(t); });
10817      var activeTabEl = null;
10818      for (var ti = 0; ti < tabs.length; ti++) {
10819        if (tabs[ti].getAttribute("data-state") === "active" || tabs[ti].getAttribute("aria-selected") === "true") { activeTabEl = tabs[ti]; break; }
10820      }
10821      var activeTab = activeTabEl ? txt(activeTabEl) : null;
10822
10823      if (typeof a.tab === "string" && a.tab.trim()) {
10824        var slug = a.tab.trim().toLowerCase().replace(/\s+/g, "-");
10825        var VALID = ["new-york", "london", "online"];
10826        if (VALID.indexOf(slug) === -1) {
10827          return out({
10828            status: "error",
10829            action: "switch the Opticon location tab",
10830            url: location.href,
10831            reason: "'" + a.tab + "' is not one of the location tabs.",
10832            availableOptions: VALID,
10833            nextStep: "Call this tool again with tab set to new-york, london or online.",
10834            summary: "The requested tab does not exist on the Opticon hub."
10835          });
10836        }
10837        var activeSlug = activeTab ? activeTab.toLowerCase().replace(/\s+/g, "-") : "";
10838        if (activeSlug !== slug) {
10839          var target = "/field-notes/opticon?tab=" + slug;
10840          var payloadNav = {
10841            status: "navigated",
10842            action: "switch the Opticon location tab to " + slug,
10843            url: location.origin + target,
10844            nextStep: "The hub is loading with the " + slug + " tab active; call optimizely-get-opticon-overview again on the loaded page to read that tab's venue, date and agenda.",
10845            summary: "Loading the Opticon hub with the " + slug + " tab active."
10846          };
10847          location.assign(target);
10848          return out(payloadNav);
10849        }
10850      }
10851
10852      /* Registration destinations: read the three bottom cards, fall back to
10853         the canonical URLs the page's own FAQ lists. */
10854      var destinations = [];
10855      var seenDest = {};
10856      function pushDest(name, dateText, href, external, note) {
10857        if (!href || seenDest[name]) return;
10858        seenDest[name] = 1;
10859        destinations.push({ destination: name, date: dateText || null, registrationUrl: href, external: !!external, note: n
10859ote });
10860      }
10861      var cards = Array.prototype.slice.call(document.querySelectorAll(".image-text-container-block > a"));
10862      for (var ci = 0; ci < cards.length; ci++) {
10863        var cardText = txt(cards[ci]);
10864        var href = cards[ci].getAttribute("href") || "";
10865        var abs = href;
10866        try { abs = new URL(href, location.origin).href; } catch (e1) {}
10867        var m = cardText.match(DATE_RE);
10868        var dateText = m ? m[0] : null;
10869        if (/new york/i.test(cardText)) pushDest("New York", dateText, abs, /events\.optimizely\.com/i.test(abs), "External Cvent registration wizard - hand the visitor this URL; the WebMCP tools cannot drive it.");
10870        else if (/london/i.test(cardText)) pushDest("London", dateText, abs, /events\.optimizely\.com/i.test(abs), "External Cvent registration wizard - hand the visitor this URL; the WebMCP tools cannot drive it.");
10871        else if (/online/i.test(cardText)) pushDest("Online", dateText, abs, false, "Native on-site form - drive it with optimizely-register-opticon-online.");
10872      }
10873      if (!destinations.length) {
10874        pushDest("New York", null, "https://events.optimizely.com/Opticon26NY", true, "External Cvent registration wizard (URL from the page's FAQ) - hand off to the visitor.");
10875        pushDest("London", null, "https://events.optimizely.com/Opticon26London", true, "External Cvent registration wizard (URL from the page's FAQ) - hand off to the visitor.");
10876        pushDest("Online", null, location.origin + "/field-notes/opticon/online-registration", false, "Native on-site form - drive it with optimizely-register-opticon-online.");
10877      }
10878
10879      /* Active tab venue, date and agenda. */
10880      var panel = document.querySelector('[role="tabpanel"][data-state="active"]');
10881      var venue = null, tabDate = null;
10882      var agenda = [];
10883      if (panel) {
10884        var vh = panel.querySelector("h2");
10885        venue = vh ? txt(vh) : null;
10886        var pm = (panel.textContent || "").match(DATE_RE);
10887        tabDate = pm ? pm[0] : null;
10888        var rows = panel.querySelectorAll("div.container.mx-auto.pb-4");
10889        for (var ri = 0; ri < rows.length && agenda.length < 30; ri++) {
10890          var timeEl = rows[ri].querySelector("p.font-body-bold");
10891          var titleEl = rows[ri].querySelector("h3");
10892          var time = timeEl ? txt(timeEl) : "";
10893          var title = titleEl ? txt(titleEl) : "";
10894          if (time || title) agenda.push({ time: time || null, session: title || null });
10895        }
10896      }
10897
10898      /* Speakers: the carousel duplicates its slide set - dedupe by name and
10899         read textContent, because off-screen duplicates are inert. */
10900      var speakers = [];
10901      var seenSpeaker = {};
10902      var slides = document.querySelectorAll("[data-carousel-item]");
10903      for (var si = 0; si < slides.length; si++) {
10904        var nameEl = slides[si].querySelector("h3");
10905        if (!nameEl) continue;
10906        var sname = collapseRepeat(nameEl.textContent || "");
10907        if (!sname || seenSpeaker[sname]) continue;
10908        seenSpeaker[sname] = 1;
10909        var roleEl = nameEl.nextElementSibling;
10910        var role = roleEl ? collapseRepeat(roleEl.textContent || "") : "";
10911        speakers.push({ name: sname, roleAndCompany: role || null });
10912      }
10913
10914      /* FAQ accordion (single-open Radix accordion). */
10915      var faqTriggers = [];
10916      var allExpandables = document.querySelectorAll("button[aria-expanded]");
10917      for (var fi = 0; fi < allExpandables.length; fi++) {
10918        var q = stripIcon(collapseRepeat(allExpandables[fi].textContent || ""));
10919        if (!q || q.length > 160) continue;
10920        if (allExpandables[fi].getAttribute("role") === "tab") continue;
10921        faqTriggers.push({ question: q, btn: allExpandables[fi] });
10922      }
10923      var faqQuestions = faqTriggers.map(function (f) { return f.question; });
10924      var expandedFaq = null;
10925      if (typeof a.expandFaq === "string" && a.expandFaq.trim()) {
10926        var wantQ = a.expandFaq.replace(/\s+/g, " ").trim().toLowerCase();
10927        var hitQ = null;
10928        for (var qi = 0; qi < faqTriggers.length; qi++) {
10929          if (faqTriggers[qi].question.toLowerCase() === wantQ) { hitQ = faqTriggers[qi]; break; }
10930        }
10931        if (!hitQ) {
10932          for (var qj = 0; qj < faqTriggers.length; qj++) {
10933            if (faqTriggers[qj].question.toLowerCase().indexOf(wantQ) !== -1) { hitQ = faqTriggers[qj]; break; }
10934          }
10935        }
10936        if (!hitQ) {
10937          return out({
10938            status: "error",
10939            action: "expand an Opticon FAQ item",
10940            url: location.href,
10941            reason: "'" + a.expandFaq + "' does not match any FAQ question on this page.",
10942            availableOptions: faqQuestions,
10943            nextStep: "Call this tool again with expandFaq set to one of the questions in availableOptions.",
10944            summary: "The requested FAQ question was not found on the Opticon hub."
10945          });
10946        }
10947        if (hitQ.btn.getAttribute("aria-expanded") !== "true") { try { hitQ.btn.click(); } catch (e2) {} }
10948        var body = null;
10949        var deadline = Date.now() + 3500;
10950        while (Date.now() < deadline) {
10951          await sleep(150);
10952          var cid = hitQ.btn.getAttribute("aria-controls");
10953          body = cid ? document.getElementById(cid) : null;
10954          if (body && collapseRepeat(body.textContent || "")) break;
10955        }
10956        expandedFaq = {
10957          question: hitQ.question,
10958          answer: body ? collapseRepeat(body.textContent || "").slice(0, 2500) : null,
10959          note: "The accordion holds one item open at a time, so this item is now the open one."
10960        };
10961        try { hitQ.btn.scrollIntoView({ behavior: "smooth", block: "center" }); } catch (e3) {}
10962      }
10963
10964      var observed = destinations.length > 0 || !!venue || speakers.length > 0;
10965      var payload = {
10966        status: observed ? "ok" : "partial",
10967        observed: observed,
10968        action: ACTION,
10969        url: location.href,
10970        locationTabs: tabNames,
10971        activeTab: activeTab,
10972        destinations: destinations,
10973        activeTabVenue: venue,
10974        activeTabDate: tabDate,
10975        agenda: agenda,
10976        speakers: speakers,
10977        faqQuestions: faqQuestions,
10978        expandedFaq: expandedFaq,
10979        onlineRegistrationToolName: "optimizely-register-opticon-online",
10980        openRegistrationToolName: "optimizely-open-opticon-registration",
10981        summary: ""
10982      };
10983      if (observed) {
10984        payload.summary = "Read the Opticon hub: " + destinations.length + " registration destination(s)" +
10985          (activeTab ? ", active tab " + activeTab : "") +
10986          (venue ? " (" + venue + (tabDate ? ", " + tabDate : "") + ")" : "") +
10987          ", " + agenda.length + " agenda row(s), " + speakers.length + " speaker(s), " + faqQuestions.length + " FAQ question(s)" +
10988          (expandedFaq ? "; expanded '" + expandedFaq.question + "'" : "") + ".";
10989      } else {
10990        payload.reason = "Neither the destination cards, the location tabs, nor the speaker carousel could be read after the page settled.";
10991        payload.nextStep = "Let https://www.optimizely.com/field-notes/opticon finish hydrating and call this tool again.";
10992        payload.summary = "The Opticon hub was open but its content could not be read yet.";
10993      }
10994      return out(payload);
10995    } catch (err) {
10996      return out({
10997        status: "error",
10998        action: ACTION,
10999        url: location.href,
11000        reason: "The read failed: " + (err && err.message ? err.message : String(err)),
11001        nextStep: "Reload the Opticon hub and call this tool again.",
11002        summary: "The Opticon hub could not be read."
11003      });
11004    }
11005  }
11006});
11007
11008mc.registerToolIf("/field-notes/opticon**", {
11009  name: "optimizely-open-opticon-registration",
11010  description: "Opens the registration flow for one Opticon 26 destination. For Online it loads the native registration page at /field-notes/opticon/online-registration, which the companion tool optimizely-register-opticon-online can then fill. For New York and L
11010ondon it loads the external Cvent registration wizard on events.optimizely.com - that wizard is a third-party multi-step form the WebMCP tools cannot drive, so the visitor completes it themselves; the tool says so in its reply. The destination URLs are read from the links on the live page where possible.",
11011  inputSchema: {
11012    type: "object",
11013    properties: {
11014      destination: {
11015        type: "string",
11016        description: "Which registration to open: online, new-york or london."
11017      }
11018    },
11019    required: ["destination"]
11020  },
11021  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
11022  execute: async function (args) {
11023    trackWebMCPTool("optimizely-open-opticon-registration");
11024    var a = args || {};
11025    function out(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
11026    try {
11027      var want = String(a.destination || "").trim().toLowerCase().replace(/\s+/g, "-");
11028      if (want === "ny") want = "new-york";
11029      var VALID = ["online", "new-york", "london"];
11030      if (VALID.indexOf(want) === -1) {
11031        return out({
11032          status: "error",
11033          action: "open an Opticon registration",
11034          url: location.href,
11035          reason: "'" + a.destination + "' is not an Opticon destination.",
11036          availableOptions: VALID,
11037          nextStep: "Call this tool again with destination set to online, new-york or london.",
11038          summary: "The requested destination does not exist."
11039        });
11040      }
11041
11042      if (want === "online") {
11043        var onlineHref = "/field-notes/opticon/online-registration";
11044        var onlineLink = document.querySelector('a[href*="/opticon/online-registration"]');
11045        if (onlineLink) onlineHref = onlineLink.getAttribute("href") || onlineHref;
11046        var onlineAbs = onlineHref;
11047        try { onlineAbs = new URL(onlineHref, location.origin).href; } catch (e0) {}
11048        if (location.pathname.replace(/\/+$/, "") === "/field-notes/opticon/online-registration") {
11049          return out({
11050            status: "ok",
11051            action: "open the Opticon Online registration",
11052            url: location.href,
11053            observed: true,
11054            nextStep: "The Online registration page is already open; call optimizely-register-opticon-online to fill the form.",
11055            summary: "Already on the Opticon Online registration page."
11056          });
11057        }
11058        var payloadOnline = {
11059          status: "navigated",
11060          action: "open the Opticon Online registration",
11061          url: onlineAbs,
11062          fillToolName: "optimizely-register-opticon-online",
11063          nextStep: "The Online registration page is loading; call optimizely-register-opticon-online on it to fill the form.",
11064          summary: "Opening the Opticon Online registration page."
11065        };
11066        location.assign(onlineHref);
11067        return out(payloadOnline);
11068      }
11069
11070      var FALLBACK = { "new-york": "https://events.optimizely.com/Opticon26NY", "london": "https://events.optimizely.com/Opticon26London" };
11071      var externalHref = null;
11072      var anchors = document.querySelectorAll('a[href*="events.optimizely.com"]');
11073      var token = want === "new-york" ? /opticon26ny/i : /opticon26london/i;
11074      for (var i = 0; i < anchors.length; i++) {
11075        var h = anchors[i].getAttribute("href") || "";
11076        if (token.test(h)) { externalHref = h; break; }
11077      }
11078      if (!externalHref) externalHref = FALLBACK[want];
11079      var payloadExt = {
11080        status: "navigated",
11081        action: "open the Opticon " + (want === "new-york" ? "New York" : "London") + " registration",
11082        url: externalHref,
11083        external: true,
11084        handOff: true,
11085        reason: "This destination registers through an external Cvent wizard on events.optimizely.com - a third-party multi-step form the WebMCP tools cannot drive.",
11086        nextStep: "The Cvent registration wizard is loading; the visitor completes it themselves. No WebMCP tool reads or fills that page.",
11087        summary: "Opening the external Cvent registration wizard for Opticon " + (want === "new-york" ? "New York" : "London") + "; the visitor takes it from there."
11088      };
11089      location.assign(externalHref);
11090      return out(payloadExt);
11091    } catch (err) {
11092      return out({
11093        status: "error",
11094        action: "open an Opticon registration",
11095        url: location.href,
11096        reason: "The tool failed while opening the destination: " + (err && err.message ? err.message : 
11096String(err)),
11097        nextStep: "Call optimizely-get-opticon-overview to re-read the destinations, then try again.",
11098        summary: "The registration destination could not be opened."
11099      });
11100    }
11101  }
11102});
11103
11104mc.registerToolIf("/field-notes/opticon**", {
11105  name: "optimizely-register-opticon-online",
11106  description: "Fills the 'Get your pass for Opticon Online' registration form (the native form on /field-notes/opticon/online-registration, also mounted in the hub's Online tab). Writes First name, Last name, Work email, Company and Job title into the form's named inputs and resolves the Country through the form's own type-ahead combobox, committing the choice and verifying it against the hidden country input the form actually submits. If the form is not on the current page it loads the online-registration page instead and says so. Returns the values read back out of the form, any per-field validation messages the form is already showing, the required items still outstanding, a ready flag and the name of the companion submit tool. Nothing is sent until that submit tool is called; the page has no consent checkbox, only a static privacy notice.",
11107  inputSchema: {
11108    type: "object",
11109    properties: {
11110      firstName: { type: "string", description: "Value for the First name field." },
11111      lastName: { type: "string", description: "Value for the Last name field." },
11112      email: { type: "string", description: "Value for the Work email field. The form's own guidance asks for a work address rather than a personal one." },
11113      company: { type: "string", description: "Value for the Company field." },
11114      jobTitle: { type: "string", description: "Value for the Job title field, for example 'Marketing Manager'." },
11115      country: { type: "string", description: "Country name to resolve in the Country type-ahead, written as a full country name, for example 'United States' or 'Sweden'. The tool types it, commits the highlighted match and verifies the value the form will submit; when nothing commits it reports that back." }
11116    },
11117    required: ["firstName", "lastName", "email", "company"]
11118  },
11119  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
11120  execute: async function (args) {
11121    trackWebMCPTool("optimizely-register-opticon-online");
11122    var a = args || {};
11123    var ACTION = "fill the Opticon Online registration form";
11124    var FORM_SEL = "form.opti-form";
11125    function out(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
11126    function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
11127    function norm(s) { return String(s == null ? "" : s).replace(/\s+/g, " ").trim(); }
11128    function nativeSet(el, value) {
11129      var proto = el.tagName === "TEXTAREA" ? window.HTMLTextAreaElement.prototype : window.HTMLInputElement.prototype;
11130      var d = Object.getOwnPropertyDescriptor(proto, "value");
11131      if (d && d.set) { d.set.call(el, value); } else { el.value = value; }
11132      el.dispatchEvent(new Event("input", { bubbles: true }));
11133      el.dispatchEvent(new Event("change", { bubbles: true }));
11134    }
11135    function fieldError(input) {
11136      if (!input) return null;
11137      var id = input.getAttribute("aria-describedby");
11138      var span = id ? document.getElementById(id) : null;
11139      if (!span || (span.classList && span.classList.contains("hidden"))) return null;
11140      var t = norm(span.textContent).replace(/ /g, "").trim();
11141      return t || null;
11142    }
11143    function key(el, type, keyName, keyCode) {
11144      try {
11145        el.dispatchEvent(new KeyboardEvent(type, { key: keyName, code: keyName, keyCode: keyCode, which: keyCode, bubbles: true, cancelable: true }));
11146      } catch (e) {}
11147    }
11148    try {
11149      var form = document.querySelector(FORM_SEL);
11150      if (!form) { try { form = await waitForElement(FORM_SEL, 6000); } catch (e0) { form = null; } }
11151      if (!form) {
11152        var dest = "/field-notes/opticon/online-registration";
11153        var payloadNav = {
11154          status: "navigated",
11155          action: ACTION,
11156          url: location.origin + dest,
11157          reason: "The registration form is not mounted on this page (on the hub it only exists inside the Online tab), so the dedicated registration page is being loaded instead.",
11158          nextStep: "Call optimizely-register-opticon-online again once /field-notes/opticon/online-registration has loaded.",
11159          summary: "Loading the Opticon Online registration page, where the form lives."
11160        };
11161        location.assign(dest);
11162        return out(payloadNav);
11163      }
11164
11165      try {
11166        form.scrollIntoView({ behavior: "smooth", block: "center" });
11167        form.style.outline = "3px solid #0037ff";
11168        form.style.outlineOffset = "4px";
11169      } catch (e1) {}
11170      await sleep(300);
11171
11172      /* Text inputs: the name attributes are the only stable handles - the
11173         Radix ids regenerate on every render. */
11174      var plan = [
11175        { key: "firstName", label: "First name", sel: 'form.opti-form input[name="firstName"]', value: a.firstName, required: true },
11176        { key: "lastName", label: "Last name", sel: 'form.opti-form input[name="lastName"]', value: a.lastName, required: true },
11177        { key: "email", label: "Work email", sel: 'form.opti-form input[name="email"]', value: a.email, required: true },
11178        { key: "company", label: "Company", sel: 'form.opti-form input[name="company"]', value: a.company, required: true },
11179        { key: "jobTitle", label: "Job title", sel: 'form.opti-form input[name="title"]', value: a.jobTitle, required: false }
11180      ];
11181      var fields = {};
11182      var missing = [];
11183      var validationErrors = [];
11184      var notes = [];
11185      for (var i = 0; i < plan.length; i++) {
11186        var spec = plan[i];
11187        var el = document.querySelector(spec.sel);
11188        if (!el) {
11189          fields[spec.key] = null;
11190          if (spec.required) missing.push(spec.label);
11191          notes.push("The '" + spec.label + "' control was not found in the form.");
11192          continue;
11193        }
11194        if (spec.value !== undefined && spec.value !== null && String(spec.value) !== "") {
11195          var wanted = String(spec.value);
11196          var ok = false;
11197          try { await fillInput(spec.sel, wanted); ok = true; } catch (e2) { ok = false; }
11198          if (!ok || norm(el.value) !== norm(wanted)) nativeSet(el, wanted);
11199          await sleep(80);
11200        }
11201        fields[spec.key] = norm(el.value);
11202        if (spec.required && !fields[spec.key]) missing.push(spec.label);
11203        var fe = fieldError(el);
11204        if (fe) validationErrors.push({ field: spec.label, message: fe });
11205      }
11206
11207      /* Country: a virtualized type-ahead. Type to filter, commit with
11208         ArrowDown+Enter (or a rendered option), then trust only the hidden
11209         input the form submits. */
11210      var countryInfo = { requested: a.country ? norm(a.country) : null, applied: null, committed: false };
11211      var countryHiddenSel = 'form.opti-form input[type="hidden"][name="country"]';
11212      if (a.country !== undefined && a.country !== null && norm(a.country) !== "") {
11213        var cInput = form.querySelector("#country-input") || document.querySelector("#country-input");
11214        if (!cInput) {
11215          notes.push("The Country combobox (#country-input) was not found in the form.");
11216        } else {
11217          var wrap = cInput.closest ? cInput.closest(".optiformsselectionelement") : null;
11218          try { (wrap || cInput).scrollIntoView({ behavior: "smooth", block: "center" }); } catch (e3) {}
11219          try { if (wrap) wrap.click(); } catch (e4) {}
11220          try { cInput.focus(); } catch (e5) {}
11221          try { cInput.click(); } catch (e6) {}
11222          await sleep(250);
11223          nativeSet(cInput, norm(a.country));
11224          key(cInput, "keyup", "a", 65);
11225          var menu = document.getElementById("country-menu");
11226          var deadlineC = Date.now() + 3000;
11227          while (Date.now() < deadlineC) {
11228            await sleep(150);
11229            if (cInput.getAttribute("aria-expanded") === "true") break;
11230            if (menu && menu.classList && !menu.classList.contains("hidden")) break;
11231          }
11232          var option = null;
11233          var rendered = document.querySelectorAll('#country-menu [role="option"]');
11234          for (var oi = 0; oi < rendered.length; oi++) {
11235            if (norm(rendered[oi].textContent).toLowerCase() === norm(a.country).toLowerCase()) { option = rendered[oi]; break; }
11236          }
11237          if (!option && rendered.length === 1) option = rendered[0];
11238          if (option) {
11239            try { option.dispatchEvent(new MouseEvent("mousedown", { bubbles: true })); } catch (e7) {}
11240            try { option.click(); } catch (e8) {}
11241          } else {
11242            key(cInput, "keydown", "ArrowDown", 40);
11243            await sleep(150);
11244            key(cInput, "keydown", "Enter", 13);
11245          }
11246          await sleep(400);
11247          var cHidden = document.querySelector(countryHiddenSel);
11248          countryInfo.applied = cHidden ? norm(cHidden.value) : null;
11249          countryInfo.committed = !!countryInfo.applied;
11250          if (!countryInfo.committed) {
11251            notes.push("The country '" + norm(a.country) + "' did not commit into the form's hidden country input; the value the form would submit is still empty. Try the full country name exactly as commonly written (for example '
11251United States').");
11252          } else if (countryInfo.applied.toLowerCase() !== norm(a.country).toLowerCase()) {
11253            notes.push("The country combobox committed '" + countryInfo.applied + "' for the requested '" + norm(a.country) + "'.");
11254          }
11255        }
11256      } else {
11257        var cHidden0 = document.querySelector(countryHiddenSel);
11258        countryInfo.applied = cHidden0 ? norm(cHidden0.value) : null;
11259        countryInfo.committed = !!countryInfo.applied;
11260      }
11261      fields.country = countryInfo.applied;
11262
11263      var ready = missing.length === 0 && validationErrors.length === 0;
11264      var payload = {
11265        status: ready && !notes.length ? "ok" : "partial",
11266        observed: true,
11267        action: ACTION,
11268        url: location.href,
11269        fields: fields,
11270        country: countryInfo,
11271        missing: missing,
11272        validationErrors: validationErrors,
11273        ready: ready,
11274        consentNote: "The form carries no consent checkbox - only a static notice linking the privacy notice at /legal/privacy-notice/.",
11275        submitToolName: "optimizely-register-opticon-online-submit",
11276        notes: notes,
11277        summary: ""
11278      };
11279      if (ready && !notes.length) {
11280        payload.nextStep = "Review the values, then call optimizely-register-opticon-online-submit to send the registration.";
11281        payload.summary = "Filled the Opticon Online registration form for " + (fields.email || "the supplied contact") + "; it is ready to submit.";
11282      } else if (missing.length || validationErrors.length) {
11283        payload.reason = (missing.length ? "Required fields still empty: " + missing.join(", ") + ". " : "") +
11284          (validationErrors.length ? "The form is showing " + validationErrors.length + " validation message(s)." : "");
11285        payload.nextStep = "Supply or correct the listed values and call this tool again before submitting.";
11286        payload.summary = "Filled the form, but it is not ready to submit yet.";
11287      } else {
11288        payload.reason = notes.join(" ");
11289        payload.nextStep = "Check the flagged items, re-run this tool if needed, then call optimizely-register-opticon-online-submit.";
11290        payload.summary = "Filled the form, but some controls could not be fully verified.";
11291      }
11292      return out(payload);
11293    } catch (err) {
11294      return out({
11295        status: "error",
11296        action: ACTION,
11297        url: location.href,
11298        reason: "The fill attempt threw an error: " + (err && err.message ? err.message : String(err)),
11299        nextStep: "Let the form finish loading and call this tool again.",
11300        summary: "Could not complete filling the Opticon Online registration form."
11301      });
11302    }
11303  }
11304});
11305
11306mc.registerToolIf("/field-notes/opticon**", {
11307  name: "optimizely-register-opticon-online-submit",
11308  description: "Clicks the Submit button on the Opticon Online registration form that optimizely-register-opticon-online filled, waits for the form to respond, and reports the outcome. Success is detected by the form's own success panel (.form-success-page) appearing or the form fieldset leaving the page - the thank-you content on this form can legitimately be empty, so the panel's presence counts as confirmation even without text. Failures are read from the per-field validation spans the form renders. It refuses to click while any required field is empty. This sends the registration to Optimizely - fill and review the values first.",
11309  inputSchema: {
11310    type: "object",
11311    properties: {
11312      confirm: { type: "boolean", description: "Set true to confirm the reviewed form should be sent. Defaults to true; set false to abort without clicking." }
11313    },
11314    required: []
11315  },
11316  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
11317  execute: async function (args) {
11318    trackWebMCPTool("optimizely-register-opticon-online-submit");
11319    var a = args || {};
11320    var ACTION = "submit the Opticon Online registration form";
11321    var FORM_SEL = "form.opti-form";
11322    function out(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
11323    function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
11324    function norm(s) { return String(s == null ? "" : s).replace(/\s+/g, " ").trim(); }
11325    function readFieldErrors(form) {
11326      var errs = [];
11327      if (!form || !form.isConnected) return errs;
11328      var inputs = form.querySelectorAll("input:not([type=hidden])");
11329      for (var i = 0; i < inputs.length; i++) {
11330        var id = inputs[i].getAttribute("aria-describedby");
11331        var span = id ? document.getElementById(id) : null;
11332        if (!span || (span.classList && span.classList.contains("hidden"))) continue;
11333        var t = norm(span.textContent).replace(/ /g, "").trim();
11334        if (!t) continue;
11335        var labEl = null;
11336        var inputId = inputs[i].getAttribute("id");
11337        if (inputId) { try { labEl = document.querySelector('label[for="' + inputId + '"]'); } catch (e) { labEl = null; } }
11338        errs.push({ field: labEl ? norm(labEl.textContent) : (inputs[i].getAttribute("name") || inputs[i].getAttribute("aria-label") || "field"), message: t });
11339      }
11340      return errs;
11341    }
11342    try {
11343      if (a.confirm === false) {
11344        return out({
11345          status: "error",
11346          action: ACTION,
11347          url: location.href,
11348          validationErrors: [],
11349          confirmation: null,
11350          reason: "confirm was false, so the form was not clicked and nothing was sent.",
11351          nextStep: "Call this tool again with confirm set to true when the reviewed values should be sent.",
11352          summary: "Submission aborted at the caller's request; nothing was sent."
11353        });
11354      }
11355      var form = document.querySelector(FORM_SEL);
11356      if (!form) {
11357        return out({
11358          status: "error",
11359          action: ACTION,
11360          url: location.href,
11361          validationErrors: [],
11362          confirmation: null,
11363          reason: "No form matching " + FORM_SEL + " is on this page, so there is nothing to submit.",
11364          nextStep: "Open /field-notes/opticon/online-registration, fill the form with optimizely-register-opticon-online, then call this tool.",
11365          summary: "The Opticon Online registration form is not on this page."
11366        });
11367      }
11368      var required = [
11369        { label: "First name", sel: 'input[name="firstName"]' },
11370        { label: "Last name", sel: 'input[name="lastName"]' },
11371        { label: "Work email", sel: 'input[name="email"]' },
11372        { label: "Company", sel: 'input[name="company"]' }
11373      ];
11374      var empty = [];
11375      for (var ri = 0; ri < required.length; ri++) {
11376        var rEl = form.querySelector(required[ri].sel);
11377        if (!rEl || !norm(rEl.value)) empty.push(required[ri].label);
11378      }
11379      if (empty.length) {
11380        return out({
11381          status: "error",
11382          action: ACTION,
11383          url: location.href,
11384          validationErrors: [],
11385          confirmation: null,
11386          reason: "These fields are still empty: " + empty.join(", ") + ".",
11387          nextStep: "Call optimizely-register-opticon-online to complete the form, then call this tool again.",
11388          summary: "Did not submit: the form is not complete."
11389        });
11390      }
11391      var btn = form.querySelector('.optiformssubmitelement button[type="submit"]') || form.querySelector('button[type="submit"]');
11392      if (!btn) {
11393        return out({
11394          status: "error",
11395          action: ACTION,
11396          url: location.href,
11397          validationErrors: [],
11398          confirmation: null,
11399          reason: "The form's Submit button was not found.",
11400          nextStep: "Let the form finish rendering and call this tool again.",
11401          summary: "Could not find the Submit button on the form."
11402        });
11403      }
11404
11405      var urlBefore = location.href;
11406      try { btn.scrollIntoView({ behavior: "smooth", block: "center" }); } catch (e0) {}
11407      btn.click();
11408
11409      var confirmation = null;
11410      var successPanel = false;
11411      var formGone = false;
11412      var errs = [];
11413      var navigated = false;
11414      var deadline = Date.now() + 18000;
11415      while (Date.now() < deadline) {
11416        await sleep(400);
11417        if (location.href !== urlBefore) { navigated = true; break; }
11418        var panel = document.querySelector(".form-success-page, .form-confirmation");
11419        if (panel) {
11420          successPanel = true;
11421          confirmation = norm(panel.textContent) || null;
11422          break;
11423        }
11424        formGone = !document.querySelector(FORM_SEL + " fieldset");
11425        if (formGone) break;
11426        var liveForm = document.querySelector(FORM_SEL);
11427        errs = readFieldErrors(liveForm);
11428        if (errs.length) break;
11429        /* the button flips to a busy label while the POST is in flight */
11430      }
11431
11432      if (navigated) {
11433        return out({
11434          status: "navigated",
11435          action: ACTION,
11436          url: location.href,
11437          validationErrors: [],
11438          confirmation: null,
11439          nextStep: "The page moved after the click; read the confirmation on the page that is now loaded.",
11440          summary: "Clicked Submit and the browser navigated to " + location.href + "."
11441        });
11442      }
11443      if (errs.length) {
11444        return out({
11445          status: "error",
11446          action: ACTION,
11447          url: location.href,
11448          validationErrors: errs,
11449          confirmation: null,
11450          submitted: false,
11451          reason: "The form rendered validation messages on " + errs.length + " field(s) instead of a confirmation.",
11452          nextStep: "Correct the flagged values with optimizely-register-opticon-online, then call this tool again.",
11453          summary: "The registration was not accepted; the form shows validation messages."
11454        });
11455      }
11456      if (successPanel || formGone) {
11457        return out({
11458          status: "ok",
11459          action: ACTION,
11460          url: location.href,
11461          validationErrors: [],
11462          confirmation: confirmation,
11463          submitted: true,
11464          confirmationNote: confirmation ? null : "The form's success state appeared without visible thank-you text, which is how this form is configured - the submission went through.",
11465          summary: confirmation
11466            ? "Submitted the Opticon Online registration; the page responded: " + confirmation
11467            : "Submitted the Opticon Online registration; the form's success state replaced the form (its thank-you content is empty by design)."
11468        });
11469      }
11470      return out({
11471        status: "partial",
11472        action: ACTION,
11473        url: location.href,
11474        observed: false,
11475        validationErrors: [],
11476        confirmation: null,
11477        submitted: null,
11478        reason: "No success panel, no validation message and no form removal was observed within 18 seconds of the click, so the outcome could not be read.",
11479        nextStep: "Look at the form on the page; only resend if it clearly still shows the unsent values, since re-clicking may register the visitor twice.",
11480        summary: "Clicked Submit but could not read a confirmation or an error from the page."
11481      });
11482    } catch (err) {
11483      return out({
11484        status: "error",
11485        action: ACTION,
11486        url: location.href,
11487        validationErrors: [],
11488        confirmation: null,
11489        reason: "The submit attempt threw an error: " + (err && err.message ? err.message : 
11489String(err)),
11490        nextStep: "Check the page for a confirmation before retrying, so the registration is not sent twice.",
11491        summary: "Could not complete submitting the Opticon Online registration form."
11492      });
11493    }
11494  }
11495});
11496})();
11497}, "dependencies": []}], "id": "4598093977485312", "name": "Optimizely WebMCP Tools", "commitId": "5343963954020352", "groupId": null, "holdback": null, "activation": {}, "integrationSettings": {"google_analytics_4": {}, "5196871765852160": {}}, "integrationStringVersion": 2, "experiments": [{"audienceIds": null, "audienceName": "Everyone else", "changes": null, "id": "5309577053339648", "integrationSettings": null, "variations": [{"id": "5481644314853376", "name": "Control", "actions": [{"viewId": "6092126671339520", "changes": []}]}, {"id": "6314365995974656", "name": "Treatment - WebMCP", "actions": [{"viewId": "6092126671339520", "changes": [{"id": "7c82e291-1414-43d2-b0f1-9572cfb48648", "type": "custom_code", "value": function($){/* WebMCP tools for optimizely - generated by the Opal WebMCP Site Analyzer */
11498(function () {
11499  'use strict';
11500  var _mcRaw = (typeof document !== 'undefined' && document.modelContext) ||
11501               (typeof navigator !== 'undefined' && navigator.modelContext);
11502  if (!_mcRaw || typeof _mcRaw.registerTool !== 'function') {
11503    console.warn('[WebMCP] No modelContext available; tools not registered.');
11504    return;
11505  }
11506
11507  function trackWebMCPTool(name) {
11508    try {
11509      window.optimizely = window.optimizely || [];
11510      window.optimizely.push({
11511        type: 'event', eventName: 'WebMCPTool',
11512        properties: { WebMCPTool: name || 'unknown' }
11513      });
11514    } catch (e) { /* tracking must never break a tool */ }
11515  }
11516
11517  function _wrapHandler(fn, toolName) {
11518    return function () { trackWebMCPTool(toolName); return fn.apply(this, arguments); };
11519  }
11520
11521  /* Wrap a descriptor's handlers for tracking exactly once — descriptors are
11522     re-registered across SPA route changes and must not be double-wrapped. */
11523  function _wrapDescriptor(descriptor) {
11524    if (!descriptor || typeof descriptor !== 'object' || descriptor.__webmcpWrapped) {
11525      return descriptor;
11526    }
11527    var _name = descriptor.name;
11528    Object.keys(descriptor).forEach(function (k) {
11529      if (typeof descriptor[k] === 'function') {
11530        descriptor[k] = _wrapHandler(descriptor[k], _name);
11531      }
11532    });
11533    try { descriptor.__webmcpWrapped = true; } catch (e) { /* frozen — already safe */ }
11534    return descriptor;
11535  }
11536
11537  /* "/x/**" matches "/x" itself as well as anything below it; "*" stays
11538     within one path segment. The site strips trailing slashes, so the bare
11539     base path is the normal case for section hubs. */
11540  function _pathMatches(pattern) {
11541    if (!pattern || pattern === '/**') { return true; }
11542    var path = location.pathname.replace(/\/+$/, '') || '/';
11543    var pat  = String(pattern).replace(/\/+$/, '') || '/';
11544    if (pat === '/') { return path === '/'; }
11545    var rx = pat
11546      .replace(/[.+?^${}()|[\]\\]/g, '\\$&')
11547      .replace(/\/\*\*/g, '\u0000')
11548      .replace(/\*\*/g, '\u0001')
11549      .replace(/\*/g, '[^/]*')
11550      .replace(/\u0000/g, '(?:\\/.*)?')
11551      .replace(/\u0001/g, '.*');
11552    try { return new RegExp('^' + rx + '$').test(path); } catch (e) { return true; }
11553  }
11554
11555  /* Conditional registrations are reconciled against the live URL on every
11556     route change - including SPA pushState/replaceState navigations that
11557     never reload the document - so one injection per document covers every
11558     path the visitor (or an agent) reaches client-side. Where the host
11559     model-context offers no way to unregister, tools that stop matching
11560     simply stay registered; each tool validates the live page itself and
11561     reports a clear error payload when called on the wrong one. */
11562  var _conditional = [];
11563
11564  function _tryUnregister(entry) {
11565    if (!entry.active) { return; }
11566    entry.active = false;
11567    try {
11568      if (entry.handle && typeof entry.handle.unregister === 'function') {
11569        entry.handle.unregister();
11570        entry.handle = null;
11571        return;
11572      }
11573    } catch (e) { /* fall through to unregisterTool */ }
11574    entry.handle = null;
11575    try {
11576      if (typeof _mcRaw.unregisterTool === 'function') {
11577        _mcRaw.unregisterTool(entry.descriptor.name);
11578      }
11579    } catch (e) { /* host cannot unregister — tool stays, guarded by its own page checks */ }
11580  }
11581
11582  function _reconcileEntry(entry) {
11583    var wanted = _pathMatches(entry.pattern);
11584    if (wanted && !entry.active) {
11585      entry.active = true;
11586      try {
11587        var out = _mcRaw.registerTool(entry.descriptor);
11588        entry.handle = null;
11589        if (out && typeof out.unregister === 'function') {
11590          entry.handle = out;
11591        } else if (out && typeof out.then === 'function') {
11592          out.then(function (h) {
11593            if (entry.active && h && typeof h.unregister === 'function') { entry.handle = h; }
11594          }, function () { /* registration rejected — leave handle null */ });
11595        }
11596      } catch (e) {
11597        entry.active = false;
11598      }
11599    } else if (!wanted && entry.active) {
11600      _tryUnregister(entry);
11601    }
11602  }
11603
11604  function _reconcileAll() {
11605    for (var i = 0; i < _conditional.length; i++) { _reconcileEntry(_conditional[i]); }
11606  }
11607
11608  var mc = {
11609    registerTool: function (descriptor) {
11610      return _mcRaw.registerTool(_wrapDescriptor(descriptor));
11611    },
11612    /* Register on pages where the tool can work - and keep that decision
11613       fresh as the SPA moves between paths without reloading. */
11614    registerToolIf: function (pattern, descriptor) {
11615      var entry = {
11616        pattern: pattern,
11617        descriptor: _wrapDescriptor(descriptor),
11618        active: false,
11619        handle: null
11620      };
11621      _conditional.push(entry);
11622      _reconcileEntry(entry);
11623      return null;
11624    }
11625  };
11626
11627  /* Route watcher: the history API is patched because SPA navigations fire
11628     no document-level event; the small delay lets the router settle the URL
11629     before the toolset flips. Kept idempotent per document. */
11630  (function _watchRoutes() {
11631    if (window.__webmcpRouteWatcher) { return; }
11632    window.__webmcpRouteWatcher = true;
11633    var lastPath = location.pathname;
11634    var timer = null;
11635    function onMaybeChange() {
11636      if (timer) { clearTimeout(timer); }
11637      timer = setTimeout(function () {
11638        timer = null;
11639        var path = location.pathname;
11640        if (path === lastPath) { return; }
11641        lastPath = path;
11642        _reconcileAll();
11643      }, 150);
11644    }
11645    try {
11646      var _push = history.pushState;
11647      history.pushState = function () {
11648        var r = _push.apply(this, arguments);
11649        onMaybeChange();
11650        return r;
11651      };
11652      var _replace = history.replaceState;
11653      history.replaceState = function () {
11654        var r = _replace.apply(this, arguments);
11655        onMaybeChange();
11656        return r;
11657      };
11658    } catch (e) { /* history not patchable — popstate/hashchange still cover back/forward */ }
11659    window.addEventListener('popstate', onMaybeChange);
11660    window.addEventListener('hashchange', onMaybeChange);
11661  })();
11662
11663  function waitForElement(selector, timeout) {
11664    timeout = timeout || 5000;
11665    return new Promise(function (resolve, reject) {
11666      var found = document.querySelector(selector);
11667      if (found) { return resolve(found); }
11668      var obs = new MutationObserver(function () {
11669        var el = document.querySelector(selector);
11670        if (el) { obs.disconnect(); clearTimeout(timer); resolve(el); }
11671      });
11672      obs.observe(document.documentElement, { childList: true, subtree: true });
11673      var timer = setTimeout(function () {
11674        obs.disconnect();
11675        reject(new Error('[WebMCP] timeout waiting for ' + selector));
11676      }, timeout);
11677    });
11678  }
11679
11680  function setNativeValue(el, value) {
11681    var desc = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(el), 'value');
11682    if (desc && desc.set) { desc.set.call(el, value); } else { el.value = value; }
11683  }
11684
11685  async function fillInput(selector, value) {
11686    var el = await waitForElement(selector);
11687    el.focus();
11688    setNativeValue(el, value);
11689    el.dispatchEvent(new Event('input',  { bubbles: true }));
11690    el.dispatchEvent(new Event('change', { bubbles: true }));
11691    return el;
11692  }
11693
11694  async function fillSelect(selector, value) {
11695    var el = await waitForElement(selector);
11696    el.value = value;
11697    if (el.selectedIndex === -1) {
11698      var opt = Array.prototype.find.call(el.options, function (o) {
11699        return o.textContent.trim().toLowerCase() === String(value).trim().toLowerCase();
11700      });
11701      if (opt) { el.value = opt.value; }
11702    }
11703    el.dispatchEvent(new Event('change', { bubbles: true }));
11704    return el;
11705  }
11706
11707  async function fillCheckbox(selector, checked) {
11708    var el = await waitForElement(selector);
11709    if (el.checked !== !!checked) { el.click(); }
11710    el.dispatchEvent(new Event('change', { bubbles: true }));
11711    return el;
11712  }
11713
11714  /* Read ONE field off ONE leaf node. '@attr' reads an attribute of the item
11715     itself; 'sel @attr' reads an attribute of a descendant. Never returns the
11716     concatenated text of a whole container. */
11717  function _field(root, spec) {
11718    if (!spec) { return null; }
11719    spec = String(spec).trim();
11720    var at = spec.indexOf('@');
11721    if (at === 0) { return root.getAttribute(spec.slice(1)); }
11722    if (at > 0) {
11723      var el0 = root.querySelector(spec.slice(0, at).trim());
11724      return el0 ? el0.getAttribute(spec.slice(at + 1)) : null;
11725    }
11726    var el = root.querySelector(spec);
11727    if (!el) { return null; }
11728    return (el.textContent || '').replace(/\s+/g, ' ').trim();
11729  }
11730
11731  function extractItems(itemSelector, fieldMap, limit) {
11732    limit = limit || 10;
11733    var nodes = Array.prototype.slice.call(document.querySelectorAll(itemSelector));
11734    return {
11735      resultCount: nodes.length,
11736      truncated: nodes.length > limit,
11737      results: nodes.slice(0, limit).map(function (n) {
11738        var o = {};
11739        Object.keys(fieldMap || {}
11739).forEach(function (k) {
11740          var v = _field(n, fieldMap[k]);
11741          if (v !== null && v !== undefined && v !== '') { o[k] = String(v).slice(0, 300); }
11742        });
11743        return o;
11744      })
11745    };
11746  }
11747
11748  /* Parse the integer the page itself states, e.g. "5 items" -> 5. */
11749  function readResultCount(selector, fallback) {
11750    if (!selector) { return fallback; }
11751    var el = document.querySelector(selector);
11752    if (!el) { return fallback; }
11753    var m = (el.textContent || '').replace(/,/g, '').match(/\d+/);
11754    return m ? parseInt(m[0], 10) : fallback;
11755  }
11756
11757  function buildUrl(template, args) {
11758    return template.replace(/\{(\w+)\}/g, function (_, k) {
11759      return args && args[k] != null ? encodeURIComponent(args[k]) : '';
11760    }).replace(/[?&]\w+=(?=&|$)/g, '').replace(/\?&/, '?').replace(/[?&]$/, '');
11761  }
11762
11763  /* ---- optimizely-request-demo-lead-form, optimizely-request-demo-lead-form-submit ---- */
11764mc.registerTool({
11765  name: "optimizely-request-demo-lead-form",
11766  description: "Fills the Optimizely lead capture form embedded in the page's main region (the 'Let's grow' / 'Get started' contact form) so a buyer can request a demo, pricing or a custom plan. It scrolls to the form, highlights it, writes First name, Last name, Work email, Company, Job title, Phone and the free-text 'What products are you interested in?' note, and picks the country from the option list the form's own combobox renders. Returns the values read back out of each input, the required fields it could not populate, whether the form is ready to send, which optional controls (state selection, marketing-consent choice, captcha) it found rendered and their state, the Optimizely end-user id carried by the form, and the name of the companion submit tool. Nothing is sent until that submit tool is called.",
11767  inputSchema: {
11768    type: "object",
11769    properties: {
11770      firstName: { type: "string", description: "Value for the First name field." },
11771      lastName: { type: "string", description: "Value for the Last name field." },
11772      email: { type: "string", description: "Value for the Work email field." },
11773      company: { type: "string", description: "Value for the Company field." },
11774      jobTitle: { type: "string", description: "Value for the Job title field." },
11775      phone: { type: "string", description: "Value for the Phone field, for example +1 234 567 8901." },
11776      country: { type: "string", description: "Country name to choose in the Country combobox. It is matched against the options the page renders at call time; if it does not match, the tool returns the options that were actually on screen." },
11777      state: { type: "string", description: "Optional state name. The state control is rendered by the page only for some countries; the tool fills it when it is present." },
11778      productsOfInterest: { type: "string", description: "Optional free text for the 'What products are you interested in?' box." },
11779      marketingConsent: { type: "string", description: "Optional marketing email preference, matched against the choices the consent control renders at call time." }
11780    },
11781    required: ["firstName", "lastName", "email", "company", "jobTitle", "phone", "country"]
11782  },
11783  execute: async function (args) {
11784    trackWebMCPTool("optimizely-request-demo-lead-form");
11785    var a = args || {};
11786    var urlBefore = location.href;
11787    var FORM_SEL = "main form";
11788    var SEL = {
11789      firstName: "main form > fieldset > div > div:nth-child(1) > div:nth-child(1) > div > div > input",
11790      lastName: "main form > fieldset > div > div:nth-child(1) > div:nth-child(2) > div > div > input",
11791      email: "main form > fieldset > div > div:nth-child(2) > div:nth-child(1) > div > div > input",
11792      company: "main form > fieldset > div > div:nth-child(3) > div:nth-child(1) > div > div > input",
11793      jobTitle: "main form > fieldset > div > div:nth-child(3) > div:nth-child(2) > div > div > input",
11794      phone: "main form > fieldset > div > div:nth-child(4) > div:nth-child(1) > div > div > input",
11795      country: "main form > fieldset > div > div:nth-child(4) > div:nth-child(2) > div > div > div > div > input",
11796      countryToggle: "main form > fieldset > div > div:nth-child(4) > div:nth-child(2) > div > div > div > div > button",
11797      countryHidden: 'main form > fieldset > div > div:nth-child(4) > div:nth-child(2) > div > input[type="hidden"]',
11798      products: "main form > fieldset > div > div:nth-child(6) > div:nth-child(1) > div > div > textarea",
11799      stateRow: "main form > fieldset > div > div:nth-child(5)",
11800      stateInput: 'main form > fieldset > div > div:nth-child(5) input:not([type="hidden"])',
11801      stateHidden: 'main form > fieldset > div > div:nth-child(5) input[type="hidden"]',
11802      consentRow: "main form > fieldset > div > div:nth-child(7)",
11803      captchaRow: "main form > fieldset > div > div:nth-child(8)"
11804    };
11805
11806    function out(payload) {
11807      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
11808    }
11809    function sleep(ms) {
11810      return new Promise(function (r) { setTimeout(r, ms); });
11811    }
11812    async function waitFor(fn, timeout) {
11813      var end = Date.now() + (timeout || 5000);
11814      for (;;) {
11815        var v = null;
11816        try { v = fn(); } catch (e) { v = null; }
11817        if (v) return v;
11818        if (Date.now() > end) return null;
11819        await sleep(150);
11820      }
11821    }
11822    function txt(el) {
11823      return el && el.textContent ? el.textContent.replace(/\s+/g, " ").trim() : "";
11824    }
11825    function visible(el) {
11826      if (!el) return false;
11827      try { if (el.closest && el.closest('[aria-hidden="true"]')) return false; } catch (e) {}
11828      var r = el.getBoundingClientRect ? el.getBoundingClientRect() : { width: 0, height: 0 };
11829      return !!(el.offsetParent || r.width || r.height);
11830    }
11831    function nativeSet(el, value) {
11832      var proto = el.tagName === "TEXTAREA" ? window.HTMLTextAreaElement.prototype : window.HTMLInputElement.prototype;
11833      var desc = Object.getOwnPropertyDescriptor(proto, "value");
11834      if (desc && desc.set) { desc.set.call(el, value); } else { el.value = value; }
11835      el.dispatchEvent(new Event("input", { bubbles: true }));
11836      el.dispatchEvent(new Event("change", { bubbles: true }));
11837    }
11838    function labelString(l) {
11839      return (l && l.textContent ? l.textContent : "").replace(/\*/g, " ").replace(/\s+/g, " ").trim();
11840    }
11841    function byLabel(root, want) {
11842      if (!root) return null;
11843      var w = String(want).toLowerCase();
11844      var labels = root.querySelectorAll("label");
11845      for (var i = 0; i < labels.length; i++) {
11846        var t = labelString(labels[i]).toLowerCase();
11847        if (t !== w && t.indexOf(w) !== 0) continue;
11848        var forId = labels[i].getAttribute("for");
11849        if (forId) {
11850          var byId = null;
11851          try { byId = root.querySelector("#" + (window.CSS && CSS.escape ? CSS.escape(forId) : forId)); } catch (e) { byId = null; }
11852          if (byId) return byId;
11853        }
11854        var scope = labels[i].parentElement;
11855        for (var d = 0; d < 4 && scope && scope !== root; d++) {
11856          var ctls = scope.querySelectorAll('input:not([type="hidden"]), textarea, select');
11857          if (ctls.length === 1) return ctls[0];
11858          if (ctls.length > 1) break;
11859          scope = scope.parentElement;
11860        }
11861      }
11862      return null;
11863    }
11864    function resolveField(want, selector) {
11865      var root = document.querySelector(FORM_SEL);
11866      var el = root ? byLabel(root, want) : null;
11867      if (el) return el;
11868      return selector ? document.querySelector(selector) : null;
11869    }
11870    function setValue(el, value) {
11871      nativeSet(el, value);
11872    }
11873    function optionNodes(anchor) {
11874      var found = [];
11875      function push(list) {
11876        for (var i = 0; i < list.length; i++) {
11877          var n = list[i];
11878          if (found.indexOf(n) === -1 && visible(n) && txt(n)) found.push(n);
11879        }
11880      }
11881      push(document.querySelectorAll('[role="option"]'));
11882      if (!found.length) push(document.querySelectorAll('[role="listbox"] li, [role="listbox"] button, [role="listbox"] [role="button"]'));
11883      if (!found.length && anchor) {
11884        var scope = anchor;
11885        for (var d = 0; d < 4 && scope.parentElement; d++) scope = scope.parentElement;
11886        push(scope.querySelectorAll('li, [role="option"]'));
11887      }
11888      return found;
11889    }
11890    async function pickFromCombobox(input, toggle, hiddenSelector, value) {
11891      try { input.focus(); } catch (e) {}
11892      try { input.click(); } catch (e) {}
11893      nativeSet(input, value);
11894      var opts = await waitFor(function () { var o = optionNodes(input); return o.length ? o : null; }, 4000);
11895      if (!opts && toggle) {
11896        try { toggle.click(); } catch (e) {}
11897        opts = await waitFor(function () { var o = optionNodes(input); return o.length ? o : null; }, 3000);
11898      }
11899      if (!opts) {
11900        return { ok: false, listMissing: true, reason: "The option list did not render after typing '" + value + "' into the combobox.", options: [] };
11901      }
11902      var want = String(value).toLowerCase().trim();
11903      var match = null;
11904      var i;
11905      for (i = 0; i < opts.length; i++) { if (txt(opts[i]).toLowerCase() === want) { match = opts[i]; break; } }
11906      if (!match) { for (i = 0; i < opts.length; i++) { if (txt(opts[i]).toLowerCase().indexOf(want) === 0) { match = opts[i]; break; } } }
11907      if (!match) {
11908        nativeSet(input, "");
11909        if (toggle) { try { toggle.click(); } catch (e) {} }
11910        var all = await waitFor(function () { var o = optionNodes(input); return o.length ? o : null; }, 3000);
11911        if (!all) all = opts;
11912        var names = [];
11913        for (i = 0; i < all.length; i++) {
11914          var n = txt(all[i]);
11915          if (n && names.indexOf(n) === -1) names.push(n);
11916        }
11917        return {
11918          ok: false,
11919          notMatched: true,
11920          reason: "'" + value + "' is not one of the options the combobox rendered on this page.",
11921          options: names.slice(0, 300),
11922          truncated: names.length > 300
11923        };
11924      }
11925      var chosen = txt(match);
11926      var mk = function (t) { return new MouseEvent(t, { bubbles: true, cancelable: true, view: window }); };
11927      try { match.dispatchEvent(mk("mousedown")); match.dispatchEvent(mk("mouseup")); } catch (e) {}
11928      try { match.click(); } catch (e) {}
11929      await sleep(500);
11930      var applied = "";
11931      var hid = hiddenSelector ? document.querySelector(hiddenSelector) : null;
11932      if (hid && hid.value) applied = hid.value;
11933      if (!applied) {
11934        var fresh = document.querySelector(FORM_SEL) ? input : input;
11935        if (fresh && fresh.value) applied = fresh.value;
11936      }
11937      return { ok: !!applied, applied: applied, chosen: chosen };
11938    }
11939    function choiceLabel(el) {
11940      if (!el) return "";
11941      var al = el.getAttribute("aria-label");
11942      if (al) return al.trim();
11943      if (el.tagName === "BUTTON") return txt(el);
11944      var wrap = el.closest ? el.closest("label") : null;
11945      if (wrap) return labelString(wrap);
11946      var id = el.getAttribute("id");
11947      if (id) {
11948        var lab = null;
11949        try { lab = document.querySelector('label[for="' + id + '"]'); } catch (e) { lab = null; }
11950        if (lab) return labelString(lab);
11951      }
11952      var sib = el.nextElementSibling;
11953      if (sib && txt(sib)) return txt(sib);
11954      var parentText = el.parentElement ? txt(el.parentElement) : "";
11955      if (parentText && parentText.length < 40) return parentText;
11956      return (el.value || "").trim();
11957    }
11958
11959    try {
11960      var form = null;
11961      try { form = await waitForElement(FORM_SEL, 12000); } catch (e) { form = document.querySelector(FORM_SEL); }
11962      if (!form) form = document.querySelector(FORM_SEL);
11963      if (!form) {
11964        return out({
11965          status: "error",
11966          action: "fill the Optimizely lead form",
11967          url: location.href,
11968          observed: false,
11969          reason: "No form was found inside the page's main region, so this page does not carry the lead capture form.",
11970          nextStep: "Open the /get-started page (or another page that shows the contact form) and call this tool again.",
11971          summary: "This page does not carry the Optimizely lead form."
11972        });
11973      }
11974      var isLeadForm = !!byLabel(form, "Work email") || !!document.querySelector(SEL.email);
11975      if (!isLeadForm) {
11976        return out({
11977          status: "error",
11978          action: "fill the Optimizely lead form",
11979          url: location.href,
11980          observed: false,
11981          reason: "A form is present but it has no 'Work email' field, so it is not the Optimizely lead capture form.",
11982          nextStep: "Open the /get-started page and call this tool again.",
11983          summary: "The form on this page is not the lead capture form."
11984        });
11985      }
11986
11987      try { form.scrollIntoView({ behavior: "smooth", block: "center" }); } catch (e) {}
11988      form.style.outline = "3px solid #0037ff";
11989      form.style.outlineOffset = "4px";
11990      await sleep(350);
11991
11992      var plan = [
11993        { key: "firstName", label: "First name", sel: SEL.firstName, value: a.firstName, required: true },
11994        { key: "lastName", label: "Last name", sel: SEL.lastName, value: a.lastName, required: true },
11995        { key: "email", label: "Work email", sel: SEL.email, value: a.email, required: true },
11996        { key: "company", label: "Company", sel: SEL.company, value: a.company, required: true },
11997        { key: "jobTitle", label: "Job title", sel: SEL.jobTitle, value: a.jobTitle, required: true },
11998        { key: "phone", label: "Phone", sel: SEL.phone, value: a.phone, required: true },
11999        { key: "productsOfInterest", label: "What products are you interested in?", sel: SEL.products, value: a.productsOfInterest, required: false }
12000      ];
12001
12002      var fields = {};
12003      var missing = [];
12004      var notes = [];
12005      var i, f, el, back;
12006      for (i = 0; i < plan.length; i++) {
12007        f = plan[i];
12008        el = resolveField(f.label, f.sel);
12009        if (!el) {
12010          notes.push("Could not locate the '" + f.label + "' control inside the form.");
12011          if (f.required) missing.push(f.label);
12012          fields[f.key] = "";
12013          continue;
12014        }
12015        if (f.value !== undefined && f.value !== null && String(f.value) !== "") {
12016          setValue(el, String(f.value));
12017          await sleep(90);
12018        }
12019        back = resolveField(f.label, f.sel);
12020        fields[f.key] = back ? (back.value || "") : "";
12021        if (f.required && !fields[f.key]) missing.push(f.label);
12022      }
12023
12024      var countryInput = resolveField("Country", SEL.country);
12025      var countryToggle = document.querySelector(SEL.countryToggle);
12026      if (!countryToggle && countryInput && countryInput.parentElement) {
12027        countryToggle = countryInput.parentElement.querySelector("button");
12028      }
12029      if (!countryInput) {
12030        fields.country = "";
12031        missing.push("Country");
12032        notes.push("Could not locate the Country combobox inside the form.");
12033      } else {
12034        var cRes = await pickFromCombobox(countryInput, countryToggle, SEL.countryHidden, String(a.country || ""));
12035        var cHidden = document.querySelector(SEL.countryHidden);
12036        var cFresh = resolveField("Country", SEL.country);
12037        fields.country = (cHidden && cHidden.value) ? cHidden.value : (cFresh ? (cFresh.value || "") : "");
12038        if (cRes && cRes.notMatched) {
12039          return out({
12040            status: "error",
12041            action: "fill the Optimizely lead form",
12042            url: location.href,
12043            observed: true,
12044            reason: cRes.reason,
12045            availableOptions: cRes.options,
12046            availableOptionsTruncated: !!cRes.truncated,
12047            fields: fields,
12048            missing: missing.concat(["Country"]),
12049            ready: false,
12050            submitToolName: "optimizely-request-demo-lead-form-submit",
12051            nextStep: "Call this tool again with a country taken from availableOptions.",
12052            summary: "Filled the other fields, but the requested country is not in the list the page rendered."
12053          });
12054        }
12055        if (cRes && cRes.listMissing) notes.push(cRes.reason);
12056        if (!fields.country) missing.push("Country");
12057      }
12058
12059      await sleep(700);
12060      var stateInfo = { rendered: false, filled: false, value: "" };
12061      var stateEl = resolveField("State", SEL.stateInput);
12062      if (stateEl && visible(stateEl)) {
12063        stateInfo.rendered = true;
12064        if (a.state) {
12065          var stToggle = stateEl.parentElement ? stateEl.parentElement.querySelector("button") : null;
12066          var sRes = await pickFromCombobox(stateEl, stToggle, SEL.stateHidden, String(a.state));
12067          if (sRes && sRes.notMatched) {
12068            notes.push("The state '" + String(a.state) + "' did not match the rendered choices: " + sRes.options.slice(0, 60).join(", ") + ".");
12069          }
12070        }
12071        var sHidden = document.querySelector(SEL.stateHidden);
12072        var sFresh = resolveField("State", SEL.stateInput);
12073        stateInfo.value = (sHidden && sHidden.value) ? sHidden.value : (sFresh ? (sFresh.value || "") : "");
12074        stateInfo.filled = !!stateInfo.value;
12075        if (!stateInfo.value) {
12076          missing.push("State");
12077          notes.push("A state control is rendered for the chosen country and is still empty.");
12078        } else {
12079          fields.state = stateInfo.value;
12080        }
12081      }
12082
12083      var consent = { rendered: false, set: false, value: null, choices: [] };
12084      var consentScope = document.querySelector(SEL.consentRow);
12085      if (!consentScope || !consentScope.querySelector('input, button, [role="radio"]')) {
12086        consentScope = null;
12087        var divs = form.querySelectorAll("div");
12088        for (i = 0; i < divs.length; i++) {
12089          var dt = txt(divs[i]);
12090          if (dt && dt.length < 400 && /receive email invitations|stay in touch|events and webinars/i.test(dt) && divs[i].querySelector('input, button, [role="radio"]')) {
12091            consentScope = divs[i];
12092            break;
12093          }
12094        }
12095      }
12096      if (consentScope) {
12097        var choices = consentScope.querySelectorAll('input[type="radio"], input[type="checkbox"], [role="radio"], button');
12098        if (choices.length) {
12099          consent.rendered = true;
12100          var ci, lbl;
12101          for (ci = 0; ci < choices.length; ci++) {
12102            lbl = choiceLabel(choices[ci]);
12103            if (lbl && consent.choices.indexOf(lbl) === -1) consent.choices.push(lbl);
12104          }
12105          if (a.marketingConsent) {
12106            var wantC = String(a.marketingConsent).toLowerCase().trim();
12107            var hit = null;
12108            for (ci = 0; ci < choices.length; ci++) {
12109              var cv = (choices[ci].value || "").toLowerCase();
12110              var ct = choiceLabel(choices[ci]).toLowerCase();
12111              if (cv === wantC || ct === wantC || (ct && ct.indexOf(wantC) === 0)) { hit = choices[ci]; break; }
12112            }
12113            if (hit) {
12114              if (!hit.checked) { try { hit.click(); } catch (e) {} }
12115              consent.set = true;
12116              await sleep(150);
12117            } else {
12118              notes.push("The marketing consent choice '" + String(a.marketingConsent) + "' did not match the rendered choices: " + consent.choices.join(", ") + ".");
12119            }
12120          }
12121          for (ci = 0; ci < choices.length; ci++) {
12122            if (choices[ci].checked || choices[ci].getAttribute("aria-checked") === "true") {
12123              consent.value = choiceLabel(choices[ci]) || choices[ci].value || null;
12124            }
12125          }
12126        }
12127      }
12128
12129      var captcha = { rendered: false, tokenPresent: false };
12130      var capToken = document.querySelector('textarea[name="g-recaptcha-response"]');
12131      var capNode = document.querySelector('iframe[src*="recaptcha"], .g-recaptcha, [data-sitekey]');
12132      captcha.rendered = !!(capToken || capNode || window.grecaptcha);
12133      captcha.tokenPresent = !!(capToken && capToken.value);
12134
12135      var hiddens = form.querySelectorAll('input[type="hidden"]');
12136      var endUserId = "";
12137      for (i = 0; i < hiddens.length; i++) {
12138        var hv = hiddens[i].value || "";
12139        if (/^oeu\d/.test(hv)) endUserId = hv;
12140      }
12141      if (!endUserId) {
12142        try {
12143          if (window.optimizely && window.optimizely.get) {
12144            var vid = window.optimizely.get("visitor_id");
12145            if (vid) endUserId = typeof vid === "string" ? vid : (vid.randomId || "");
12146          }
12147        } catch (e) {}
12148      }
12149
12150      if (location.href !== urlBefore) {
12151        return out({
12152          status: "navigated",
12153          action: "fill the Optimizely lead form",
12154          url: location.href,
12155          nextStep: "The browser moved to a different page while the form was being filled. Call optimizely-request-demo-lead-form again on the page that is now loaded.",
12156          summary: "The page navigated while the form was being filled, so the field values were not confirmed."
12157        });
12158      }
12159
12160      var observedAny = false;
12161      for (var k in fields) { if (fields[k]) { observedAny = true; break; } }
12162
12163      var payload = {
12164        status: observedAny ? (missing.length ? "partial" : "ok") : "partial",
12165        action: "fill the Optimizely lead form",
12166        url: location.href,
12167        observed: observedAny,
12168        fields: fields,
12169        missing: missing,
12170        ready: observedAny && missing.length === 0,
12171        optionalControls: { stateSelection: stateInfo, marketingConsent: consent, captcha: captcha },
12172        trackingInputsLeftUntouched: hiddens.length,
12173        optimizelyEndUserId: endUserId || null,
12174        submitToolName: "optimizely-request-demo-lead-form-submit",
12175        notes: notes,
12176        summary: ""
12177      };
12178      if (!observedAny) {
12179        payload.reason = "No value could be read back out of any field after filling, so the form state could not be confirmed.";
12180        payload.nextStep = "Let the form finish loading and call this tool again.";
12181        payload.summary = "The form was found but no field value could be read back.";
12182      } else if (missing.length) {
12183        payload.reason = "These required fields are still empty after filling: " + missing.join(", ") + ".";
12184        payload.nextStep = "Supply values for the listed fields and call this tool again before submitting.";
12185        payload.summary = "Filled the Optimizely lead form, but " + missing.length + " required field(s) are still empty.";
12186      } else {
12187        payload.nextStep = "Review the values, then call optimizely-request-demo-lead-form-submit to send the lead to Optimizely.";
12188        payload.summary = "Filled and verified the Optimizely lead form for " + (fields.email || "the supplied contact") + "; it is ready to submit.";
12189      }
12190      return out(payload);
12191    } catch (err) {
12192      return out({
12193        status: "error",
12194        action: "fill the Optimizely lead form",
12195        url: location.href,
12196        observed: false,
12197        reason: "The fill attempt threw an error: " + String(err && err.message ? err.message : err),
12198        nextStep: "Let the form finish loading and call this tool again.",
12199        summary: "Could not complete filling the Optimizely lead form."
12200      });
12201    }
12202  }
12203});
12204
12205mc.registerTool({
12206  name: "optimizely-request-demo-lead-form-submit",
12207  description: "Clicks the submit button on the Optimizely lead capture form that optimizely-request-demo-lead-form filled, waits for the page to settle, and reports what it observes: the confirmation or thank-you text if the lead was accepted, each per-field validation message paired with the label of the field it belongs to, any captcha message, and the URL after the click. This submits the form to Optimizely, so fill and review the values first.",
12208  inputSchema: {
12209    type: "object",
12210    properties: {
12211      confirm: { type: "boolean", description: "Set true to confirm the reviewed form should be sent. Defaults to true; set false to abort without clicking." }
12212    },
12213    required: []
12214  },
12215  execute: async function (args) {
12216    trackWebMCPTool("optimizely-request-demo-lead-form-submit");
12217    var a = args || {};
12218    var FORM_SEL = "main form";
12219    var SEL = {
12220      country: "main form > fieldset > div > div:nth-child(4) > div:nth-child(2) > div > div > div > div > input",
12221      countryToggle: "main form > fieldset > div > div:nth-child(4) > div:nth-child(2) > div > div > div > div > button",
12222      submit: "main form > fieldset > div > div:nth-child(9) > div > div > button"
12223    };
12224
12225    function out(payload) {
12226      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
12227    }
12228    function sleep(ms) {
12229      return new Promise(function (r) { setTimeout(r, ms); });
12230    }
12231    function txt(el) {
12232      return el && el.textContent ? el.textContent.replace(/\s+/g, " ").trim() : "";
12233    }
12234    function visible(el) {
12235      if (!el) return false;
12236      try { if (el.closest && el.closest('[aria-hidden="true"]')) return false; } catch (e) {}
12237      var r = el.getBoundingClientRect ? el.getBoundingClientRect() : { width: 0, height: 0 };
12238      return !!(el.offsetParent || r.width || r.height);
12239    }
12240    function labelString(l) {
12241      return (l && l.textContent ? l.textContent : "").replace(/\*/g, " ").replace(/\s+/g, " ").trim();
12242    }
12243    function fieldLabelFor(node, root) {
12244      var scope = node;
12245      for (var d = 0; d < 5 && scope && scope !== root; d++) {
12246        scope = scope.parentElement;
12247        if (!scope) break;
12248        var lab = scope.querySelector("label");
12249        if (lab && labelString(lab)) return labelString(lab);
12250      }
12251      return null;
12252    }
12253    function readErrors(root) {
12254      var out2 = [];
12255      var seen = {};
12256      if (!root) return out2;
12257      var nodes = root.querySelectorAll('span, p, [role="alert"], [aria-live]');
12258      for (var i = 0; i < nodes.length; i++) {
12259        var n = nodes[i];
12260        if (n.children && n.children.length) continue;
12261        if (n.tagName === "LABEL") continue;
12262        var t = txt(n);
12263        if (!t || t.length > 200) continue;
12264        if (!/this field|is required|valid|invalid|should be in|format|captcha|robot|try again/i.test(t)) continue;
12265        if (!visible(n)) continue;
12266        var key = t.toLowerCase();
12267        if (seen[key]) continue;
12268        seen[key] = true;
12269        out2.push({ field: fieldLabelFor(n, root), message: t });
12270      }
12271      var invalid = root.querySelectorAll('[aria-invalid="true"]');
12272      for (var j = 0; j < invalid.length; j++) {
12273        var m = invalid[j].validationMessage || "The field is marked invalid.";
12274        var k2 = (fieldLabelFor(invalid[j], root) || "") + "|" + m.toLowerCase();
12275        if (seen[k2]) continue;
12276        seen[k2] = true;
12277        out2.push({ field: fieldLabelFor(invalid[j], root), message: m });
12278      }
12279      return out2;
12280    }
12281    function newText(before, after) {
12282      if (!after) return "";
12283      if (before && after.indexOf(before) === 0) return after.slice(before.length).trim().slice(0, 400);
12284      var chunks = after.split(/(?:\.\s+|\n+)/);
12285      var keep = [];
12286      for (var i = 0; i < chunks.length; i++) {
12287        var c = chunks[i].trim();
12288        if (c && (!before || before.indexOf(c) === -1)) keep.push(c);
12289      }
12290      return keep.join(". ").slice(0, 400);
12291    }
12292
12293    try {
12294      if (a.confirm === false) {
12295        return out({
12296          status: "error",
12297          action: "submit the Optimizely lead form",
12298          url: location.href,
12299          observed: false,
12300          validationErrors: [],
12301          confirmation: null,
12302          reason: "confirm was false, so the form was not clicked and nothing was sent.",
12303          nextStep: "Call this tool again with confirm set to true when the reviewed values should be sent.",
12304          summary: "Submission aborted at the caller's request; nothing was sent."
12305        });
12306      }
12307
12308      var form = document.querySelector(FORM_SEL);
12309      if (!form) {
12310        return out({
12311          status: "error",
12312          action: "submit the Optimizely lead form",
12313          url: location.href,
12314          observed: false,
12315          validationErrors: [],
12316          confirmation: null,
12317          reason: "No form was found inside the page's main region, so there is nothing to submit here.",
12318          nextStep: "Open the page that carries the lead form, call optimizely-request-demo-lead-form, then call this tool.",
12319          summary: "No lead form is present on this page."
12320        });
12321      }
12322
12323      var container = form.parentElement || document.querySelector("main") || document.body;
12324      var beforeText = txt(container);
12325      var urlBefore = location.href;
12326
12327      var btn = null;
12328      var buttons = form.querySelectorAll("button");
12329      var countryInput = document.querySelector(SEL.country);
12330      var comboRoot = countryInput && countryInput.closest ? countryInput.closest("div") : null;
12331      for (var bi = buttons.length - 1; bi >= 0; bi--) {
12332        var b = buttons[bi];
12333        if (comboRoot && comboRoot.contains(b)) continue;
12334        if (b === document.querySelector(SEL.countryToggle)) continue;
12335        if (b.disabled) continue;
12336        if (!visible(b)) continue;
12337        if (!txt(b)) continue;
12338        btn = b;
12339        break;
12340      }
12341      if (!btn) btn = document.querySelector(SEL.submit);
12342      if (!btn) {
12343        return out({
12344          status: "error",
12345          action: "submit the Optimizely lead form",
12346          url: location.href,
12347          observed: false,
12348          validationErrors: [],
12349          confirmation: null,
12350          reason: "The form's submit button could not be located inside 'main form'.",
12351          nextStep: "Let the form finish loading, call optimizely-request-demo-lead-form again, then retry this tool.",
12352          summary: "Could not find the submit button on the lead form."
12353        });
12354      }
12355
12356      var actionAttr = form.getAttribute("action");
12357      if (actionAttr) {
12358        var navUrl = buildUrl(actionAttr, {});
12359        var navPayload = {
12360          status: "navigated",
12361          action: "submit the Optimizely lead form",
12362          url: navUrl,
12363          nextStep: "The browser is loading " + navUrl + ". Read the confirmation on the page that loads.",
12364          summary: "Submitted the lead form through its native submit; the browser is navigating to the form's action URL."
12365        };
12366        if (form.requestSubmit) { form.requestSubmit(btn); } else { form.submit(); }
12367        return out(navPayload);
12368      }
12369
12370      try { btn.scrollIntoView({ behavior: "smooth", block: "center" }); } catch (e) {}
12371      btn.click();
12372
12373      var deadline = Date.now() + 20000;
12374      var errors = [];
12375      var confirmation = "";
12376      var navigated = false;
12377      while (Date.now() < deadline) {
12378        await sleep(400);
12379        if (location.href !== urlBefore) { navigated = true; break; }
12380        var liveForm = document.querySelector(FORM_SEL);
12381        errors = readErrors(liveForm || container);
12382        if (errors.length) break;
12383        var stillThere = document.contains(form) && visible(form);
12384        var nowText = txt(container);
12385        if (!stillThere) {
12386          confirmation = newText(beforeText, nowText) || nowText.slice(0, 400);
12387          break;
12388        }
12389        if (nowText !== beforeText) {
12390          var added = newText(beforeText, nowText);
12391          if (added && /thank|thanks|received|in touch|shortly|success|contact you|reach out|we will/i.test(added)) {
12392            confirmation = added;
12393            break;
12394          }
12395        }
12396      }
12397
12398      if (navigated) {
12399        return out({
12400          status: "navigated",
12401          action: "submit the Optimizely lead form",
12402          url: location.href,
12403          nextStep: "The browser moved to a new page after the click. Read the confirmation on the page that is now loaded.",
12404          summary: "The lead form was submitted and the browser navigated to " + location.href + "."
12405        });
12406      }
12407
12408      var captchaError = null;
12409      var fieldErrors = [];
12410      for (var ei = 0; ei < errors.length; ei++) {
12411        if (/captcha|robot/i.test(errors[ei].message)) { captchaError = errors[ei].message; }
12412        else { fieldErrors.push(errors[ei]); }
12413      }
12414
12415      var payload = {
12416        status: "partial",
12417        action: "submit the Optimizely lead form",
12418        url: location.href,
12419        observed: !!(confirmation || errors.length),
12420        validationErrors: fieldErrors,
12421        confirmation: confirmation || null,
12422        captchaError: captchaError,
12423        summary: ""
12424      };
12425
12426      if (fieldErrors.length || captchaError) {
12427        payload.status = "error";
12428        payload.reason = captchaError && !fieldErrors.length
12429          ? "The form reported a captcha problem: " + captchaError
12430          : "The form reported validation errors on " + fieldErrors.length + " field(s).";
12431        payload.nextStep = "Correct the listed fields with optimizely-request-demo-lead-form, then call this tool again.";
12432        payload.summary = "The lead form was not accepted; the page showed validation messages.";
12433      } else if (confirmation) {
12434        payload.status = "ok";
12435        var liveForm2 = document.querySelector(FORM_SEL);
12436        if (liveForm2) { liveForm2.style.outline = ""; liveForm2.style.outlineOffset = ""; }
12437        payload.summary = "The lead form was submitted and the page showed a confirmation.";
12438      } else {
12439        payload.reason = "No confirmation content and no validation message appeared within 20 seconds of the click, so the outcome could not be read.";
12440        payload.nextStep = "Check the page: if the form is still displayed, call optimizely-request-demo-lead-form to re-check the values and submit again.";
12441        payload.summary = "Clicked submit but could not read a confirmation or an error from the page.";
12442      }
12443      return out(payload);
12444    } catch (err) {
12445      return out({
12446        status: "error",
12447        action: "submit the Optimizely lead form",
12448        url: location.href,
12449        observed: false,
12450        validationErrors: [],
12451        confirmation: null,
12452        reason: "The submit attempt threw an error: " + String(err && err.message ? err.message : err),
12453        nextStep: "Refill the form with optimizely-request-demo-lead-form and try again.",
12454        summary: "Could not complete submitting the Optimizely lead form."
12455      });
12456    }
12457  }
12458});
12459
12460  /* ---- optimizely-search-optimizely-site, optimizely-search-optimizely-site-results 
12460---- */
12461mc.registerToolIf("/search", {
12462  name: "optimizely-search-optimizely-site",
12463  description: "Run a keyword search on the Optimizely site search page and return the matching results as structured objects: title, description excerpt, absolute URL, link title and thumbnail image, plus the total match count read from the page's own 'Showing X-Y of N results' line. Takes a keyword and an optional 1-based page number (12 results per page), carried as the search and page URL parameters. When applying the keyword or the page requires loading a new results URL the tool returns status 'navigated' with that URL, and the caller should then run optimizely-search-optimizely-site-results to read the settled results.",
12464  inputSchema: {
12465    type: "object",
12466    properties: {
12467      keyword: {
12468        type: "string",
12469        description: "Free-text keyword to search the site for, for example 'experimentation', 'A/B testing' or 'Snowflake'."
12470      },
12471      page: {
12472        type: "integer",
12473        minimum: 1,
12474        description: "1-based results page to read (12 results per page). Defaults to 1."
12475      },
12476      limit: {
12477        type: "integer",
12478        minimum: 1,
12479        maximum: 50,
12480        description: "Maximum number of result cards to return from the page that is read. Defaults to 12."
12481      }
12482    },
12483    required: ["keyword"]
12484  },
12485  execute: async function (args) {
12486    trackWebMCPTool("optimizely-search-optimizely-site");
12487
12488    var COUNT_SELECTOR = 'div:has(> nav[aria-label="Pagination"]) > p';
12489    var RESULTS_SELECTOR = "#SearchResults";
12490    var EXTRACTORS = [
12491      {
12492        sel: "#SearchResults div.h-full",
12493        map: { title: "h2", description: "p", url: "a @href", linkTitle: "a @title", thumbnail: "img.rounded-sm @src", thumbnailAlt: "img.rounded-sm @alt" }
12494      },
12495      {
12496        sel: '#SearchResults [class*="h-full"]',
12497        map: { title: "h2", description: "p", url: "a @href", linkTitle: "a @title", thumbnail: "img @src", thumbnailAlt: "img @alt" }
12498      },
12499      {
12500        sel: "#SearchResults a[href][title]",
12501        map: { title: "h2", description: "p", url: "@href", linkTitle: "@title", thumbnail: "img @src", thumbnailAlt: "img @alt" }
12502      }
12503    ];
12504
12505    function done(p) {
12506      return { content: [{ type: "text", text: JSON.stringify(p) }] };
12507    }
12508    function q1(sel) {
12509      try { return document.querySelector(sel); } catch (e) { return null; }
12510    }
12511    function qa(sel) {
12512      try { return Array.prototype.slice.call(document.querySelectorAll(sel)); } catch (e) { return []; }
12513    }
12514    function sleep(ms) {
12515      return new Promise(function (r) { setTimeout(r, ms); });
12516    }
12517    function urlKeyword() {
12518      var p = new URLSearchParams(location.search);
12519      return (p.get("search") || p.get("q") || "").trim();
12520    }
12521    function countElement() {
12522      var el = q1(COUNT_SELECTOR);
12523      if (el) return el;
12524      var ps = qa("main p").concat(qa("p"));
12525      for (var i = 0; i < ps.length; i++) {
12526        if (/showing\s+[\d,]+\s*-\s*[\d,]+\s+of\s+[\d,]+/i.test(ps[i].textContent || "")) return ps[i];
12527      }
12528      return null;
12529    }
12530    function countInfo() {
12531      var el = countElement();
12532      var text = el ? (el.textContent || "").replace(/\s+/g, " ").trim() : "";
12533      var total = null, from = null, to = null;
12534      var m = text.match(/of\s+([\d,]+)/i);
12535      if (m) total = parseInt(m[1].replace(/,/g, ""), 10);
12536      var r = text.match(/([\d,]+)\s*-\s*([\d,]+)/);
12537      if (r) { from = parseInt(r[1].replace(/,/g, ""), 10); to = parseInt(r[2].replace(/,/g, ""), 10); }
12538      if ((total === null || !isFinite(total)) && el) {
12539        try {
12540          var rc = readResultCount(COUNT_SELECTOR, null);
12541          total = (typeof rc === "number" && isFinite(rc)) ? rc : null;
12542        } catch (e) { total = null; }
12543      }
12544      return {
12545        element: el,
12546        text: text || null,
12547        total: (typeof total === "number" && isFinite(total)) ? total : null,
12548        from: (typeof from === "number" && isFinite(from)) ? from : null,
12549        to: (typeof to === "number" && isFinite(to)) ? to : null
12550      };
12551    }
12552    function firstTitle() {
12553      var h = q1("#SearchResults h2");
12554      return h ? (h.textContent || "").replace(/\s+/g, " ").trim() : "";
12555    }
12556    function itemCount() {
12557      for (var i = 0; i < EXTRACTORS.length; i++) {
12558        var n = qa(EXTRACTORS[i].sel).length;
12559        if (n) return n;
12560      }
12561      return 0;
12562    }
12563    function absolutize(list) {
12564      return (list || []).map(function (it) {
12565        var o = {};
12566        for (var k in it) { if (Object.prototype.hasOwnProperty.call(it, k)) o[k] = it[k]; }
12567        if (o.url) { try { o.url = new URL(o.url, location.origin).href; } catch (e) {} }
12568        if (o.thumbnail) {
12569          try {
12570            var u = new URL(o.thumbnail, location.origin);
12571            o.thumbnail = u.href;
12572            if (/cmp\.optimizely\.com$/i.test(u.hostname)) {
12573              u.searchParams.set("width", "1200");
12574              o.thumbnailWide = u.toString();
12575            }
12576          } catch (e) {}
12577        }
12578        if (o.thumbnailAlt === "") delete o.thumbnailAlt;
12579        return o;
12580      });
12581    }
12582    function highlight(el) {
12583      if (!el) return;
12584      try {
12585        el.scrollIntoView({ behavior: "smooth", block: "center" });
12586        el.style.outline = "3px solid #0037ff";
12587        el.style.outlineOffset = "2px";
12588        setTimeout(function () { try { el.style.outline = ""; el.style.outlineOffset = ""; } catch (e) {} }, 6000);
12589      } catch (e) {}
12590    }
12591    function visibleControl(sel) {
12592      var list = qa(sel);
12593      var i;
12594      for (i = 0; i < list.length; i++) {
12595        if (list[i].disabled) continue;
12596        if (list[i].offsetParent !== null || (list[i].getClientRects && list[i].getClientRects().length)) return list[i];
12597      }
12598      for (i = 0; i < list.length; i++) { if (!list[i].disabled) return list[i]; }
12599      return null;
12600    }
12601    function renderedPageNumbers() {
12602      var seen = [];
12603      qa('nav[aria-label="Pagination"] button[aria-label^="Page "]').forEach(function (b) {
12604        var v = (b.getAttribute("aria-label") || "").replace(/^Page\s+/i, "").trim();
12605        if (v && seen.indexOf(v) === -1) seen.push(v);
12606      });
12607      return seen;
12608    }
12609    async function settle(maxMs) {
12610      try { await waitForElement(RESULTS_SELECTOR, 8000); } catch (e) {}
12611      var deadline = Date.now() + (maxMs || 6000);
12612      var lastSignature = "";
12613      var stableFor = 0;
12614      while (Date.now() < deadline) {
12615        var info = countInfo();
12616        var signature = String(info.text) + "|" + itemCount() + "|" + firstTitle();
12617        if (signature === lastSignature && itemCount() > 0) {
12618          stableFor += 250;
12619          if (stableFor >= 500) break;
12620        } else {
12621          stableFor = 0;
12622          lastSignature = signature;
12623        }
12624        await sleep(250);
12625      }
12626    }
12627    async function readResults(limit) {
12628      for (var i = 0; i < EXTRACTORS.length; i++) {
12629        try {
12630          var got = await extractItems(EXTRACTORS[i].sel, EXTRACTORS[i].map, limit);
12631          if (got && got.results && got.results.length) return got;
12632        } catch (e) {}
12633      }
12634      return null;
12635    }
12636    function readPayload(keyword, page, extracted, info, appliedBy) {
12637      var results = extracted ? absolutize(extracted.results) : [];
12638      var totalFromPage = info.total;
12639      var observed = results.length > 0 || totalFromPage !== null;
12640      var resultCount = totalFromPage !== null ? totalFromPage : (results.length ? results.length : null);
12641      var p = {
12642        status: observed ? "ok" : "partial",
12643        observed: observed,
12644        action: "read the Optimizely site search results",
12645        url: location.href,
12646        keyword: keyword,
12647        page: page,
12648        appliedBy: appliedBy,
12649        resultCount: resultCount,
12650        countSource: totalFromPage !== null ? "page count element" : (results.length ? "counted result cards" : null),
12651        countText: info.text,
12652        rangeStart: info.from,
12653        rangeEnd: info.to,
12654        returnedCount: results.length,
12655        truncated: extracted ? !!extracted.truncated : false,
12656        results: results,
12657        availablePages: renderedPageNumbers(),
12658        resultsToolName: "optimizely-search-optimizely-site-results",
12659        summary: observed
12660          ? ("Search for '" + keyword + "' shows " + (resultCount === null ? "an unknown number of" : resultCount) + " matches; returned " + results.length + " result(s) from page " + page + ".")
12661          : ("Could not read any search results for '" + keyword + "' on page " + page + ".")
12662      };
12663      if (!observed) {
12664        p.reason = "The results region (" + RESULTS_SELECTOR + ") and the 'Showing X-Y of N results' count were both unreadable after waiting for the page to settle.";
12665        p.nextStep = "Reload /search?search=" + encodeURIComponent(keyword) + " and call optimizely-search-optimizely-site-results once the results list has rendered.";
12666      }
12667      return p;
12668    }
12669
12670    try {
12671      args = args || {};
12672      var keyword = typeof args.keyword === "string" ? args.keyword.trim() : "";
12673      var page = parseInt(args.page, 10);
12674      if (!page || page < 1) page = 1;
12675      var limit = parseInt(args.limit, 10);
12676      if (!limit || limit < 1) limit = 12;
12677      if (limit > 50) limit = 50;
12678
12679      if (!keyword) {
12680        return done({
12681          status: "error",
12682          action: "run a site search",
12683          url: location.href,
12684          reason: "The keyword argument was empty, so there is nothing to search for.",
12685          nextStep: "Call optimizely-search-optimizely-site again with a non-empty keyword, for example 'experimentation'.",
12686          summary: "No keyword was supplied, so no search was run."
12687        });
12688      }
12689
12690      // Outcome 5: read the URL before acting, so navigation is decided from the live page.
12691      var urlBefore = location.href;
12692      var params = new URLSearchParams(location.search);
12693      var currentKeyword = urlKeyword();
12694      var currentPage = parseInt(params.get("page") || "1", 10);
12695      if (!currentPage || currentPage < 1) currentPage = 1;
12696      var sameKeyword = currentKeyword.toLowerCase() === keyword.toLowerCase();
12697
12698      // Guard against a page number that the live result set cannot have.
12699      if (sameKeyword) {
12700        var pre = countInfo();
12701        if (pre.total !== null) {
12702          var pageSize = (pre.from !== null && pre.to !== null && pre.to >= pre.from) ? (pre.to - pre.from + 1) : 12;
12703          if (pageSize > 0) {
12704            var maxPage = Math.max(1, Math.ceil(pre.total / pageSize));
12705            if (page > maxPage) {
12706              return done({
12707                status: "error",
12708                action: "open a results page that does not exist",
12709                url: location.href,
12710                reason: "Page " + page + " was requested but the page reports " + pre.total + " results at " + pageSize + " per page, so the last page is " + maxPage + ".",
12711                availableOptions: renderedPageNumbers(),
12712                maxPage: maxPage,
12713                countText: pre.text,
12714                nextStep: "Call optimizely-search-optimizely-site again with a page between 1 and " + maxPage + ".",
12715                summary: "Page " + page + " is beyond the last page (" + maxPage + ") for '" + keyword + "'."
12716              });
12717            }
12718          }
12719        }
12720      }
12721
12722      var beforeCountText = countInfo().text;
12723      var beforeFirstTitle = firstTitle();
12724      var i;
12725
12726      // Case A: the live URL already carries this keyword and page - read it in place.
12727      if (sameKeyword && currentPage === page && q1(RESULTS_SELECTOR)) {
12728        var input = q1("#search") || q1('form input[type="search"]') || q1('input[type="search"]');
12729        if (input && page === 1 && (input.value || "").trim().toLowerCase() !== keyword.toLowerCase()) {
12730          var sel = input.id === "search" ? "#search" : 'input[type="search"]';
12731          try { await fillInput(sel, keyword); } catch (e) {}
12732          if ((input.value || "") !== keyword) {
12733            try {
12734              var d0 = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value");
12735              if (d0 && d0.set) { d0.set.call(input, keyword); } else { input.value = keyword; }
12736              input.dispatchEvent(new Event("input", { bubbles: true }));
12737              input.dispatchEvent(new Event("change", { bubbles: true }));
12738            } catch (e) {}
12739          }
12740        }
12741        await settle(7000);
12742        if (location.href !== urlBefore) {
12743          return done({
12744            status: "navigated",
12745            action: "the page moved to a new search URL while reading",
12746            url: location.href,
12747            keyword: keyword,
12748            page: page,
12749            nextStep: "Call optimizely-search-optimizely-site-results on the loaded page to read the settled results and the total count.",
12750            summary: "The search page changed URL, so the results were not read here."
12751          });
12752        }
12753        var infoA = countInfo();
12754        var extractedA = await readResults(limit);
12755        highlight(q1(RESULTS_SELECTOR) || infoA.element);
12756        return done(readPayload(keyword, page, extractedA, infoA, "already loaded URL"));
12757      }
12758
12759      // Case B1: same keyword, different page - the pagination controls are client side, try them first.
12760      if (sameKeyword && q1('nav[aria-label="Pagination"]')) {
12761        var btn = visibleControl('nav[aria-label="Pagination"] button[aria-label="Page ' + page + '"]');
12762        if (!btn && page === currentPage + 1) btn = visibleControl('nav[aria-label="Pagination"] button[aria-label="Next page"]');
12763        if (!btn && page === currentPage - 1) btn = visibleControl('nav[aria-label="Pagination"] button[aria-label="Previous page"]');
12764        if (btn) {
12765          try { btn.click(); } catch (e) {}
12766          var movedB1 = false;
12767          var deadlineB1 = Date.now() + 6000;
12768          while (Date.now() < deadlineB1) {
12769            if (location.href !== urlBefore) {
12770              return done({
12771                status: "navigated",
12772                action: "click the pagination control for page " + page,
12773                url: location.href,
12774                keyword: keyword,
12775                page: page,
12776                nextStep: "Call optimizely-search-optimizely-site-results on the loaded page to read the settled results and the total count.",
12777                summary: "Paging to page " + page + " changed the URL; the results page is loading."
12778              });
12779            }
12780            var nowInfo = countInfo();
12781            if ((nowInfo.text && nowInfo.text !== beforeCountText) || (firstTitle() && firstTitle() !== beforeFirstTitle)) { movedB1 = true; break; }
12782            await sleep(250);
12783          }
12784          if (movedB1) {
12785            await settle(6000);
12786            if (location.href !== urlBefore) {
12787              return done({
12788                status: "navigated",
12789                action: "click the pagination control for page " + page,
12790                url: location.href,
12791                keyword: keyword,
12792                page: page,
12793                nextStep: "Call optimizely-search-optimizely-site-results on the loaded page to read the settled results and the total count.",
12794                summary: "Paging to page " + page + " changed the URL; the results page is loading."
12795              });
12796            }
12797            var infoB1 = countInfo();
12798            var extractedB1 = await readResults(limit);
12799            highlight(q1(RESULTS_SELECTOR) || infoB1.element);
12800            return done(readPayload(keyword, page, extractedB1, infoB1, "clicked the pagination control"));
12801          }
12802        }
12803      }
12804
12805      // Case B2: new keyword - type it into the page's own search input and submit it in place.
12806      if (!sameKeyword) {
12807        var box = q1("#search") || q1('form input[type="search"]') || q1('input[type="search"]');
12808        if (box) {
12809          var boxSel = box.id === "search" ? "#search" : 'input[type="search"]';
12810          try { box.focus(); } catch (e) {}
12811          try { await fillInput(boxSel, keyword); } catch (e) {}
12812          if ((box.value || "") !== keyword) {
12813            try {
12814              var d1 = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value");
12815              if (d1 && d1.set) { d1.set.call(box, keyword); } else { box.value = keyword; }
12816              box.dispatchEvent(new Event("input", { bubbles: true }));
12817              box.dispatchEvent(new Event("change", { bubbles: true }));
12818            } catch (e) {}
12819          }
12820          // The search form has no action attribute; keep a stray native submit from
12821          // unloading the document before this call can return its payload.
12822          try {
12823            var formEl = box.form || box.closest("form");
12824            if (formEl) {
12825              formEl.addEventListener("submit", function (ev) { ev.preventDefault(); }, { once: true, capture: true });
12826            }
12827          } catch (e) {}
12828          try {
12829            ["keydown", "keypress", "keyup"].forEach(function (t) {
12830              box.dispatchEvent(new KeyboardEvent(t, { key: "Enter", code: "Enter", keyCode: 13, which: 13, bubbles: true, cancelable: true }));
12831            });
12832          } catch (e) {}
12833
12834          var appliedInPage = false;
12835          var deadlineB2 = Date.now() + 3000;
12836          while (Date.now() < deadlineB2) {
12837            if (location.href !== urlBefore) {
12838              return done({
12839                status: "navigated",
12840                action: "submit the keyword in the site search box",
12841                url: location.href,
12842                keyword: keyword,
12843                page: page,
12844                nextStep: "Call optimizely-search-optimizely-site-results on the loaded page to read the settled results and the total count.",
12845                summary: "Submitting '" + keyword + "' moved the browser to a new search URL, which is now loading."
12846              });
12847            }
12848            var live = countInfo();
12849            if ((live.text && live.text !== beforeCountText) || (firstTitle() && firstTitle() !== beforeFirstTitle)) { appliedInPage = true; break; }
12850            await sleep(250);
12851          }
12852
12853          if (appliedInPage && page === 1) {
12854            await settle(6000);
12855            if (location.href !== urlBefore) {
12856              return done({
12857                status: "navigated",
12858                action: "submit the keyword in the site search box",
12859                url: location.href,
12860                keyword: keyword,
12861                page: page,
12862                nextStep: "Call optimizely-search-optimizely-site-results on the loaded page to read the settled results and the total count.",
12863                summary: "The search page changed URL while settling; the results page is loading."
12864              });
12865            }
12866            var infoB2 = countInfo();
12867            var extractedB2 = await readResults(limit);
12868            highlight(q1(RESULTS_SELECTOR) || infoB2.element);
12869            return done(readPayload(keyword, page, extractedB2, infoB2, "typed the keyword into the search box"));
12870          }
12871        }
12872      }
12873
12874      // Fallback: load the results URL. Payload is built first and nothing is awaited after the navigation.
12875      var target = buildUrl("/search?search={keyword}&page={page}", { keyword: keyword, page: page > 1 ? String(page) : "" });
12876      var absolute = target;
12877      try { absolute = new URL(target, location.href).href; } catch (e) {}
12878      var navPayload = {
12879        status: "navigated",
12880        action: "load the search results URL for the keyword",
12881        url: absolute,
12882        keyword: keyword,
12883        page: page,
12884        limit: limit,
12885        resultsToolName: "optimizely-search-optimizely-site-results",
12886        nextStep: "The search results page is now loading; call optimizely-search-optimizely-site-results on it to read the settled results and the total count.",
12887        summary: "Loading Optimizely site search for '" + keyword + "' (page " + page + ")."
12888      };
12889      var navText = JSON.stringify(navPayload);
12890      location.assign(target);
12891      return { content: [{ type: "text", text: navText }] };
12892    } catch (err) {
12893      return done({
12894        status: "error",
12895        action: "run a site search",
12896        url: location.href,
12897        reason: "The search tool failed while driving the page: " + (err && err.message ? err.message : String(err)),
12898        nextStep: "Reload /search and try optimizely-search-optimizely-site again, or call optimizely-search-optimizely-site-results to read whatever is currently listed.",
12899        summary: "The site search could not be completed."
12900      });
12901    }
12902  }
12903});
12904
12905mc.registerToolIf("/search", {
12906  name: "optimizely-search-optimizely-site-results",
12907  description: "Read the Optimizely search results page that is currently loaded and return its results as structured objects: title, description excerpt, absolute URL, link title and thumbnail image, together with the total match count parsed from the page's own 'Showing X-Y of N results' line, the keyword and page number taken from the URL, and the page numbers offered by the pagination controls. Scrolls to and highlights the results region it read.",
12908  inputSchema: {
12909    type: "object",
12910    properties: {
12911      limit: {
12912        type: "integer",
12913        minimum: 1,
12914        maximum: 50,
12915        description: "Maximum number of result cards to return from the current page. Defaults to 12."
12916      }
12917    },
12918    required: []
12919  },
12920  execute: async function (args) {
12921    trackWebMCPTool("optimizely-search-optimizely-site-results");
12922
12923    var COUNT_SELECTOR = 'div:has(> nav[aria-label="Pagination"]) > p';
12924    var RESULTS_SELECTOR = "#SearchResults";
12925    var EXTRACTORS = [
12926      {
12927        sel: "#SearchResults div.h-full",
12928        map: { title: "h2", description: "p", url: "a @href", linkTitle: "a @title", thumbnail: "img.rounded-sm @src", thumbnailAlt: "img.rounded-sm @alt" }
12929      },
12930      {
12931        sel: '#SearchResults [class*="h-full"]',
12932        map: { title: "h2", description: "p", url: "a @href", linkTitle: "a @title", thumbnail: "img @src", thumbnailAlt: "img @alt" }
12933      },
12934      {
12935        sel: "#SearchResults a[href][title]",
12936        map: { title: "h2", description: "p", url: "@href", linkTitle: "@title", thumbnail: "img @src", thumbnailAlt: "img @alt" }
12937      }
12938    ];
12939
12940    function done(p) {
12941      return { content: [{ type: "text", text: JSON.stringify(p) }] };
12942    }
12943    function q1(sel) {
12944      try { return document.querySelector(sel); } catch (e) { return null; }
12945    }
12946    function qa(sel) {
12947      try { return Array.prototype.slice.call(document.querySelectorAll(sel)); } catch (e) { return []; }
12948    }
12949    function sleep(ms) {
12950      return new Promise(function (r) { setTimeout(r, ms); });
12951    }
12952    function countElement() {
12953      var el = q1(COUNT_SELECTOR);
12954      if (el) return el;
12955      var ps = qa("main p").concat(qa("p"));
12956      for (var i = 0; i < ps.length; i++) {
12957        if (/showing\s+[\d,]+\s*-\s*[\d,]+\s+of\s+[\d,]+/i.test(ps[i].textContent || "")) return ps[i];
12958      }
12959      return null;
12960    }
12961    function countInfo() {
12962      var el = countElement();
12963      var text = el ? (el.textContent || "").replace(/\s+/g, " ").trim() : "";
12964      var total = null, from = null, to = null;
12965      var m = text.match(/of\s+([\d,]+)/i);
12966      if (m) total = parseInt(m[1].replace(/,/g, ""), 10);
12967      var r = text.match(/([\d,]+)\s*-\s*([\d,]+)/);
12968      if (r) { from = parseInt(r[1].replace(/,/g, ""), 10); to = parseInt(r[2].replace(/,/g, ""), 10); }
12969      if ((total === null || !isFinite(total)) && el) {
12970        try {
12971          var rc = readResultCount(COUNT_SELECTOR, null);
12972          total = (typeof rc === "number" && isFinite(rc)) ? rc : null;
12973        } catch (e) { total = null; }
12974      }
12975      return {
12976        element: el,
12977        text: text || null,
12978        total: (typeof total === "number" && isFinite(total)) ? total : null,
12979        from: (typeof from === "number" && isFinite(from)) ? from : null,
12980        to: (typeof to === "number" && isFinite(to)) ? to : null
12981      };
12982    }
12983    function firstTitle() {
12984      var h = q1("#SearchResults h2");
12985      return h ? (h.textContent || "").replace(/\s+/g, " ").trim() : "";
12986    }
12987    function itemCount() {
12988      for (var i = 0; i < EXTRACTORS.length; i++) {
12989        var n = qa(EXTRACTORS[i].sel).length;
12990        if (n) return n;
12991      }
12992      return 0;
12993    }
12994    function absolutize(list) {
12995      return (list || []).map(function (it) {
12996        var o = {};
12997        for (var k in it) { if (Object.prototype.hasOwnProperty.call(it, k)) o[k] = it[k]; }
12998        if (o.url) { try { o.url = new URL(o.url, location.origin).href; } catch (e) {} }
12999        if (o.thumbnail) {
13000          try {
13001            var u = new URL(o.thumbnail, location.origin);
13002            o.thumbnail = u.href;
13003            if (/cmp\.optimizely\.com$/i.test(u.hostname)) {
13004              u.searchParams.set("width", "1200");
13005              o.thumbnailWide = u.toString();
13006            }
13007          } catch (e) {}
13008        }
13009        if (o.thumbnailAlt === "") delete o.thumbnailAlt;
13010        return o;
13011      });
13012    }
13013    function highlight(el) {
13014      if (!el) return;
13015      try {
13016        el.scrollIntoView({ behavior: "smooth", block: "center" });
13017        el.style.outline = "3px solid #0037ff";
13018        el.style.outlineOffset = "2px";
13019        setTimeout(function () { try { el.style.outline = ""; el.style.outlineOffset = ""; } catch (e) {} }, 6000);
13020      } catch (e) {}
13021    }
13022    function renderedPageNumbers() {
13023      var seen = [];
13024      qa('nav[aria-label="Pagination"] button[aria-label^="Page "]').forEach(function (b) {
13025        var v = (b.getAttribute("aria-label") || "").replace(/^Page\s+/i, "").trim();
13026        if (v && seen.indexOf(v) === -1) seen.push(v);
13027      });
13028      return seen;
13029    }
13030    function nextEnabled() {
13031      var list = qa('nav[aria-label="Pagination"] button[aria-label="Next page"]');
13032      for (var i = 0; i < list.length; i++) { if (!list[i].disabled) return true; }
13033      return false;
13034    }
13035
13036    try {
13037      args = args || {};
13038      var limit = parseInt(args.limit, 10);
13039      if (!limit || limit < 1) limit = 12;
13040      if (limit > 50) limit = 50;
13041
13042      var params = new URLSearchParams(location.search);
13043      var keyword = (params.get("search") || params.get("q") || "").trim();
13044      var page = parseInt(params.get("page") || "1", 10);
13045      if (!page || page < 1) page = 1;
13046
13047      try { await waitForElement(RESULTS_SELECTOR, 10000); } catch (e) {}
13048
13049      var deadline = Date.now() + 8000;
13050      var lastSignature = "";
13051      var stableFor = 0;
13052      while (Date.now() < deadline) {
13053        var info0 = countInfo();
13054        var signature = String(info0.text) + "|" + itemCount() + "|" + firstTitle();
13055        if (signature === lastSignature && itemCount() > 0) {
13056          stableFor += 250;
13057          if (stableFor >= 500) break;
13058        } else {
13059          stableFor = 0;
13060          lastSignature = signature;
13061        }
13062        await sleep(250);
13063      }
13064
13065      var info = countInfo();
13066      var extracted = null;
13067      for (var i = 0; i < EXTRACTORS.length && !extracted; i++) {
13068        try {
13069          var got = await extractItems(EXTRACTORS[i].sel, EXTRACTORS[i].map, limit);
13070          if (got && got.results && got.results.length) extracted = got;
13071        } catch (e) {}
13072      }
13073
13074      var results = extracted ? absolutize(extracted.results) : [];
13075      var observed = results.length >
13075 0 || info.total !== null;
13076      var resultCount = info.total !== null ? info.total : (results.length ? results.length : null);
13077      highlight(q1(RESULTS_SELECTOR) || info.element);
13078
13079      var payload = {
13080        status: observed ? "ok" : "partial",
13081        observed: observed,
13082        action: "read the settled Optimizely search results page",
13083        url: location.href,
13084        keyword: keyword || null,
13085        page: page,
13086        resultCount: resultCount,
13087        countSource: info.total !== null ? "page count element" : (results.length ? "counted result cards" : null),
13088        countText: info.text,
13089        rangeStart: info.from,
13090        rangeEnd: info.to,
13091        returnedCount: results.length,
13092        truncated: extracted ? !!extracted.truncated : false,
13093        results: results,
13094        availablePages: renderedPageNumbers(),
13095        hasNextPage: nextEnabled(),
13096        summary: observed
13097          ? ("Read " + results.length + " result(s) from page " + page + (keyword ? " for '" + keyword + "'" : "") + "; the page reports " + (resultCount === null ? "an unknown total" : resultCount + " matches") + ".")
13098          : ("No search results could be read from this page" + (keyword ? " for '" + keyword + "'" : "") + ".")
13099      };
13100      if (!observed) {
13101        payload.reason = "Neither the results region (" + RESULTS_SELECTOR + ") nor the 'Showing X-Y of N results' count could be read after waiting for the page to settle.";
13102        payload.nextStep = "Confirm the browser is on /search with a search parameter, give the page a moment to finish loading, then call optimizely-search-optimizely-site-results again.";
13103      }
13104      return done(payload);
13105    } catch (err) {
13106      return done({
13107        status: "error",
13108        action: "read the Optimizely search results page",
13109        url: location.href,
13110        reason: "Reading the results failed: " + (err && err.message ? err.message : String(err)),
13111        nextStep: "Reload the /search URL and call optimizely-search-optimizely-site-results again.",
13112        summary: "The search results could not be read."
13113      });
13114    }
13115  }
13116});
13117
13118  /* ---- optimizely-filter-search-facets ---- */
13119mc.registerToolIf("/search", {
13120  name: "optimizely-filter-search-facets",
13121  description: "Narrows the results on the Optimizely search page (/search) by ticking or unticking named facet checkboxes in the sidebar 'Topics' and 'Products' groups, and can click the 'Clear' button next to the 'Filters (n)' label to drop every applied facet. Facet names are matched against the label text present on the page at call time, and a group's '...' button is used to reveal further values before matching. Returns the page's own result count text and total, the 'Filters (n)' label, the facet names each group currently shows as selected, the facet names each group currently lists (with any group that still hides values behind its '...' button flagged), the current URL query string, and the visible result cards with title, summary, link and image.",
13122  inputSchema: {
13123    type: "object",
13124    properties: {
13125      topics: {
13126        type: "array",
13127        items: { type: "string" },
13128        description: "Facet names in the sidebar 'Topics' group to tick, written the way they read on the page, for example 'A/B Testing' or 'Content management'. Matched case-insensitively against the facet labels present at call time."
13129      },
13130      products: {
13131        type: "array",
13132        items: { type: "string" },
13133        description: "Facet names in the sidebar 'Products' group to tick, for example 'Configured Commerce' or 'Content Management System'. Matched case-insensitively against the facet labels present at call time."
13134      },
13135      deselectTopics: {
13136        type: "array",
13137        items: { type: "string" },
13138        description: "Facet names in the 'Topics' group to untick, if they are currently applied."
13139      },
13140      deselectProducts: {
13141        type: "array",
13142        items: { type: "string" },
13143        description: "Facet names in the 'Products' group to untick, if they are currently applied."
13144      },
13145      clearFilters: {
13146        type: "boolean",
13147        description: "Click the 'Clear' button next to the 'Filters (n)' label first, remov
13147ing every applied facet before the requested facets are ticked."
13148      },
13149      limit: {
13150        type: "number",
13151        description: "Maximum number of result cards to return from the rendered page. Defaults to 12."
13152      }
13153    },
13154    required: []
13155  },
13156  execute: async function (args) {
13157    trackWebMCPTool("optimizely-filter-search-facets");
13158
13159    var a = args || {};
13160    var ACTION = "toggle search facets and read results";
13161    var COUNT_SEL = 'main main > div > div > div:nth-of-type(2) > div:nth-of-type(2) > div > div:nth-of-type(2) > div > div > p';
13162    var ITEM_SEL = 'main main > div > div > div:nth-of-type(2) > div:nth-of-type(2) > div > div > div > div';
13163    var ITEM_SEL_FALLBACK = 'main main a[href^="/field-notes/"]';
13164    var CLEAR_SEL = 'aside > div:nth-of-type(1) > div:nth-of-type(2) > button';
13165    var FILTERS_SEL = 'aside > div:nth-of-type(1) > div:nth-of-type(1)';
13166    var GROUP_SEL = 'aside > div:nth-of-type(2) > div';
13167
13168    var startPath = location.pathname;
13169    var unloading = false;
13170    try {
13171      window.addEventListener("beforeunload", function () { unloading = true; }, { once: true });
13172    } catch (e) {}
13173
13174    function pack(p) {
13175      p.url = location.href;
13176      return { content: [{ type: "text", text: JSON.stringify(p) }] };
13177    }
13178    function txt(el) {
13179      return el ? String(el.textContent || "").replace(/\s+/g, " ").trim() : "";
13180    }
13181    function norm(s) {
13182      return String(s == null ? "" : s).toLowerCase().replace(/\s+/g, " ").trim();
13183    }
13184    function squash(s) {
13185      return norm(s).replace(/[^a-z0-9]/g, "");
13186    }
13187    function sleep(ms) {
13188      return new Promise(function (r) { setTimeout(r, ms); });
13189    }
13190    function toArray(v) {
13191      if (Array.isArray(v)) return v.filter(function (x) { return typeof x === "string" && x.trim(); });
13192      if (typeof v === "string" && v.trim()) return [v];
13193      return [];
13194    }
13195    function unique(list) {
13196      var out = [];
13197      list.forEach(function (x) { if (out.indexOf(x) === -1) out.push(x); });
13198      return out;
13199    }
13200    function isVisible(el) {
13201      if (!el) return false;
13202      var r = el.getBoundingClientRect();
13203      return !!(el.offsetParent || r.width || r.height);
13204    }
13205
13206    function findFiltersLabel() {
13207      var el = document.querySelector(FILTERS_SEL);
13208      if (el && /filters/i.test(txt(el))) return el;
13209      var aside = document.querySelector("aside");
13210      if (!aside) return el || null;
13211      var nodes = Array.prototype.slice.call(aside.querySelectorAll("div, span, p, h2, h3, h4"));
13212      var hit = null;
13213      nodes.forEach(function (n) {
13214        if (hit) return;
13215        if (/^filters\s*\(\d+\)/i.test(txt(n))) hit = n;
13216      });
13217      return hit || el || null;
13218    }
13219    function filtersCount() {
13220      var t = txt(findFiltersLabel());
13221      var m = t.match(/\((\d+)\)/);
13222      return m ? parseInt(m[1], 10) : null;
13223    }
13224    function findClearButton() {
13225      var el = document.querySelector(CLEAR_SEL);
13226      if (el && /clear/i.test(txt(el))) return el;
13227      var aside = document.querySelector("aside");
13228      if (!aside) return null;
13229      var buttons = Array.prototype.slice.call(aside.querySelectorAll("button"));
13230      var hit = null;
13231      buttons.forEach(function (b) {
13232        if (hit) return;
13233        if (/^clear(\s+all)?$/i.test(txt(b))) hit = b;
13234      });
13235      return hit || el || null;
13236    }
13237    function findCountEl() {
13238      var el = document.querySelector(COUNT_SEL);
13239      if (el && /\d/.test(txt(el))) return el;
13240      var scope = document.querySelector("main") || document.body;
13241      var nodes = Array.prototype.slice.call(scope.querySelectorAll("p, span, div"));
13242      var hit = null;
13243      nodes.forEach(function (n) {
13244        if (hit) return;
13245        if (n.children.length === 0 && /\bof\s+[\d,]+\s+results?\b/i.test(txt(n))) hit = n;
13246      });
13247      return hit || el || null;
13248    }
13249
13250    function isSelected(label, btn) {
13251      var input = label ? label.querySelector('input[type="checkbox"]') : null;
13252      if (input) return !!input.checked;
13253      var probes = [btn, label];
13254      for (var i = 0; i < probes.length; i++) {
13255        var el = probes[i];
13256        if (!el || !el.getAttribute) continue;
13257        var ac = el.getAttribute("aria-checked");
13258        if (ac === "true") return true;
13259        if (ac === "false") return false;
13260        var ap = el.getAttribute("aria-pressed");
13261        if (ap === "true") return true;
13262        if (ap === "false") return false;
13263        var ds = el.getAttribute("data-state") || el.getAttribute("data-checked") || el.getAttribute("data-selected");
13264        if (ds) {
13265          if (/^(unchecked|off|false)$/i.test(ds)) return false;
13266          if (/(checked|selected|active|on|true)/i.test(ds)) return true;
13267        }
13268        var cls = el.getAttribute("class") || "";
13269        if (/(^|[\s_-])(is-)?(checked|selected|active)([\s_-]|$)/i.test(cls)) return true;
13270      }
13271      return null;
13272    }
13273
13274    function readGroups() {
13275      var aside = document.querySelector("aside");
13276      var groups = [];
13277      if (!aside) return groups;
13278      var nodes = Array.prototype.slice.call(document.querySelectorAll(GROUP_SEL));
13279      if (!nodes.length) {
13280        nodes = Array.prototype.slice.call(aside.querySelectorAll("div")).filter(function (d) {
13281          return d.querySelector("label button");
13282        });
13283      }
13284      nodes.forEach(function (node) {
13285        var header = node.querySelector(":scope > div > div:nth-of-type(1)") || node.querySelector(":scope > div") || null;
13286        var body = node.querySelector(":scope > div > div:nth-of-type(2)") || node;
13287        var heading = header ? header.querySelector("h2, h3, h4, h5, h6") : null;
13288        if (!body.querySelector("label button") && !(heading && txt(heading))) return;
13289        var name = txt(header).replace(/\btoggle\b/gi, "").trim();
13290        if (!name) name = "Group " + (groups.length + 1);
13291        var expander = null;
13292        Array.prototype.slice.call(body.querySelectorAll(":scope > button")).forEach(function (b) {
13293          if (expander) return;
13294          var t = txt(b);
13295          if (/^\.{2,3}$/.test(t) || /^…$/.test(t) || /(show\s+)?more/i.test(t)) expander = b;
13296        });
13297        var collapse = header ? header.querySelector("button") : null;
13298        if (groups.filter(function (g) { return g.body === body; }).length) return;
13299        groups.push({ name: name, node: node, body: body, expander: expander, collapse: collapse });
13300      });
13301      return groups;
13302    }
13303
13304    function entriesOf(g) {
13305      if (!g || !g.body) return [];
13306      var labels = Array.prototype.slice.call(g.body.querySelectorAll("label")).filter(function (l) {
13307        return l.querySelector("button");
13308      });
13309      return labels.map(function (l) {
13310        var btn = l.querySelector("button");
13311        var spans = Array.prototype.slice.call(l.querySelectorAll("span")).filter(function (s) {
13312          return s.getAttribute("aria-hidden") !== "true" && txt(s);
13313        });
13314        var name = spans.length ? txt(spans[0]) : "";
13315        if (!name && btn) name = btn.getAttribute("aria-label") || btn.getAttribute("title") || "";
13316        if (!name) name = txt(l);
13317        name = name.replace(/\s*\(\d+\)\s*$/, "").trim();
13318        return { name: name, label: l, btn: btn, selected: isSelected(l, btn) };
13319      }).filter(function (e) { return e.name && e.btn; });
13320    }
13321
13322    function pickGroup(groups, kind) {
13323      var hit = null;
13324      groups.forEach(function (g) {
13325        if (hit) return;
13326        if (norm(g.name).indexOf(kind) !== -1) hit = g;
13327      });
13328      if (hit) return hit;
13329      if (kind === "topics" && groups.length >= 1) return groups[0];
13330      if (kind === "products" && groups.length >= 2) return groups[1];
13331      return null;
13332    }
13333
13334    function matchFacet(entries, requested) {
13335      var want = norm(requested);
13336      var hit = null;
13337      entries.forEach(function (e) { if (!hit && norm(e.name) === want) hit = e; });
13338      if (hit) return hit;
13339      var sq = squash(requested);
13340      entries.forEach(function (e) { if (!hit && squash(e.name) === sq) hit = e; });
13341      if (hit) return hit;
13342      var partial = entries.filter(function (e) { return norm(e.name).indexOf(want) !== -1 || want.indexOf(norm(e.name)) !== -1; });
13343      if (partial.length === 1) return partial[0];
13344      return null;
13345    }
13346
13347    function signature() {
13348      var items = document.querySelectorAll(ITEM_SEL);
13349      var first = items.length ? txt(items[0].querySelector("h2")) : "";
13350      return txt(findCountEl()) + "|" + txt(findFiltersLabel()) + "|" + items.length + "|" + first;
13351    }
13352
13353    async function settle(prevSig, timeout) {
13354      var t0 = Date.now();
13355      var changed = false;
13356      while (Date.now() - t0 < (timeout || 6000)) {
13357        await sleep(120);
13358        if (signature() !== prevSig) { changed = true; break; }
13359      }
13360      var last = signature();
13361      for (var i = 0; i < 12; i++) {
13362        await sleep(150);
13363        var s = signature();
13364        if (s === last) break;
13365        last = s;
13366      }
13367      return changed;
13368    }
13369
13370    async function ensureGroupOpen(kind) {
13371      var g = pickGroup(readGroups(), kind);
13372      if (!g) return;
13373      var open = entriesOf(g).some(function (e) { return isVisible(e.label); });
13374      if (open) return;
13375      if (g.collapse) {
13376        g.collapse.click();
13377        await sleep(400);
13378      }
13379    }
13380
13381    async function expandGroup(kind) {
13382      var g = pickGroup(readGroups(), kind);
13383      if (!g || !g.expander) return false;
13384      var before = entriesOf(g).length;
13385      g.expander.click();
13386      for (var i = 0; i < 20; i++) {
13387        await sleep(150);
13388        var g2 = pickGroup(readGroups(), kind);
13389        if (g2 && entriesOf(g2).length > before) return true;
13390      }
13391      return false;
13392    }
13393
13394    function navigatedPayload() {
13395      return {
13396        status: "navigated",
13397        action: ACTION,
13398        nextStep: "The browser left the search page while the facets were being applied. Wait for the new page to load, then call optimizely-filter-search-facets again on /search.",
13399        summary: "The page navigated away from /search before the facet results could be read."
13400      };
13401    }
13402
13403    try {
13404      try { await waitForElement("aside", 12000); } catch (e) {}
13405      var aside = document.querySelector("aside");
13406      if (!aside) {
13407        return pack({
13408          status: "error",
13409          action: ACTION,
13410          reason: "The search sidebar (aside) never appeared, so no facet checkbox could be located.",
13411          nextStep: "Reload https://www.optimizely.com/search, let it finish hydrating, then call optimizely-filter-search-facets again.",
13412          summary: "Could not find the search filter sidebar on this page."
13413        });
13414      }
13415      try { await waitForElement(COUNT_SEL, 8000); } catch (e) {}
13416
13417      try {
13418        aside.style.outline = "3px solid #0037ff";
13419        aside.scrollIntoView({ behavior: "smooth", block: "start" });
13420      } catch (e) {}
13421
13422      var groups = readGroups();
13423      if (!groups.length) {
13424        return pack({
13425          status: "error",
13426          action: ACTION,
13427          reason: "No facet groups with checkbox labels were found inside the sidebar.",
13428          nextStep: "Reload /search, confirm the Topics and Products filters are visible, then call optimizely-filter-search-facets again.",
13429          summary: "The Topics and Products facet groups were not present in the sidebar."
13430        });
13431      }
13432
13433      var requests = [];
13434      toArray(a.topics).forEach(function (n) { requests.push({ kind: "topics", name: n, desired: true }); });
13435      toArray(a.products).forEach(function (n) { requests.push({ kind: "products", name: n, desired: true }); });
13436      toArray(a.deselectTopics).forEach(function (n) { requests.push({ kind: "topics", name: n, desired: false }); });
13437      toArray(a.deselectProducts).forEach(function (n) { requests.push({ kind: "products", name: n, desired: false }); });
13438
13439      var kinds = unique(requests.map(function (r) { return r.kind; }));
13440      var missingGroups = [];
13441
13442      for (var ki = 0; ki < kinds.length; ki++) {
13443        var kind = kinds[ki];
13444        var g0 = pickGroup(readGroups(), kind);
13445        if (!g0) { missingGroups.push(kind); continue; }
13446        await ensureGroupOpen(kind);
13447        var wanted = requests.filter(function (r) { return r.kind === kind; });
13448        var gNow = pickGroup(readGroups(), kind);
13449        var ents = entriesOf(gNow);
13450        var allFound = wanted.every(function (r) { return !!matchFacet(ents, r.name); });
13451        if (!allFound) await expandGroup(kind);
13452      }
13453
13454      if (missingGroups.length) {
13455        return pack({
13456          status: "error",
13457          action: ACTION,
13458          reason: "No sidebar facet group could be resolved for: " + missingGroups.join(", ") + ". Groups found on the page: " + groups.map(function (g) { return g.name; }).join(", ") + ".",
13459          availableOptions: groups.map(function (g) { return g.name; }),
13460          nextStep: "Call optimizely-filter-search-facets again using a facet from one of the groups listed in availableOptions.",
13461          summary: "Could not resolve the requested facet group in the sidebar."
13462        });
13463      }
13464
13465      var unmatched = [];
13466      var affectedKinds = kinds.slice();
13467      requests.forEach(function (r) {
13468        var g = pickGroup(readGroups(), r.kind);
13469        var ents = entriesOf(g);
13470        if (!matchFacet(ents, r.name)) unmatched.push((r.kind === "topics" ? "Topics" : "Products") + ": " + r.name);
13471      });
13472
13473      function facetMap(kindList) {
13474        var out = {};
13475        var gs = readGroups();
13476        gs.forEach(function (g) {
13477          var k = norm(g.name).indexOf("topic") !== -1 ? "topics" : (norm(g.name).indexOf("product") !== -1 ? "products" : null);
13478          if (kindList && k && kindList.indexOf(k) === -1) return;
13479          out[g.name] = entriesOf(g).map(function (e) { return e.name; });
13480        });
13481        return out;
13482      }
13483
13484      if (unmatched.length) {
13485        var avail = [];
13486        var map = facetMap(affectedKinds);
13487        Object.keys(map).forEach(function (k) { avail = avail.concat(map[k]); });
13488        return pack({
13489          status: "error",
13490          action: ACTION,
13491          reason: "These facet names are not among the checkboxes currently on the page: " + unmatched.join(", ") + ".",
13492          availableOptions: unique(avail),
13493          availableFacets: facetMap(null),
13494          nextStep: "Call optimizely-filter-search-facets again with a facet name taken from availableOptions.",
13495          summary: "One or more requested facet names do not exist in the sidebar right now."
13496        });
13497      }
13498
13499      var changes = [];
13500      var cleared = false;
13501
13502      if (a.clearFilters === true) {
13503        var clearBtn = findClearButton();
13504        if (!clearBtn) {
13505          changes.push({ group: "all", facet: "Clear", requested: "cleared", applied: false, verified: false, note: "The Clear button next to the Filters label could not be located." });
13506        } else {
13507          var sigC = signature();
13508          clearBtn.click();
13509          await settle(sigC, 8000);
13510          cleared = true;
13511          if (unloading || location.pathname !== startPath) return pack(navigatedPayload());
13512          changes.push({ group: "all", facet: "Clear", requested: "cleared", applied: true, verified: filtersCount() === 0 || filtersCount() === null, appliedFilterCount: filtersCount() });
13513        }
13514      }
13515
13516      for (var ri = 0; ri < requests.length; ri++) {
13517        var req = requests[ri];
13518        var groupNow = pickGroup(readGroups(), req.kind);
13519        var groupLabel = groupNow ? groupNow.name : (req.kind === "topics" ? "Topics" : "Products");
13520        var entry = groupNow ? matchFacet(entriesOf(groupNow), req.name) : null;
13521        if (!entry) {
13522          changes.push({ group: groupLabel, facet: req.name, requested: req.desired ? "selected" : "deselected", applied: false, verified: false, note: "The facet row disappeared from the sidebar before it could be clicked." });
13523          continue;
13524        }
13525        if (entry.selected === req.desired) {
13526          changes.push({ group: groupLabel, facet: entry.name, requested: req.desired ? "selected" : "deselected", applied: false, verified: true, state: req.desired ? "selected" : "not selected", note: "Already in the requested state." });
13527          continue;
13528        }
13529
13530        var before = filtersCount();
13531        var sig = signature();
13532        entry.btn.click();
13533        await settle(sig, 8000);
13534        if (unloading || location.pathname !== startPath) return pack(navigatedPayload());
13535
13536        var groupAfter = pickGroup(readGroups(), req.kind);
13537        var entryAfter = groupAfter ? matchFacet(entriesOf(groupAfter), entry.name) : null;
13538        var state = entryAfter ? entryAfter.selected : null;
13539        var after = filtersCount();
13540        if (state === null && before !== null && after !== null && after !== before) {
13541          state = after > before;
13542        }
13543
13544        if (state !== null && state !== req.desired) {
13545          var sig2 = signature();
13546          if (entryAfter && entryAfter.btn) {
13547            entryAfter.btn.click();
13548            await settle(sig2, 8000);
13549            if (unloading || location.pathname !== startPath) return pack(navigatedPayload());
13550            var groupFinal = pickGroup(readGroups(), req.kind);
13551            var entryFinal = groupFinal ? matchFacet(entriesOf(groupFinal), entry.name) : null;
13552            state = entryFinal ? entryFinal.selected : null;
13553            var after2 = filtersCount();
13554            if (state === null && after !== null && after2 !== null && after2 !== after) state = after2 > after;
13555          }
13556        }
13557
13558        changes.push({
13559          group: groupLabel,
13560          facet: entry.name,
13561          requested: req.desired ? "selected" : "deselected",
13562          applied: true,
13563          verified: state === req.desired,
13564          state: state === null ? "unknown" : (state ? "selected" : "not selected"),
13565          appliedFilterCount: filtersCount()
13566        });
13567      }
13568
13569      await settle(signature(), 1200);
13570      if (unloading || location.pathname !== startPath) return pack(navigatedPayload());
13571
13572      var finalGroups = readGroups();
13573      var selectedFacets = {};
13574      var availableFacets = {};
13575      var hiddenOptionsRemain = [];
13576      var anyStateReadable = false;
13577      finalGroups.forEach(function (g) {
13578        var ents = entriesOf(g);
13579        availableFacets[g.name] = ents.length ? ents.map(function (e) { return e.name; }) : null;
13580        var known = ents.filter(function (e) { return e.selected !== null; });
13581        if (known.length) {
13582          anyStateReadable = true;
13583          selectedFacets[g.name] = ents.filter(function (e) { return e.selected === true; }).map(function (e) { return e.name; });
13584        } else {
13585          selectedFacets[g.name] = null;
13586        }
13587        if (g.expander || !ents.length) hiddenOptionsRemain.push(g.name);
13588      });
13589
13590      var countEl = findCountEl();
13591      var countText = countEl ? txt(countEl) : null;
13592      var total = null;
13593      if (countText) {
13594        var m = countText.replace(/,/g, "").match(/of\s+(\d+)/i);
13595        if (m) total = parseInt(m[1], 10);
13596      }
13597      if (total === null) {
13598        try {
13599          var rc = readResultCount(COUNT_SEL, null);
13600          if (typeof rc === "number" && !isNaN(rc)) total = rc;
13601        } catch (e) {}
13602      }
13603
13604      var limit = typeof a.limit === "number" && a.limit > 0 ? Math.min(Math.floor(a.limit), 50) : 12;
13605      var extracted = null;
13606      try {
13607        extracted = await extractItems(ITEM_SEL, { title: "h2", summary: "p", url: "a @href", image: "img @src" }, limit);
13608      } catch (e) { extracted = null; }
13609      var results = extracted && Array.isArray(extracted.results) ? extracted.results : [];
13610      results = results.filter(function (r) { return r && (r.title || r.url); });
13611      if (!results.length) {
13612        try {
13613          var alt = await extractItems(ITEM_SEL_FALLBACK, { title: "h2", summary: "p", url: "@href", image: "img @src" }, limit);
13614          if (alt && Array.isArray(alt.results)) {
13615            results = alt.results.filter(function (r) { return r && (r.title || r.url); });
13616            extracted = alt;
13617          }
13618        } catch (e) {}
13619      }
13620      var seen = {};
13621      results = results.filter(function (r) {
13622        var key = (r.url || "") + "|" + (r.title || "");
13623        if (seen[key]) return false;
13624        seen[key] = true;
13625        return true;
13626      });
13627
13628      try {
13629        var firstItem = document.querySelector(ITEM_SEL) || document.querySelector(ITEM_SEL_FALLBACK);
13630        var region = firstItem ? (firstItem.parentElement || firstItem) : countEl;
13631        if (region && region.style) {
13632          region.style.outline = "3px solid #0037ff";
13633          region.scrollIntoView({ behavior: "smooth", block: "center" });
13634        }
13635      } catch (e) {}
13636
13637      var filtersText = txt(findFiltersLabel()) || null;
13638      var appliedFilterCount = filtersCount();
13639      var observed = total !== null || results.length > 0;
13640      var unverified = changes.filter(function (c) { return c.applied && !c.verified; });
13641
13642      var payload = {
13643        status: "ok",
13644        action: ACTION,
13645        observed: observed,
13646        clearedFilters: cleared,
13647        changes: changes,
13648        filtersLabel: filtersText,
13649        appliedFilterCount: appliedFilterCount,
13650        selectedFacets: selectedFacets,
13651        facetStateReadable: anyStateReadable,
13652        availableFacets: availableFacets,
13653        groupsWithMoreOptionsHidden: hiddenOptionsRemain,
13654        resultCountText: countText,
13655        resultCount: total,
13656        renderedCount: results.length,
13657        truncated: extracted && typeof extracted.truncated === "boolean" ? extracted.truncated : (total !== null && results.length < total),
13658        results: results,
13659        urlQuery: location.search || "",
13660        summary: ""
13661      };
13662
13663      if (!observed) {
13664        payload.status = "partial";
13665        payload.reason = "The result count element and the result cards could not be read after the facets were toggled, so no result data was observed.";
13666        payload.nextStep = "Wait for the search results to finish rendering and call optimizely-filter-search-facets again to read the count and cards.";
13667        payload.summary = "Facet changes were attempted but no result count or result cards could be read from the page.";
13668      } else if (unverified.length) {
13669        payload.status = "partial";
13670        payload.reason = "These facet toggles were clicked but their selected state could not be confirmed from the page: " + unverified.map(function (c) { return c.group + " / " + c.facet; }).join(", ") + ".";
13671        payload.nextStep = "Check selectedFacets and filtersLabel, and call optimizely-filter-search-facets again if the facet is not applied as expected.";
13672        payload.summary = "Applied the facet clicks and read " + (total === null ? results.length + " rendered" : total) + " results, but could not confirm every checkbox state.";
13673      } else {
13674        var appliedNames = [];
13675        Object.keys(selectedFacets).forEach(function (k) {
13676          if (Array.isArray(selectedFacets[k]) && selectedFacets[k].length) {
13677            appliedNames.push(k + ": " + selectedFacets[k].join(", "));
13678          }
13679        });
13680        payload.summary = (cleared ? "Cleared the applied facets and " : "") +
13681          (total === null
13682            ? "read " + results.length + " result cards from the search page"
13683            : "read " + total + " results (" + results.length + " cards on this page)") +
13684          (filtersText ? " with " + filtersText : "") +
13685          (appliedNames.length ? " - " + appliedNames.join("; ") : "") + ".";
13686      }
13687
13688      return pack(payload);
13689    } catch (err) {
13690      return pack({
13691        status: "error",
13692        action: ACTION,
13693        reason: "The facet interaction failed: " + (err && err.message ? err.message : String(err)),
13694        nextStep: "Reload https://www.optimizely.com/search and call optimizely-filter-search-facets again.",
13695        summary: "Could not apply the requested search facets because the page interaction failed."
13696      });
13697    }
13698  }
13699});
13700
13701  /* ---- optimizely-search-connectors-directory ---- */
13702mc.registerToolIf("/connectors", {
13703  name: "optimizely-search-connectors-directory",
13704  description: "Search and filter the connectors directory listing on /connectors. A keyword or a category facet is applied through the listing's own URL parameters, which reloads the page: those calls return status navigated, and optimizely-extract-connector-listing reads the settled results. The Optimizely certified switch and the pagination are click-driven and are applied in place, returning the listing result count, the connector cards then rendered (name, description, the full Works with category list, partner and detail page URL), the filters that ended up applied and the category labels currently available on the page. Category labels are read off the page at call time; call once with no arguments to see them in availableCategories.",
13705  inputSchema: {
13706    type: "object",
13707    properties: {
13708      keyword: {
13709        type: "string",
13710        description: "Text to type into the sidebar Refine by search field, for example wordpress, salesforce or amazon. Pass an empty string to clear the search box."
13711      },
13712      categories: {
13713        type: "array",
13714        items: { type: "string" },
13715        description: "Category facet labels to tick, spelled as they appear in the sidebar Categories list. Call once without categories and read availableCategories to see the labels currently on the page."
13716      },
13717      certifiedOnly: {
13718        type: "boolean",
13719        description: "Set the Optimizely certified switch on (true) or off (false)."
13720      },
13721      page: {
13722        type: "integer",
13723        description: "Results page to open using the listing pagination buttons, for example 2."
13724      },
13725      clearExistingFilters: {
13726        type: "boolean",
13727        description: "Click the sidebar Clear button before applying the requested filters."
13728      },
13729      limit: {
13730        type: "integer",
13731        description: "Maximum number of connector cards to return. Defaults to 8, the number the listing renders per page."
13732      }
13733    },
13734    additionalProperties: false
13735  },
13736  execute: async function (args) {
13737    trackWebMCPTool("optimizely-search-connectors-directory");
13738
13739    var ACTION = "search and filter the connectors directory";
13740    var LIST = "#ListingResults";
13741    var ITEM = "#ListingResults div.grid > div.h-full";
13742    var COUNT = "#ListingResults p.text-body-xxs";
13743    var CHIP = "span.font-overline";
13744    var FIELDS = {
13745      url: "a.group @href",
13746      name: "h3",
13747      description: "p",
13748      categories: "span.font-overline",
13749      partner: "span.text-body-s"
13750    };
13751
13752    var a = args || {};
13753    var startPath = location.pathname;
13754
13755    function out(p) {
13756      return { content: [{ type: "text", text: JSON.stringify(p) }] };
13757    }
13758    function sleep(ms) {
13759      return new Promise(function (r) { setTimeout(r, ms); });
13760    }
13761    function txt(el) {
13762      return el ? (el.textContent || "").replace(/\s+/g, " ").trim() : "";
13763    }
13764    function visible(el) {
13765      return !!(el && (el.offsetWidth || el.offsetHeight || (el.getClientRects && el.getClientRects().length)));
13766    }
13767    function isOn(el) {
13768      if (!el) return false;
13769      var s = el.getAttribute("aria-checked") || el.getAttribute("data-state") || "";
13770      return s === "true" || s === "checked";
13771    }
13772    function asideRoot() {
13773      return document.querySelector("aside") || document.body || document;
13774    }
13775    function sig() {
13776      var c = document.querySelector(COUNT);
13777      var cards = document.querySelectorAll(ITEM);
13778      var first = cards.length ? txt(cards[0]).slice(0, 90) : "";
13779      return txt(c) + "||" + cards.length + "||" + first + "||" + location.search;
13780    }
13781    async function settle(before, timeout) {
13782      var deadline = Date.now() + (timeout || 5000);
13783      while (Date.now() < deadline) {
13784        await sleep(150);
13785        if (location.pathname !== startPath) return true;
13786        if (sig() !== before) { await sleep(500); return true; }
13787      }
13788      await sleep(300);
13789      return false;
13790    }
13791    function navigatedAway() {
13792      return location.pathname !== startPath;
13793    }
13794    function navPayload() {
13795      return out({
13796        status: "navigated",
13797        action: ACTION,
13798        url: location.href,
13799        nextStep: "Wait for the new document to finish loading, then call optimizely-search-connectors-directory again once a /connectors listing page is open.",
13800        summary: "The interaction moved the browser to " + location.href + ", so the connectors listing was not read here."
13801      });
13802    }
13803    function reactSet(el, v) {
13804      var d = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value");
13805      if (d && d.set) { d.set.call(el, v); } else { el.value = v; }
13806      el.dispatchEvent(new Event("input", { bubbles: true }));
13807      el.dispatchEvent(new Event("change", { bubbles: true }));
13808    }
13809    function categoryControls() {
13810      var root = asideRoot();
13811      var found = [];
13812      var seen = [];
13813      var labels = root.querySelectorAll("label");
13814      var i;
13815      for (i = 0; i < labels.length; i++) {
13816        var lb = labels[i];
13817        var btn = lb.querySelector('button[role="checkbox"]');
13818        if (!btn) continue;
13819        var inp = lb.querySelector('input[type="checkbox"]');
13820        var nm = (inp && inp.getAttribute("name")) || btn.getAttribute("aria-label") || txt(lb);
13821        nm = (nm || "").replace(/\s+/g, " ").trim();
13822        if (!nm) continue;
13823        found.push({ name: nm, button: btn });
13824        seen.push(btn);
13825      }
13826      var loose = root.querySelectorAll('button[role="checkbox"]');
13827      for (i = 0; i < loose.length; i++) {
13828        if (seen.indexOf(loose[i]) !== -1) continue;
13829        var nm2 = loose[i].getAttribute("aria-label") || loose[i].getAttribute("title") || txt(loose[i].parentElement);
13830        nm2 = (nm2 || "").replace(/\s+/g, " ").trim();
13831        if (!nm2) continue;
13832        found.push({ name: nm2, button: loose[i] });
13833      }
13834      return found;
13835    }
13836    async function expandCategories() {
13837      var root = asideRoot();
13838      var containers = root.querySelectorAll('div[id^="radix-"]');
13839      for (var i = 0; i < containers.length; i++) {
13840        var c = containers[i];
13841        if (!c.querySelector('button[role="checkbox"]')) continue;
13842        var kids = c.children || [];
13843        for (var k = 0; k < kids.length; k++) {
13844          var b = kids[k];
13845          if (!b || b.tagName !== "BUTTON") continue;
13846          if (b.getAttribute("aria-expanded") !== null) continue;
13847          if (/toggle/i.test(txt(b))) continue;
13848          if (!visible(b)) continue;
13849          var before = categoryControls().length;
13850          b.click();
13851          await sleep(450);
13852          return categoryControls().length > before;
13853        }
13854      }
13855      return false;
13856    }
13857    function certifiedSwitch() {
13858      return asideRoot().querySelector('button[role="switch"]') || document.querySelector('button[role="switch"]');
13859    }
13860    function clearButton() {
13861      var btns = asideRoot().querySelectorAll("button");
13862      for (var i = 0; i < btns.length; i++) {
13863        var t = txt(btns[i]);
13864        if (/^clear( all)?$/i.test(t) || /^reset filters$/i.test(t)) return btns[i];
13865      }
13866      return null;
13867    }
13868
13869    try {
13870      try { await waitForElement(LIST, 8000); } catch (e) {}
13871
13872      var limit = (typeof a.limit === "number" && a.limit > 0) ? Math.min(Math.floor(a.limit), 48) : 8;
13873
13874      var searchSel = null;
13875      var candidates = ['aside form input[type="search"]', 'form input[type="search"]', 'input[type="search"]'];
13876      for (var ci = 0; ci < candidates.length; ci++) {
13877        if (document.querySelector(candidates[ci])) { searchSel = candidates[ci]; break; }
13878      }
13879
13880      if (typeof a.keyword === "string" && !searchSel) {
13881        return out({
13882          status: "error",
13883          action: ACTION,
13884          url: location.href,
13885          reason: "No search input could be found in the connectors sidebar, so the keyword could not be typed.",
13886          nextStep: "Confirm a /connectors listing page is open and fully loaded, then call this tool again, or filter by category only.",
13887          summary: "The Refine by search field was not present, so the keyword was not applied."
13888        });
13889      }
13890
13891      if (a.clearExistingFilters === true) {
13892        var cb = clearButton();
13893        if (cb) {
13894          var s0 = sig();
13895          cb.click();
13896          await settle(s0, 5000);
13897          if (navigatedAway()) return navPayload();
13898        }
13899      }
13900
13901      try { await expandCategories(); } catch (e) {}
13902      if (navigatedAway()) return navPayload();
13903
13904      var controls = categoryControls();
13905      var available = controls.map(function (c) { return c.name; });
13906
13907      var wanted = [];
13908      if (Array.isArray(a.categories)) {
13909        wanted = a.categories.filter(function (v) { return typeof v === "string" && v.trim() !== ""; });
13910      } else if (typeof a.categories === "string" && a.categories.trim() !== "") {
13911        wanted = [a.categories];
13912      }
13913
13914      var resolved = [];
13915      var unknown = [];
13916      for (var wi = 0; wi < wanted.length; wi++) {
13917        var want = wanted[wi].replace(/\s+/g, " ").trim().toLowerCase();
13918        var hit = null;
13919        for (var mi = 0; mi < controls.length; mi++) {
13920          if (controls[mi].name.toLowerCase() === want) { hit = controls[mi]; break; }
13921        }
13922        if (!hit) {
13923          for (var mj = 0; mj < controls.length; mj++) {
13924            if (controls[mj].name.toLowerCase().indexOf(want) !== -1) { hit = controls[mj]; break; }
13925          }
13926        }
13927        if (hit) { resolved.push(hit); } else { unknown.push(wanted[wi]); }
13928      }
13929
13930      if (unknown.length) {
13931        return out({
13932          status: "error",
13933          action: ACTION,
13934          url: location.href,
13935          reason: "These category labels are not on the page right now: " + unknown.join(", ") + ".",
13936          availableOptions: available,
13937          nextStep: "Call the tool again with one of the labels listed in availableOptions, or with no categories to search across all of them.",
13938          summary: "The requested category filter was not found in the sidebar, so nothing was applied."
13939        });
13940      }
13941
13942      var sigBefore = sig();
13943      var didRequest = false;
13944
13945      // Keyword and category facets both go through the listing's own URL
13946      // parameters rather than through the sidebar controls.
13947      //
13948      //   - The Refine by search field applies on submit, not on input, so
13949      //     typing into it leaves the listing on its previous result set. It
13950      //     carries no name attribute, so a scripted form submit posts an empty
13951      //     query and reloads unfiltered.
13952      //   - Ticking a category facet does nothing while a search is applied —
13953      //     a real human click fails the same way, so it is the page's own
13954      //     behaviour, not a scripting artefact.
13955      //
13956      // ?search= and a comma-joined ?product= apply both in any state, which is
13957      // how the listing encodes them itself. This mirrors the partner directory
13958      // and agent directory tools, which also drive their filters by URL.
13959      var wantsKeyword = (typeof a.keyword === "string");
13960      var wantsCategories = (resolved.length > 0);
13961
13962      if (wantsKeyword || wantsCategories) {
13963        var target = new URL(location.href);
13964
13965        if (wantsKeyword) {
13966          var wantSearch = a.keyword.trim();
13967          if (wantSearch === "") { target.searchParams.delete("search"); }
13968          else { target.searchParams.set("search", wantSearch); }
13969        }
13970
13971        var appliedCategories = [];
13972        if (wantsCategories) {
13973          for (var ai = 0; ai < resolved.length; ai++) { appliedCategories.push(resolved[ai].name); }
13974          target.searchParams.set("product", appliedCategories.join(","));
13975        }
13976        target.searchParams.delete("page");
13977
13978        if (target.href !== location.href) {
13979          // The switch and the pagination are click-driven and do not survive
13980          // the reload, so say plainly that they still need a follow-up call.
13981          var deferred = [];
13982          if (typeof a.certifiedOnly === "boolean") deferred.push("certifiedOnly");
13983          if (typeof a.page === "number" && a.page > 0) deferred.push("page");
13984
13985          location.assign(target.href);
13986          return out({
13987            status: "navigated",
13988            action: ACTION,
13989            url: target.href,
13990            keyword: wantsKeyword ? (a.keyword.trim() || null) : null,
13991            categoriesApplied: appliedCategories,
13992            deferred: deferred,
13993            nextStep: deferred.length
13994              ? "Wait for the listing to load, then call this tool again with " + deferred.join(" and ") + " to apply the rest."
13995              : "Wait for the listing to load, then call optimizely-extract-connector-listing to read the results.",
13996            summary: "Applied " +
13997              (wantsKeyword && a.keyword.trim() !== "" ? "the keyword \"" + a.keyword.trim() + "\"" : "")+
13998              (wantsKeyword && a.keyword.trim() === "" ? "an empty keyword" : "") +
13999              (wantsKeyword && wantsCategories ? " and " : "") +
14000              (wantsCategories ? "the category filter " + appliedCategories.join(", ") : "") +
14001              " through the listing's own URL parameters, so the connectors listing is reloading." +
14002              (deferred.length ? " " + deferred.join(" and ") + " still need a follow-up call." : "")
14003          });
14004        }
14005
14006        // The listing is already in the requested state, so nothing needs to
14007        // reload — keep the visible search field in step and carry on.
14008        if (wantsKeyword) {
14009          var sEl = document.querySelector(searchSel);
14010          if (sEl && sEl.value !== a.keyword) {
14011            try { await fillInput(searchSel, a.keyword); } catch (e) {}
14012            if (sEl.value !== a.keyword) { reactSet(sEl, a.keyword); }
14013          }
14014        }
14015      }
14016
14017      if (typeof a.certifiedOnly === "boolean") {
14018        var sw = certifiedSwitch();
14019        if (sw) {
14020          didRequest = true;
14021          if (isOn(sw) !== a.certifiedOnly) { sw.click(); await sleep(350); }
14022        }
14023      }
14024
14025      // Category facets are applied by the URL branch above; clicking them here
14026      // is a no-op once a search is in play, so nothing is clicked.
14027
14028      if (didRequest) {
14029        await settle(sigBefore, 9000);
14030        if (navigatedAway()) return navPayload();
14031      }
14032
14033      var pageNote = null;
14034      if (typeof a.page === "number" && a.page > 0) {
14035        var pageNo = Math.floor(a.page);
14036        var pbtns = document.querySelectorAll('#ListingResults button[aria-label="Page ' + pageNo + '"]');
14037        var pbtn = null;
14038        for (var pi = 0; pi < pbtns.length; pi++) {
14039          if (visible(pbtns[pi])) { pbtn = pbtns[pi]; break; }
14040        }
14041        if (!pbtn && pbtns.length) pbtn = pbtns[0];
14042        if (pbtn) {
14043          var s2 = sig();
14044          pbtn.click();
14045          await settle(s2, 9000);
14046          if (navigatedAway()) return navPayload();
14047        } else {
14048          pageNote = "Page " + pageNo + " has no pagination button in the current result set, so the first page of results is reported.";
14049        }
14050      }
14051
14052      var changed = sig() !== sigBefore;
14053
14054      var region = document.querySelector(LIST);
14055      if (region) {
14056        try { region.scrollIntoView({ behavior: "smooth", block: "start" }); } catch (e) {}
14057        var prevOutline = region.style.outline;
14058        region.style.outline = "3px solid #0037ff";
14059        setTimeout(function () { region.style.outline = prevOutline; }, 5000);
14060      }
14061
14062      var extracted = { resultCount: 0, truncated: false, results: [] };
14063      try { extracted = await extractItems(ITEM, FIELDS, limit); } catch (e) {}
14064      var results = (extracted && extracted.results) || [];
14065      var nodesFound = (extracted && typeof extracted.resultCount === "number") ? extracted.resultCount : results.length;
14066
14067      var cardNodes = Array.prototype.slice.call(document.querySelectorAll(ITEM));
14068      for (var xi = 0; xi < results.length; xi++) {
14069        var cardNode = cardNodes[xi];
14070        if (!cardNode || !results[xi]) continue;
14071        var chips = cardNode.querySelectorAll(CHIP);
14072        var chipList = [];
14073        for (var xj = 0; xj < chips.length; xj++) {
14074          var chipText = txt(chips[xj]);
14075          if (chipText && chipList.indexOf(chipText) === -1) chipList.push(chipText);
14076        }
14077        if (chipList.length) results[xi].categories = chipList;
14078      }
14079
14080      var countEl = document.querySelector(COUNT);
14081      var countText = txt(countEl);
14082      var total = null;
14083      var countSource = null;
14084      var m = countText.match(/of\s+([\d.,]+)/i);
14085      if (m) {
14086        total = parseInt(m[1].replace(/[.,]/g, ""), 10);
14087        countSource = "count line";
14088      }
14089      if (total === null && countEl) {
14090        try {
14091          var rc = await readResultCount(COUNT, null);
14092          if (typeof rc === "number" && !isNaN(rc)) { total = rc; countSource = "count line"; }
14093        } catch (e) {}
14094      }
14095      var listText = region ? txt(region) : "";
14096      if (total === null && /no results found/i.test(listText)) {
14097        total = 0;
14098        countSource = "empty state";
14099      }
14100      if (total === null && nodesFound > 0) {
14101        total = nodesFound;
14102        countSource = "cards on this page";
14103      }
14104
14105      var searchBack = searchSel ? document.querySelector(searchSel) : null;
14106      var certBack = certifiedSwitch();
14107      var nowControls = categoryControls();
14108      var checkedNow = [];
14109      for (var ni = 0; ni < nowControls.length; ni++) {
14110        if (isOn(nowControls[ni].button)) checkedNow.push(nowControls[ni].name);
14111      }
14112      var filtersLabel = null;
14113      var flCandidates = asideRoot().querySelectorAll("div");
14114      for (var fi = 0; fi < flCandidates.length; fi++) {
14115        var ft = txt(flCandidates[fi]);
14116        if (/^Filters\s*\(\d+\)$/i.test(ft)) { filtersLabel = ft; break; }
14117      }
14118
14119      var observed = (total !== null) || results.length > 0;
14120
14121      var payload = {
14122        status: "ok",
14123        action: ACTION,
14124        url: location.href,
14125        observed: observed,
14126        keyword: searchBack ? searchBack.value : null,
14127        certifiedOnly: certBack ? isOn(certBack) : null,
14128        categoriesApplied: checkedNow,
14129        activeFilters: filtersLabel,
14130        resultCount: total,
14131        resultCountText: countText || null,
14132        resultCountSource: countSource,
14133        resultsOnPage: results.length,
14134        truncated: !!(extracted && extracted.truncated),
14135        results: results,
14136        availableCategories: (function () {
14137          var names = [];
14138          for (var i = 0; i < nowControls.length; i++) {
14139            if (names.indexOf(nowControls[i].name) === -1) names.push(nowControls[i].name);
14140          }
14141          return names;
14142        })(),
14143        summary: ""
14144      };
14145
14146      if (!observed) {
14147        payload.status = "partial";
14148        payload.observed = false;
14149        payload.reason = "The listing region reported no count line and no connector cards could be read after the filters were applied.";
14150        payload.nextStep = "Reload /connectors, wait for the listing to render, then call this tool again.";
14151        payload.summary = "The connectors listing did not settle into a readable state, so no count or cards were read.";
14152        return out(payload);
14153      }
14154
14155      if (pageNote) {
14156        payload.status = "partial";
14157        payload.reason = pageNote;
14158        payload.nextStep = "Check resultCount to work out how many pages the filtered list has, then request a page within that range.";
14159      } else if (didRequest && !changed) {
14160        var suggested = null;
14161        try {
14162          suggested = buildUrl("/connectors?search={keyword}&product={categories}&page={page}", {
14163            keyword: (typeof a.keyword === "string" ? a.keyword : ""),
14164            categories: checkedNow.join(","),
14165            page: (typeof a.page === "number" && a.page > 1) ? String(Math.floor(a.page)) : ""
14166          });
14167        } catch (e) { suggested = null; }
14168        payload.status = "partial";
14169        payload.reason = "The result count and cards did not change after the filters were set, so the returned list may still be the previous one.";
14170        payload.nextStep = "Call this tool again, or open " + (suggested || "/connectors with search and product query parameters") + " and then call this tool to read the settled listing.";
14171      }
14172
14173      var head = payload.resultCountText ? ("The listing reports " + payload.resultCountText) : ("The listing shows " + payload.resultCount + " matching connectors");
14174      payload.summary = head + " and " + payload.resultsOnPage + " connector cards were read from the page.";
14175      if (payload.status === "partial" && payload.reason) {
14176        payload.summary = payload.summary + " " + payload.reason;
14177      }
14178
14179      return out(payload);
14180    } catch (err) {
14181      return out({
14182        status: "error",
14183        action: ACTION,
14184        url: location.href,
14185        reason: "The tool hit an unexpected problem while driving the connectors filters: " + ((err && err.message) ? err.message : 
14185String(err)),
14186        nextStep: "Reload /connectors and call this tool again with a simpler set of filters.",
14187        summary: "The connectors search could not be completed."
14188      });
14189    }
14190  }
14191});
14192
14193  /* ---- optimizely-extract-connector-listing ---- */
14194mc.registerToolIf("/connectors/**", {
14195  name: "optimizely-extract-connector-listing",
14196  description: "Reads the Optimizely connector directory on the current page and returns it as structured data. On the connectors listing it reads the connector cards rendered in the results region and returns an array of records with the connector name, description, the 'Works with' product list, the publisher name, the publisher logo image URL and the absolute detail URL, together with the total result count and the current page position parsed from the page's own 'Showing X-Y of N results' line. On an individual connector page it returns the single record: name, description, the publisher shown under 'Built by', the 'Works with' product list, the logo image URL, the pricing note and the documentation link URL. It reads the live DOM at call time, so the payload reflects whichever filters, search text or pagination page is active, and it scrolls to and highlights the region the data was read from.",
14197  inputSchema: {
14198    type: "object",
14199    properties: {
14200      limit: {
14201        type: "number",
14202        description: "Maximum number of connector cards to return from the listing page. Defaults to 100."
14203      },
14204      logoWidth: {
14205        type: "number",
14206        description: "Optional pixel width to request for each CMP logo image URL; adds a width query parameter to the returned logo URL."
14207      }
14208    },
14209    required: []
14210  },
14211  execute: async function (args) {
14212    trackWebMCPTool("optimizely-extract-connector-listing");
14213    var a = args || {};
14214    var startUrl = location.href;
14215    var reply = function (payload) {
14216      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
14217    };
14218
14219    try {
14220      var settle = function (ms) { return new Promise(function (r) { setTimeout(r, ms); }); };
14221
14222      var text = function (el) {
14223        if (!el || typeof el.textContent !== "string") return "";
14224        return el.textContent.replace(/\s+/g, " ").trim();
14225      };
14226
14227      var visibleText = function (el) {
14228        if (!el) return "";
14229        if (el.getAttribute && el.getAttribute("aria-hidden") === "true") return "";
14230        var hidden = el.querySelectorAll ? el.querySelectorAll('[aria-hidden="true"]') : [];
14231        if (!hidden || !hidden.length) return text(el);
14232        var whole = text(el);
14233        for (var h = 0; h < hidden.length; h++) {
14234          var part = text(hidden[h]);
14235          if (part) whole = whole.split(part).join(" ");
14236        }
14237        return whole.replace(/\s+/g, " ").trim();
14238      };
14239
14240      var absUrl = function (href) {
14241        if (!href) return null;
14242        try { return new URL(href, location.origin).href; } catch (e) { return href; }
14243      };
14244
14245      var num = function (s) {
14246        if (s === null || s === undefined) return null;
14247        var digits = String(s).replace(/[^0-9]/g, "");
14248        if (!digits) return null;
14249        var n = parseInt(digits, 10);
14250        return isNaN(n) ? null : n;
14251      };
14252
14253      var logoFromStyle = function (style) {
14254        if (!style) return null;
14255        var m = /mask-image\s*:\s*url\(\s*['"]?([^'")]+)['"]?\s*\)/i.exec(style);
14256        return m ? m[1] : null;
14257      };
14258
14259      var withWidth = function (u) {
14260        if (!u || typeof a.logoWidth !== "number" || !(a.logoWidth > 0)) return u || null;
14261        try {
14262          var url = new URL(u, location.origin);
14263          url.searchParams.set("width", String(Math.floor(a.logoWidth)));
14264          return url.toString();
14265        } catch (e) { return u; }
14266      };
14267
14268      var highlight = function (el) {
14269        if (!el || !el.style) return false;
14270        try {
14271          var previous = el.style.outline;
14272          el.style.outline = "3px solid #0037ff";
14273          if (el.scrollIntoView) el.scrollIntoView({ behavior: "smooth", block: "center" });
14274          setTimeout(function () { el.style.outline = previous; }, 5000);
14275          return true;
14276        } catch (e) { return false; }
14277      };
14278
14279      var path = location.pathname.replace(/\/+$/, "");
14280      var marker = "/connectors";
14281      var idx = path.toLowerCase().indexOf(marker);
14282      var rest = idx >= 0 ? path.slice(idx + marker.length) : "";
14283      var isDetail = rest.replace(/^\/+/, "").length > 0;
14284
14285      /* ---------------------------------------------------------------- */
14286      /* LISTING PAGE                                                      */
14287      /* ---------------------------------------------------------------- */
14288      if (!isDetail) {
14289        var region = null;
14290        try {
14291          region = await waitForElement("#ListingResults", 12000);
14292        } catch (e) {
14293          region = document.querySelector("#ListingResults");
14294        }
14295
14296        if (!region) {
14297          return reply({
14298            status: "partial",
14299            observed: false,
14300            action: "read the connector listing",
14301            pageType: "connector-listing",
14302            resultCount: null,
14303            totalResults: null,
14304            renderedCount: 0,
14305            results: [],
14306            url: location.href,
14307            reason: "The results region #ListingResults was not present on this page within 12 seconds, so no connector cards could be read.",
14308            nextStep: "Confirm the connectors listing has finished loading (and that no consent overlay is blocking it), then call this tool again.",
14309            summary: "The connector results region did not appear, so nothing was read from the page."
14310          });
14311        }
14312
14313        try { await waitForElement("#ListingResults a.group", 8000); } catch (e) { /* cards may legitimately be absent */ }
14314        await settle(400);
14315        var highlighted = highlight(region);
14316
14317        var limit = (typeof a.limit === "number" && a.limit > 0) ? Math.floor(a.limit) : 100;
14318
14319        var extracted = await extractItems("#ListingResults a.group", {
14320          detailPath: "@href",
14321          name: "h3.font-headline",
14322          description: "p.font-body",
14323          publisher: "span.font-body.text-body-s",
14324          publisherLogoLabel: 'span[role="img"] @aria-label',
14325          logoMaskStyle: 'span[role="img"] @style'
14326        }, limit) || {};
14327
14328        var rows = Array.isArray(extracted.results) ? extracted.results : [];
14329        var cards = Array.prototype.slice.call(document.querySelectorAll("#ListingResults a.group"));
14330
14331        var results = rows.map(function (row, i) {
14332          var card = cards[i] || null;
14333          var works = [];
14334          if (card) {
14335            Array.prototype.forEach.call(card.querySelectorAll("span.font-overline"), function (chip) {
14336              var t = visibleText(chip);
14337              var normalised = t.replace(/:$/, "").toLowerCase();
14338              if (!t || normalised === "works with") return;
14339              if (works.indexOf(t) === -1) works.push(t);
14340            });
14341          }
14342          var logoUrl = withWidth(logoFromStyle(row.logoMaskStyle));
14343          return {
14344            name: row.name || null,
14345            description: row.description || null,
14346            worksWith: works,
14347            publisher: row.publisher || row.publisherLogoLabel || null,
14348            logoUrl: logoUrl || null,
14349            logoAltText: row.publisherLogoLabel || null,
14350            detailUrl: absUrl(row.detailPath)
14351          };
14352        });
14353
14354        var countEl = document.querySelector("#ListingResults p.text-body-xxs");
14355        var countText = text(countEl);
14356        var m = /showing\s+([\d,]+)\s*[-–—]\s*([\d,]+)\s+of\s+([\d,]+)/i.exec(countText);
14357        var showingFrom = m ? num(m[1]) : null;
14358        var showingTo = m ? num(m[2]) : null;
14359        var totalResults = m ? num(m[3]) : null;
14360        var countSource = m ? "page count line" : null;
14361
14362        if (totalResults === null && countEl) {
14363          var rc = null;
14364          try { rc = await readResultCount("#ListingResults p.text-body-xxs", null); } catch (e) { rc = null; }
14365          if (typeof rc === "number" && !isNaN(rc)) {
14366            totalResults = rc;
14367            countSource = "readResultCount";
14368          }
14369        }
14370
14371        var currentPage = null;
14372        var pageButton = document.querySelector('#ListingResults nav[aria-label="Pagination"] button[aria-current="page"]');
14373        if (pageButton) currentPage = num(pageButton.getAttribute("aria-label") || text(pageButton));
14374
14375        var lastPageNumber = null;
14376        Array.prototype.forEach.call(
14377          document.querySelectorAll('#ListingResults nav[aria-label="Pagination"] button[aria-label]'),
14378          function (b) {
14379            var label = b.getAttribute("aria-label") || "";
14380            if (/^page\s+\d+$/i.test(label.trim())) {
14381              var n = num(label);
14382              if (n !== null && (lastPageNumber === null || n > lastPageNumber)) lastPageNumber = n;
14383            }
14384          }
14385        );
14386
14387        var renderedCount = results.length;
14388        var resultCount = totalResults !== null ? totalResults : (renderedCount > 0 ? renderedCount : null);
14389        if (countSource === null && renderedCount > 0) countSource = "rendered cards";
14390
14391        var status;
14392        var reason = null;
14393        var nextStep = null;
14394        if (renderedCount > 0) {
14395          status = "ok";
14396        } else if (totalResults === 0) {
14397          status = "ok";
14398        } else {
14399          status = "partial";
14400          reason = "The results region was present but no connector cards matching #ListingResults a.group could be read from it.";
14401          nextStep = "Let the listing finish rendering after the current filter, search or pagination change, then call this tool again.";
14402        }
14403
14404        if (location.href !== startUrl) {
14405          return reply({
14406            status: "navigated",
14407            action: "read the connector listing",
14408            url: location.href,
14409            nextStep: "The page navigated while it was being read. Call optimizely-extract-connector-listing again on the page that is now loading.",
14410            summary: "The page navigated during the read, so the listing was not reported."
14411          });
14412        }
14413
14414        var listingSummary;
14415        if (status === "ok" && totalResults !== null) {
14416          listingSummary = "Read " + renderedCount + " connector card" + (renderedCount === 1 ? "" : "s") +
14417            " from the listing, out of " + totalResults + " total result" + (totalResults === 1 ? "" : "s") +
14418            (currentPage !== null ? " (page " + currentPage + (lastPageNumber !== null ? " of " + lastPageNumber : "") + ")" : "") + ".";
14419        } else if (status === "ok") {
14420          listingSummary = "Read " + renderedCount + " connector card" + (renderedCount === 1 ? "" : "s") + " from the listing.";
14421        } else {
14422          listingSummary = "The results region was found but no connector cards could be read from it.";
14423        }
14424
14425        var listingPayload = {
14426          status: status,
14427          observed: renderedCount > 0 || totalResults !== null,
14428          action: "read the connector listing",
14429          pageType: "connector-listing",
14430          resultCount: resultCount,
14431          totalResults: totalResults,
14432          renderedCount: renderedCount,
14433          showingFrom: showingFrom,
14434          showingTo: showingTo,
14435          countText: countText || null,
14436          countSource: countSource,
14437          currentPage: currentPage,
14438          lastPageNumber: lastPageNumber,
14439          truncated: extracted.truncated === true,
14440          highlighted: highlighted,
14441          results: results,
14442          url: location.href,
14443          summary: listingSummary
14444        };
14445        if (reason) listingPayload.reason = reason;
14446        if (nextStep) listingPayload.nextStep = nextStep;
14447        return reply(listingPayload);
14448      }
14449
14450      /* ---------------------------------------------------------------- */
14451      /* CONNECTOR DETAIL PAGE                                             */
14452      /* ---------------------------------------------------------------- */
14453      var heading = null;
14454      try {
14455        heading = await waitForElement("h1", 12000);
14456      } catch (e) {
14457        heading = document.querySelector("h1");
14458      }
14459      await settle(400);
14460
14461      var root = document.querySelector("article") || document.querySelector("main") || document.body;
14462      if (heading && root && !root.contains(heading)) root = document.body;
14463      var headingName = visibleText(heading) || null;
14464
14465      var KNOWN_LABELS = ["built by", "works with", "documentation", "pricing", "category", "categories"];
14466
14467      var findLabel = function (scope, wanted) {
14468        var wantedLower = wanted.toLowerCase();
14469        var nodes = (scope || document).querySelectorAll("span,p,div,h2,h3,h4,h5,h6,dt,strong,label,li");
14470        for (var i = 0; i < nodes.length; i++) {
14471          var n = nodes[i];
14472          if (n.children && n.children.length > 0) continue;
14473          var t = visibleText(n).replace(/:$/, "").trim().toLowerCase();
14474          if (t === wantedLower) return n;
14475        }
14476        return null;
14477      };
14478
14479      var valuesFor = function (labelEl) {
14480        var out = [];
14481        var push = function (t) {
14482          if (!t) return;
14483          var normalised = t.replace(/:$/, "").trim().toLowerCase();
14484          if (KNOWN_LABELS.indexOf(normalised) !== -1) return;
14485          if (out.indexOf(t) === -1) out.push(t);
14486        };
14487        if (!labelEl) return out;
14488
14489        var sibling = labelEl.nextElementSibling;
14490        while (sibling) {
14491          var siblingText = visibleText(sibling);
14492          if (KNOWN_LABELS.indexOf(siblingText.replace(/:$/, "").trim().toLowerCase()) !== -1) break;
14493          if (siblingText) {
14494            if (!sibling.children || sibling.children.length === 0) {
14495              push(siblingText);
14496            } else {
14497              var leaves = sibling.querySelectorAll("span,a,li,p,dd");
14498              var added = false;
14499              for (var i = 0; i < leaves.length; i++) {
14500                if (leaves[i].children && leaves[i].children.length > 0) continue;
14501                var leafText = visibleText(leaves[i]);
14502                if (leafText) { push(leafText); added = true; }
14503              }
14504              if (!added) push(siblingText);
14505            }
14506          }
14507          sibling = sibling.nextElementSibling;
14508        }
14509
14510        if (!out.length && labelEl.parentElement) {
14511          var kids = labelEl.parentElement.children;
14512          for (var j = 0; j < kids.length; j++) {
14513            var kid = kids[j];
14514            if (kid === labelEl || kid.contains(labelEl)) continue;
14515            push(visibleText(kid));
14516          }
14517        }
14518        return out;
14519      };
14520
14521      var linkNear = function (labelEl) {
14522        if (!labelEl) return null;
14523        var sibling = labelEl.nextElementSibling;
14524        while (sibling) {
14525          if (sibling.matches && sibling.matches("a[href]")) {
14526            return { url: absUrl(sibling.getAttribute("href")), text: visibleText(sibling) || null };
14527          }
14528          var nested = sibling.querySelector ? sibling.querySelector("a[href]") : null;
14529          if (nested) return { url: absUrl(nested.getAttribute("href")), text: visibleText(nested) || null };
14530          sibling = sibling.nextElementSibling;
14531        }
14532        var scoped = labelEl.parentElement ? labelEl.parentElement.querySelector("a[href]") : null;
14533        if (scoped) return { url: absUrl(scoped.getAttribute("href")), text: visibleText(scoped) || null };
14534        return null;
14535      };
14536
14537      var pickLogo = function (scope) {
14538        var imgs = Array.prototype.slice.call((scope || document).querySelectorAll("img[src]"));
14539        for (var i = 0; i < imgs.length; i++) {
14540          var im = imgs[i];
14541          if (im.closest && im.closest("nav, footer")) continue;
14542          if (im.getAttribute("aria-hidden") === "true") continue;
14543          var src = im.getAttribute("src") || "";
14544          if (/cmp\.optimizely\.com/i.test(src)) {
14545            return { url: withWidth(absUrl(src)), alt: im.getAttribute("alt") || null };
14546          }
14547        }
14548        var masked = (scope || document).querySelector('span[role="img"][style*="mask-image"]');
14549        if (masked) {
14550          return {
14551            url: withWidth(logoFromStyle(masked.getAttribute("style"))),
14552            alt: masked.getAttribute("aria-label") || null
14553          };
14554        }
14555        return null;
14556      };
14557
14558      var descriptionFromDom = null;
14559      if (heading) {
14560        var headingScope = heading.parentElement || root;
14561        var candidateParagraph = headingScope ? headingScope.querySelector("p") : null;
14562        if (!candidateParagraph) {
14563          var afterHeading = heading.nextElementSibling;
14564          while (afterHeading && !candidateParagraph) {
14565            if (afterHeading.matches && afterHeading.matches("p")) candidateParagraph = afterHeading;
14566            else if (afterHeading.querySelector) candidateParagraph = afterHeading.querySelector("p");
14567            afterHeading = afterHeading.nextElementSibling;
14568          }
14569        }
14570        descriptionFromDom = visibleText(candidateParagraph) || null;
14571      }
14572
14573      var builtByLabel = findLabel(root, "Built by");
14574      var worksWithLabel = findLabel(root, "Works with");
14575      var documentationLabel = findLabel(root, "Documentation");
14576
14577      var publisherValues = valuesFor(builtByLabel);
14578      var publisherFromDom = publisherValues.length ? publisherValues[0] : null;
14579
14580      var worksWithFromDom = valuesFor(worksWithLabel);
14581      if (!worksWithFromDom.length) {
14582        Array.prototype.forEach.call(root.querySelectorAll("span.font-overline"), function (chip) {
14583          if (chip.closest && chip.closest("nav")) return;
14584          var t = visibleText(chip);
14585          if (t && worksWithFromDom.indexOf(t) === -1 && KNOWN_LABELS.indexOf(t.replace(/:$/, "").toLowerCase()) === -1) {
14586            worksWithFromDom.push(t);
14587          }
14588        });
14589      }
14590
14591      var documentationLink = linkNear(documentationLabel);
14592      if (!documentationLink) {
14593        var supportAnchor = root.querySelector('a[href*="support.optimizely.com"]') || document.querySelector('a[href*="support.optimizely.com"]');
14594        if (supportAnchor) {
14595          documentationLink = { url: absUrl(supportAnchor.getAttribute("href")), text: visibleText(supportAnchor) || null };
14596        }
14597      }
14598
14599      var pricingNote = null;
14600      var pricingHeadings = root.querySelectorAll("h2,h3,h4");
14601      for (var p = 0; p < pricingHeadings.length; p++) {
14602        if (visibleText(pricingHeadings[p]).replace(/:$/, "").toLowerCase() === "pricing") {
14603          var afterPricing = pricingHeadings[p].nextElementSibling;
14604          while (afterPricing && !pricingNote) {
14605            var pricingText = visibleText(afterPricing);
14606            if (pricingText) pricingNote = pricingText;
14607            afterPricing = afterPricing.nextElementSibling;
14608          }
14609          break;
14610        }
14611      }
14612
14613      var logo = pickLogo(root);
14614
14615      /* Next.js data island, used only to fill fields the DOM did not yield,
14616         and only when the record clearly matches this connector page. */
14617      var slug = (rest.replace(/^\/+/, "").split("/")[0] || "").toLowerCase();
14618      var islandRecord = null;
14619      try {
14620        var island = document.getElementById("__NEXT_DATA__");
14621        var pageProps = island ? (JSON.parse(island.textContent || "{}").props || {}).pageProps : null;
14622        if (pageProps) {
14623          var matches = function (rec) {
14624            var recName = typeof rec.name === "string" ? rec.name : (typeof rec.title === "string" ? rec.title : null);
14625            if (headingName && recName && recName.trim().toLowerCase() === headingName.trim().toLowerCase()) return true;
14626            var recSlug = rec.slug || rec.url || rec.href || rec.path;
14627            if (slug && typeof recSlug === "string" && recSlug.toLowerCase().indexOf(slug) !== -1) return true;
14628            return false;
14629          };
14630          var queue = [{ value: pageProps, depth: 0 }];
14631          var budget = 3000;
14632          while (queue.length && budget > 0) {
14633            budget--;
14634            var node = queue.shift();
14635            var value = node.value;
14636            if (!value || typeof value !== "object" || node.depth > 6) continue;
14637            if (!Array.isArray(value)) {
14638              var hasName = typeof value.name === "string" || typeof value.title === "string";
14639              var hasDescription = typeof value.description === "string" || typeof value.summary === "string";
14640              if (hasName && hasDescription && matches(value)) { islandRecord = value; break; }
14641            }
14642            var children = Array.isArray(value) ? value : Object.keys(value).map(function (k) { return value[k]; });
14643            for (var c = 0; c < children.length; c++) {
14644              if (children[c] && typeof children[c] === "object") queue.push({ value: children[c], depth: node.depth + 1 });
14645            }
14646          }
14647        }
14648      } catch (e) { islandRecord = null; }
14649
14650      var fieldSources = {
14651        name: headingName ? "dom" : null,
14652        description: descriptionFromDom ? "dom" : null,
14653        publisher: publisherFromDom ? "dom" : null,
14654        worksWith: worksWithFromDom.length ? "dom" : null,
14655        logoUrl: (logo && logo.url) ? "dom" : null,
14656        pricingNote: pricingNote ? "dom" : null,
14657        documentationUrl: (documentationLink && documentationLink.url) ? "dom" : null
14658      };
14659
14660      var name = headingName;
14661      var description = descriptionFromDom;
14662      var publisher = publisherFromDom;
14663
14664      if (islandRecord) {
14665        if (!name && (typeof islandRecord.name === "string" || typeof islandRecord.title === "string")) {
14666          name = islandRecord.name || islandRecord.title;
14667          fieldSources.name = "next-data-island";
14668        }
14669        if (!description && (typeof islandRecord.description === "string" || typeof islandRecord.summary === "string")) {
14670          description = islandRecord.description || islandRecord.summary;
14671          fieldSources.description = "next-data-island";
14672        }
14673        if (!publisher) {
14674          var vendor = islandRecord.publisher || islandRecord.vendor || islandRecord.company || islandRecord.builtBy;
14675          if (typeof vendor === "string") { publisher = vendor; fieldSources.publisher = "next-data-island"; }
14676          else if (vendor && typeof vendor === "object" && typeof vendor.name === "string") { publisher = vendor.name; fieldSources.publisher = "next-data-island"; }
14677        }
14678      }
14679
14680      var connector = {
14681        name: name || null,
14682        description: description || null,
14683        publisher: publisher || null,
14684        worksWith: worksWithFromDom,
14685        logoUrl: (logo && logo.url) || null,
14686        logoAltText: (logo && logo.alt) || null,
14687        pricingNote: pricingNote || null,
14688        documentationUrl: (documentationLink && documentationLink.url) || null,
14689        documentationLinkText: (documentationLink && documentationLink.text) || null,
14690        detailUrl: location.href
14691      };
14692
14693      var missingFields = [];
14694      ["name", "description", "publisher", "logoUrl", "pricingNote", "documentationUrl"].forEach(function (key) {
14695        if (!connector[key]) missingFields.push(key);
14696      });
14697      if (!connector.worksWith.length) missingFields.push("worksWith");
14698
14699      var detailHighlighted = highlight(heading ? (heading.closest("section, article, header, div") || heading) : root);
14700
14701      if (location.href !== startUrl) {
14702        return reply({
14703          status: "navigated",
14704          action: "read the connector detail page",
14705          url: location.href,
14706          nextStep: "The page navigated while it was being read. Call optimizely-extract-connector-listing again on the page that is now loading.",
14707          summary: "The page navigated during the read, so the connector record was not reported."
14708        });
14709      }
14710
14711      var observedDetail = !!(connector.name || connector.description);
14712      if (!observedDetail) {
14713        return reply({
14714          status: "partial",
14715          observed: false,
14716          action: "read the connector detail page",
14717          pageType: "connector-detail",
14718          resultCount: null,
14719          results: [],
14720          connector: connector,
14721          missingFields: missingFields,
14722          fieldSources: fieldSources,
14723          highlighted: detailHighlighted,
14724          url: location.href,
14725          reason: "Neither the connector name heading nor its description paragraph could be read from the settled page.",
14726          nextStep: "Let the connector page finish rendering, then call this tool again.",
14727          summary: "The connector page was open but no name or description could be read from it."
14728        });
14729      }
14730
14731      var detailStatus = missingFields.length ? "partial" : "ok";
14732      var detailPayload = {
14733        status: detailStatus,
14734        observed: true,
14735        action: "read the connector detail page",
14736        pageType: "connector-detail",
14737        resultCount: 1,
14738        results: [connector],
14739        connector: connector,
14740        missingFields: missingFields,
14741        fieldSources: fieldSources,
14742        highlighted: detailHighlighted,
14743        url: location.href,
14744        summary: "Read the connector record for " + (connector.name || "this page") +
14745          (connector.publisher ? " published by " + connector.publisher : "") +
14746          (connector.worksWith.length ? ", works with " + connector.worksWith.join(", ") : "") + "."
14747      };
14748      if (missingFields.length) {
14749        detailPayload.reason = "These fields were not present on the rendered page: " + missingFields.join(", ") + ".";
14750        detailPayload.nextStep = "Use the fields that were read; if a missing field is required, open the connector page again once it has fully rendered and call this tool a second time.";
14751      }
14752      return reply(detailPayload);
14753
14754    } catch (err) {
14755      return reply({
14756        status: "error",
14757        action: "read the connector directory data",
14758        url: location.href,
14759        reason: "The extraction failed: " + (err && err.message ? err.message : String(err)),
14760        nextStep: "Reload the connectors page, wait for the results region to render, then call this tool again.",
14761        summary: "The connector data could not be read because the extraction raised an error."
14762      });
14763    }
14764  }
14765});
14766
14767  /* ---- optimizely-request-connector-contact-publisher, optimizely-request-connector-contact-publisher-submit ---- */
14768mc.registerToolIf("/connectors/*", {
14769  name: "optimizely-request-connector-contact-publisher",
14770  description: "Fills the 'Request this connector' enquiry form on an Optimizely connector detail page. Scrolls to and highlights the request section, writes first name, last name, email, phone including country code and message into the form fields, and ticks the 'I agree to share my information with Optimizely and the provider of this listing' consent control. Returns the values read back out of each field, the page_key and form_key as rendered for this connector, which required fields are still empty, and whether the form is ready to send. This tool does not send anything - call optimizely-request-connector-contact-publisher-submit to send the request to the publisher.",
14771  inputSchema: {
14772    type: "object",
14773    properties: {
14774      firstName: { type: "string", description: "Value for the First name field." },
14775      lastName: { type: "string", description: "Value for the Last name field." },
14776      email: { type: "string", description: "Value for the Email field, for example [email protected]." },
14777      phone: { type: "string", description: "Value for the 'Phone incl. country code' field, for example +1 415 555 0134." },
14778      message: { type: "string", description: "Message to the connector publisher describing what is being requested." },
14779      consent: { type: "boolean", description: "Whether to tick the consent control 'I agree to share my information with Optimizely and the provider of this listing'. Defaults to true; the request is not accepted without it." }
14780    },
14781    required: ["firstName", "lastName", "email"]
14782  },
14783  execute: async function (args) {
14784    trackWebMCPTool("optimizely-request-connector-contact-publisher");
14785    var a = args || {};
14786    var reply = function (payload) { return { content: [{ type: "text", text: JSON.stringify(payload) }] }; };
14787    var sleep = function (ms) { return new Promise(function (r) { setTimeout(r, ms); }); };
14788    var ACTION = "fill the connector request form";
14789    try {
14790      var FORM_SEL = "form.opti-form";
14791      var form = document.querySelector(FORM_SEL);
14792      if (!form) {
14793        var anchor = document.querySelector('a[href*="#connector-vb"]');
14794        if (anchor) { try { anchor.click(); } catch (e0) {} }
14795        try { form = await waitForElement(FORM_SEL, 8000); } catch (e1) { form = document.querySelector(FORM_SEL); }
14796      }
14797      if (!form) {
14798        return reply({
14799          status: "error",
14800          action: ACTION,
14801          url: location.href,
14802          reason: "No element matching form.opti-form was found on this page.",
14803          nextStep: "Open a connector detail page under /connectors/ (for example /connectors/wordpress/) and call this tool again.",
14804          summary: "The connector request form is not present on this page."
14805        });
14806      }
14807
14808      var section = document.querySelector("#connector-vb") || form;
14809      try { section.scrollIntoView({ behavior: "smooth", block: "center" }); } catch (e2) {}
14810      try { form.style.outline = "3px solid #0037ff"; form.style.outlineOffset = "4px"; } catch (e3) {}
14811      await sleep(250);
14812
14813      var nativeSet = function (el, value) {
14814        var proto = el.tagName === "TEXTAREA" ? window.HTMLTextAreaElement.prototype : window.HTMLInputElement.prototype;
14815        var desc = Object.getOwnPropertyDescriptor(proto, "value");
14816        if (desc && desc.set) { desc.set.call(el, value); } else { el.value = value; }
14817        el.dispatchEvent(new Event("input", { bubbles: true }));
14818        el.dispatchEvent(new Event("change", { bubbles: true }));
14819      };
14820
14821      var findControl = function (name, sel, labelText) {
14822        var el = sel ? document.querySelector(sel) : null;
14823        if (el) return el;
14824        el = form.querySelector('[name="' + name + '"]');
14825        if (el) return el;
14826        var labels = form.querySelectorAll("label");
14827        for (var i = 0; i < labels.length; i++) {
14828          var t = (labels[i].textContent || "").trim().toLowerCase();
14829          if (labelText && t.indexOf(labelText) === 0) {
14830            var forId = labels[i].getAttribute("for");
14831            if (forId) { var byId = document.getElementById(forId); if (byId) return byId; }
14832            var inner = labels[i].querySelector("input, textarea");
14833            if (inner) return inner;
14834          }
14835        }
14836        return null;
14837      };
14838
14839      var specs = [
14840        { key: "firstName", sel: 'form.opti-form input[name="firstName"]', label: "first name", value: a.firstName },
14841        { key: "lastName", sel: 'form.opti-form input[name="lastName"]', label: "last name", value: a.lastName },
14842        { key: "email", sel: 'form.opti-form input[name="email"]', label: "email", value: a.email },
14843        { key: "phone", sel: 'form.opti-form input[name="phone"]', label: "phone", value: a.phone },
14844        { key: "message", sel: 'form.opti-form textarea[name="message"]', label: "message", value: a.message }
14845      ];
14846
14847      var fields = {};
14848      var notFound = [];
14849      var mismatched = [];
14850
14851      for (var s = 0; s < specs.length; s++) {
14852        var spec = specs[s];
14853        var el = findControl(spec.key, spec.sel, spec.label);
14854        if (!el) {
14855          if (spec.value !== undefined && spec.value !== null && String(spec.value) !== "") { notFound.push(spec.key); }
14856          fields[spec.key] = null;
14857          continue;
14858        }
14859        if (spec.value !== undefined && spec.value !== null && String(spec.value) !== "") {
14860          var wanted = String(spec.value);
14861          var filled = false;
14862          if (spec.sel && document.querySelector(spec.sel)) {
14863            try { await fillInput(spec.sel, wanted); filled = true; } catch (e4) { filled = false; }
14864          }
14865          if (!filled) { nativeSet(el, wanted); }
14866          await sleep(80);
14867          if (String(el.value || "") !== wanted) {
14868            nativeSet(el, wanted);
14869            await sleep(120);
14870          }
14871          if (String(el.value || "") !== wanted) { mismatched.push(spec.key); }
14872        }
14873        fields[spec.key] = String(el.value || "");
14874      }
14875
14876      var consentWanted = (a.consent === undefined || a.consent === null) ? true : !!a.consent;
14877      var consentBtn = document.querySelector('form.opti-form button[role="checkbox"]') || form.querySelector('[role="checkbox"]');
14878      var consentInput = document.querySelector('form.opti-form input[type="checkbox"]') || form.querySelector('input[type="checkbox"]');
14879      var consentMirror = document.querySelector('form.opti-form input[name="i_have_read_and_agree_to_the_terms_and_conditions"]');
14880
14881      var consentState = function () {
14882        if (consentBtn) {
14883          var ac = consentBtn.getAttribute("aria-checked");
14884          if (ac !== null) { return ac === "true"; }
14885          if (consentBtn.getAttribute("data-state")) { return consentBtn.getAttribute("data-state") === "checked"; }
14886        }
14887        if (consentInput) { return !!consentInput.checked; }
14888        if (consentMirror) {
14889          return String(consentMirror.value || "").trim() !== "";
14890        }
14891        return false;
14892      };
14893
14894      var consentControlFound = !!(consentBtn || consentInput);
14895      if (consentControlFound) {
14896        if (consentBtn && consentState() !== consentWanted) {
14897          try { consentBtn.click(); } catch (e5) {}
14898          await sleep(200);
14899        }
14900        if (consentState() !== consentWanted && consentInput) {
14901          var done = false;
14902          if (document.querySelector('form.opti-form input[type="checkbox"]')) {
14903            try { await fillCheckbox('form.opti-form input[type="checkbox"]', consentWanted); done = true; } catch (e6) { done = false; }
14904          }
14905          if (!done) { try { consentInput.click(); } catch (e7) {} }
14906          await sleep(200);
14907        }
14908      }
14909      var consentNow = consentControlFound ? consentState() : false;
14910      fields.consent = consentNow;
14911
14912      var readHidden = function (sel, name) {
14913        var el = document.querySelector(sel) || form.querySelector('[name="' + name + '"]');
14914        return el ? String(el.value || "") : null;
14915      };
14916      var pageKey = readHidden('form.opti-form input[name="page_key"]', "page_key");
14917      var formKey = readHidden('form.opti-form input[name="form_key"]', "form_key");
14918      var consentMirrorValue = consentMirror ? String(consentMirror.value || "") : null;
14919
14920      var missing = [];
14921      var requiredKeys = ["firstName", "lastName", "email"];
14922      for (var r = 0; r < requiredKeys.length; r++) {
14923        var k = requiredKeys[r];
14924        if (fields[k] === null || fields[k] === undefined || String(fields[k]).trim() === "") { missing.push(k); }
14925      }
14926      if (!consentControlFound) { missing.push("consent (control not found)"); }
14927      else if (!consentNow) { missing.push("consent"); }
14928
14929      var captchaEl = form.querySelector('iframe[src*="recaptcha"], iframe[src*="hcaptcha"], .g-recaptcha, .h-captcha, [data-sitekey]');
14930
14931      var ready = missing.length === 0;
14932      var problems = notFound.length > 0 || mismatched.length > 0 || (consentControlFound && consentNow !== consentWanted) || !consentControlFound;
14933      var status = problems ? "partial" : (ready ? "ok" : "partial");
14934
14935      var payload = {
14936        status: status,
14937        action: ACTION,
14938        url: location.href,
14939        fields: fields,
14940        missing: missing,
14941        ready: ready,
14942        pageKey: pageKey,
14943        formKey: formKey,
14944        consentMirrorValue: consentMirrorValue,
14945        captchaDetected: !!captchaEl,
14946        submitToolName: "optimizely-request-connector-contact-publisher-submit",
14947        summary: ready
14948          ? "Filled the connector request form and ticked the consent control; the values are in the form and ready to send."
14949          : "Filled the connector request form, but " + missing.length + " required item(s) are still outstanding: " + missing.join(", ") + "."
14950      };
14951      if (status !== "ok") {
14952        var reasons = [];
14953        if (notFound.length) { reasons.push("no control on the page for: " + notFound.join(", ")); }
14954        if (mismatched.length) { reasons.push("the page did not keep the value written into: " + mismatched.join(", ")); }
14955        if (!consentControlFound) { reasons.push("the consent control was not found inside form.opti-form"); }
14956        else if (consentNow !== consentWanted) { reasons.push("the consent control stayed " + (consentNow ? "ticked" : "unticked")); }
14957        if (!reasons.length && missing.length) { reasons.push("required fields still empty: " + missing.join(", ")); }
14958        payload.reason = reasons.join("; ") + ".";
14959        payload.nextStep = "Call this tool again with the outstanding values, or fill them by hand on the page, then call optimizely-request-connector-contact-publisher-submit.";
14960      } else {
14961        payload.nextStep = "Call optimizely-request-connector-contact-publisher-submit to send the request to the connector publisher.";
14962      }
14963      return reply(payload);
14964    } catch (err) {
14965      return reply({
14966        status: "error",
14967        action: ACTION,
14968        url: location.href,
14969        reason: "The tool threw while filling the form: " + (err && err.message ? err.message : String(err)),
14970        nextStep: "Check that the 'Request this connector' section is present on the page, then try again.",
14971        summary: "Could not complete filling the connector request form."
14972      });
14973    }
14974  }
14975});
14976
14977mc.registerToolIf("/connectors/*", {
14978  name: "optimizely-request-connector-contact-publisher-submit",
14979  description: "Sends the connector request that is currently filled in on this Optimizely connector detail page b
14979y clicking 'Contact publisher', waits for the page to respond, and returns the confirmation text from the success panel that replaces the form, any validation messages read from the form, the values that were sent and the connector's page_key and form_key. Run optimizely-request-connector-contact-publisher first; sending contacts the connector publisher and cannot be undone.",
14980  inputSchema: {
14981    type: "object",
14982    properties: {
14983      confirm: { type: "boolean", description: "Set true to confirm sending the request to the connector publisher. Defaults to true." }
14984    }
14985  },
14986  execute: async function (args) {
14987    trackWebMCPTool("optimizely-request-connector-contact-publisher-submit");
14988    var a = args || {};
14989    var reply = function (payload) { return { content: [{ type: "text", text: JSON.stringify(payload) }] }; };
14990    var sleep = function (ms) { return new Promise(function (r) { setTimeout(r, ms); }); };
14991    var ACTION = "submit the connector request form";
14992    try {
14993      if (a.confirm === false) {
14994        return reply({
14995          status: "error",
14996          action: ACTION,
14997          url: location.href,
14998          validationErrors: [],
14999          confirmation: null,
15000          reason: "The caller passed confirm: false, so nothing was sent.",
15001          nextStep: "Call this tool again with confirm: true when the request should go to the connector publisher.",
15002          summary: "Submission cancelled; the connector request was not sent."
15003        });
15004      }
15005
15006      var FORM_SEL = "form.opti-form";
15007      var form = document.querySelector(FORM_SEL);
15008      if (!form) {
15009        try { form = await waitForElement(FORM_SEL, 6000); } catch (e0) { form = document.querySelector(FORM_SEL); }
15010      }
15011      if (!form) {
15012        return reply({
15013          status: "error",
15014          action: ACTION,
15015          url: location.href,
15016          validationErrors: [],
15017          confirmation: null,
15018          reason: "No element matching form.opti-form was found on this page.",
15019          nextStep: "Open a connector detail page under /connectors/, fill the form with optimizely-request-connector-contact-publisher, then call this tool again.",
15020          summary: "The connector request form is not present on this page."
15021        });
15022      }
15023
15024      var val = function (sel, name) {
15025        var el = document.querySelector(sel) || form.querySelector('[name="' + name + '"]');
15026        return el ? String(el.value || "") : null;
15027      };
15028      var fieldsSent = {
15029        firstName: val('form.opti-form input[name="firstName"]', "firstName"),
15030        lastName: val('form.opti-form input[name="lastName"]', "lastName"),
15031        email: val('form.opti-form input[name="email"]', "email"),
15032        phone: val('form.opti-form input[name="phone"]', "phone"),
15033        message: val('form.opti-form textarea[name="message"]', "message")
15034      };
15035      var pageKey = val('form.opti-form input[name="page_key"]', "page_key");
15036      var formKey = val('form.opti-form input[name="form_key"]', "form_key");
15037
15038      var consentBtn = document.querySelector('form.opti-form button[role="checkbox"]') || form.querySelector('[role="checkbox"]');
15039      var consentInput = document.querySelector('form.opti-form input[type="checkbox"]') || form.querySelector('input[type="checkbox"]');
15040      var consentMirror = document.querySelector('form.opti-form input[name="i_have_read_and_agree_to_the_terms_and_conditions"]');
15041      var consentState = function () {
15042        if (consentBtn) {
15043          var ac = consentBtn.getAttribute("aria-checked");
15044          if (ac !== null) { return ac === "true"; }
15045          if (consentBtn.getAttribute("data-state")) { return consentBtn.getAttribute("data-state") === "checked"; }
15046        }
15047        if (consentInput) { return !!consentInput.checked; }
15048        if (consentMirror) {
15049          return String(consentMirror.value || "").trim() !== "";
15050        }
15051        return false;
15052      };
15053      fieldsSent.consent = consentState();
15054
15055      if (!fieldsSent.consent) {
15056        return reply({
15057          status: "error",
15058          action: ACTION,
15059          url: location.href,
15060          validationErrors: ["I agree to share my information with Optimizely and the provider of this listing: n
15060ot ticked"],
15061          confirmation: null,
15062          fieldsSent: fieldsSent,
15063          pageKey: pageKey,
15064          formKey: formKey,
15065          reason: "The consent control in form.opti-form is not ticked, and the request is not accepted without it.",
15066          nextStep: "Call optimizely-request-connector-contact-publisher with consent: true, then call this tool again.",
15067          summary: "Did not send: the consent control is not ticked."
15068        });
15069      }
15070
15071      var labelFor = function (el) {
15072        if (!el) { return "field"; }
15073        var id = el.getAttribute("id");
15074        if (id) {
15075          var lab = form.querySelector('label[for="' + id + '"]');
15076          if (lab && (lab.textContent || "").trim()) { return (lab.textContent || "").trim(); }
15077        }
15078        var aria = el.getAttribute("aria-label");
15079        if (aria) { return aria.trim(); }
15080        var ph = el.getAttribute("placeholder");
15081        if (ph) { return ph.trim(); }
15082        return el.getAttribute("name") || el.tagName.toLowerCase();
15083      };
15084
15085      var textOf = function (node) {
15086        if (!node) { return ""; }
15087        if (node.getAttribute && node.getAttribute("aria-hidden") === "true") { return ""; }
15088        return String(node.textContent || "").replace(/\s+/g, " ").trim();
15089      };
15090
15091      var IN_PROGRESS_TEXT = /^(submitting|loading)[.…]*$/i;
15092
15093      var readConfirmationText = function () {
15094        var scope = document.querySelector("#connector-vb") || document;
15095        var nodes = scope.querySelectorAll(".form-success-page, .form-confirmation");
15096        for (var i = 0; i < nodes.length; i++) {
15097          var t = textOf(nodes[i]);
15098          if (t && !IN_PROGRESS_TEXT.test(t)) { return t; }
15099        }
15100        return "";
15101      };
15102
15103      var readValidationErrors = function () {
15104        var out = [];
15105        var seen = {};
15106        var f = document.querySelector(FORM_SEL);
15107        if (!f) { return out; }
15108        var push = function (msg) {
15109          var m = String(msg || "").replace(/\s+/g, " ").trim();
15110          if (!m || m.length > 300 || seen[m]) { return; }
15111          seen[m] = true;
15112          out.push(m);
15113        };
15114        var nodes = f.querySelectorAll('[role="alert"], [class*="error"], [class*="Error"], [data-error]');
15115        for (var i = 0; i < nodes.length && out.length < 10; i++) { push(textOf(nodes[i])); }
15116        var invalids = f.querySelectorAll('[aria-invalid="true"]');
15117        for (var k = 0; k < invalids.length && out.length < 10; k++) {
15118          var el = invalids[k];
15119          var described = el.getAttribute("aria-describedby");
15120          var msg = "";
15121          if (described) {
15122            var ids = described.split(/\s+/);
15123            for (var d = 0; d < ids.length; d++) {
15124              var dn = document.getElementById(ids[d]);
15125              var dt = textOf(dn);
15126              if (dt) { msg = dt; break; }
15127            }
15128          }
15129          if (!msg && el.validationMessage) { msg = el.validationMessage; }
15130          push(labelFor(el) + ": " + (msg || "is not valid"));
15131        }
15132        var ctrls = f.querySelectorAll("input, textarea, select");
15133        for (var c = 0; c < ctrls.length && out.length < 10; c++) {
15134          var ctl = ctrls[c];
15135          if (ctl.willValidate && ctl.validity && !ctl.validity.valid && ctl.validationMessage) {
15136            push(labelFor(ctl) + ": " + ctl.validationMessage);
15137          }
15138        }
15139        return out;
15140      };
15141
15142      var submitBtn = document.querySelector('form.opti-form button[type="submit"]') || form.querySelector('button[type="submit"]');
15143      if (!submitBtn) {
15144        return reply({
15145          status: "error",
15146          action: ACTION,
15147          url: location.href,
15148          validationErrors: [],
15149          confirmation: null,
15150          fieldsSent: fieldsSent,
15151          pageKey: pageKey,
15152          formKey: formKey,
15153          reason: "The 'Contact publisher' submit button (form.opti-form button[type=\"submit\"]) was not found.",
15154          nextStep: "Scroll to the 'Request this connector' section and check the form rendered fully, then call this tool again.",
15155          summary: "Could not find the 'Contact publisher' button."
15156        });
15157      }
15158
15159      var urlBefore = location.href;
15160      var baseBefore = urlBefore.split("#")[0];
15161      var actionAttr = String(form.getAttribute("action") || "").trim();
15162      var isNativeSubmit = actionAttr !== "" && !/^javascript:/i.test(actionAttr) && actionAttr.charAt(0) !== "#";
15163
15164      if (isNativeSubmit) {
15165        var dest = actionAttr;
15166        try { dest = new URL(actionAttr, location.href).toString(); } catch (e1) { dest = actionAttr; }
15167        var navPayload = {
15168          status: "navigated",
15169          action: ACTION,
15170          url: dest,
15171          fieldsSent: fieldsSent,
15172          pageKey: pageKey,
15173          formKey: formKey,
15174          validationErrors: [],
15175          confirmation: null,
15176          nextStep: "The browser is now loading " + dest + "; read the confirmation on that page once it has settled.",
15177          summary: "Clicked 'Contact publisher' on a native submit form, so the browser is navigating to the form action URL."
15178        };
15179        submitBtn.click();
15180        return reply(navPayload);
15181      }
15182
15183      submitBtn.click();
15184
15185      var deadline = Date.now() + 15000;
15186      var confirmation = "";
15187      var errors = [];
15188      var formGone = false;
15189      var navigated = false;
15190      while (Date.now() < deadline) {
15191        await sleep(400);
15192        if (location.href.split("#")[0] !== baseBefore) { navigated = true; break; }
15193        confirmation = readConfirmationText();
15194        errors = readValidationErrors();
15195        formGone = !document.querySelector(FORM_SEL);
15196        if (confirmation || errors.length > 0) { break; }
15197      }
15198      if (!navigated && !confirmation && errors.length === 0) {
15199        await sleep(1000);
15200        confirmation = readConfirmationText();
15201        errors = readValidationErrors();
15202        formGone = !document.querySelector(FORM_SEL);
15203      }
15204
15205      if (navigated) {
15206        return reply({
15207          status: "navigated",
15208          action: ACTION,
15209          url: location.href,
15210          fieldsSent: fieldsSent,
15211          pageKey: pageKey,
15212          formKey: formKey,
15213          validationErrors: [],
15214          confirmation: null,
15215          nextStep: "The page changed after clicking 'Contact publisher'; read the confirmation on the page that is now loaded.",
15216          summary: "Clicking 'Contact publisher' moved the browser to " + location.href + "."
15217        });
15218      }
15219
15220      if (errors.length > 0) {
15221        return reply({
15222          status: "error",
15223          action: ACTION,
15224          url: location.href,
15225          validationErrors: errors,
15226          confirmation: confirmation || null,
15227          fieldsSent: fieldsSent,
15228          pageKey: pageKey,
15229          formKey: formKey,
15230          submitted: false,
15231          reason: "The form reported validation problems after clicking 'Contact publisher': " + errors.join("; "),
15232          nextStep: "Fix the flagged values with optimizely-request-connector-contact-publisher, then call this tool again.",
15233          summary: "The connector request was not accepted; the form shows " + errors.length + " validation message(s)."
15234        });
15235      }
15236
15237      if (confirmation) {
15238        return reply({
15239          status: "ok",
15240          action: ACTION,
15241          url: location.href,
15242          validationErrors: [],
15243          confirmation: confirmation,
15244          fieldsSent: fieldsSent,
15245          pageKey: pageKey,
15246          formKey: formKey,
15247          submitted: true,
15248          summary: "Sent the connector request; the page responded: " + confirmation
15249        });
15250      }
15251
15252      return reply({
15253        status: "partial",
15254        action: ACTION,
15255        url: location.href,
15256        validationErrors: [],
15257        confirmation: null,
15258        observed: false,
15259        submitted: null,
15260        formStillPresent: !formGone,
15261        fieldsSent: fieldsSent,
15262        pageKey: pageKey,
15263        formKey: formKey,
15264        reason: formGone
15265          ? "The form was removed from the page after clicking 'Contact publisher', but no success panel text could be read, so the confirmation wording is unknown."
15266          : "Clicked 'Contact publisher' but no success panel and no validation text appeared within about 16 seconds.",
15267        nextStep: "Look at the 'Request this connector' section on the page to see whether a confirmation appeared, and only resend if it clearly did not.",
15268        summary: "Clicked 'Contact publisher' but could not read an outcome from the page, so the request may or may not have gone through."
15269      });
15270    } catch (err) {
15271      return reply({
15272        status: "error",
15273        action: ACTION,
15274        url: location.href,
15275        validationErrors: [],
15276        confirmation: null,
15277        reason: "The tool threw while submitting the form: " + (err && err.message ? err.message : String(err)),
15278        nextStep: "Check the 'Request this connector' section on the page for a confirmation before trying again.",
15279        summary: "Could not complete the connector request submission."
15280      });
15281    }
15282  }
15283});
15284
15285  /* ---- optimizely-browse-field-notes-library, optimizely-browse-field-notes-library-results ---- */
15286mc.registerToolIf("/field-notes/**", {
15287  name: "optimizely-browse-field-notes-library",
15288  description: "Browse and filter the Field Notes resource library on the current listing page: applies a topic filter and/or a free-text keyword and can jump to a page number. Returns the resources rendered in the listing as objects with title, content type, publication date, reading time where shown, topic tags, excerpt, thumbnail image URL and absolute URL, together with the listing's own 'Showing X-Y of N results' total and the filter options that are actually on the page at call time.",
15289  inputSchema: {
15290    type: "object",
15291    properties: {
15292      topic: {
15293        type: "string",
15294        description: "Topic to filter by, spelled as it appears on the page (for example AI, Analytics, Commerce, Content management, Experimentation, Marketing, Personalization). The options are read off the live page at call time; an unrecognised value comes back with availableOptions so the call can be retried."
15295      },
15296      keyword: {
15297        type: "string",
15298        description: "Free-text keyword to search the listing for."
15299      },
15300      page: {
15301        type: "integer",
15302        minimum: 1,
15303        description: "Listing page number to show. Defaults to the page currently displayed."
15304      },
15305      limit: {
15306        type: "integer",
15307        minimum: 1,
15308        maximum: 50,
15309        description: "Maximum number of resources to return. Defaults to 12."
15310      }
15311    },
15312    required: []
15313  },
15314  execute: async function (args) {
15315    trackWebMCPTool("optimizely-browse-field-notes-library");
15316
15317    var input = args || {};
15318    var limit = Math.max(1, Math.min(50, parseInt(input.limit, 10) || 12));
15319    var COUNT_SELECTOR = 'div:has(> p + nav) > p';
15320    var ITEM_SELECTOR = 'a[href^="/field-notes/"]';
15321    var SCOPE_ATTR = "data-webmcp-fieldnotes";
15322    var SCOPED_ITEMS = '[data-webmcp-fieldnotes="scope"] a[href^="/field-notes/"]';
15323    var COUNT_RE = /showing\s+[\d,]+\s*[-–]\s*[\d,]+\s+of\s+[\d,]+\s+results?/i;
15324    var DATE_RE = /^[A-Za-z]{3,9}\.?\s+\d{1,2},\s*\d{4}$/;
15325    var TIME_RE = /^\d+\s*(minute|minutes|min|mins|hour|hours|hr|hrs)\b/i;
15326    var FIELD_MAP = {
15327      url: "@href",
15328      title: "h3",
15329      excerpt: "p",
15330      thumbnail: "img @src",
15331      thumbnailAlt: "img @alt",
15332      contentType: "div > div > div:nth-child(2) > div:first-child > div > span:nth-child(2)",
15333      publicationDate: "div > div > div:nth-child(2) > div:first-child > span:nth-child(2)",
15334      readingTime: "div > div > div:nth-child(2) > div:first-child > span:nth-child(3)"
15335    };
15336
15337    var startUrl = location.href;
15338    var unloading = false;
15339    var markUnload = function () { unloading = true; };
15340    window.addEventListener("beforeunload", markUnload);
15341    window.addEventListener("pagehide", markUnload);
15342
15343    function done(payload) {
15344      try {
15345        window.removeEventListener("beforeunload", markUnload);
15346        window.removeEventListener("pagehide", markUnload);
15347      } catch (e) {}
15348      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
15349    }
15350
15351    function txt(el) {
15352      return el && el.textContent ? el.textContent.replace(/\s+/g, " ").trim() : "";
15353    }
15354    function clean(v) {
15355      return v == null ? "" : String(v).replace(/\s+/g, " ").trim();
15356    }
15357    function norm(v) {
15358      return clean(v).toLowerCase();
15359    }
15360    function sleep(ms) {
15361      return new Promise(function (r) { setTimeout(r, ms); });
15362    }
15363    function isVisible(el) {
15364      if (!el) return false;
15365      try {
15366        if (el.closest && el.closest('[aria-hidden="true"]')) return false;
15367        if (el.getAttribute && el.getAttribute("aria-hidden") === "true") return false;
15368        var r = el.getBoundingClientRect();
15369        return !!(r.width || r.height || el.getClientRects().length);
15370      } catch (e) { return true; }
15371    }
15372    function isIconText(t) {
15373      return /^[a-z0-9]+(_[a-z0-9]+)*$/.test(t || "");
15374    }
15375
15376    var cachedCount = null;
15377    function findCountEl() {
15378      if (cachedCount && cachedCount.isConnected && /showing/i.test(txt(cachedCount))) return cachedCount;
15379      var el = null;
15380      try { el = document.querySelector(COUNT_SELECTOR); } catch (e) { el = null; }
15381      if (el && /showing/i.test(txt(el))) { cachedCount = el; return el; }
15382      var nodes = document.querySelectorAll("p, span");
15383      for (var i = 0; i < nodes.length; i++) {
15384        if (nodes[i].children.length) continue;
15385        if (COUNT_RE.test(txt(nodes[i]))) { cachedCount = nodes[i]; return nodes[i]; }
15386      }
15387      cachedCount = null;
15388      return null;
15389    }
15390
15391    function collectCards(scope) {
15392      var out = [];
15393      var seen = {};
15394      var list = scope.querySelectorAll(ITEM_SELECTOR);
15395      for (var i = 0; i < list.length; i++) {
15396        var node = list[i];
15397        var href = node.getAttribute("href") || "";
15398        var parts = href.split("?")[0].split("#")[0].split("/").filter(Boolean);
15399        if (parts.length < 3) continue;
15400        if (!node.querySelector("h3")) continue;
15401        if (!isVisible(node)) continue;
15402        if (seen[href]) continue;
15403        seen[href] = true;
15404        out.push(node);
15405      }
15406      return out;
15407    }
15408
15409    function findListingScope(countEl) {
15410      var node = countEl ? countEl.parentElement : null;
15411      var hops = 0;
15412      while (node && hops < 14) {
15413        if (collectCards(node).length >= 2) return node;
15414        node = node.parentElement;
15415        hops++;
15416      }
15417      var main = document.querySelector("main");
15418      if (main && collectCards(main).length >= 2) return main;
15419      return document.body;
15420    }
15421
15422    function signature() {
15423      var c = findCountEl();
15424      var scope = findListingScope(c);
15425      var hrefs = collectCards(scope).map(function (x) { return x.getAttribute("href"); }).join("|");
15426      return (c ? txt(c) : "") + "||" + hrefs;
15427    }
15428
15429    async function settle(baseline, timeout) {
15430      var t0 = Date.now();
15431      while (Date.now() - t0 < timeout) {
15432        await sleep(250);
15433        if (unloading) return "unloading";
15434        if (signature() !== baseline) {
15435          var stable = signature();
15436          var stableSince = Date.now();
15437          while (Date.now() - t0 < timeout) {
15438            await sleep(200);
15439            if (unloading) return "unloading";
15440            var now = signature();
15441            if (now === stable) {
15442              if (Date.now() - stableSince > 400) return "changed";
15443            } else {
15444              stable = now;
15445              stableSince = Date.now();
15446            }
15447          }
15448          return "changed";
15449        }
15450      }
15451      return "unchanged";
15452    }
15453
15454    function groupOf(label) {
15455      try {
15456        var node = label;
15457        var hops = 0;
15458        while (node && node !== document.body && hops < 6) {
15459          var prev = node.previousElementSibling;
15460          while (prev) {
15461            var head = null;
15462            if (prev.matches && prev.matches("h2, h3, h4, h5")) head = prev;
15463            else if (prev.querySelector) head = prev.querySelector("h2, h3, h4, h5");
15464            if (head && txt(head)) return txt(head);
15465            prev = prev.previousElementSibling;
15466          }
15467          node = node.parentElement;
15468          hops++;
15469        }
15470      } catch (e) {}
15471      return "";
15472    }
15473
15474    function facetControls() {
15475      var out = [];
15476      var labels = document.querySelectorAll("aside label");
15477      for (var i = 0; i < labels.length; i++) {
15478        var lb = labels[i];
15479        if (!isVisible(lb)) continue;
15480        var name = "";
15481        var hidden = lb.querySelector("input[name]");
15482        if (hidden) name = clean(hidden.getAttribute("name"));
15483        if (!name) {
15484          var spans = lb.querySelectorAll("span");
15485          for (var s = spans.length - 1; s >= 0; s--) {
15486            if (spans[s].children.length) continue;
15487            var st = txt(spans[s]);
15488            if (st) { name = st; break; }
15489          }
15490        }
15491        if (!name) name = txt(lb);
15492        if (!name) continue;
15493        var control = lb.querySelector('button[role="checkbox"]') || lb.querySelector("button") || lb.querySelector('input[type="checkbox"]') || lb;
15494        out.push({ label: name, group: groupOf(lb) || "Filters", el: control, kind: "facet" });
15495      }
15496      return out;
15497    }
15498
15499    function chipControls(countEl) {
15500      var rows = [];
15501      var seenParents = [];
15502      var buttons = document.querySelectorAll("button");
15503      for (var i = 0; i < buttons.length; i++) {
15504        var parent = buttons[i].parentElement;
15505        if (!parent || seenParents.indexOf(parent) !== -1) continue;
15506        seenParents.push(parent);
15507        if (parent.closest("nav") || parent.closest("footer") || parent.closest("header") || parent.closest("dialog") || parent.closest("aside")) continue;
15508        var kids = [];
15509        var numeric = 0;
15510        for (var j = 0; j < parent.children.length; j++) {
15511          var child = parent.children[j];
15512          if (!child.tagName || child.tagName !== "BUTTON") continue;
15513          if (!isVisible(child)) continue;
15514          var t = txt(child);
15515          if (!t || t === "..." || t.length > 40) continue;
15516          if (/^\d+$/.test(t)) numeric++;
15517          kids.push({ label: t, el: child, kind: "chip" });
15518        }
15519        if (kids.length >= 3 && numeric < kids.length / 2) rows.push({ parent: parent, options: kids });
15520      }
15521      if (!rows.length) return [];
15522      if (countEl) {
15523        for (var r = 0; r < rows.length; r++) {
15524          var hops = 0;
15525          var node = countEl;
15526          while (node && !node.contains(rows[r].parent)) { node = node.parentElement; hops++; }
15527          rows[r].distance = node ? hops : 999;
15528        }
15529        rows.sort(function (x, y) { return (x.distance || 0) - (y.distance || 0); });
15530      }
15531      return rows[0].options.map(function (o) {
15532        o.group = "Topics";
15533        return o;
15534      });
15535    }
15536
15537    function filterOptions() {
15538      var facets = facetControls();
15539      if (facets.length) return facets;
15540      return chipControls(findCountEl());
15541    }
15542
15543    function revealButtonPresent() {
15544      var btns = document.querySelectorAll("aside button, main button");
15545      for (var i = 0; i < btns.length; i++) {
15546        if (txt(btns[i]) === "..." && isVisible(btns[i]) && !btns[i].closest("nav")) return true;
15547      }
15548      return false;
15549    }
15550
15551    async function revealHiddenOptions() {
15552      var clicked = 0;
15553      var btns = document.querySelectorAll("aside button, main button");
15554      for (var i = 0; i < btns.length && clicked < 5; i++) {
15555        if (txt(btns[i]) !== "..." || !isVisible(btns[i])) continue;
15556        if (btns[i].closest("nav")) continue;
15557        try { btns[i].click(); clicked++; } catch (e) {}
15558      }
15559      if (clicked) await sleep(450);
15560      return clicked;
15561    }
15562
15563    async function expandFacetGroups() {
15564      var aside = document.querySelector("aside");
15565      if (!aside) return;
15566      var collapsed = aside.querySelectorAll('button[aria-expanded="false"]');
15567      var clicked = 0;
15568      for (var i = 0; i < collapsed.length && clicked < 5; i++) {
15569        try { collapsed[i].click(); clicked++; } catch (e) {}
15570      }
15571      if (!clicked) {
15572        var heads = aside.querySelectorAll("h4, h3");
15573        for (var h = 0; h < heads.length && clicked < 5; h++) {
15574          var header = heads[h].parentElement;
15575          var group = header ? header.parentElement : null;
15576          if (!group || group.querySelector("label")) continue;
15577          var toggle = header.querySelector("button");
15578          if (!toggle) continue;
15579          try { toggle.click(); clicked++; } catch (e) {}
15580        }
15581      }
15582      if (clicked) await sleep(400);
15583    }
15584
15585    async function openFilterPanel() {
15586      var btns = document.querySelectorAll("button");
15587      for (var i = 0; i < btns.length; i++) {
15588        var b = btns[i];
15589        if (!isVisible(b)) continue;
15590        if (b.closest("nav") || b.closest("footer") || b.closest("header")) continue;
15591        if (/^filters?(\s*\(\d+\))?$/i.test(txt(b))) {
15592          try { b.click(); } catch (e) { return false; }
15593          await sleep(700);
15594          return true;
15595        }
15596      }
15597      return false;
15598    }
15599
15600    function findSearchSelector() {
15601      var candidates = ['aside form input[type="search"]', 'form input[type="search"]', 'input[type="search"]'];
15602      for (var i = 0; i < candidates.length; i++) {
15603        var el = document.querySelector(candidates[i]);
15604        if (el && isVisible(el)) return candidates[i];
15605      }
15606      return null;
15607    }
15608
15609    function findSearchTrigger() {
15610      var btns = document.querySelectorAll("button");
15611      for (var i = 0; i < btns.length; i++) {
15612        var b = btns[i];
15613        if (!isVisible(b)) continue;
15614        if (b.closest("nav") || b.closest("footer") || b.closest("header")) continue;
15615        var label = b.getAttribute("aria-label") || b.getAttribute("title") || "";
15616        if (norm(txt(b)) === "search" || /search/i.test(label)) return b;
15617      }
15618      return null;
15619    }
15620
15621    function paginationNavs() {
15622      var out = [];
15623      var labelled = document.querySelectorAll('nav[aria-label="Pagination"]');
15624      for (var i = 0; i < labelled.length; i++) out.push(labelled[i]);
15625      var c = findCountEl();
15626      if (c && c.parentElement) {
15627        var navs = c.parentElement.querySelectorAll("nav");
15628        for (var k = 0; k < navs.length; k++) if (out.indexOf(navs[k]) === -1) out.push(navs[k]);
15629        if (c.parentElement.parentElement) {
15630          var navs2 = c.parentElement.parentElement.querySelectorAll("nav");
15631          for (var m = 0; m < navs2.length; m++) if (out.indexOf(navs2[m]) === -1) out.push(navs2[m]);
15632        }
15633      }
15634      return out;
15635    }
15636
15637    function findPageButton(n) {
15638      var navs = paginationNavs();
15639      for (var i = 0; i < navs.length; i++) {
15640        var direct = navs[i].querySelector('button[aria-label="Page ' + n + '"]');
15641        if (direct && isVisible(direct)) return direct;
15642        var btns = navs[i].querySelectorAll("button");
15643        for (var j = 0; j < btns.length; j++) {
15644          if (txt(btns[j]) === String(n) && isVisible(btns[j])) return btns[j];
15645        }
15646      }
15647      return null;
15648    }
15649
15650    function parseRange(text) {
15651      var out = { start: null, end: null, total: null, page: null, totalPages: null };
15652      if (!text) return out;
15653      var t = String(text).replace(/,/g, "");
15654      var m = /showing\s+(\d+)\s*[-–]\s*(\d+)\s+of\s+(\d+)/i.exec(t);
15655      if (!m) {
15656        var m2 = /of\s+(\d+)\s+results?/i.exec(t);
15657        if (m2) out.total = parseInt(m2[1], 10);
15658        return out;
15659      }
15660      out.start = parseInt(m[1], 10);
15661      out.end = parseInt(m[2], 10);
15662      out.total = parseInt(m[3], 10);
15663      var size = out.end - out.start + 1;
15664      if (size > 0) {
15665        out.page = Math.floor((out.start - 1) / size) + 1;
15666        out.totalPages = Math.ceil(out.total / size);
15667      }
15668      return out;
15669    }
15670
15671    function typeFromHref(href) {
15672      var m = /^\/field-notes\/([^\/]+)\//.exec(href || "");
15673      if (!m) return "";
15674      var map = { articles: "Article", guides: "Guide", videos: "Video", "customer-stories": "Customer story", events: "Event" };
15675      return map[m[1]] || "";
15676    }
15677
15678    function leafSpans(card) {
15679      var out = [];
15680      var spans = card.querySelectorAll("span");
15681      for (var i = 0; i < spans.length; i++) {
15682        var s = spans[i];
15683        if (s.children.length) continue;
15684        if (s.getAttribute("aria-hidden") === "true") continue;
15685        if (s.closest('[aria-hidden="true"]')) continue;
15686        var t = txt(s);
15687        if (!t) continue;
15688        out.push({ el: s, text: t });
15689      }
15690      return out;
15691    }
15692
15693    function describeCard(card, base) {
15694      base = base || {};
15695      var href = card.getAttribute("href") || "";
15696      var absolute = href;
15697      try { absolute = new URL(href, location.origin).href; } catch (e) {}
15698
15699      var leaves = leafSpans(card);
15700      var typeText = "";
15701      var dateText = "";
15702      var timeText = "";
15703      var loose = [];
15704      for (var i = 0; i < leaves.length; i++) {
15705        var t = leaves[i].text;
15706        if (isIconText(t)) continue;
15707        if (!typeText && i > 0 && isIconText(leaves[i - 1].text)) { typeText = t; continue; }
15708        if (!dateText && DATE_RE.test(t)) { dateText = t; continue; }
15709        if (!timeText && TIME_RE.test(t)) { timeText = t; continue; }
15710        loose.push(t);
15711      }
15712
15713      var titleEl = card.querySelector("h3");
15714      var title = clean(base.title) || txt(titleEl);
15715      var excerptEl = card.querySelector("p");
15716      var excerpt = clean(base.excerpt) || txt(excerptEl);
15717      var img = card.querySelector("img");
15718      var thumbnail = clean(base.thumbnail) || (img ? clean(img.getAttribute("src")) : "");
15719      var thumbnailAlt = base.thumbnailAlt != null ? clean(base.thumbnailAlt) : (img ? clean(img.getAttribute("alt")) : "");
15720
15721      var contentType = clean(base.contentType);
15722      if (!contentType || isIconText(contentType) || DATE_RE.test(contentType) || TIME_RE.test(contentType)) contentType = typeText;
15723      if (!contentType || isIconText(contentType)) contentType = typeFromHref(href);
15724
15725      var publicationDate = clean(base.publicationDate);
15726      if (!DATE_RE.test(publicationDate)) publicationDate = DATE_RE.test(dateText) ? dateText : "";
15727
15728      var readingTime = clean(base.readingTime);
15729      if (!TIME_RE.test(readingTime)) readingTime = TIME_RE.test(timeText) ? timeText : "";
15730
15731      var topics = [];
15732      for (var k = 0; k < loose.length; k++) {
15733        var tag = loose[k];
15734        if (!tag || tag.length > 60) continue;
15735        if (tag === contentType || tag === publicationDate || tag === readingTime) continue;
15736        if (title && tag === title) continue;
15737        if (excerpt && tag === excerpt) continue;
15738        if (DATE_RE.test(tag) || TIME_RE.test(tag)) continue;
15739        if (topics.indexOf(tag) === -1) topics.push(tag);
15740      }
15741
15742      return {
15743        title: title || null,
15744        contentType: contentType || null,
15745        publicationDate: publicationDate || null,
15746        readingTime: readingTime || null,
15747        topics: topics,
15748        excerpt: excerpt || null,
15749        thumbnail: thumbnail || null,
15750        thumbnailAlt: thumbnailAlt || null,
15751        url: absolute || null
15752      };
15753    }
15754
15755    async function buildResults() {
15756      var countEl = findCountEl();
15757      var scope = findListingScope(countEl);
15758      var scoped = scope !== document.body;
15759      var extracted = null;
15760      try {
15761        scope.setAttribute(SCOPE_ATTR, "scope");
15762        extracted = await extractItems(SCOPED_ITEMS, FIELD_MAP, limit * 4);
15763      } catch (e) {
15764        extracted = null;
15765      }
15766      var byUrl = {};
15767      if (extracted && extracted.results) {
15768        for (var i = 0; i < extracted.results.length; i++) {
15769          var row = extracted.results[i];
15770          if (row && row.url) byUrl[String(row.url)] = row;
15771        }
15772      }
15773      var cards = collectCards(scope);
15774      var results = [];
15775      for (var c = 0; c < cards.length && results.length < limit; c++) {
15776        var href = cards[c].getAttribute("href") || "";
15777        results.push(describeCard(cards[c], byUrl[href]));
15778      }
15779      try { scope.removeAttribute(SCOPE_ATTR); } catch (e) {}
15780      return { countEl: countEl, scope: scope, scoped: scoped, results: results, available: cards.length };
15781    }
15782
15783    function highlight(el) {
15784      if (!el || !el.style) return;
15785      try {
15786        var prevOutline = el.style.outline;
15787        var prevOffset = el.style.outlineOffset;
15788        el.style.outline = "3px solid #0037ff";
15789        el.style.outlineOffset = "2px";
15790        el.scrollIntoView({ behavior: "smooth", block: "center" });
15791        setTimeout(function () {
15792          try {
15793            el.style.outline = prevOutline;
15794            el.style.outlineOffset = prevOffset;
15795          } catch (e) {}
15796        }, 4000);
15797      } catch (e) {}
15798    }
15799
15800    function navigatedPayload(actionText) {
15801      return {
15802        status: "navigated",
15803        action: actionText,
15804        url: location.href,
15805        nextStep: "The browser is loading another page; call optimizely-browse-field-notes-library-results once it has settled to read the resources and the total.",
15806        summary: "The page started navigating, so the listing could not be read from here."
15807      };
15808    }
15809
15810    function navigateWithUrl(reason) {
15811      var isHub = /^\/field-notes\/?$/.test(location.pathname);
15812      var wantsFilter = !!(input.topic || input.keyword);
15813      var base = isHub && wantsFilter ? "/field-notes/articles" : location.pathname.replace(/\/+$/, "");
15814      var params = [];
15815      var values = {};
15816      if (input.topic) { params.push("topic={topic}"); values.topic = String(input.topic); }
15817      if (input.keyword) { params.push("search={keyword}"); values.keyword = String(input.keyword); }
15818      if (input.page) { params.push("page={page}"); values.page = String(parseInt(input.page, 10)); }
15819      var target = base + (params.length ? "?" + params.join("&") : "");
15820      var built = target;
15821      try {
15822        built = params.length ? buildUrl(target, values) : base;
15823      } catch (e) {
15824        built = base;
15825      }
15826      var absolute = built;
15827      try { absolute = new URL(built, location.origin).href; } catch (e) {}
15828      var payload = {
15829        status: "navigated",
15830        action: "load the Field Notes listing with the requested filters in the URL",
15831        url: absolute,
15832        appliedFilters: {
15833          topic: input.topic ? String(input.topic) : null,
15834          keyword: input.keyword ? String(input.keyword) : null,
15835          page: input.page ? parseInt(input.page, 10) : null
15836        },
15837        reason: "Fell back to URL parameters because " + reason + "." + (isHub && wantsFilter ? " Topic and keyword parameters take effect on the article listing at /field-notes/articles." : ""),
15838        nextStep: "The listing is loading at " + absolute + "; call optimizely-browse-field-notes-library-results to read the resources and the total once it has settled.",
15839        summary: "Loading the Field Notes listing with the requested filters applied through the URL."
15840      };
15841      var out = done(payload);
15842      location.assign(absolute);
15843      return out;
15844    }
15845
15846    try {
15847      var beforeCountEl = findCountEl();
15848      var countBefore = beforeCountEl ? txt(beforeCountEl) : "";
15849      var baseline = signature();
15850      var applied = { topic: null, keyword: null, page: null };
15851      var interacted = false;
15852      var settleOutcome = "not-needed";
15853      var wantTopic = clean(input.topic);
15854      var wantKeyword = clean(input.keyword);
15855      var wantPage = input.page != null && String(input.page) !== "" ? parseInt(input.page, 10) : null;
15856      if (isNaN(wantPage)) wantPage = null;
15857
15858      if (wantTopic) {
15859        await expandFacetGroups();
15860        await revealHiddenOptions();
15861        var options = filterOptions();
15862        if (!options.length) {
15863          var opened = await openFilterPanel();
15864          if (opened) {
15865            await expandFacetGroups();
15866            await revealHiddenOptions();
15867            options = filterOptions();
15868          }
15869        }
15870        if (!options.length) {
15871          return navigateWithUrl("no topic filter control could be found on this page");
15872        }
15873        var target = norm(wantTopic);
15874        var hit = null;
15875        for (var oi = 0; oi < options.length; oi++) {
15876          if (norm(options[oi].label) === target) { hit = options[oi]; break; }
15877        }
15878        if (!hit) {
15879          for (var oj = 0; oj < options.length; oj++) {
15880            if (norm(options[oj].label).indexOf(target) === 0) { hit = options[oj]; break; }
15881          }
15882        }
15883        if (!hit) {
15884          var labels = options.map(function (o) { return o.label; });
15885          var grouped = options.map(function (o) { return { label: o.label, group: o.group || null }; });
15886          return done({
15887            status: "error",
15888            action: "apply a topic filter to the Field Notes listing",
15889            url: location.href,
15890            reason: "The value '" + wantTopic + "' is not one of the filter options currently rendered on this listing.",
15891            availableOptions: labels,
15892            availableOptionsByGroup: grouped,
15893            optionsComplete: !revealButtonPresent(),
15894            nextStep: "Call the tool again with one of the values in availableOptions, or omit topic to browse the unfiltered listing.",
15895            summary: "Could not apply the topic '" + wantTopic + "' because the listing offers a different set of filter values."
15896          });
15897        }
15898        try {
15899          hit.el.click();
15900          interacted = true;
15901          applied.topic = hit.label;
15902        } catch (e) {
15903          return navigateWithUrl("the topic control '" + hit.label + "' could not be clicked");
15904        }
15905        await sleep(300);
15906        if (unloading) return done(navigatedPayload("apply the topic filter '" + hit.label + "'"));
15907      }
15908
15909      if (wantKeyword) {
15910        var searchSelector = findSearchSelector();
15911        if (!searchSelector) {
15912          var trigger = findSearchTrigger();
15913          if (trigger) {
15914            try { trigger.click(); } catch (e) {}
15915            try { await waitForElement('input[type="search"]', 5000); } catch (e) {}
15916            searchSelector = findSearchSelector();
15917          }
15918        }
15919        if (!searchSelector) {
15920          return navigateWithUrl("the keyword search input could not be opened on this page");
15921        }
15922        var inputEl = document.querySelector(searchSelector);
15923        var form = inputEl && inputEl.closest ? inputEl.closest("form") : null;
15924        if (form && form.getAttribute("action")) {
15925          return navigateWithUrl("the search form submits natively to " + form.getAttribute("action"));
15926        }
15927        try {
15928          await fillInput(searchSelector, wantKeyword);
15929          applied.keyword = wantKeyword;
15930          interacted = true;
15931        } catch (e) {
15932          return navigateWithUrl("the keyword could not be typed into the search input");
15933        }
15934        try {
15935          ["keydown", "keyup"].forEach(function (type) {
15936            inputEl.dispatchEvent(new KeyboardEvent(type, { key: "Enter", code: "Enter", keyCode: 13, which: 13, bubbles: true }));
15937          });
15938        } catch (e) {}
15939        await sleep(300);
15940        if (unloading) return done(navigatedPayload("search the Field Notes listing for '" + wantKeyword + "'"));
15941      }
15942
15943      if (interacted) {
15944        settleOutcome = await settle(baseline, 9000);
15945        if (unloading) return done(navigatedPayload("apply the requested Field Notes filters"));
15946      }
15947
15948      if (wantPage) {
15949        var nowRange = parseRange(findCountEl() ? txt(findCountEl()) : "");
15950        if (nowRange.page !== wantPage) {
15951          var pageBtn = findPageButton(wantPage);
15952          if (!pageBtn) {
15953            return navigateWithUrl("the pagination does not expose a button for page " + wantPage);
15954          }
15955          var pageBaseline = signature();
15956          try {
15957            pageBtn.click();
15958            interacted = true;
15959            applied.page = wantPage;
15960          } catch (e) {
15961            return navigateWithUrl("the button for page " + wantPage + " could not be clicked");
15962          }
15963          await sleep(300);
15964          if (unloading) return done(navigatedPayload("open page " + wantPage + " of the Field Notes listing"));
15965          settleOutcome = await settle(pageBaseline, 9000);
15966          if (unloading) return done(navigatedPayload("open page " + wantPage + " of the Field Notes listing"));
15967        } else {
15968          applied.page = wantPage;
15969        }
15970      }
15971
15972      var built = await buildResults();
15973      var countEl = built.countEl;
15974      var countText = countEl ? txt(countEl) : "";
15975      var range = parseRange(countText);
15976      var resultCount = range.total;
15977      var countSource = resultCount != null ? "listing-count-text" : "none";
15978      if (resultCount == null) {
15979        try {
15980          var rc = readResultCount(COUNT_SELECTOR, null);
15981          if (typeof rc === "number" && !isNaN(rc) && rc > 0 && countText) {
15982            resultCount = rc;
15983            countSource = "listing-count-text";
15984          }
15985        } catch (e) {}
15986      }
15987      if (resultCount == null && built.results.length) {
15988        resultCount = built.results.length;
15989        countSource = "rendered-items";
15990      }
15991
15992      highlight(built.scope);
15993
15994      var liveOptions = filterOptions();
15995      var observed = !!(countText || built.results.length);
15996      var filterConfirmed = null;
15997      if (applied.topic || applied.keyword || applied.page) {
15998        filterConfirmed = settleOutcome === "changed" || (countText && countText !== countBefore) || location.href !== startUrl;
15999      }
16000
16001      var actionBits = [];
16002      if (applied.topic) actionBits.push("topic " + applied.topic);
16003      if (applied.keyword) actionBits.push("keyword '" + applied.keyword + "'");
16004      if (applied.page) actionBits.push("page " + applied.page);
16005      var actionText = actionBits.length
16006        ? "filter the Field Notes listing by " + actionBits.join(" and ")
16007        : "read the Field Notes listing";
16008
16009      var payload = {
16010        status: observed ? "ok" : "partial",
16011        action: actionText,
16012        url: location.href,
16013        listingPath: location.pathname,
16014        observed: observed,
16015        appliedFilters: {
16016          topic: applied.topic,
16017          keyword: applied.keyword,
16018          page: applied.page
16019        },
16020        filterConfirmed: filterConfirmed,
16021        countText: countText || null,
16022        resultCount: resultCount == null ? null : resultCount,
16023        countSource: countSource,
16024        rangeStart: range.start,
16025        rangeEnd: range.end,
16026        page: range.page != null ? range.page : applied.page,
16027        totalPages: range.totalPages,
16028        renderedCount: built.results.length,
16029        truncated: built.available > built.results.length,
16030        scopedToListing: built.scoped,
16031        results: built.results,
16032        filterOptions: liveOptions.map(function (o) { return { label: o.label, group: o.group || null }; }),
16033        filterOptionsComplete: !revealButtonPresent(),
16034        summary: ""
16035      };
16036
16037      if (!observed) {
16038        payload.reason = "Neither the 'Showing X-Y of N results' element nor any resource card could be read after the interaction settled.";
16039        payload.nextStep = "Reload " + location.pathname + " and call the tool again, or call it with a page number to force the listing to re-render.";
16040        payload.summary = "Could not read the Field Notes listing: no count element and no resource cards were present after the page settled.";
16041      } else {
16042        var bits = [];
16043        bits.push("Read " + built.results.length + " Field Notes resource" + (built.results.length === 1 ? "" : "s") + " from " + location.pathname);
16044        if (countText) bits.push("(" + countText + ")");
16045        if (applied.topic) bits.push("with topic " + applied.topic);
16046        if (applied.keyword) bits.push("matching '" + applied.keyword + "'");
16047        if (applied.page) bits.push("on page " + applied.page);
16048        var sentence = bits.join(" ") + ".";
16049        if (filterConfirmed === false) {
16050          sentence += " The listing did not visibly change after the filter was applied, so the numbers may still be the unfiltered ones.";
16051          payload.nextStep = "Call the tool again, or open the listing with topic and search query parameters, to confirm the filter took effect.";
16052        }
16053        payload.summary = sentence;
16054      }
16055
16056      return done(payload);
16057    } catch (err) {
16058      return done({
16059        status: "error",
16060        action: "browse the Field Notes listing",
16061        url: location.href,
16062        reason: "The tool hit an unexpected page error: " + (err && err.message ? err.message : String(err)),
16063        nextStep: "Reload the Field Notes listing page and call the tool again.",
16064        summary: "Could not browse the Field Notes listing because of an unexpected page error."
16065      });
16066    }
16067  }
16068});
16069
16070mc.registerToolIf("/field-notes/**", {
16071  name: "optimizely-browse-field-notes-library-results",
16072  description: "Read the Field Notes listing that is currently loaded and return the resources it shows as objects with title, content type, publication date, reading time where shown, topic tags, excerpt, thumbnail image URL and absolute URL, plus the listing's own 'Showing X-Y of N results' total, the current page and the filter options rendered on the page. Use this after a call that loaded a filtered listing URL.",
16073  inputSchema: {
16074    type: "object",
16075    properties: {
16076      limit: {
16077        type: "integer",
16078        minimum: 1,
16079        maximum: 50,
16080        description: "Maximum number of resources to return. Defaults to 12."
16081      }
16082    },
16083    required: []
16084  },
16085  execute: async function (args) {
16086    trackWebMCPTool("optimizely-browse-field-notes-library-results");
16087
16088    var input = args || {};
16089    var limit = Math.max(1, Math.min(50, parseInt(input.limit, 10) || 12));
16090    var COUNT_SELECTOR = 'div:has(> p + nav) > p';
16091    var ITEM_SELECTOR = 'a[href^="/field-notes/"]';
16092    var SCOPE_ATTR = "data-webmcp-fieldnotes";
16093    var SCOPED_ITEMS = '[data-webmcp-fieldnotes="scope"] a[href^="/field-notes/"]';
16094    var COUNT_RE = /showing\s+[\d,]+\s*[-–]\s*[\d,]+\s+of\s+[\d,]+\s+results?/i;
16095    var DATE_RE = /^[A-Za-z]{3,9}\.?\s+\d{1,2},\s*\d{4}$/;
16096    var TIME_RE = /^\d+\s*(minute|minutes|min|mins|hour|hours|hr|hrs)\b/i;
16097    var FIELD_MAP = {
16098      url: "@href",
16099      title: "h3",
16100      excerpt: "p",
16101      thumbnail: "img @src",
16102      thumbnailAlt: "img @alt",
16103      contentType: "div > div > div:nth-child(2) > div:first-child > div > span:nth-child(2)",
16104      publicationDate: "div > div > div:nth-child(2) > div:first-child > span:nth-child(2)",
16105      readingTime: "div > div > div:nth-child(2) > div:first-child > span:nth-child(3)"
16106    };
16107
16108    function wrap(payload) {
16109      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
16110    }
16111    function txt(el) {
16112      return el && el.textContent ? el.textContent.replace(/\s+/g, " ").trim() : "";
16113    }
16114    function clean(v) {
16115      return v == null ? "" : String(v).replace(/\s+/g, " ").trim();
16116    }
16117    function sleep(ms) {
16118      return new Promise(function (r) { setTimeout(r, ms); });
16119    }
16120    function isVisible(el) {
16121      if (!el) return false;
16122      try {
16123        if (el.closest && el.closest('[aria-hidden="true"]')) return false;
16124        if (el.getAttribute && el.getAttribute("aria-hidden") === "true") return false;
16125        var r = el.getBoundingClientRect();
16126        return !!(r.width || r.height || el.getClientRects().length);
16127      } catch (e) { return true; }
16128    }
16129    function isIconText(t) {
16130      return /^[a-z0-9]+(_[a-z0-9]+)*$/.test(t || "");
16131    }
16132    function findCountEl() {
16133      var el = null;
16134      try { el = document.querySelector(COUNT_SELECTOR); } catch (e) { el = null; }
16135      if (el && /showing/i.test(txt(el))) return el;
16136      var nodes = document.querySelectorAll("p, span");
16137      for (var i = 0; i < nodes.length; i++) {
16138        if (nodes[i].children.length) continue;
16139        if (COUNT_RE.test(txt(nodes[i]))) return nodes[i];
16140      }
16141      return null;
16142    }
16143    function collectCards(scope) {
16144      var out = [];
16145      var seen = {};
16146      var list = scope.querySelectorAll(ITEM_SELECTOR);
16147      for (var i = 0; i < list.length; i++) {
16148        var node = list[i];
16149        var href = node.getAttribute("href") || "";
16150        var parts = href.split("?")[0].split("#")[0].split("/").filter(Boolean);
16151        if (parts.length < 3) continue;
16152        if (!node.querySelector("h3")) continue;
16153        if (!isVisible(node)) continue;
16154        if (seen[href]) continue;
16155        seen[href] = true;
16156        out.push(node);
16157      }
16158      return out;
16159    }
16160    function findListingScope(countEl) {
16161      var node = countEl ? countEl.parentElement : null;
16162      var hops = 0;
16163      while (node && hops < 14) {
16164        if (collectCards(node).length >= 2) return node;
16165        node = node.parentElement;
16166        hops++;
16167      }
16168      var main = document.querySelector("main");
16169      if (main && collectCards(main).length >= 2) return main;
16170      return document.body;
16171    }
16172    function parseRange(text) {
16173      var out = { start: null, end: null, total: null, page: null, totalPages: null };
16174      if (!text) return out;
16175      var t = String(text).replace(/,/g, "");
16176      var m = /showing\s+(\d+)\s*[-–]\s*(\d+)\s+of\s+(\d+)/i.exec(t);
16177      if (!m) {
16178        var m2 = /of\s+(\d+)\s+results?/i.exec(t);
16179        if (m2) out.total = parseInt(m2[1], 10);
16180        return out;
16181      }
16182      out.start = parseInt(m[1], 10);
16183      out.end = parseInt(m[2], 10);
16184      out.total = parseInt(m[3], 10);
16185      var size = out.end - out.start + 1;
16186      if (size > 0) {
16187        out.page = Math.floor((out.start - 1) / size) + 1;
16188        out.totalPages = Math.ceil(out.total / size);
16189      }
16190      return out;
16191    }
16192    function typeFromHref(href) {
16193      var m = /^\/field-notes\/([^\/]+)\//.exec(href || "");
16194      if (!m) return "";
16195      var map = { articles: "Article", guides: "Guide", videos: "Video", "customer-stories": "Customer story", events: "Event" };
16196      return map[m[1]] || "";
16197    }
16198    function leafSpans(card) {
16199      var out = [];
16200      var spans = card.querySelectorAll("span");
16201      for (var i = 0; i < spans.length; i++) {
16202        var s = spans[i];
16203        if (s.children.length) continue;
16204        if (s.getAttribute("aria-hidden") === "true") continue;
16205        if (s.closest('[aria-hidden="true"]')) continue;
16206        var t = txt(s);
16207        if (!t) continue;
16208        out.push({ el: s, text: t });
16209      }
16210      return out;
16211    }
16212    function describeCard(card, base) {
16213      base = base || {};
16214      var href = card.getAttribute("href") || "";
16215      var absolute = href;
16216      try { absolute = new URL(href, location.origin).href; } catch (e) {}
16217      var leaves = leafSpans(card);
16218      var typeText = "";
16219      var dateText = "";
16220      var timeText = "";
16221      var loose = [];
16222      for (var i = 0; i < leaves.length; i++) {
16223        var t = leaves[i].text;
16224        if (isIconText(t)) continue;
16225        if (!typeText && i > 0 && isIconText(leaves[i - 1].text)) { typeText = t; continue; }
16226        if (!dateText && DATE_RE.test(t)) { dateText = t; continue; }
16227        if (!timeText && TIME_RE.test(t)) { timeText = t; continue; }
16228        loose.push(t);
16229      }
16230      var title = clean(base.title) || txt(card.querySelector("h3"));
16231      var excerpt = clean(base.excerpt) || txt(card.querySelector("p"));
16232      var img = card.querySelector("img");
16233      var thumbnail = clean(base.thumbnail) || (img ? clean(img.getAttribute("src")) : "");
16234      var thumbnailAlt = base.thumbnailAlt != null ? clean(base.thumbnailAlt) : (img ? clean(img.getAttribute("alt")) : "");
16235      var contentType = clean(base.contentType);
16236      if (!contentType || isIconText(contentType) || DATE_RE.test(contentType) || TIME_RE.test(contentType)) contentType = typeText;
16237      if (!contentType || isIconText(contentType)) contentType = typeFromHref(href);
16238      var publicationDate = clean(base.publicationDate);
16239      if (!DATE_RE.test(publicationDate)) publicationDate = DATE_RE.test(dateText) ? dateText : "";
16240      var readingTime = clean(base.readingTime);
16241      if (!TIME_RE.test(readingTime)) readingTime = TIME_RE.test(timeText) ? timeText : "";
16242      var topics = [];
16243      for (var k = 0; k < loose.length; k++) {
16244        var tag = loose[k];
16245        if (!tag || tag.length > 60) continue;
16246        if (tag === contentType || tag === publicationDate || tag === readingTime) continue;
16247        if (title && tag === title) continue;
16248        if (excerpt && tag === excerpt) continue;
16249        if (DATE_RE.test(tag) || TIME_RE.test(tag)) continue;
16250        if (topics.indexOf(tag) === -1) topics.push(tag);
16251      }
16252      return {
16253        title: title || null,
16254        contentType: contentType || null,
16255        publicationDate: publicationDate || null,
16256        readingTime: readingTime || null,
16257        topics: topics,
16258        excerpt: excerpt || null,
16259        thumbnail: thumbnail || null,
16260        thumbnailAlt: thumbnailAlt || null,
16261        url: absolute || null
16262      };
16263    }
16264    function highlight(el) {
16265      if (!el || !el.style) return;
16266      try {
16267        var prevOutline = el.style.outline;
16268        var prevOffset = el.style.outlineOffset;
16269        el.style.outline = "3px solid #0037ff";
16270        el.style.outlineOffset = "2px";
16271        el.scrollIntoView({ behavior: "smooth", block: "center" });
16272        setTimeout(function () {
16273          try {
16274            el.style.outline = prevOutline;
16275            el.style.outlineOffset = prevOffset;
16276          } catch (e) {}
16277        }, 4000);
16278      } catch (e) {}
16279    }
16280
16281    try {
16282      try { await waitForElement(COUNT_SELECTOR, 9000); } catch (e) {}
16283      await sleep(400);
16284
16285      var countEl = findCountEl();
16286      var scope = findListingScope(countEl);
16287      var extracted = null;
16288      try {
16289        scope.setAttribute(SCOPE_ATTR, "scope");
16290        extracted = await extractItems(SCOPED_ITEMS, FIELD_MAP, limit * 4);
16291      } catch (e) {
16292        extracted = null;
16293      }
16294      var byUrl = {};
16295      if (extracted && extracted.results) {
16296        for (var i = 0; i < extracted.results.length; i++) {
16297          var row = extracted.results[i];
16298          if (row && row.url) byUrl[String(row.url)] = row;
16299        }
16300      }
16301      var cards = collectCards(scope);
16302      var results = [];
16303      for (var c = 0; c < cards.length && results.length < limit; c++) {
16304        results.push(describeCard(cards[c], byUrl[cards[c].getAttribute("href") || ""]));
16305      }
16306      try { scope.removeAttribute(SCOPE_ATTR); } catch (e) {}
16307
16308      var countText = countEl ? txt(countEl) : "";
16309      var range = parseRange(countText);
16310      var resultCount = range.total;
16311      var countSource = resultCount != null ? "listing-count-text" : "none";
16312      if (resultCount == null && results.length) {
16313        resultCount = results.length;
16314        countSource = "rendered-items";
16315      }
16316      highlight(scope);
16317
16318      var observed = !!(countText || results.length);
16319      var payload = {
16320        status: observed ? "ok" : "partial",
16321        action: "read the Field Notes listing currently on screen",
16322        url: location.href,
16323        listingPath: location.pathname,
16324        observed: observed,
16325        countText: countText || null,
16326        resultCount: resultCount == null ? null : resultCount,
16327        countSource: countSource,
16328        rangeStart: range.start,
16329        rangeEnd: range.end,
16330        page: range.page,
16331        totalPages: range.totalPages,
16332        renderedCount: results.length,
16333        truncated: cards.length > results.length,
16334        scopedToListing: scope !== document.body,
16335        results: results,
16336        summary: ""
16337      };
16338      if (!observed) {
16339        payload.reason = "The listing count element never appeared and no resource cards with a heading were present on this page.";
16340        payload.nextStep = "Reload " + location.pathname + " and call this tool again once the listing has rendered.";
16341        payload.summary = "Could not read the Field Notes listing: nothing had rendered by the time the tool looked.";
16342      } else {
16343        payload.summary = "Read " + results.length + " Field Notes resource" + (results.length === 1 ? "" : "s") + " from " + location.pathname + (countText ? " (" + countText + ")" : "") + ".";
16344      }
16345      return wrap(payload);
16346    } catch (err) {
16347      return wrap({
16348        status: "error",
16349        action: "read the Field Notes listing currently on screen",
16350        url: location.href,
16351        reason: "The tool hit an unexpected page error: " + (err && err.message ? err.message : String(err)),
16352        nextStep: "Reload the Field Notes listing page and call the tool again.",
16353        summary: "Could not read the Field Notes listing because of an unexpected page error."
16354      });
16355    }
16356  }
16357});
16358
16359  /* ---- optimizely-find-optimizely-events, optimizely-find-optimizely-events-results, optimizely-find-optimizely-events-open ---- */
16360mc.registerToolIf("/field-notes/events/**", {
16361  name: "optimizely-find-optimizely-events",
16362  description: "Search, filter and page through the Optimizely events and webinars listing at /field-notes/events. Applies a free-text query, a topic and a page number to the listing and returns the events as objects with name, date, format ('On-location event' or 'Online event'), topic tags, description, card image and event-page URL, plus the page's own 'Showing X-Y of N results' counter. When the requested query, topic and page are the ones already showing, it reads and returns them directly; otherwise it opens the matching listing URL and names the tool that reads the loaded page.",
16363  inputSchema: {
16364    type: "object",
16365    properties: {
16366      search: {
16367        type: "string",
16368        description: "Free-text query to apply to the listing, for example 'opticon' or 'webinar'. Omit to keep the query already applied; pass an empty string to clear it."
16369      },
16370      topic: {
16371        type: "string",
16372        description: "Topic to filter by, for example 'AI'. The available topics are read from the live page at call time; a value that is not offered comes back with the list that is actually available."
16373      },
16374      page: {
16375        type: "integer",
16376        minimum: 1,
16377        description: "1-based page of the card grid (6 events per page). Omit to keep the current page, or to start at page 1 when the query or topic changes."
16378      },
16379      limit: {
16380        type: "integer",
16381        minimum: 1,
16382        maximum: 60,
16383        description: "Maximum number of events to return when the listing is read in place. Defaults to 24."
16384      }
16385    },
16386    required: []
16387  },
16388  execute: async function (args) {
16389    trackWebMCPTool("optimizely-find-optimizely-events");
16390    var a = args || {};
16391    var LISTING_PATH = "/field-notes/events";
16392    var ITEM_SELECTOR = 'main a[href^="/field-notes/events/"]';
16393    var COUNT_SELECTOR = "main div:has(> p):has(> nav) > p";
16394    var FIELD_MAP = {
16395      url: "@href",
16396      name: "h3",
16397      date: "div > div > div:nth-child(2) > div:nth-child(1) > span",
16398      format: "div > div > div:nth-child(2) > div:nth-child(1) > div > span:nth-child(2)",
16399      description: "p",
16400      topics: "div > div > div:nth-child(2) > div:last-child > span",
16401      image: "img @src"
16402    };
16403    var startUrl = location.href;
16404
16405    function wrap(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
16406    function txt(el) { return el && el.textContent ? el.textContent.replace(/\s+/g, " ").trim() : ""; }
16407    function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
16408    function cards() {
16409      try { return Array.prototype.slice.call(document.querySelectorAll(ITEM_SELECTOR)); } catch (e) { return []; }
16410    }
16411    function highlight(el) {
16412      if (!el) return;
16413      try {
16414        el.scrollIntoView({ behavior: "smooth", block: "center" });
16415        el.style.outline = "3px solid #0037ff";
16416        el.style.outlineOffset = "2px";
16417        setTimeout(function () { try { el.style.outline = ""; el.style.outlineOffset = ""; } catch (e) {} }, 6000);
16418      } catch (e) {}
16419    }
16420    function regionFor(list) {
16421      if (!list.length) return document.querySelector("main");
16422      var node = list[0].parentElement, best = list[0], hops = 0;
16423      while (node && hops < 6) {
16424        if (node.querySelectorAll('a[href^="/field-notes/events/"]').length >= list.length) { best = node; break; }
16425        node = node.parentElement;
16426        hops++;
16427      }
16428      return best;
16429    }
16430    function counterElement() {
16431      var el = null;
16432      try { el = document.querySelector(COUNT_SELECTOR); } catch (e) { el = null; }
16433      if (el) return el;
16434      var ps = Array.prototype.slice.call(document.querySelectorAll("main p"));
16435      for (var i = 0; i < ps.length; i++) {
16436        if (/showing\s+[\d,]+\s*-\s*[\d,]+\s+of\s+[\d,]+/i.test(txt(ps[i]))) return ps[i];
16437      }
16438      return null;
16439    }
16440    function emptyStateText() {
16441      var nodes = Array.prototype.slice.call(document.querySelectorAll("main p, main h2, main h3, main div"));
16442      for (var i = 0; i < nodes.length; i++) {
16443        var t = txt(nodes[i]);
16444        if (t && t.length < 200 && /no results found/i.test(t)) return t;
16445      }
16446      return "";
16447    }
16448    async function liveTopics() {
16449      var out = [], seen = {};
16450      function add(t) {
16451        t = (t || "").replace(/\s+/g, " ").trim();
16452        if (!t || t.length > 48) return;
16453        if (/^(filter|filters|search|clear|clear all|apply|reset|reset filters|close|all|all topics|sort|previous|next)$/i.test(t)) return;
16454        var k = t.toLowerCase();
16455        if (seen[k]) return;
16456        seen[k] = 1;
16457        out.push(t);
16458      }
16459      var panelBtn = null;
16460      var btns = Array.prototype.slice.call(document.querySelectorAll("main button"));
16461      for (var i = 0; i < btns.length; i++) {
16462        var lbl = txt(btns[i]) || btns[i].getAttribute("aria-label") || btns[i].getAttribute("title") || "";
16463        if (/^filters?$/i.test(lbl.trim())) { panelBtn = btns[i]; break; }
16464      }
16465      var opened = false;
16466      if (panelBtn) {
16467        try { panelBtn.click(); opened = true; await sleep(800); } catch (e) {}
16468      }
16469      Array.prototype.slice.call(document.querySelectorAll("main select option")).forEach(function (o) {
16470        if (o.getAttribute("value") !== "") add(txt(o));
16471      });
16472      Array.prototype.slice.call(document.querySelectorAll("main label")).forEach(function (l) { add(txt(l)); });
16473      Array.prototype.slice.call(document.querySelectorAll('main input[type="checkbox"], main input[type="radio"]')).forEach(function (inp) {
16474        add(inp.getAttribute("aria-label") || inp.getAttribute("value") || "");
16475      });
16476      Array.prototype.slice.call(document.querySelectorAll('main [role="option"], main [role="menuitemcheckbox"], main [role="checkbox"]')).forEach(function (n) {
16477        add(n.getAttribute("aria-label") || txt(n));
16478      });
16479      if (opened) { try { panelBtn.click(); await sleep(200); } catch (e) {} }
16480      cards().forEach(function (c) {
16481        Array.prototype.slice.call(c.querySelectorAll(FIELD_MAP.topics)).forEach(function (s) {
16482          if (s.getAttribute("aria-hidden") === "true") return;
16483          add(txt(s));
16484        });
16485      });
16486      return out;
16487    }
16488    async function readListing(limit) {
16489      try { await waitForElement(ITEM_SELECTOR, 8000); } catch (e) {}
16490      try { await waitForElement(COUNT_SELECTOR, 2500); } catch (e) {}
16491      await sleep(350);
16492      var list = cards();
16493      highlight(regionFor(list));
16494      var data = null;
16495      try { data = await extractItems(ITEM_SELECTOR, FIELD_MAP, limit); } catch (e) { data = null; }
16496      var results = (data && data.results) || [];
16497      var truncated = !!(data && data.truncated);
16498      var counterText = txt(counterElement());
16499      var total = null;
16500      var m = counterText.match(/of\s+([\d,]+)/i);
16501      if (m) total = parseInt(m[1].replace(/,/g, ""), 10);
16502      if (total === null || isNaN(total)) {
16503        total = null;
16504        var parsed = null;
16505        try { parsed = await readResultCount(COUNT_SELECTOR, null); } catch (e) { parsed = null; }
16506        if (typeof parsed === "number" && !isNaN(parsed)) total = parsed;
16507      }
16508      var empty = emptyStateText();
16509      if (total === null && empty) total = 0;
16510      if (total === null && results.length) total = results.length;
16511      return {
16512        results: results,
16513        truncated: truncated,
16514        counterText: counterText,
16515        total: total,
16516        empty: empty,
16517        shown: results.length
16518      };
16519    }
16520
16521    try {
16522      var onListing = location.pathname.replace(/\/+$/, "") === LISTING_PATH;
16523      var params = new URLSearchParams(location.search);
16524      var curSearch = onListing ? (params.get("search") || "").trim() : "";
16525      var curTopic = onListing ? (params.get("topic") || "").trim() : "";
16526      var curPage = onListing ? ((params.get("page") || "1").trim() || "1") : "1";
16527
16528      var limit = parseInt(a.limit, 10);
16529      if (isNaN(limit) || limit < 1) limit = 24;
16530      if (limit > 60) limit = 60;
16531
16532      var tgtSearch = typeof a.search === "string" ? a.search.trim() : curSearch;
16533      var tgtTopic = typeof a.topic === "string" ? a.topic.trim() : curTopic;
16534
16535      if (tgtTopic && onListing && tgtTopic.toLowerCase() !== curTopic.toLowerCase()) {
16536        var options = await liveTopics();
16537        var exact = null, partial = null;
16538        for (var i = 0; i < options.length; i++) {
16539          if (options[i].toLowerCase() === tgtTopic.toLowerCase()) { exact = options[i]; break; }
16540          if (!partial && options[i].toLowerCase().indexOf(tgtTopic.toLowerCase()) !== -1) partial = options[i];
16541        }
16542        if (exact) {
16543          tgtTopic = exact;
16544        } else if (partial) {
16545          tgtTopic = partial;
16546        } else if (options.length >= 3) {
16547          return wrap({
16548            status: "error",
16549            action: "check the requested topic against the topics offered on the events listing",
16550            url: location.href,
16551            requestedTopic: tgtTopic,
16552            availableOptions: options,
16553            reason: "The topic '" + tgtTopic + "' does not match any topic read from the listing's Filter control or from the topic tags on the event cards at call time.",
16554            nextStep: "Call optimizely-find-optimizely-events again with one of availableOptions as topic, or drop topic and pass a search query instead.",
16555            summary: "Topic '" + tgtTopic + "' was not among the topics available on the events listing."
16556          });
16557        }
16558      }
16559
16560      var reqPage = parseInt(a.page, 10);
16561      var tgtPage;
16562      if (!isNaN(reqPage) && reqPage >= 1) tgtPage = String(reqPage);
16563      else if (tgtSearch !== curSearch || tgtTopic !== curTopic) tgtPage = "1";
16564      else tgtPage = curPage;
16565
16566      if (onListing && tgtSearch === curSearch && tgtTopic === curTopic && tgtPage === curPage) {
16567        var read = await readListing(limit);
16568        var applied = { search: tgtSearch, topic: tgtTopic, page: parseInt(tgtPage, 10) };
16569        if (read.results.length) {
16570          return wrap({
16571            status: "ok",
16572            action: "read the events listing already showing the requested query, topic and page",
16573            url: location.href,
16574            appliedFilters: applied,
16575            observed: true,
16576            resultCount: read.total,
16577            shownOnPage: read.shown,
16578            truncated: read.truncated,
16579            counterText: read.counterText,
16580            results: read.results,
16581            summary: "Read " + read.results.length + " event(s) from the listing" + (read.total !== null ? "; the page counter reports " + read.total + " matching result(s)" : "") + "."
16582          });
16583        }
16584        if (read.empty) {
16585          var topicsNow = await liveTopics();
16586          return wrap({
16587            status: "ok",
16588            action: "read the events listing already showing the requested query, topic and page",
16589            url: location.href,
16590            appliedFilters: applied,
16591            observed: true,
16592            resultCount: 0,
16593            shownOnPage: 0,
16594            truncated: false,
16595            counterText: read.counterText,
16596            emptyState: read.empty,
16597            results: [],
16598            availableOptions: topicsNow,
16599            nextStep: "Call optimizely-find-optimizely-events again with a broader search, a topic from availableOptions, or page 1.",
16600            summary: "The listing reported no results for this query, topic and page."
16601          });
16602        }
16603        return wrap({
16604          status: "partial",
16605          action: "read the events listing already showing the requested query, topic and page",
16606          url: location.href,
16607          appliedFilters: applied,
16608          observed: false,
16609          resultCount: null,
16610          shownOnPage: 0,
16611          truncated: false,
16612          counterText: read.counterText,
16613          results: [],
16614          reason: "No event cards matched " + ITEM_SELECTOR + " and no 'No results found' message was present, so neither the events nor the result count could be read from this page.",
16615          nextStep: "Wait for the listing to finish rendering and call optimizely-find-optimizely-events-results, or reload /field-notes/events and try again.",
16616          summary: "The events grid could not be read on this page, so nothing is being reported as found."
16617        });
16618      }
16619
16620      var target = buildUrl("/field-notes/events?search={search}&topic={topic}&page={page}", {
16621        search: tgtSearch,
16622        topic: tgtTopic,
16623        page: tgtPage === "1" ? "" : tgtPage
16624      });
16625      var targetHref = target;
16626      try { targetHref = new URL(target, location.href).href; } catch (e) {}
16627      var payload = {
16628        status: "navigated",
16629        action: "open the events listing with the requested search, topic and page",
16630        url: targetHref,
16631        fromUrl: startUrl,
16632        appliedFilters: { search: tgtSearch, topic: tgtTopic, page: parseInt(tgtPage, 10) },
16633        nextStep: "The events listing is loading at that URL. Call optimizely-find-optimizely-events-results to read the events and the 'Showing X-Y of N results' counter from the loaded page.",
16634        summary: "Opening the events listing" + (tgtSearch ? " for '" + tgtSearch + "'" : "") + (tgtTopic ? " with topic '" + tgtTopic + "'" : "") + " at page " + tgtPage + "."
16635      };
16636      location.assign(target);
16637      return wrap(payload);
16638    } catch (err) {
16639      return wrap({
16640        status: "error",
16641        action: "apply the requested search, topic and page to the events listing",
16642        url: location.href,
16643        reason: "The tool failed while working with the page: " + (err && err.message ? err.message : String(err)),
16644        nextStep: "Reload /field-notes/events and call optimizely-find-optimizely-events again with simpler arguments.",
16645        summary: "The events listing could not be searched or filtered because of an unexpected page error."
16646      });
16647    }
16648  }
16649});
16650
16651mc.registerToolIf("/field-notes/events/**", {
16652  name: "optimizely-find-optimizely-events-results",
16653  description: "Read the Optimizely events and webinars listing as it currently stands at /field-notes/events and return each event as an object with name, date, format ('On-location event' or 'Online event'), topic tags, description, card image and event-page URL, together with the page's own 'Showing X-Y of N results' counter and the search, topic and page currently in the URL. Scrolls to and highlights the results grid the data was read from.",
16654  inputSchema: {
16655    type: "object",
16656    properties: {
16657      limit: {
16658        type: "integer",
16659        minimum: 1,
16660        maximum: 60,
16661        description: "Maximum number of events to return. Defaults to 24."
16662      }
16663    },
16664    required: []
16665  },
16666  execute: async function (args) {
16667    trackWebMCPTool("optimizely-find-optimizely-events-results");
16668    var a = args || {};
16669    var ITEM_SELECTOR = 'main a[href^="/field-notes/events/"]';
16670    var COUNT_SELECTOR = "main div:has(> p):has(> nav) > p";
16671    var FIELD_MAP = {
16672      url: "@href",
16673      name: "h3",
16674      date: "div > div > div:nth-child(2) > div:nth-child(1) > span",
16675      format: "div > div > div:nth-child(2) > div:nth-child(1) > div > span:nth-child(2)",
16676      description: "p",
16677      topics: "div > div > div:nth-child(2) > div:last-child > span",
16678      image: "img @src"
16679    };
16680
16681    function wrap(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
16682    function txt(el) { return el && el.textContent ? el.textContent.replace(/\s+/g, " ").trim() : ""; }
16683    function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
16684    function cards() {
16685      try { return Array.prototype.slice.call(document.querySelectorAll(ITEM_SELECTOR)); } catch (e) { return []; }
16686    }
16687    function highlight(el) {
16688      if (!el) return;
16689      try {
16690        el.scrollIntoView({ behavior: "smooth", block: "center" });
16691        el.style.outline = "3px solid #0037ff";
16692        el.style.outlineOffset = "2px";
16693        setTimeout(function () { try { el.style.outline = ""; el.style.outlineOffset = ""; } catch (e) {} }, 6000);
16694      } catch (e) {}
16695    }
16696    function regionFor(list) {
16697      if (!list.length) return document.querySelector("main");
16698      var node = list[0].parentElement, best = list[0], hops = 0;
16699      while (node && hops < 6) {
16700        if (node.querySelectorAll('a[href^="/field-notes/events/"]').length >= list.length) { best = node; break; }
16701        node = node.parentElement;
16702        hops++;
16703      }
16704      return best;
16705    }
16706    function counterElement() {
16707      var el = null;
16708      try { el = document.querySelector(COUNT_SELECTOR); } catch (e) { el = null; }
16709      if (el) return el;
16710      var ps = Array.prototype.slice.call(document.querySelectorAll("main p"));
16711      for (var i = 0; i < ps.length; i++) {
16712        if (/showing\s+[\d,]+\s*-\s*[\d,]+\s+of\s+[\d,]+/i.test(txt(ps[i]))) return ps[i];
16713      }
16714      return null;
16715    }
16716    function emptyStateText() {
16717      var nodes = Array.prototype.slice.call(document.querySelectorAll("main p, main h2, main h3, main div"));
16718      for (var i = 0; i < nodes.length; i++) {
16719        var t = txt(nodes[i]);
16720        if (t && t.length < 200 && /no results found/i.test(t)) return t;
16721      }
16722      return "";
16723    }
16724    async function liveTopics() {
16725      var out = [], seen = {};
16726      function add(t) {
16727        t = (t || "").replace(/\s+/g, " ").trim();
16728        if (!t || t.length > 48) return;
16729        if (/^(filter|filters|search|clear|clear all|apply|reset|reset filters|close|all|all topics|sort|previous|next)$/i.test(t)) return;
16730        var k = t.toLowerCase();
16731        if (seen[k]) return;
16732        seen[k] = 1;
16733        out.push(t);
16734      }
16735      var panelBtn = null;
16736      var btns = Array.prototype.slice.call(document.querySelectorAll("main button"));
16737      for (var i = 0; i < btns.length; i++) {
16738        var lbl = txt(btns[i]) || btns[i].getAttribute("aria-label") || btns[i].getAttribute("title") || "";
16739        if (/^filters?$/i.test(lbl.trim())) { panelBtn = btns[i]; break; }
16740      }
16741      var opened = false;
16742      if (panelBtn) {
16743        try { panelBtn.click(); opened = true; await sleep(800); } catch (e) {}
16744      }
16745      Array.prototype.slice.call(document.querySelectorAll("main select option")).forEach(function (o) {
16746        if (o.getAttribute("value") !== "") add(txt(o));
16747      });
16748      Array.prototype.slice.call(document.querySelectorAll("main label")).forEach(function (l) { add(txt(l)); });
16749      Array.prototype.slice.call(document.querySelectorAll('main input[type="checkbox"], main input[type="radio"]')).forEach(function (inp) {
16750        add(inp.getAttribute("aria-label") || inp.getAttribute("value") || "");
16751      });
16752      Array.prototype.slice.call(document.querySelectorAll('main [role="option"], main [role="menuitemcheckbox"], main [role="checkbox"]')).forEach(function (n) {
16753        add(n.getAttribute("aria-label") || txt(n));
16754      });
16755      if (opened) { try { panelBtn.click(); await sleep(200); } catch (e) {} }
16756      cards().forEach(function (c) {
16757        Array.prototype.slice.call(c.querySelectorAll(FIELD_MAP.topics)).forEach(function (s) {
16758          if (s.getAttribute("aria-hidden") === "true") return;
16759          add(txt(s));
16760        });
16761      });
16762      return out;
16763    }
16764
16765    try {
16766      var limit = parseInt(a.limit, 10);
16767      if (isNaN(limit) || limit < 1) limit = 24;
16768      if (limit > 60) limit = 60;
16769
16770      var params = new URLSearchParams(location.search);
16771      var applied = {
16772        search: (params.get("search") || "").trim(),
16773        topic: (params.get("topic") || "").trim(),
16774        page: parseInt((params.get("page") || "1").trim() || "1", 10)
16775      };
16776
16777      try { await waitForElement(ITEM_SELECTOR, 8000); } catch (e) {}
16778      try { await waitForElement(COUNT_SELECTOR, 2500); } catch (e) {}
16779      await sleep(350);
16780
16781      var list = cards();
16782      highlight(regionFor(list));
16783
16784      var data = null;
16785      try { data = await extractItems(ITEM_SELECTOR, FIELD_MAP, limit); } catch (e) { data = null; }
16786      var results = (data && data.results) || [];
16787      var truncated = !!(data && data.truncated);
16788      var counterText = txt(counterElement());
16789      var total = null;
16790      var m = counterText.match(/of\s+([\d,]+)/i);
16791      if (m) total = parseInt(m[1].replace(/,/g, ""), 10);
16792      if (total === null || isNaN(total)) {
16793        total = null;
16794        var parsed = null;
16795        try { parsed = await readResultCount(COUNT_SELECTOR, null); } catch (e) { parsed = null; }
16796        if (typeof parsed === "number" && !isNaN(parsed)) total = parsed;
16797      }
16798      var empty = emptyStateText();
16799
16800      if (results.length) {
16801        if (total === null) total = results.length;
16802        return wrap({
16803          status: "ok",
16804          action: "read the events listing on the loaded page",
16805          url: location.href,
16806          appliedFilters: applied,
16807          observed: true,
16808          resultCount: total,
16809          shownOnPage: results.length,
16810          truncated: truncated,
16811          counterText: counterText,
16812          results: results,
16813          nextStep: "Call optimizely-find-optimizely-events-open with the url of the event you want, or optimizely-find-optimizely-events with a page number to see more.",
16814          summary: "Read " + results.length + " event(s) from the listing; the page counter reports " + total + " matching result(s)."
16815        });
16816      }
16817
16818      if (empty) {
16819        var topicsNow = await liveTopics();
16820        return wrap({
16821          status: "ok",
16822          action: "read the events listing on the loaded page",
16823          url: location.href,
16824          appliedFilters: applied,
16825          observed: true,
16826          resultCount: 0,
16827          shownOnPage: 0,
16828          truncated: false,
16829          counterText: counterText,
16830          emptyState: empty,
16831          results: [],
16832          availableOptions: topicsNow,
16833          nextStep: "Call optimizely-find-optimizely-events again with a broader search, a topic from availableOptions, or page 1.",
16834          summary: "The listing reported no results for the search, topic and page currently applied."
16835        });
16836      }
16837
16838      return wrap({
16839        status: "partial",
16840        action: "read the events listing on the loaded page",
16841        url: location.href,
16842        appliedFilters: applied,
16843        observed: false,
16844        resultCount: null,
16845        shownOnPage: 0,
16846        truncated: false,
16847        counterText: counterText,
16848        results: [],
16849        reason: "No event cards matched " + ITEM_SELECTOR + " and no 'No results found' message was present, so neither the events nor the result count could be read.",
16850        nextStep: "Wait for the listing to finish rendering and call optimizely-find-optimizely-events-results again, or reload /field-notes/events.",
16851        summary: "The events grid could not be read, so no events and no count are being reported."
16852      });
16853    } catch (err) {
16854      return wrap({
16855        status: "error",
16856        action: "read the events listing on the loaded page",
16857        url: location.href,
16858        reason: "The tool failed while reading the page: " + (err && err.message ? err.message : 
16858String(err)),
16859        nextStep: "Reload /field-notes/events and call optimizely-find-optimizely-events-results again.",
16860        summary: "The events listing could not be read because of an unexpected page error."
16861      });
16862    }
16863  }
16864});
16865
16866mc.registerToolIf("/field-notes/events/**", {
16867  name: "optimizely-find-optimizely-events-open",
16868  description: "Open a specific Optimizely event page from the events listing, or follow the listing's 'Register now' CTA to the Camp Opticon hub at /field-notes/opticon/, so the visitor lands on the registration step. Takes the event-page URL returned by the listing tools. Returns the destination that was opened, how it was opened and the event names and URLs currently listed on the page.",
16869  inputSchema: {
16870    type: "object",
16871    properties: {
16872      eventUrl: {
16873        type: "string",
16874        description: "Path or full URL of the event page to open, exactly as returned in the 'url' field of a listing result, for example /field-notes/events/opticon-new-york/."
16875      },
16876      openRegistration: {
16877        type: "boolean",
16878        description: "Set true to follow the hero 'Register now' CTA to the Camp Opticon registration hub at /field-notes/opticon/ instead of opening a single event page."
16879      }
16880    },
16881    required: []
16882  },
16883  execute: async function (args) {
16884    trackWebMCPTool("optimizely-find-optimizely-events-open");
16885    var a = args || {};
16886    var ITEM_SELECTOR = 'main a[href^="/field-notes/events/"]';
16887    var HUB_PATH = "/field-notes/opticon/";
16888    var startUrl = location.href;
16889
16890    function wrap(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
16891    function txt(el) { return el && el.textContent ? el.textContent.replace(/\s+/g, " ").trim() : ""; }
16892    function collapseRepeat(s) { return String(s || "").replace(/(.{4,}?)\1{2,}/g, "$1").replace(/\s+/g, " ").trim(); }
16893    function trimSlash(s) { return String(s || "").replace(/\/+$/, ""); }
16894    function highlight(el) {
16895      if (!el) return;
16896      try {
16897        el.scrollIntoView({ behavior: "smooth", block: "center" });
16898        el.style.outline = "3px solid #0037ff";
16899        el.style.outlineOffset = "2px";
16900        setTimeout(function () { try { el.style.outline = ""; el.style.outlineOffset = ""; } catch (e) {} }, 6000);
16901      } catch (e) {}
16902    }
16903    function listedEvents() {
16904      var out = [], seen = {};
16905      var list = [];
16906      try { list = Array.prototype.slice.call(document.querySelectorAll(ITEM_SELECTOR)); } catch (e) { list = []; }
16907      list.forEach(function (c) {
16908        var href = c.getAttribute("href");
16909        if (!href || seen[href]) return;
16910        seen[href] = 1;
16911        out.push({ name: txt(c.querySelector("h3")), url: href });
16912      });
16913      return out;
16914    }
16915    function anchorFor(dest) {
16916      var anchors = Array.prototype.slice.call(document.querySelectorAll("main a[href]"));
16917      for (var i = 0; i < anchors.length; i++) {
16918        var h = anchors[i].getAttribute("href");
16919        if (!h) continue;
16920        if (h === dest || trimSlash(h) === trimSlash(dest)) return anchors[i];
16921      }
16922      return null;
16923    }
16924
16925    try {
16926      var raw = typeof a.eventUrl === "string" ? a.eventUrl.trim() : "";
16927      var wantRegistration = a.openRegistration === true || /^register(\s*now)?$/i.test(raw);
16928      if (wantRegistration && !/^\/field-notes\//.test(raw) && !/^https?:\/\//i.test(raw)) raw = "";
16929
16930      var available = listedEvents();
16931      var dest = "";
16932
16933      if (raw) {
16934        var validPath = /^\/field-notes\//.test(raw);
16935        var validAbsolute = /^https:\/\/(www\.optimizely\.com|events\.optimizely\.com)\//i.test(raw);
16936        if (!validPath && !validAbsolute) {
16937          return wrap({
16938            status: "error",
16939            action: "open the requested event page",
16940            url: location.href,
16941            requestedUrl: raw,
16942            availableOptions: available.map(function (e) { return e.url; }),
16943            availableEvents: available,
16944            reason: "'" + raw + "' is not an Optimizely event destination; expected a /field-notes/... path or an optimizely.com URL taken from a listing result.",
16945            nextStep: "Call optimizely-find-optimizely-events-results to list the events, then call this tool again with one of the returned url values.",
16946            summary: "The requested destination was not a recognised Optimizely event URL."
16947          });
16948        }
16949        dest = raw;
16950      } else if (wantRegistration) {
16951        dest = HUB_PATH;
16952      } else {
16953        return wrap({
16954          status: "error",
16955          action: "open an event page",
16956          url: location.href,
16957          availableOptions: available.map(function (e) { return e.url; }),
16958          availableEvents: available,
16959          reason: "No eventUrl was supplied and openRegistration was not set, so there was no destination to open.",
16960          nextStep: "Call this tool again with eventUrl set to one of availableOptions, or with openRegistration true to reach the Camp Opticon registration hub.",
16961          summary: "No event destination was requested, so nothing was opened."
16962        });
16963      }
16964
16965      var destHref = dest;
16966      try { destHref = new URL(dest, location.href).href; } catch (e) {}
16967
16968      if (trimSlash(destHref.split("#")[0]) === trimSlash(location.href.split("#")[0])) {
16969        var heading = document.querySelector("main h1");
16970        var name = collapseRepeat(txt(heading));
16971        if (name) {
16972          highlight(heading);
16973          return wrap({
16974            status: "ok",
16975            action: "confirm the requested event page is already open",
16976            url: location.href,
16977            observed: true,
16978            eventName: name,
16979            eventUrl: location.pathname,
16980            nextStep: "Read this page for its registration link or form and follow it to complete registration.",
16981            summary: "Already on the event page '" + name + "'."
16982          });
16983        }
16984        return wrap({
16985          status: "partial",
16986          action: "confirm the requested event page is already open",
16987          url: location.href,
16988          observed: false,
16989          eventName: null,
16990          eventUrl: location.pathname,
16991          reason: "The browser is already at the requested URL but no main h1 heading could be read to confirm which event this page is.",
16992          nextStep: "Wait for the page to finish rendering and read it again.",
16993          summary: "The requested URL is already open but the event name could not be read."
16994        });
16995      }
16996
16997      var anchor = anchorFor(dest);
16998      var known = null;
16999      for (var i = 0; i < available.length; i++) {
17000        if (trimSlash(available[i].url) === trimSlash(dest)) { known = available[i]; break; }
17001      }
17002
17003      var payload = {
17004        status: "navigated",
17005        action: wantRegistration && dest === HUB_PATH ? "follow the 'Register now' CTA to the Camp Opticon registration hub" : "open the requested event page",
17006        url: destHref,
17007        fromUrl: startUrl,
17008        opened: { name: known ? known.name : null, url: dest },
17009        method: anchor ? "clicked the matching link on the page" : "opened the URL directly",
17010        listedEvents: available,
17011        nextStep: wantRegistration && dest === HUB_PATH
17012          ? "The Camp Opticon hub is loading; choose the New York, London or Online registration destination on that page."
17013          : "The event page is loading; read it for its registration link or form and follow that to complete registration.",
17014        summary: wantRegistration && dest === HUB_PATH
17015          ? "Opening the Camp Opticon registration hub at " + HUB_PATH + "."
17016          : "Opening the event page " + dest + (known && known.name ? " ('" + known.name + "')" : "") + "."
17017      };
17018
17019      if (anchor) {
17020        anchor.click();
17021        return wrap(payload);
17022      }
17023      location.assign(dest);
17024      return wrap(payload);
17025    } catch (err) {
17026      return wrap({
17027        status: "error",
17028        action: "open the requested event page",
17029        url: location.href,
17030        reason: "The tool failed while opening the destination: " + (err && err.message ? err.message : String(err)),
17031        nextStep: "Call optimizely-find-optimizely-events-results to re-read the listing, then retry with a url from the results.",
17032        summary: "The event destination could not be opened because of an unexpected page error."
17033      });
17034    }
17035  }
17036});
17037
17038  /* ---- optimizely-search-opal-agent-directory, optimizely-search-opal-agent-directory-results ---- */
17039mc.registerToolIf("/agents/**", {
17040  name: "optimizely-search-opal-agent-directory",
17041  description: "Searches and filters the Optimizely Opal AI agent directory on /agents/. Accepts a free-text keyword, a Product facet value, a 'Built By' publisher (name or key) and a page number, reads the Product and Built By options the page offers at call time and, when those options can be read, checks the requested values against them, then applies the state through the directory's own ?search=, ?product=, ?builtBy= and ?page= URL parameters. When the directory is already showing exactly the requested state it returns the listing itself: the directory's own result total plus, for each agent, its name, short description, any card badge such as NEW, the publisher name and logo, the card thumbnail image when the card carries one, and the absolute detail page URL. When the state has to change it navigates the directory to that URL and returns status navigated together with the name of the results tool that reads the settled page. When a requested facet value is not among the options read from the page it returns status error with the available options.",
17042  inputSchema: {
17043    type: "object",
17044    properties: {
17045      search: {
17046        type: "string",
17047        description: "Free-text keyword to search the agent directory for, for example 'blog' or 'variation'."
17048      },
17049      product: {
17050        type: "string",
17051        description: "Product facet value to filter by, for example 'Opal' or 'Web Experimentation'. Checked against the Product options read from the page at call time; an unknown value comes back as an error listing the options that are really there."
17052      },
17053      builtBy: {
17054        type: "string",
17055        description: "Built By publisher to filter by. Accepts the publisher display name, for example 'Niteco', or the directory's builtBy key. Checked against the Built By options read from the page at call time."
17056      },
17057      page: {
17058        type: "integer",
17059        minimum: 1,
17060        description: "Result page of the directory to open. Page 1 is the first page."
17061      },
17062      limit: {
17063        type: "integer",
17064        minimum: 1,
17065        maximum: 50,
17066        description: "Maximum number of agent cards to return when the directory is already showing the requested state. Defaults to 12."
17067      }
17068    },
17069    additionalProperties: false
17070  },
17071  execute: async function (args) {
17072    trackWebMCPTool("optimizely-search-opal-agent-directory");
17073    var a = args || {};
17074    var RESULTS_TOOL = "optimizely-search-opal-agent-directory-results";
17075    var LIST_SELECTOR = "#ListingResults";
17076    var ITEM_SELECTOR = "#ListingResults a.group";
17077    var COUNT_SELECTOR = "#ListingResults p.text-body-xxs";
17078    var TEMPLATE = "/agents/?search={search}&product={product}&builtBy={builtBy}&page={page}";
17079    var FIELD_MAP = {
17080      url: "@href",
17081      name: "h3",
17082      description: "p.line-clamp-3",
17083      badge: "span.font-overline",
17084      thumbnail: "img.object-cover @src",
17085      publisher: "div.pr-6 > span",
17086      publisherLogo: "img.object-contain @src",
17087      publisherLogoAlt: "img.object-contain @alt"
17088    };
17089
17090    function respond(payload) {
17091      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
17092    }
17093    function txt(el) {
17094      return el && el.textContent ? el.textContent.replace(/\s+/g, " ").trim() : "";
17095    }
17096    function num(s) {
17097      var n = parseInt(String(s).replace(/[^\d]/g, ""), 10);
17098      return isNaN(n) ? null : n;
17099    }
17100    function absolute(u) {
17101      if (!u) return null;
17102      try { return new URL(u, location.origin).href; } catch (e) { return u; }
17103    }
17104    function delay(ms) {
17105      return new Promise(function (r) { setTimeout(r, ms); });
17106    }
17107    function highlight(el) {
17108      try {
17109        if (!el) return;
17110        el.scrollIntoView({ behavior: "smooth", block: "center" });
17111        var prev = el.style.outline;
17112        el.style.outline = "3px solid #0037ff";
17113        setTimeout(function () { try { el.style.outline = prev; } catch (e) {} }, 5000);
17114      } catch (e) {}
17115    }
17116
17117    function sliceBalanced(text, start) {
17118      var depth = 0, inStr = false, esc = false;
17119      var max = Math.min(text.length, start + 400000);
17120      for (var i = start; i < max; i++) {
17121        var c = text.charAt(i);
17122        if (inStr) {
17123          if (esc) { esc = false; }
17124          else if (c === "\\") { esc = true; }
17125          else if (c === '"') { inStr = false; }
17126          continue;
17127        }
17128        if (c === '"') { inStr = true; continue; }
17129        if (c === "{") { depth++; }
17130        else if (c === "}") { depth--; if (depth === 0) return text.slice(start, i + 1); }
17131      }
17132      return null;
17133    }
17134    function pushOption(bucket, value, label, count) {
17135      var v = String(value == null ? label : value).trim();
17136      var l = String(label == null ? value : label).trim();
17137      if (!v) return;
17138      for (var i = 0; i < bucket.length; i++) {
17139        if (bucket[i].value.toLowerCase() === v.toLowerCase()) return;
17140      }
17141      bucket.push({ value: v, label: l || v, count: (typeof count === "number" && isFinite(count)) ? count : null });
17142    }
17143    function harvestFacets(obj, out) {
17144      if (!obj || typeof obj !== "object") return;
17145      Object.keys(obj).forEach(function (key) {
17146        var list = obj[key];
17147        if (!Array.isArray(list)) return;
17148        var bucket = /^product$/i.test(key) ? out.product : (/^builtby$/i.test(key) ? out.builtBy : null);
17149        if (!bucket) return;
17150        list.forEach(function (entry) {
17151          if (!entry || typeof entry !== "object") return;
17152          var value = entry.name != null ? entry.name : (entry.value != null ? entry.value : entry.label);
17153          var label = entry.label != null ? entry.label : value;
17154          if (value == null) return;
17155          pushOption(bucket, value, label, entry.count);
17156        });
17157      });
17158    }
17159    function facetsFromPayload() {
17160      var out = { product: [], builtBy: [] };
17161      try {
17162        var scripts = document.querySelectorAll("script");
17163        for (var i = 0; i < scripts.length; i++) {
17164          var raw = scripts[i].textContent || "";
17165          if (raw.indexOf("initialFacets") === -1) continue;
17166          var variants = [raw, raw.replace(/\\"/g, '"')];
17167          for (var v = 0; v < variants.length; v++) {
17168            var text = variants[v];
17169            var idx = text.indexOf('"initialFacets"');
17170            var guard = 0;
17171            while (idx !== -1 && guard < 5) {
17172              guard++;
17173              var braceStart = text.indexOf("{", idx);
17174              if (braceStart === -1) break;
17175              var json = sliceBalanced(text, braceStart);
17176              if (json) {
17177                try { harvestFacets(JSON.parse(json), out); } catch (e) {}
17178              }
17179              idx = text.indexOf('"initialFacets"', idx + 1);
17180            }
17181            if (out.product.length || out.builtBy.length) break;
17182          }
17183          if (out.product.length || out.builtBy.length) break;
17184        }
17185      } catch (e) {}
17186      return out;
17187    }
17188    function facetsFromDom() {
17189      var out = { product: [], builtBy: [] };
17190      function labelFor(input) {
17191        var lab = "";
17192        try {
17193          if (input.id) {
17194            var byFor = document.querySelector('label[for="' + input.id.replace(/"/g, '\\"') + '"]');
17195            if (byFor) lab = txt(byFor);
17196          }
17197          if (!lab && input.closest) {
17198            var parentLabel = input.closest("label");
17199            if (parentLabel) lab = txt(parentLabel);
17200          }
17201          if (!lab && input.parentElement) lab = txt(input.parentElement);
17202          if (!lab) lab = input.getAttribute("aria-label") || "";
17203        } catch (e) {}
17204        return lab.replace(/\s*\(\s*\d+\s*\)\s*$/, "").trim();
17205      }
17206      function countFor(input) {
17207        try {
17208          var host = (input.closest && input.closest("label")) || input.parentElement;
17209          var m = txt(host).match(/\((\d+)\)\s*$/);
17210          return m ? parseInt(m[1], 10) : null;
17211        } catch (e) { return null; }
17212      }
17213      try {
17214        var inputs = document.querySelectorAll('input[type="checkbox"], input[type="radio"]');
17215        for (var i = 0; i < inputs.length; i++) {
17216          var el = inputs[i];
17217          var nm = (el.getAttribute("name") || "").trim().toLowerCase();
17218          var bucket = nm === "product" ? out.product : (nm === "builtby" ? out.builtBy : null);
17219          if (!bucket) continue;
17220          var lbl = labelFor(el);
17221          pushOption(bucket, el.getAttribute("value") || lbl, lbl, countFor(el));
17222        }
17223        var links = document.querySelectorAll('a[href*="product="], a[href*="builtBy="], a[href*="builtby="]');
17224        for (var j = 0; j < links.length; j++) {
17225          try {
17226            var u = new URL(links[j].getAttribute("href"), location.origin);
17227            var pv = u.searchParams.get("product");
17228            var bv = u.searchParams.get("builtBy") || u.searchParams.get("builtby");
17229            var lt = txt(links[j]).replace(/\s*\(\s*\d+\s*\)\s*$/, "").trim();
17230            if (pv) pushOption(out.product, pv, lt || pv, null);
17231            if (bv) pushOption(out.builtBy, bv, lt || bv, null);
17232          } catch (e) {}
17233        }
17234      } catch (e) {}
17235      return out;
17236    }
17237    function findFilterButton() {
17238      var scoped = document.querySelector("div.z-39 > button");
17239      if (scoped && /filter/i.test(txt(scoped) + " " + (scoped.getAttribute("aria-label") || ""))) return scoped;
17240      var btns = document.querySelectorAll("button");
17241      for (var i = 0; i < btns.length; i++) {
17242        var t = txt(btns[i]);
17243        var al = (btns[i].getAttribute("aria-label") || "").trim();
17244        if (/^filters?\b/i.test(t) || /^filters?$/i.test(al)) return btns[i];
17245      }
17246      return scoped || null;
17247    }
17248    async function readLiveFacets() {
17249      var facets = facetsFromDom();
17250      var source = (facets.product.length || facets.builtBy.length) ? "facet controls on the page" : null;
17251      if (!source) {
17252        var btn = findFilterButton();
17253        if (btn) {
17254          try { btn.click(); } catch (e) {}
17255          for (var t = 0; t < 12; t++) {
17256            await delay(200);
17257            facets = facetsFromDom();
17258            if (facets.product.length || facets.builtBy.length) { source = "Filter panel"; break; }
17259          }
17260          try { btn.click(); } catch (e) {}
17261        }
17262      }
17263      if (!source) {
17264        var fromPayload = facetsFromPayload();
17265        if (fromPayload.product.length || fromPayload.builtBy.length) {
17266          facets = fromPayload;
17267          source = "listing configuration payload in the page";
17268        }
17269      }
17270      return { facets: facets, source: source };
17271    }
17272    function matchOption(list, wanted) {
17273      var w = String(wanted).trim().toLowerCase();
17274      var i;
17275      for (i = 0; i < list.length; i++) {
17276        if (String(list[i].value).toLowerCase() === w) return list[i];
17277      }
17278      for (i = 0; i < list.length; i++) {
17279        if (String(list[i].label).toLowerCase() === w) return list[i];
17280      }
17281      if (w.length >= 3) {
17282        for (i = 0; i < list.length; i++) {
17283          var lab = String(list[i].label).toLowerCase();
17284          if (lab && (lab.indexOf(w) !== -1 || w.indexOf(lab) !== -1)) return list[i];
17285        }
17286      }
17287      return null;
17288    }
17289    async function readListing(limit) {
17290      var out = {
17291        countText: "",
17292        totalResults: null,
17293        showingFrom: null,
17294        showingTo: null,
17295        cardsFound: 0,
17296        truncated: false,
17297        results: []
17298      };
17299      var countEl = document.querySelector(COUNT_SELECTOR);
17300      out.countText = txt(countEl);
17301      var m = out.countText.match(/showing\s+([\d,]+)\s*[-–]\s*([\d,]+)\s+of\s+([\d,]+)/i);
17302      if (m) {
17303        out.showingFrom = num(m[1]);
17304        out.showingTo = num(m[2]);
17305        out.totalResults = num(m[3]);
17306      } else {
17307        var alt = out.countText.match(/of\s+([\d,]+)/i);
17308        if (alt) out.totalResults = num(alt[1]);
17309      }
17310      if (out.totalResults === null) {
17311        try {
17312          var rc = await readResultCount(COUNT_SELECTOR, null);
17313          if (typeof rc === "number" && isFinite(rc)) out.totalResults = rc;
17314        } catch (e) {}
17315      }
17316      var items = null;
17317      try { items = await extractItems(ITEM_SELECTOR, FIELD_MAP, limit); } catch (e) { items = null; }
17318      if (items && items.results) {
17319        out.cardsFound = typeof items.resultCount === "number" ? items.resultCount : items.results.length;
17320        out.truncated = !!items.truncated;
17321        out.results = items.results.map(function (r) {
17322          return {
17323            name: r.name || null,
17324            description: r.description || null,
17325            badge: r.badge || null,
17326            publisher: r.publisher || r.publisherLogoAlt || null,
17327            publisherLogo: absolute(r.publisherLogo),
17328            thumbnail: absolute(r.thumbnail),
17329            url: absolute(r.url)
17330          };
17331        });
17332      }
17333      return out;
17334    }
17335    function stateOf(href) {
17336      var u = new URL(href, location.origin);
17337      var p = u.searchParams;
17338      return {
17339        path: u.pathname.replace(/\/+$/, "").toLowerCase(),
17340        search: (p.get("search") || "").trim().toLowerCase(),
17341        product: (p.get("product") || "").trim().toLowerCase(),
17342        builtBy: (p.get("builtBy") || p.get("builtby") || "").trim().toLowerCase(),
17343        page: String(parseInt(p.get("page") || "1", 10) || 1)
17344      };
17345    }
17346
17347    try {
17348      var startUrl = location.href;
17349      var limit = Math.max(1, Math.min(50, parseInt(a.limit, 10) || 12));
17350      var wantSearch = a.search != null ? String(a.search).trim() : "";
17351      var wantProduct = a.product != null ? String(a.product).trim() : "";
17352      var wantBuiltBy = a.builtBy != null ? String(a.builtBy).trim() : "";
17353      var wantPage = a.page != null ? Math.max(1, parseInt(a.page, 10) || 1) : 1;
17354
17355      var live = { facets: { product: [], builtBy: [] }, source: null };
17356      if (wantProduct || wantBuiltBy) {
17357        live = await readLiveFacets();
17358      }
17359      var resolvedProduct = wantProduct;
17360      var resolvedBuiltBy = wantBuiltBy;
17361
17362      if (wantProduct && live.facets.product.length) {
17363        var hitProduct = matchOption(live.facets.product, wantProduct);
17364        if (!hitProduct) {
17365          return respond({
17366            status: "error",
17367            action: "apply a Product filter on the Opal agent directory",
17368            url: location.href,
17369            invalidArgument: "product",
17370            requestedValue: wantProduct,
17371            reason: "The Product facet value '" + wantProduct + "' is not among the Product options currently offered by the directory (read from the " + live.source + ").",
17372            availableOptions: live.facets.product,
17373            nextStep: "Retry with one of the entries in availableOptions, passing its value or its label as the product argument.",
17374            summary: "No Product option on the directory matches '" + wantProduct + "'; " + live.facets.product.length + " Product options are available."
17375          });
17376        }
17377        resolvedProduct = hitProduct.value;
17378      }
17379      if (wantBuiltBy && live.facets.builtBy.length) {
17380        var hitBuiltBy = matchOption(live.facets.builtBy, wantBuiltBy);
17381        if (!hitBuiltBy) {
17382          return respond({
17383            status: "error",
17384            action: "apply a Built By filter on the Opal agent directory",
17385            url: location.href,
17386            invalidArgument: "builtBy",
17387            requestedValue: wantBuiltBy,
17388            reason: "The Built By value '" + wantBuiltBy + "' is not among the publisher options currently offered by the directory (read from the " + live.source + ").",
17389            availableOptions: live.facets.builtBy,
17390            nextStep: "Retry with one of the entries in availableOptions, passing its value (the builtBy key) or its label (the publisher name) as the builtBy argument.",
17391            summary: "No Built By option on the directory matches '" + wantBuiltBy + "'; " + live.facets.builtBy.length + " publisher options are available."
17392          });
17393        }
17394        resolvedBuiltBy = hitBuiltBy.value;
17395      }
17396
17397      var appliedFilters = {
17398        search: wantSearch || null,
17399        product: resolvedProduct || null,
17400        builtBy: resolvedBuiltBy || null,
17401        page: wantPage
17402      };
17403
17404      var target = buildUrl(TEMPLATE, {
17405        search: wantSearch,
17406        product: resolvedProduct,
17407        builtBy: resolvedBuiltBy,
17408        page: wantPage > 1 ? String(wantPage) : ""
17409      });
17410      if (!target) target = "/agents/";
17411      target = String(target).replace(/[?&]+$/, "");
17412      var targetAbs = absolute(target);
17413
17414      var current = stateOf(location.href);
17415      var wanted = stateOf(targetAbs);
17416      var sameState =
17417        current.path === wanted.path &&
17418        current.search === wanted.search &&
17419        current.product === wanted.product &&
17420        current.builtBy === wanted.builtBy &&
17421        current.page === wanted.page;
17422
17423      if (sameState) {
17424        var listRegion = null;
17425        try { listRegion = await waitForElement(LIST_SELECTOR, 8000); } catch (e) { listRegion = null; }
17426        if (!listRegion) listRegion = document.querySelector(LIST_SELECTOR);
17427        if (!listRegion) {
17428          return respond({
17429            status: "partial",
17430            action: "read the Opal agent directory listing",
17431            url: location.href,
17432            observed: false,
17433            resultCount: null,
17434            results: [],
17435            appliedFilters: appliedFilters,
17436            reason: "The directory results region (" + LIST_SELECTOR + ") did not appear on this page, so no agent cards or result total could be read.",
17437            nextStep: "Open " + location.origin + "/agents/ and call this tool again, or call " + RESULTS_TOOL + " on the directory page.",
17438            summary: "The requested directory state is already in the URL but the results region was not found."
17439          });
17440        }
17441        highlight(listRegion);
17442        await delay(400);
17443        var listing = await readListing(limit);
17444        var resultCount = listing.totalResults !== null ? listing.totalResults : (listing.cardsFound || null);
17445        var observed = (listing.countText !== "") || listing.results.length > 0;
17446        var status = "ok";
17447        var reason = null;
17448        var nextStep = null;
17449        if (!observed || (resultCount === null && listing.results.length === 0)) {
17450          status = "partial";
17451          observed = false;
17452          reason = "Neither the directory's own result total nor any agent card could be read out of the settled listing.";
17453          nextStep = "Reload " + targetAbs + " and call " + RESULTS_TOOL + " once the cards are visible.";
17454        } else if (listing.results.length === 0 && resultCount !== null && resultCount > 0) {
17455          status = "partial";
17456          reason = "The directory reports " + resultCount + " results but no agent card could be read from the listing.";
17457          nextStep = "Call " + RESULTS_TOOL + " again once the cards have rendered.";
17458        }
17459        var payload = {
17460          status: status,
17461          action: "read the Opal agent directory listing for the requested search and filters",
17462          url: location.href,
17463          observed: observed,
17464          appliedFilters: appliedFilters,
17465          resultCount: resultCount,
17466          returnedCount: listing.results.length,
17467          truncated: listing.truncated,
17468          countText: listing.countText || null,
17469          showingFrom: listing.showingFrom,
17470          showingTo: listing.showingTo,
17471          results: listing.results,
17472          resultsToolName: RESULTS_TOOL,
17473          summary: status === "ok"
17474            ? ("The directory is already showing this state and reports " + (resultCount === null ? "an unread number of" : resultCount) + " results; returned " + listing.results.length + " agent card(s).")
17475            : "The directory is already showing this state but the listing could not be fully read."
17476        };
17477        if (live.source) {
17478          payload.facetOptions = live.facets;
17479          payload.facetOptionsSource = live.source;
17480        }
17481        if (reason) payload.reason = reason;
17482        if (nextStep) payload.nextStep = nextStep;
17483        return respond(payload);
17484      }
17485
17486      var navPayload = respond({
17487        status: "navigated",
17488        action: "open the Opal agent directory with the requested search and filters",
17489        url: targetAbs,
17490        fromUrl: startUrl,
17491        appliedFilters: appliedFilters,
17492        resultsToolName: RESULTS_TOOL,
17493        nextStep: "The directory is now loading " + targetAbs + ". Call " + RESULTS_TOOL + " on that page to read the result total and the agent cards, and pass a higher page argument to this tool to walk further pages.",
17494        summary: "Opening the Opal agent directory at " + targetAbs + "."
17495      });
17496      var pushed = false;
17497      try {
17498        if (window.next && window.next.router && typeof window.next.router.push === "function") {
17499          window.next.router.push(target);
17500          pushed = true;
17501        }
17502      } catch (e) {
17503        pushed = false;
17504      }
17505      if (!pushed) location.assign(target);
17506      return navPayload;
17507    } catch (err) {
17508      return respond({
17509        status: "error",
17510        action: "search the Opal agent directory",
17511        url: location.href,
17512        reason: "The tool hit an unexpected error: " + (err && err.message ? err.message : String(err)),
17513        nextStep: "Reload " + location.origin + "/agents/ and try again with simpler arguments.",
17514        summary: "The Opal agent directory search could not be completed."
17515      });
17516    }
17517  }
17518});
17519
17520mc.registerToolIf("/agents/**", {
17521  name: "optimizely-search-opal-agent-directory-results",
17522  description: "Reads the Opal AI agent directory listing that is currently on screen at /agents/ and returns the directory's own result total plus one object per agent card: the agent name, its short description, any card badge such as NEW, the publisher name and logo, the card thumbnail image when the card carries one, and the absolute agent detail page URL. Also returns the search and facet values in the current URL, the range shown, the current page number and the page numbers the pagination offers, and it scrolls to and highlights the results region so a human can see where the data came from.",
17523  inputSchema: {
17524    type: "object",
17525    properties: {
17526      limit: {
17527        type: "integer",
17528        minimum: 1,
17529        maximum: 50,
17530        description: "Maximum number of agent cards to return. Defaults to 12."
17531      }
17532    },
17533    additionalProperties: false
17534  },
17535  execute: async function (args) {
17536    trackWebMCPTool("optimizely-search-opal-agent-directory-results");
17537    var a = args || {};
17538    var LIST_SELECTOR = "#ListingResults";
17539    var ITEM_SELECTOR = "#ListingResults a.group";
17540    var COUNT_SELECTOR = "#ListingResults p.text-body-xxs";
17541    var PAGINATION_SELECTOR = '#ListingResults nav[aria-label="Pagination"]';
17542    var FIELD_MAP = {
17543      url: "@href",
17544      name: "h3",
17545      description: "p.line-clamp-3",
17546      badge: "span.font-overline",
17547      thumbnail: "img.object-cover @src",
17548      publisher: "div.pr-6 > span",
17549      publisherLogo: "img.object-contain @src",
17550      publisherLogoAlt: "img.object-contain @alt"
17551    };
17552
17553    function respond(payload) {
17554      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
17555    }
17556    function txt(el) {
17557      return el && el.textContent ? el.textContent.replace(/\s+/g, " ").trim() : "";
17558    }
17559    function num(s) {
17560      var n = parseInt(String(s).replace(/[^\d]/g, ""), 10);
17561      return isNaN(n) ? null : n;
17562    }
17563    function absolute(u) {
17564      if (!u) return null;
17565      try { return new URL(u, location.origin).href; } catch (e) { return u; }
17566    }
17567    function delay(ms) {
17568      return new Promise(function (r) { setTimeout(r, ms); });
17569    }
17570    function highlight(el) {
17571      try {
17572        if (!el) return;
17573        el.scrollIntoView({ behavior: "smooth", block: "center" });
17574        var prev = el.style.outline;
17575        el.style.outline = "3px solid #0037ff";
17576        setTimeout(function () { try { el.style.outline = prev; } catch (e) {} }, 5000);
17577      } catch (e) {}
17578    }
17579
17580    try {
17581      var limit = Math.max(1, Math.min(50, parseInt(a.limit, 10) || 12));
17582      var params = new URL(location.href).searchParams;
17583      var activeFilters = {
17584        search: params.get("search") || null,
17585        product: params.get("product") || null,
17586        builtBy: params.get("builtBy") || params.get("builtby") || null,
17587        page: parseInt(params.get("page") || "1", 10) || 1
17588      };
17589
17590      var region = null;
17591      try { region = await waitForElement(LIST_SELECTOR, 8000); } catch (e) { region = null; }
17592      if (!region) region = document.querySelector(LIST_SELECTOR);
17593      if (!region) {
17594        return respond({
17595          status: "partial",
17596          action: "read the Opal agent directory listing",
17597          url: location.href,
17598          observed: false,
17599          resultCount: null,
17600          results: [],
17601          activeFilters: activeFilters,
17602          reason: "The directory results region (" + LIST_SELECTOR + ") is not present on this page, so no result total and no agent cards could be read.",
17603          nextStep: "Open " + location.origin + "/agents/ (optionally with ?search=, ?product=, ?builtBy= or ?page=) and call this tool again.",
17604          summary: "No Opal agent directory listing was found on this page."
17605        });
17606      }
17607      highlight(region);
17608      await delay(400);
17609
17610      // The count line updates a beat before the card grid re-renders, so a
17611      // read taken right after a filter change can pair the new total with the
17612      // previous result set's cards. Wait for the two to agree, and say so in
17613      // the payload when they never do.
17614      var countEl = null;
17615      var countText = "";
17616      var totalResults = null, showingFrom = null, showingTo = null;
17617      var expectedCards = null, renderedCards = 0, cardsSettled = false;
17618      var settleDeadline = Date.now() + 6000;
17619      while (true) {
17620        countEl = document.querySelector(COUNT_SELECTOR);
17621        countText = txt(countEl);
17622        totalResults = null;
17623        showingFrom = null;
17624        showingTo = null;
17625        var m = countText.match(/showing\s+([\d,]+)\s*[-–]\s*([\d,]+)\s+of\s+([\d,]+)/i);
17626        if (m) {
17627          showingFrom = num(m[1]);
17628          showingTo = num(m[2]);
17629          totalResults = num(m[3]);
17630        } else {
17631          var alt = countText.match(/of\s+([\d,]+)/i);
17632          if (alt) totalResults = num(alt[1]);
17633        }
17634        expectedCards = (showingFrom !== null && showingTo !== null) ? (showingTo - showingFrom + 1) : null;
17635        renderedCards = document.querySelectorAll(ITEM_SELECTOR).length;
17636        cardsSettled = (expectedCards === null) || (renderedCards === expectedCards);
17637        if (cardsSettled || Date.now() > settleDeadline) break;
17638        await delay(250);
17639      }
17640      if (totalResults === null) {
17641        try {
17642          var rc = await readResultCount(COUNT_SELECTOR, null);
17643          if (typeof rc === "number" && isFinite(rc)) totalResults = rc;
17644        } catch (e) {}
17645      }
17646
17647      var items = null;
17648      try { items = await extractItems(ITEM_SELECTOR, FIELD_MAP, limit); } catch (e) { items = null; }
17649      var cardsFound = items && typeof items.resultCount === "number" ? items.resultCount : ((items && items.results) ? items.results.length : 0);
17650      var truncated = !!(items && items.truncated);
17651      var results = (items && items.results ? items.results : []).map(function (r) {
17652        return {
17653          name: r.name || null,
17654          description: r.description || null,
17655          badge: r.badge || null,
17656          publisher: r.publisher || r.publisherLogoAlt || null,
17657          publisherLogo: absolute(r.publisherLogo),
17658          thumbnail: absolute(r.thumbnail),
17659          url: absolute(r.url)
17660        };
17661      });
17662
17663      var page = null;
17664      var pagesOffered = [];
17665      var pagination = document.querySelector(PAGINATION_SELECTOR);
17666      if (pagination) {
17667        var currentBtn = pagination.querySelector('[aria-current="page"]');
17668        if (currentBtn) {
17669          var cm = (currentBtn.getAttribute("aria-label") || "").match(/(\d+)/);
17670          page = cm ? parseInt(cm[1], 10) : num(txt(currentBtn));
17671        }
17672        var pageBtns = pagination.querySelectorAll("button[aria-label], a[aria-label]");
17673        for (var i = 0; i < pageBtns.length; i++) {
17674          var lm = (pageBtns[i].getAttribute("aria-label") || "").match(/page\s+(\d+)/i);
17675          if (lm) {
17676            var pn = parseInt(lm[1], 10);
17677            if (pagesOffered.indexOf(pn) === -1) pagesOffered.push(pn);
17678          }
17679        }
17680        pagesOffered.sort(function (x, y) { return x - y; });
17681      }
17682      if (page === null) page = activeFilters.page;
17683      var lastPageOffered = pagesOffered.length ? pagesOffered[pagesOffered.length - 1] : null;
17684      var hasNextPage = (showingTo !== null && totalResults !== null) ? (showingTo < totalResults) : null;
17685
17686      var resultCount = totalResults !== null ? totalResults : (cardsFound || null);
17687      var observed = (countText !== "") || results.length > 0;
17688      var status = "ok";
17689      var reason = null;
17690      var nextStep = null;
17691      if (!observed || (resultCount === null && results.length === 0)) {
17692        status = "partial";
17693        observed = false;
17694        reason = "Neither the directory's own result total nor any agent card could be read out of the results region.";
17695        nextStep = "Reload " + location.href + " and call this tool again once the agent cards are visible.";
17696      } else if (results.length === 0 && resultCount !== null && resultCount > 0) {
17697        status = "partial";
17698        reason = "The directory reports " + resultCount + " results but no agent card could be read from the listing.";
17699        nextStep = "Call this tool again once the cards have rendered.";
17700      } else if (!cardsSettled) {
17701        status = "partial";
17702        reason = "The count line reads \"" + countText + "\" but " + renderedCards + " card(s) are rendered, so the listing had not finished re-rendering. The cards returned may still be the previous result set.";
17703        nextStep = "Call this tool again in a moment to read the settled listing.";
17704      }
17705
17706      var payload = {
17707        status: status,
17708        action: "read the Opal agent directory listing on this page",
17709        url: location.href,
17710        observed: observed,
17711        activeFilters: activeFilters,
17712        resultCount: resultCount,
17713        returnedCount: results.length,
17714        cardsOnPage: cardsFound,
17715        truncated: truncated,
17716        countText: countText || null,
17717        showingFrom: showingFrom,
17718        showingTo: showingTo,
17719        page: page,
17720        pagesOffered: pagesOffered,
17721        lastPageOffered: lastPageOffered,
17722        hasNextPage: hasNextPage,
17723        results: results,
17724        summary: status === "ok"
17725          ? ("Read " + results.length + " agent card(s) from the directory, which reports " + (resultCount === null ? "an unread number of" : resultCount) + " results" + (page ? " on page " + page : "") + ".")
17726          : "The Opal agent directory listing on this page could not be fully read."
17727      };
17728      if (reason) payload.reason = reason;
17729      if (nextStep) payload.nextStep = nextStep;
17730      return respond(payload);
17731    } catch (err) {
17732      return respond({
17733        status: "error",
17734        action: "read the Opal agent directory listing",
17735        url: location.href,
17736        reason: "The tool hit an unexpected error: " + (err && err.message ? err.message : String(err)),
17737        nextStep: "Reload the directory page and call this tool again.",
17738        summary: "The Opal agent directory listing could not be read."
17739      });
17740    }
17741  }
17742});
17743
17744  /* ---- optimizely-find-implementation-partner ---- */
17745mc.registerToolIf("/partners", {
17746  name: "optimizely-find-implementation-partner",
17747  description: "Searches the Optimizely partner directory on /partners. Types a keyword into the partner search input, opens the Countries, Specializations and Industry Expertises filter groups and toggles the options asked for (option labels are read off the live page at call time and matched by their visible text), can move to a given results page and can clear every active filter first. Returns the matching partners as objects with name, description, tier (for example Premier Platinum or Platinum), number of global certifications, number of product specializations, the full badge list and the partner website URL, together with the page's own 'Showing 1-8 of 183 results' text so the caller gets the total and the current page position, plus the filter options the tool toggled.",
17748  inputSchema: {
17749    type: "object",
17750    properties: {
17751      keyword: {
17752        type: "string",
17753        description: "Text to type into the partner search input, for example an agency name, a city or a technology. Omit to leave the current search term untouched."
17754      },
17755      countries: {
17756        type: "array",
17757        items: { type: "string" },
17758        description: "Country filter labels exactly as shown in the Countries group, for example '
17758United States' or 'Sweden'. Matched against the options read from the live page at call time; the hidden countries behind the 'more' button are revealed automatically. Unknown values return status 'error' with the list of options that are actually available."
17759      },
17760      specializations: {
17761        type: "array",
17762        items: { type: "string" },
17763        description: "Specialization filter labels as shown in the Specializations group, for example 'CMS' or 'Commerce'. The group is opened and its options are enumerated at call time; unknown values return status 'error' with the available options."
17764      },
17765      industryExpertises: {
17766        type: "array",
17767        items: { type: "string" },
17768        description: "Industry Expertises filter labels as shown in that group, for example 'Retail' or 'Financial Services'. The group is opened and its options are enumerated at call time; unknown values return status 'error' with the available options."
17769      },
17770      page: {
17771        type: "integer",
17772        description: "Results page to land on, 1 being the first page. Applied after the keyword and the filters, using the page's own pagination controls."
17773      },
17774      clearFilters: {
17775        type: "boolean",
17776        description: "Set true to press the Clear control next to the Filters heading before applying anything else."
17777      },
17778      limit: {
17779        type: "integer",
17780        description: "Maximum number of partner cards to return from the page currently shown. Defaults to 8, which is one full page."
17781      }
17782    }
17783  },
17784  execute: async function (args) {
17785    trackWebMCPTool("optimizely-find-implementation-partner");
17786
17787    var FORM = 'form:has(input[type="search"])';
17788    var SEARCH_INPUT = FORM + ' input[type="search"]';
17789    var SEARCH_BUTTON = FORM + ' button';
17790    var COUNT_SEL = "div:has(> nav) > p";
17791    var ITEM_SEL = "a:has(ul li)";
17792    var GROUPS = {
17793      countries: {
17794        label: "Countries",
17795        toggle: FORM + " + div > button",
17796        panel: FORM + " + div > div",
17797        more: FORM + " + div > div > button"
17798      },
17799      specializations: {
17800        label: "Specializations",
17801        toggle: FORM + " + div + div > button",
17802        panel: FORM + " + div + div > div",
17803        more: FORM + " + div + div > div > button"
17804      },
17805      industryExpertises: {
17806        label: "Industry Expertises",
17807        toggle: FORM + " + div + div + div > button",
17808        panel: FORM + " + div + div + div > div",
17809        more: FORM + " + div + div + div > div > button"
17810      }
17811    };
17812
17813    function done(payload) {
17814      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
17815    }
17816    function sleep(ms) {
17817      return new Promise(function (r) { setTimeout(r, ms); });
17818    }
17819    function txt(el) {
17820      return el ? (el.textContent || "").replace(/\s+/g, " ").trim() : "";
17821    }
17822    function norm(s) {
17823      return String(s == null ? "" : s).replace(/\s+/g, " ").trim().toLowerCase();
17824    }
17825    function toList(v) {
17826      if (v == null) return [];
17827      var raw = Array.isArray(v) ? v : (typeof v === "string" ? v.split(",") : [v]);
17828      var out = [];
17829      for (var i = 0; i < raw.length; i++) {
17830        var s = String(raw[i] == null ? "" : raw[i]).trim();
17831        if (s) out.push(s);
17832      }
17833      return out;
17834    }
17835    function countElement() {
17836      return document.querySelector(COUNT_SEL);
17837    }
17838    function countText() {
17839      var e = countElement();
17840      return e ? txt(e) : null;
17841    }
17842    // The pagination nav sits beside the results count paragraph. Resolving it from
17843    // that paragraph keeps it away from the site header, which is also a div holding
17844    // a direct child nav and comes first in document order.
17845    function pagerNode() {
17846      var countEl = countElement();
17847      var scope = countEl ? countEl.parentElement : null;
17848      if (!scope) return null;
17849      var navs = scope.querySelectorAll("nav");
17850      for (var i = 0; i < navs.length; i++) {
17851        var bs = navs[i].querySelectorAll("button");
17852        for (var j = 0; j < bs.length; j++) {
17853          if (/^\d+$/.test(txt(bs[j]))) return navs[i];
17854        }
17855      }
17856      return navs.length ? navs[0] : null;
17857    }
17858    function parseCount(t, perHint) {
17859      var info = { rangeStart: null, rangeEnd: null, total: null, page: null, pageCount: null, perPage: null };
17860      if (!t) return info;
17861      var m = t.match(/(\d[\d,]*)\s*[-–—]\s*(\d[\d,]*)\s+of\s+(\d[\d,]*)/i);
17862      if (m) {
17863        info.rangeStart = parseInt(m[1].replace(/,/g, ""), 10);
17864        info.rangeEnd = parseInt(m[2].replace(/,/g, ""), 10);
17865        info.total = parseInt(m[3].replace(/,/g, ""), 10);
17866        var span = info.rangeEnd - info.rangeStart + 1;
17867        var per = (info.rangeEnd < info.total || !perHint) ? span : perHint;
17868        if (per > 0) {
17869          info.perPage = per;
17870          info.page = Math.floor((info.rangeStart - 1) / per) + 1;
17871          info.pageCount = Math.ceil(info.total / per);
17872        }
17873        return info;
17874      }
17875      var one = t.match(/of\s+(\d[\d,]*)/i);
17876      if (one) info.total = parseInt(one[1].replace(/,/g, ""), 10);
17877      return info;
17878    }
17879    function highlight(el) {
17880      if (!el || !el.style) return;
17881      try {
17882        el.style.outline = "3px solid #0037ff";
17883        el.style.outlineOffset = "2px";
17884        setTimeout(function () {
17885          try { el.style.outline = ""; el.style.outlineOffset = ""; } catch (e) {}
17886        }, 6000);
17887      } catch (e) {}
17888    }
17889    function scrollTo(el) {
17890      if (!el || !el.scrollIntoView) return;
17891      try { el.scrollIntoView({ behavior: "smooth", block: "center" }); } catch (e) { try { el.scrollIntoView(); } catch (e2) {} }
17892    }
17893    async function settle(prevText, timeout) {
17894      var deadline = Date.now() + (timeout || 10000);
17895      var last = countText();
17896      var lastChange = Date.now();
17897      var changed = last !== null && last !== prevText;
17898      while (Date.now() < deadline) {
17899        await sleep(200);
17900        var now = countText();
17901        if (now !== last) {
17902          last = now;
17903          lastChange = Date.now();
17904          if (now !== null && now !== prevText) changed = true;
17905          continue;
17906        }
17907        var quiet = Date.now() - lastChange;
17908        if (changed && quiet > 700) break;
17909        if (!changed && quiet > 2200) break;
17910      }
17911      return countText();
17912    }
17913    function filtersHeading() {
17914      var hs = document.querySelectorAll("h2");
17915      for (var i = 0; i < hs.length; i++) {
17916        if (/^filters\b/i.test(txt(hs[i]))) return hs[i];
17917      }
17918      return null;
17919    }
17920    function activeFilterCount() {
17921      var h = filtersHeading();
17922      if (!h) return null;
17923      var m = txt(h).match(/\((\d+)\)/);
17924      return m ? parseInt(m[1], 10) : 0;
17925    }
17926    function optionLabelText(labelEl) {
17927      var nodes = labelEl.querySelectorAll("span, p, div");
17928      for (var i = 0; i < nodes.length; i++) {
17929        var n = nodes[i];
17930        if (n.getAttribute && n.getAttribute("aria-hidden") === "true") continue;
17931        if (n.querySelector && n.querySelector("button")) continue;
17932        var t = txt(n);
17933        if (t) return t;
17934      }
17935      return txt(labelEl);
17936    }
17937    function listOptions(panelSel) {
17938      var labels = document.querySelectorAll(panelSel + " label");
17939      var out = [];
17940      for (var i = 0; i < labels.length; i++) {
17941        var t = optionLabelText(labels[i]);
17942        if (t) out.push({ el: labels[i], text: t });
17943      }
17944      return out;
17945    }
17946    function optionTexts(opts) {
17947      var out = [];
17948      for (var i = 0; i < opts.length; i++) out.push(opts[i].text);
17949      return out;
17950    }
17951    function findOption(opts, want) {
17952      var w = norm(want);
17953      var i;
17954      for (i = 0; i < opts.length; i++) if (norm(opts[i].text) === w) return opts[i];
17955      var partial = [];
17956      for (i = 0; i < opts.length; i++) {
17957        var o = norm(opts[i].text);
17958        if (o.indexOf(w) !== -1 || w.indexOf(o) !== -1) partial.push(opts[i]);
17959      }
17960      return partial.length === 1 ? partial[0] : null;
17961    }
17962    function isChecked(labelEl) {
17963      var input = labelEl.querySelector('input[type="checkbox"], input[type="radio"]');
17964      if (input) return !!input.checked;
17965      var btn = labelEl.querySelector("button");
17966      if (btn) {
17967        var a = btn.getAttribute("aria-checked") || btn.getAttribute("aria-pressed") || btn.getAttribute("data-state");
17968        if (a === "true" || a === "checked" || a === "on") return true;
17969        if (a === "false" || a === "unchecked" || a === "off") return false;
17970      }
17971      return null;
17972    }
17973    function leftPage() {
17974      return location.origin + location.pathname !== startOrigin + startPath;
17975    }
17976
17977    var startOrigin = location.origin;
17978    var startPath = location.pathname;
17979    var a = args || {};
17980    var keyword = (a.keyword == null) ? null : String(a.keyword);
17981    var wantCountries = toList(a.countries);
17982    var wantSpecializations = toList(a.specializations);
17983    var wantIndustries = toList(a.industryExpertises);
17984    var wantPage = parseInt(a.page, 10);
17985    if (!(wantPage > 0)) wantPage = null;
17986    var limit = parseInt(a.limit, 10);
17987    if (!(limit > 0)) limit = 8;
17988    if (limit > 50) limit = 50;
17989    var applied = { keyword: null, countries: [], specializations: [], industryExpertises: [], cleared: false, page: null };
17990    var optionsSeen = {};
17991    var notes = [];
17992
17993    try {
17994      var formNode = document.querySelector(FORM);
17995      if (!formNode) {
17996        try { await waitForElement(FORM, 8000); } catch (e) {}
17997        formNode = document.querySelector(FORM);
17998      }
17999      if (!formNode) {
18000        return done({
18001          status: "error",
18002          action: "search the Optimizely partner directory",
18003          url: location.href,
18004          reason: "The partner search form was not found on this page.",
18005          nextStep: "Open https://www.optimizely.com/partners and call this tool again once the directory has loaded.",
18006          summary: "The partner directory search form is not present, so nothing could be searched."
18007        });
18008      }
18009
18010      // Runtime navigation check: a real native submit would take the document away,
18011      // so build the destination first and hand the caller a navigated payload.
18012      if (keyword !== null && formNode.getAttribute("action")) {
18013        var dest = buildUrl(location.pathname + "?q={keyword}&countries={countries}&specializations={specializations}&page={page}", {
18014          keyword: keyword,
18015          countries: wantCountries.join(","),
18016          specializations: wantSpecializations.join(","),
18017          page: wantPage ? String(wantPage) : ""
18018        });
18019        var navPayload = {
18020          status: "navigated",
18021          action: "submit the partner directory search",
18022          url: dest,
18023          nextStep: "The partner directory is loading with the search applied. Call optimizely-find-implementation-partner again with 
18023no keyword to read the settled results.",
18024          summary: "The partner search form submits natively, so the directory was reloaded with the search applied."
18025        };
18026        location.assign(dest);
18027        return done(navPayload);
18028      }
18029
18030      scrollTo(formNode);
18031      highlight(formNode);
18032
18033      try { await waitForElement(COUNT_SEL, 10000); } catch (e) { notes.push("The results count element did not appear within 10 seconds."); }
18034      var firstInfo = parseCount(countText(), null);
18035      var perPageHint = (firstInfo.rangeEnd !== null && firstInfo.total !== null && firstInfo.rangeEnd < firstInfo.total) ? firstInfo.perPage : null;
18036
18037      // 1. Clear existing filters when asked.
18038      if (a.clearFilters === true) {
18039        var h2 = filtersHeading();
18040        var clearBtn = null;
18041        if (h2) {
18042          var sib = h2.nextElementSibling;
18043          if (sib && sib.tagName === "BUTTON") clearBtn = sib;
18044          if (!clearBtn && h2.parentElement) {
18045            var cands = h2.parentElement.querySelectorAll("button");
18046            for (var ci = 0; ci < cands.length; ci++) {
18047              if (/^clear\b/i.test(txt(cands[ci]))) { clearBtn = cands[ci]; break; }
18048            }
18049          }
18050        }
18051        if (clearBtn) {
18052          var beforeClear = countText();
18053          clearBtn.click();
18054          applied.cleared = true;
18055          await settle(beforeClear, 9000);
18056        } else {
18057          notes.push("The Clear control next to the Filters heading was not found, so existing filters were left in place.");
18058        }
18059      }
18060
18061      // 2. Keyword.
18062      if (keyword !== null) {
18063        try { await fillInput(SEARCH_INPUT, keyword); } catch (e) { notes.push("The search input could not be filled."); }
18064        var searchBtn = document.querySelector(SEARCH_BUTTON);
18065        var beforeSearch = countText();
18066        if (searchBtn) {
18067          searchBtn.click();
18068        } else {
18069          var inputEl = document.querySelector(SEARCH_INPUT);
18070          if (inputEl) {
18071            inputEl.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", code: "Enter", keyCode: 13, which: 13, bubbles: true }));
18072          } else {
18073            notes.push("Neither the search button nor the search input could be located.");
18074          }
18075        }
18076        await settle(beforeSearch, 10000);
18077        var readBack = document.querySelector(SEARCH_INPUT);
18078        applied.keyword = readBack ? readBack.value : null;
18079        if (leftPage()) {
18080          return done({
18081            status: "navigated",
18082            action: "submit the partner directory search",
18083            url: location.href,
18084            nextStep: "A new page is loading. Call optimizely-find-implementation-partner again once it has settled to read the partner results.",
18085            summary: "Searching the directory moved the browser to a different page, so no results were read here."
18086          });
18087        }
18088      }
18089
18090      // 3. Filter groups - options are enumerated from the live page.
18091      var groupOrder = [
18092        { key: "countries", wanted: wantCountries },
18093        { key: "specializations", wanted: wantSpecializations },
18094        { key: "industryExpertises", wanted: wantIndustries }
18095      ];
18096      for (var gi = 0; gi < groupOrder.length; gi++) {
18097        var key = groupOrder[gi].key;
18098        var wanted = groupOrder[gi].wanted;
18099        if (!wanted.length) continue;
18100        var g = GROUPS[key];
18101        var opts = listOptions(g.panel);
18102        if (!opts.length) {
18103          var toggleEl = document.querySelector(g.toggle);
18104          if (toggleEl) {
18105            toggleEl.click();
18106            try { await waitForElement(g.panel + " label", 6000); } catch (e) {}
18107            await sleep(350);
18108            opts = listOptions(g.panel);
18109          }
18110        }
18111        if (!opts.length) {
18112          return done({
18113            status: "error",
18114            action: "open the " + g.label + " filter group",
18115            url: location.href,
18116            reason: "The " + g.label + " filter group did not expose any options after its toggle was clicked.",
18117            availableOptions: [],
18118            group: g.label,
18119            appliedFilters: applied,
18120            nextStep: "Retry once the page has finished loading, or filter with a keyword instead.",
18121            summary: "No " + g.label + " options were readable on the page."
18122          });
18123        }
18124        var missing = [];
18125        for (var wi = 0; wi < wanted.length; wi++) {
18126          var want = wanted[wi];
18127          var hit = findOption(opts, want);
18128          if (!hit) {
18129            var moreBtn = document.querySelector(g.more);
18130            if (moreBtn && !(moreBtn.closest && moreBtn.closest("label"))) {
18131              moreBtn.click();
18132              await sleep(500);
18133              opts = listOptions(g.panel);
18134              hit = findOption(opts, want);
18135            }
18136          }
18137          if (!hit) { missing.push(want); continue; }
18138          var state = isChecked(hit.el);
18139          if (state === true) {
18140            applied[key].push(hit.text);
18141            continue;
18142          }
18143          var clickTarget = hit.el.querySelector("button") || hit.el.querySelector('input[type="checkbox"]') || hit.el;
18144          var beforeFilter = countText();
18145          clickTarget.click();
18146          applied[key].push(hit.text);
18147          await settle(beforeFilter, 10000);
18148          opts = listOptions(g.panel);
18149        }
18150        optionsSeen[key] = optionTexts(opts);
18151        if (missing.length) {
18152          return done({
18153            status: "error",
18154            action: "apply " + g.label + " filters on the partner directory",
18155            url: location.href,
18156            reason: "These " + g.label + " values are not among the options on the page: " + missing.join(", ") + ".",
18157            availableOptions: optionsSeen[key],
18158            group: g.label,
18159            appliedFilters: applied,
18160            activeFilterCount: activeFilterCount(),
18161            nextStep: "Call this tool again using a value from availableOptions for " + key + ".",
18162            summary: "Could not apply " + missing.join(", ") + " because the " + g.label + " group does not offer those options."
18163          });
18164        }
18165      }
18166
18167      // 4. Pagination.
18168      if (wantPage) {
18169        var pageNote = null;
18170        for (var guard = 0; guard < 30; guard++) {
18171          var nowInfo = parseCount(countText(), perPageHint);
18172          if (nowInfo.perPage) perPageHint = nowInfo.rangeEnd !== null && nowInfo.total 
18172!== null && nowInfo.rangeEnd < nowInfo.total ? nowInfo.perPage : perPageHint;
18173          if (nowInfo.page === wantPage) break;
18174          var pager = pagerNode();
18175          if (!pager) { pageNote = "The pagination controls were not found, so the page position was left unchanged."; break; }
18176          var buttons = pager.querySelectorAll("button");
18177          if (!buttons.length) { pageNote = "The pagination controls carried no buttons."; break; }
18178          var direct = null, nextBtn = null, prevBtn = null, bi;
18179          for (bi = 0; bi < buttons.length; bi++) {
18180            var bt = txt(buttons[bi]);
18181            if (bt === String(wantPage) && !buttons[bi].disabled) direct = buttons[bi];
18182            if (/chevron_right|next/i.test(bt)) nextBtn = buttons[bi];
18183            if (/chevron_left|previous|prev/i.test(bt)) prevBtn = buttons[bi];
18184          }
18185          if (!nextBtn) nextBtn = buttons[buttons.length - 1];
18186          if (!prevBtn) prevBtn = buttons[0];
18187          var stepBtn = direct;
18188          if (!stepBtn) stepBtn = (nowInfo.page !== null && wantPage < nowInfo.page) ? prevBtn : nextBtn;
18189          if (!stepBtn || stepBtn.disabled) { pageNote = "Page " + wantPage + " could not be reached with the pagination controls."; break; }
18190          var beforePage = countText();
18191          stepBtn.click();
18192          await settle(beforePage, 10000);
18193          var afterInfo = parseCount(countText(), perPageHint);
18194          if (afterInfo.page === nowInfo.page) { pageNote = "The pagination controls stopped at page " + (afterInfo.page === null ? "unknown" : afterInfo.page) + "."; break; }
18195        }
18196        if (pageNote) notes.push(pageNote);
18197        applied.page = wantPage;
18198      }
18199
18200      if (leftPage()) {
18201        return done({
18202          status: "navigated",
18203          action: "filter the partner directory",
18204          url: location.href,
18205          nextStep: "A new page is loading. Call optimizely-find-implementation-partner again once it has settled to read the partner results.",
18206          summary: "The interaction moved the browser to a different page, so no results were read here."
18207        });
18208      }
18209
18210      // 5. Read the settled results.
18211      try { await waitForElement(ITEM_SEL, 8000); } catch (e) { notes.push("No partner cards were present after waiting 8 seconds."); }
18212      await settle(countText(), 3000);
18213
18214      var extracted = { resultCount: null, truncated: false, results: [] };
18215      try {
18216        var got = await extractItems(ITEM_SEL, {
18217          websiteUrl: "@href",
18218          name: "div > div > div:nth-child(2) > div > span",
18219          description: "div > div > div:nth-child(1)",
18220          tier: "ul li:nth-child(1)"
18221        }, limit);
18222        if (got) extracted = got;
18223      } catch (e) {
18224        notes.push("The partner cards could not be extracted: " + (e && e.message ? e.message : "unknown error") + ".");
18225      }
18226
18227      var cards = document.querySelectorAll(ITEM_SEL);
18228      var badgeLists = [];
18229      for (var ni = 0; ni < cards.length; ni++) {
18230        var lis = cards[ni].querySelectorAll("ul li");
18231        var badges = [];
18232        for (var li = 0; li < lis.length; li++) {
18233          if (lis[li].getAttribute && lis[li].getAttribute("aria-hidden") === "true") continue;
18234          var bt = txt(lis[li]);
18235          if (bt) badges.push(bt);
18236        }
18237        badgeLists.push(badges);
18238      }
18239
18240      var rows = extracted.results || [];
18241      var results = [];
18242      for (var ri = 0; ri < rows.length; ri++) {
18243        var row = rows[ri] || {};
18244        var badgeList = badgeLists[ri] || [];
18245        var certs = null, specs = null, tier = null, other = [];
18246        for (var bj = 0; bj < badgeList.length; bj++) {
18247          var b = badgeList[bj];
18248          var mc1 = b.match(/(\d[\d,]*)\s*global certification/i);
18249          var ms1 = b.match(/(\d[\d,]*)\s*product specialization/i);
18250          if (mc1) { certs = parseInt(mc1[1].replace(/,/g, ""), 10); continue; }
18251          if (ms1) { specs = parseInt(ms1[1].replace(/,/g, ""), 10); continue; }
18252          if (tier === null) tier = b; else other.push(b);
18253        }
18254        if (tier === null && row.tier && !/global certification|product specialization/i.test(row.tier)) tier = row.tier;
18255        var name = (row.name || "").trim();
18256        var desc = (row.description || "").replace(/\s+/g, " ").trim();
18257        for (var bk = 0; bk < badgeList.length; bk++) {
18258          desc = desc.split(badgeList[bk]).join(" ");
18259        }
18260        desc = desc.replace(/\s+/g, " ").trim();
18261        if (name && norm(desc) === norm(name)) desc = "";
18262        var href = (row.websiteUrl || "").trim();
18263        if (href && href.indexOf("//") === -1 && href.charAt(0) === "/") href = location.origin + href;
18264        results.push({
18265          name: name || null,
18266          description: desc || null,
18267          tier: tier || null,
18268          globalCertifications: certs,
18269          productSpecializations: specs,
18270          badges: badgeList,
18271          websiteUrl: href || null
18272        });
18273      }
18274
18275      var finalText = countText();
18276      var info = parseCount(finalText, perPageHint);
18277      var total = info.total;
18278      if (total === null) {
18279        try {
18280          var rc = await readResultCount(COUNT_SEL, null);
18281          if (typeof rc === "number" && !isNaN(rc)) total = rc;
18282        } catch (e) {}
18283      }
18284
18285      var region = cards.length ? (cards[0].parentElement || cards[0]) : null;
18286      if (region) { scrollTo(region); highlight(region); }
18287
18288      var observed = (total !== null) || results.length > 0 || finalText !== null;
18289      var activeCount = activeFilterCount();
18290      var searchNow = document.querySelector(SEARCH_INPUT);
18291      var currentKeyword = searchNow ? searchNow.value : null;
18292
18293      var payload = {
18294        status: observed ? "ok" : "partial",
18295        action: "search and filter the Optimizely partner directory",
18296        url: location.href,
18297        observed: observed,
18298        keyword: currentKeyword,
18299        appliedFilters: applied,
18300        activeFilterCount: activeCount,
18301        countText: finalText,
18302        resultCount: total !== null ? total : (results.length ? results.length : null),
18303        returnedCount: results.length,
18304        rangeStart: info.rangeStart,
18305        rangeEnd: info.rangeEnd,
18306        page: info.page,
18307        pageCount: info.pageCount,
18308        truncated: !!extracted.truncated,
18309        results: results,
18310        availableOptions: optionsSeen,
18311        summary: ""
18312      };
18313      if (notes.length) payload.notes = notes;
18314
18315      if (!observed) {
18316        payload.status = "partial";
18317        payload.reason = "The results count element and the partner cards were both absent after the interaction, so nothing could be read back from the page.";
18318        payload.nextStep = "Wait for the directory to finish loading and call optimizely-find-implementation-partner again.";
18319        payload.summary = "No partner results could be read from the page.";
18320      } else if (!results.length) {
18321        payload.status = total === 0 ? "ok" : "partial";
18322        payload.reason = total === 0 ? undefined : "The count element was read but no partner cards were extracted from the grid.";
18323        if (total !== 0) payload.nextStep = "Call the tool again after the grid has rendered, or clear the filters and retry.";
18324        payload.summary = total === 0
18325          ? "No partners match the current search and filters."
18326          : "Read the count text '" + finalText + "' but could not extract any partner cards.";
18327      } else {
18328        payload.summary = "Read " + results.length + " partner" + (results.length === 1 ? "" : "s") +
18329          (total !== null ? " out of " + total : "") +
18330          (info.page !== null ? " on page " + info.page + (info.pageCount !== null ? " of " + info.pageCount : "") : "") +
18331          (activeCount ? " with " + activeCount + " filter" + (activeCount === 1 ? "" : "s") + " active" : "") + ".";
18332      }
18333      return done(payload);
18334    } catch (err) {
18335      return done({
18336        status: "error",
18337        action: "search and filter the Optimizely partner directory",
18338        url: location.href,
18339        reason: "The tool failed while driving the directory: " + (err && err.message ? err.message : 
18339String(err)) + ".",
18340        appliedFilters: applied,
18341        nextStep: "Reload https://www.optimizely.com/partners and call optimizely-find-implementation-partner again with fewer filters.",
18342        summary: "The partner directory search could not be completed."
18343      });
18344    }
18345  }
18346});
18347
18348  /* ---- optimizely-apply-become-solution-partner, optimizely-apply-become-solution-partner-submit ---- */
18349mc.registerToolIf("/partners", {
18350  name: "optimizely-apply-become-solution-partner",
18351  description: "Fills the 'Become a solution partner' application form in the Become a partner section of the Optimizely partners page: first name, last name, company, job title, work email, website URL, country, number of company employees, prior Optimizely product experience, whether a current client is interested, product interest, interest in building an Optimizely delivery practice, and the free-text answer to 'What interests you about partnering with Optimizely?'. Scrolls the form into view, highlights it, writes each value through React-aware setters, then reads the values back out of the form and returns them together with the fields it treats as required that are still empty, a ready flag, the name of the companion submit tool that commits the application, and the path of the separate technology-partner application form.",
18352  inputSchema: {
18353    type: "object",
18354    properties: {
18355      firstName: { type: "string", description: "First name of the person applying." },
18356      lastName: { type: "string", description: "Last name of the person applying." },
18357      company: { type: "string", description: "Company or agency name for the 'Your Company' field." },
18358      jobTitle: { type: "string", description: "Job title of the person applying." },
18359      workEmail: { type: "string", description: "Work email address." },
18360      websiteUrl: { type: "string", description: "Company website URL, for example https://example.com." },
18361      country: { type: "string", description: "Country name to resolve in the Country typeahead, for example United States. The tool types the name, picks the matching suggestion the page renders and verifies the resolved value; if nothing matches it returns the suggestions it actually saw." },
18362      numberOfCompanyEmployees: { type: "string", description: "Company size band as offered by the page's 'Number of Company Employees' select, for example Under 50 or 1000-2000. The live options are read at call time and returned when the value does not match." },
18363      optimizelyProductExperience: { type: "string", description: "Answer to 'Do you have experience working with Optimizely products', normally Yes or No; matched against the live select options." },
18364      currentClientInterested: { type: "string", description: "Answer to 'Do you have a current client interested in one of our products?', normally Yes or No; matched against the live select options." },
18365      deliveryPracticeInterest: { type: "string", description: "Answer to 'Are you interested in building an Optimizely delivery practice as part of your agency services offering?', normally Yes or No; matched against the live select options." },
18366      productInterest: { type: "string", description: "Free text for the 'Product interest' box, for example which Optimizely products the partnership would cover." },
18367      partneringInterest: { type: "string", description: "Free text answer to 'What interests you about partnering with Optimizely?'." }
18368    },
18369    required: ["firstName", "lastName", "company", "jobTitle", "workEmail", "websiteUrl"]
18370  },
18371  execute: async function (args) {
18372    trackWebMCPTool("optimizely-apply-become-solution-partner");
18373    var TOOL = "optimizely-apply-become-solution-partner";
18374    var SUBMIT_TOOL = "optimizely-apply-become-solution-partner-submit";
18375    var TECH_PATH = "/partners/become-a-partner/tech/";
18376    var ACTION = "fill the Become a solution partner application form";
18377    function out(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
18378    try {
18379      var a = args || {};
18380      function norm(s) { return (s === null || s === undefined) ? "" : String(s).replace(/\s+/g, " ").trim(); }
18381      function low(s) { return norm(s).toLowerCase(); }
18382      function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
18383      function visible(el) {
18384        if (!el) return false;
18385        if (el.getAttribute && el.getAttribute("aria-hidden") === "true") return false;
18386        var cs = null;
18387        try { cs = window.getComputedStyle(el); } catch (e) { cs = null; }
18388        if (cs && (cs.display === "none" || cs.visibility === "hidden")) return false;
18389        var r = el.getBoundingClientRect();
18390        return !!(r.width || r.height || el.offsetParent);
18391      }
18392      function nativeSet(el, value) {
18393        var proto = window.HTMLInputElement.prototype;
18394        if (el.tagName === "TEXTAREA") proto = window.HTMLTextAreaElement.prototype;
18395        else if (el.tagName === "SELECT") proto = window.HTMLSelectElement.prototype;
18396        var d = Object.getOwnPropertyDescriptor(proto, "value");
18397        if (d && d.set) d.set.call(el, value); else el.value = value;
18398        el.dispatchEvent(new Event("input", { bubbles: true }));
18399        el.dispatchEvent(new Event("change", { bubbles: true }));
18400      }
18401
18402      var FORM_SELECTOR = 'main form:has(fieldset)';
18403      var form = null;
18404      try { form = document.querySelector(FORM_SELECTOR); } catch (e) { form = null; }
18405      if (!form) {
18406        var allForms = document.querySelectorAll("main form, form");
18407        for (var fi = 0; fi < allForms.length; fi++) {
18408          if (allForms[fi].querySelector("fieldset") && low(allForms[fi].textContent).indexOf("become a solution partner") !== -1) { form = allForms[fi]; break; }
18409        }
18410        if (!form) {
18411          for (var fj = 0; fj < allForms.length; fj++) { if (allForms[fj].querySelector("fieldset")) { form = allForms[fj]; break; } }
18412        }
18413      }
18414      if (!form) {
18415        return out({
18416          status: "error",
18417          action: ACTION,
18418          url: location.href,
18419          reason: "The Become a solution partner form could not be found in the page.",
18420          nextStep: "Open https://www.optimizely.com/partners, follow the 'Become a partner' link to the application section, then call " + TOOL + " again.",
18421          summary: "Could not find the solution partner application form on this page."
18422        });
18423      }
18424
18425      try {
18426        form.style.outline = "3px solid #0037ff";
18427        form.style.outlineOffset = "4px";
18428        form.scrollIntoView({ behavior: "smooth", block: "center" });
18429      } catch (e) { }
18430
18431      var TEXT_TAGS = 'input[type="text"], input[type="email"], input[type="url"], input[type="tel"], input:not([type])';
18432      var FIELDS = [
18433        { key: "firstName", label: "First name", kind: "text", tags: TEXT_TAGS, required: true, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(1) > div:nth-child(1) > div > div > input[type="text"]' },
18434        { key: "lastName", label: "Last name", kind: "text", tags: TEXT_TAGS, required: true, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(1) > div:nth-child(2) > div > div > input[type="text"]' },
18435        { key: "company", label: "Your Company", kind: "text", tags: TEXT_TAGS, required: true, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(1) > div:nth-child(3) > div > div > input[type="text"]' },
18436        { key: "jobTitle", label: "Job title", kind: "text", tags: TEXT_TAGS, required: true, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(1) > div:nth-child(4) > div > div > input[type="text"]' },
18437        { key: "workEmail", label: "Work email", kind: "text", tags: TEXT_TAGS, required: true, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(1) > div:nth-child(5) > div > div > input[type="text"]' },
18438        { key: "websiteUrl", label: "Website URL", kind: "text", tags: TEXT_TAGS, required: true, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(1) > div:nth-child(6) > div > div > input[type="text"]' },
18439        { key: "numberOfCompanyEmployees", label: "Number of Company Employees", kind: "select", tags: "select", required: false, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(1) > div:nth-child(7) > div > select' },
18440        { key: "partneringInterest", label: "What interests you about partnering with Optimizely?", kind: "textarea", tags: "textarea", required: false, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(2) > div > div > div > textarea' },
18441        { key: "optimizelyProductExperience", label: "Do you have experience working with Optimizely products", kind: "select", tags: "select", required: false, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(3) > div > div > select' },
18442        { key: "currentClientInterested", label: "Do you have a current client interested in one of our products?", kind: "select", tags: "select", required: false, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(4) > div > div > select' },
18443        { key: "productInterest", label: "Product interest", kind: "textarea", tags: "textarea", required: false, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(5) > div > div > div > textarea' },
18444        { key: "deliveryPracticeInterest", label: "Are you interested in building an Optimizely delivery practice", kind: "select", tags: "select", required: false, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(6) > div > div > select' }
18445      ];
18446      var COUNTRY = { key: "country", label: "Country", kind: "country", tags: 'input[type="text"], input
18446:not([type]), input[type="search"]', required: false, selector: 'main form:has(fieldset) fieldset > div > div:nth-child(1) > div:nth-child(8) > div > div > div > div > input' };
18447      var COUNTRY_HIDDEN_SELECTOR = 'main form:has(fieldset) fieldset > div > div:nth-child(1) > div:nth-child(8) > div > input[type="hidden"]';
18448
18449      function labelFor(el) {
18450        var parts = [];
18451        if (el.getAttribute) {
18452          if (el.getAttribute("aria-label")) parts.push(el.getAttribute("aria-label"));
18453          if (el.getAttribute("placeholder")) parts.push(el.getAttribute("placeholder"));
18454          if (el.getAttribute("name")) parts.push(el.getAttribute("name"));
18455          if (el.getAttribute("title")) parts.push(el.getAttribute("title"));
18456        }
18457        if (el.id) {
18458          try {
18459            var esc = (window.CSS && CSS.escape) ? CSS.escape(el.id) : el.id;
18460            var lf = document.querySelector('label[for="' + esc + '"]');
18461            if (lf) parts.push(lf.textContent);
18462          } catch (e) { }
18463        }
18464        if (!parts.length && el.tagName === "SELECT") {
18465          var sibBtn = el.previousElementSibling;
18466          if (sibBtn && sibBtn.tagName === "BUTTON") {
18467            var sibSpan = sibBtn.querySelector("span");
18468            parts.push((sibSpan || sibBtn).textContent);
18469          }
18470        }
18471        var own = el.closest ? el.closest("label") : null;
18472        if (own) parts.push(own.textContent);
18473        if (!parts.length) {
18474          var node = el.parentElement, hops = 0;
18475          while (node && hops < 5 && node !== form) {
18476            var labs = node.querySelectorAll("label, legend");
18477            if (labs.length === 1) { parts.push(labs[0].textContent); break; }
18478            if (labs.length > 1) break;
18479            node = node.parentElement; hops++;
18480          }
18481        }
18482        if (!parts.length) {
18483          var p = el.parentElement, h = 0;
18484          while (p && h < 4 && !parts.length) {
18485            var prev = p.previousElementSibling;
18486            if (prev && !prev.querySelector("input, select, textarea")) {
18487              var pt = norm(prev.textContent);
18488              if (pt && pt.length < 140) parts.push(pt);
18489            }
18490            p = p.parentElement; h++;
18491          }
18492        }
18493        return low(parts.join(" | "));
18494      }
18495
18496      function locate(spec) {
18497        var structural = null;
18498        try { structural = document.querySelector(spec.selector); } catch (e) { structural = null; }
18499        if (structural && form.contains(structural)) {
18500          var lt = labelFor(structural);
18501          if (!lt || lt.indexOf(low(spec.label)) !== -1) return structural;
18502        }
18503        var want = low(spec.label);
18504        var candidates = form.querySelectorAll(spec.tags);
18505        for (var i = 0; i < candidates.length; i++) {
18506          if (labelFor(candidates[i]).indexOf(want) !== -1) return candidates[i];
18507        }
18508        if (want.length > 24) {
18509          var shortWant = want.slice(0, 24);
18510          for (var k = 0; k < candidates.length; k++) {
18511            if (labelFor(candidates[k]).indexOf(shortWant) !== -1) return candidates[k];
18512          }
18513        }
18514        if (structural && form.contains(structural)) return structural;
18515        return null;
18516      }
18517
18518      function optionsOf(sel) {
18519        var res = [];
18520        for (var i = 0; i < sel.options.length; i++) {
18521          var o = sel.options[i];
18522          var lbl = norm(o.textContent) || norm(o.value);
18523          if (!lbl || lbl === "-- choose --") continue;
18524          res.push({ value: o.value, label: lbl });
18525        }
18526        return res;
18527      }
18528      function matchOption(sel, wanted) {
18529        var w = low(wanted);
18530        var opts = optionsOf(sel);
18531        for (var i = 0; i < opts.length; i++) { if (low(opts[i].value) === w || low(opts[i].label) === w) return opts[i]; }
18532        for (var j = 0; j < opts.length; j++) { if (low(opts[j].label).indexOf(w) === 0 || low(opts[j].value).indexOf(w) === 0) return opts[j]; }
18533        return null;
18534      }
18535
18536      var located = {};
18537      for (var s = 0; s < FIELDS.length; s++) { located[FIELDS[s].key] = locate(FIELDS[s]); }
18538      var countryInput = locate(COUNTRY);
18539
18540      var invalid = [];
18541      var selectPlan = [];
18542      for (var v = 0; v < FIELDS.length; v++) {
18543        var spec = FIELDS[v];
18544        if (spec.kind !== "select") continue;
18545        var raw = a[spec.key];
18546        if (raw === undefined || raw === null || norm(raw) === "") continue;
18547        var selEl = located[spec.key];
18548        if (!selEl) continue;
18549        var m = matchOption(selEl, raw);
18550        if (!m) {
18551          invalid.push({
18552            field: spec.key,
18553            label: spec.label,
18554            requested: norm(raw),
18555            availableOptions: optionsOf(selEl).map(function (o) { return o.label; })
18556          });
18557        } else {
18558          selectPlan.push({ spec: spec, el: selEl, option: m });
18559        }
18560      }
18561      if (invalid.length) {
18562        return out({
18563          status: "error",
18564          action: ACTION,
18565          url: location.href,
18566          reason: "The value requested for " + invalid[0].field + " (" + invalid[0].requested + ") is not one of the options this page currently offers for '" + invalid[0].label + "'. Nothing was written to the form.",
18567          invalidFields: invalid,
18568          availableOptions: invalid[0].availableOptions,
18569          nextStep: "Call " + TOOL + " again using one of the values listed in availableOptions for " + invalid[0].field + ".",
18570          summary: "Did not fill the form: " + invalid[0].requested + " is not an option offered for " + invalid[0].label + "."
18571        });
18572      }
18573
18574      var notWritten = [];
18575      var skippedHidden = [];
18576      var notFound = [];
18577
18578      async function writeValue(spec, el, value) {
18579        var target = String(value);
18580        if (spec.kind === "select") {
18581          var usedHelper = false;
18582          try {
18583            if (typeof fillSelect === "function" && document.querySelector(spec.selector) === el) {
18584              await fillSelect(spec.selector, target);
18585              usedHelper = true;
18586            }
18587          } catch (e) { usedHelper = false; }
18588          if (low(el.value) !== low(target)) nativeSet(el, target);
18589          return usedHelper;
18590        }
18591        try {
18592          if (typeof fillInput === "function" && document.querySelector(spec.selector) === el) {
18593            await fillInput(spec.selector, target);
18594          }
18595        } catch (e) { }
18596        if (norm(el.value) !== norm(target)) nativeSet(el, target);
18597        try { el.dispatchEvent(new FocusEvent("focusout", { bubbles: true })); } catch (e) { }
18598        return true;
18599      }
18600
18601      for (var w = 0; w < FIELDS.length; w++) {
18602        var fs = FIELDS[w];
18603        if (fs.kind === "select") continue;
18604        var val = a[fs.key];
18605        if (val === undefined || val === null || norm(val) === "") continue;
18606        var el = located[fs.key];
18607        if (!el) { notFound.push(fs.key); continue; }
18608        if (!visible(el) && norm(el.value)) { skippedHidden.push({ field: fs.key, existingValue: norm(el.value) }); continue; }
18609        await writeValue(fs, el, norm(val));
18610      }
18611      for (var p2 = 0; p2 < selectPlan.length; p2++) {
18612        await writeValue(selectPlan[p2].spec, selectPlan[p2].el, selectPlan[p2].option.value);
18613      }
18614      for (var nf = 0; nf < FIELDS.length; nf++) {
18615        var fk = FIELDS[nf];
18616        if (a[fk.key] !== undefined && a[fk.key] !== null && norm(a[fk.key]) !== "" && !located[fk.key] && notFound.indexOf(fk.key) === -1) notFound.push(fk.key);
18617      }
18618
18619      var countryReport = null;
18620      if (a.country !== undefined && a.country !== null && norm(a.country) !== "") {
18621        if (!countryInput) {
18622          notFound.push("country");
18623          countryReport = { requested: norm(a.country), resolved: "", shown: "", suggestionsSeen: [] };
18624        } else {
18625          var hidden = null;
18626          try { hidden = document.querySelector(COUNTRY_HIDDEN_SELECTOR); } catch (e) { hidden = null; }
18627          var group = countryInput.parentElement, gh = 0;
18628          while (group && gh < 5 && group !== form) {
18629            if (group.querySelector('input[type="hidden"]')) break;
18630            group = group.parentElement; gh++;
18631          }
18632          if (!group || group === form) group = countryInput.closest("div") || form;
18633          if (!hidden || !group.contains(hidden)) {
18634            var h2 = group.querySelector('input[type="hidden"]');
18635            if (h2) hidden = h2;
18636          }
18637          try { countryInput.focus(); } catch (e) { }
18638          nativeSet(countryInput, norm(a.country));
18639          try { countryInput.dispatchEvent(new KeyboardEvent("keyup", { bubbles: true, key: norm(a.country).slice(-1) })); } catch (e) { }
18640          var picked = null;
18641          var seen = [];
18642          for (var t = 0; t < 20 && !picked; t++) {
18643            await sleep(150);
18644            var nodes = group.querySelectorAll('[role="option"], li, ul button, ul div, [role="listbox"] div');
18645            seen = [];
18646            for (var n = 0; n < nodes.length; n++) {
18647              var nd = nodes[n];
18648              if (!visible(nd)) continue;
18649              if (nd.querySelector && nd.querySelector('[role="option"], li')) continue;
18650              var txt = norm(nd.textContent);
18651              if (!txt || txt.length > 60) continue;
18652              if (seen.indexOf(txt) === -1) seen.push(txt);
18653              if (!picked && low(txt) === low(a.country)) picked = nd;
18654            }
18655            if (!picked) {
18656              for (var q = 0; q < nodes.length; q++) {
18657                var nd2 = nodes[q];
18658                if (!visible(nd2)) continue;
18659                var t2 = norm(nd2.textContent);
18660                if (t2 && t2.length <= 60 && low(t2).indexOf(low(a.country)) === 0) { picked = nd2; break; }
18661              }
18662            }
18663          }
18664          if (picked) {
18665            try { picked.dispatchEvent(new MouseEvent("mousedown", { bubbles: true })); } catch (e) { }
18666            try { picked.click(); } catch (e) { }
18667          } else {
18668            try { countryInput.dispatchEvent(new KeyboardEvent("keydown", { bubbles: true, key: "Enter", code: "Enter", keyCode: 13, which: 13 })); } catch (e) { }
18669          }
18670          await sleep(500);
18671          countryReport = {
18672            requested: norm(a.country),
18673            resolved: hidden ? norm(hidden.value) : "",
18674            shown: norm(countryInput.value),
18675            suggestionsSeen: seen.slice(0, 40),
18676            chosenSuggestion: picked ? norm(picked.textContent) : null
18677          };
18678          if (!countryReport.resolved && low(countryReport.shown) !== low(a.country)) notWritten.push("country");
18679        }
18680      }
18681
18682      await sleep(600);
18683
18684      var fields = {};
18685      for (var r = 0; r < FIELDS.length; r++) {
18686        var rs = FIELDS[r];
18687        var rel = located[rs.key] && located[rs.key].isConnected ? located[rs.key] : locate(rs);
18688        located[rs.key] = rel;
18689        if (!rel) continue;
18690        fields[rs.key] = norm(rel.value);
18691      }
18692      if (countryInput) {
18693        var hid = null;
18694        try { hid = document.querySelector(COUNTRY_HIDDEN_SELECTOR); } catch (e) { hid = null; }
18695        fields.country = norm(countryInput.value) || (hid ? norm(hid.value) : "");
18696        fields.countryResolvedValue = hid ? norm(hid.value) : "";
18697      }
18698
18699      for (var c = 0; c < FIELDS.length; c++) {
18700        var cs2 = FIELDS[c];
18701        var want2 = a[cs2.key];
18702        if (want2 === undefined || want2 === null || norm(want2) === "") continue;
18703        var got = fields[cs2.key];
18704        if (got === undefined) continue;
18705        var okWrite = low(got) === low(want2) || (cs2.kind === "select" && low(got).indexOf(low(want2)) === 0) || low(got).indexOf(low(want2)) !== -1;
18706        if (!okWrite && notWritten.indexOf(cs2.key) === -1) notWritten.push(cs2.key);
18707      }
18708
18709      var missing = [];
18710      for (var mq = 0; mq < FIELDS.length; mq++) {
18711        var ms = FIELDS[mq];
18712        if (!ms.required) continue;
18713        if (!located[ms.key]) { missing.push(ms.key); continue; }
18714        if (!norm(located[ms.key].value)) missing.push(ms.key);
18715      }
18716
18717      var readAnything = Object.keys(fields).length > 0;
18718      var ready = missing.length === 0;
18719      var status = (readAnything && ready && notWritten.length === 0 && notFound.length === 0) ? "ok" : (readAnything ? "partial" : "partial");
18720      var payload = {
18721        status: status,
18722        observed: readAnything,
18723        action: ACTION,
18724        fields: fields,
18725        missing: missing,
18726        notWritten: notWritten,
18727        fieldsNotFound: notFound,
18728        skippedPrefilledHiddenFields: skippedHidden,
18729        country: countryReport,
18730        ready: ready && notWritten.length === 0,
18731        submitToolName: SUBMIT_TOOL,
18732        techPartnerApplicationPath: TECH_PATH,
18733        url: location.href,
18734        summary: ready && notWritten.length === 0 && notFound.length === 0
18735          ? "Filled the Become a solution partner form and read every value back; call " + SUBMIT_TOOL + " to send the application."
18736          : "Filled what could be written into the Become a solution partner form; some values are still missing or did not stick."
18737      };
18738      if (payload.status !== "ok") {
18739        var bits = [];
18740        if (missing.length) bits.push("required fields still empty: " + missing.join(", "));
18741        if (notWritten.length) bits.push("values that did not stick in the DOM: " + notWritten.join(", "));
18742        if (notFound.length) bits.push("controls not found on the page: " + notFound.join(", "));
18743        if (!readAnything) bits.push("no field value could be read back out of the form");
18744        payload.reason = bits.join("; ") + ".";
18745        payload.nextStep = "Supply or correct the listed values and call " + TOOL + " again; only call " + SUBMIT_TOOL + " once ready is true.";
18746      }
18747      return out(payload);
18748    } catch (err) {
18749      return out({
18750        status: "error",
18751        action: ACTION,
18752        url: location.href,
18753        reason: "The tool hit an unexpected error: " + (err && err.message ? err.message : 
18753String(err)),
18754        nextStep: "Reload https://www.optimizely.com/partners, scroll to the Become a partner section, then call " + TOOL + " again.",
18755        summary: "Could not fill the solution partner form because of an unexpected error."
18756      });
18757    }
18758  }
18759});
18760
18761mc.registerToolIf("/partners", {
18762  name: "optimizely-apply-become-solution-partner-submit",
18763  description: "Clicks the 'Become a solution partner' button in the Become a partner section of the Optimizely partners page to send the application that optimizely-apply-become-solution-partner filled in, waits for the page to settle, and returns what it observes: the validation messages the form renders, the confirmation text if one appears, any other new message text in the form area, and the URL after the click. If the click triggers a native form navigation instead, it returns that the page navigated and the destination URL.",
18764  inputSchema: { type: "object", properties: {} },
18765  execute: async function (args) {
18766    trackWebMCPTool("optimizely-apply-become-solution-partner-submit");
18767    var FILL_TOOL = "optimizely-apply-become-solution-partner";
18768    var ACTION = "submit the Become a solution partner application form";
18769    function out(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
18770    try {
18771      function norm(s) { return (s === null || s === undefined) ? "" : String(s).replace(/\s+/g, " ").trim(); }
18772      function low(s) { return norm(s).toLowerCase(); }
18773      function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
18774      function visible(el) {
18775        if (!el) return false;
18776        if (el.getAttribute && el.getAttribute("aria-hidden") === "true") return false;
18777        var cs = null;
18778        try { cs = window.getComputedStyle(el); } catch (e) { cs = null; }
18779        if (cs && (cs.display === "none" || cs.visibility === "hidden")) return false;
18780        var r = el.getBoundingClientRect();
18781        return !!(r.width || r.height || el.offsetParent);
18782      }
18783
18784      var form = null;
18785      try { form = document.querySelector('main form:has(fieldset)'); } catch (e) { form = null; }
18786      if (!form) {
18787        var allForms = document.querySelectorAll("main form, form");
18788        for (var fi = 0; fi < allForms.length; fi++) {
18789          if (allForms[fi].querySelector("fieldset") && low(allForms[fi].textContent).indexOf("become a solution partner") !== -1) { form = allForms[fi]; break; }
18790        }
18791        if (!form) { for (var fj = 0; fj < allForms.length; fj++) { if (allForms[fj].querySelector("fieldset")) { form = allForms[fj]; break; } } }
18792      }
18793      if (!form) {
18794        return out({
18795          status: "error",
18796          action: ACTION,
18797          url: location.href,
18798          validationErrors: [],
18799          confirmation: null,
18800          reason: "The Become a solution partner form is not present on this page, so there is nothing to submit.",
18801          nextStep: "Open https://www.optimizely.com/partners, call " + FILL_TOOL + " to fill the application, then call this tool again.",
18802          summary: "Could not submit: the solution partner form was not found."
18803        });
18804      }
18805
18806      var btn = null;
18807      try { btn = document.querySelector('main form:has(fieldset) fieldset > div > div:nth-child(10) > div > div:nth-child(1) > button'); } catch (e) { btn = null; }
18808      if (!btn || !form.contains(btn) || low(btn.textContent).indexOf("become a solution partner") === -1) {
18809        var byText = null;
18810        var buttons = form.querySelectorAll("button");
18811        for (var b = 0; b < buttons.length; b++) { if (low(buttons[b].textContent) === "become a solution partner") { byText = buttons[b]; break; } }
18812        if (!byText) { for (var b2 = 0; b2 < buttons.length; b2++) { if (low(buttons[b2].textContent).indexOf("become a solution partner") !== -1) { byText = buttons[b2]; break; } } }
18813        if (byText) btn = byText;
18814      }
18815      if (!btn || !form.contains(btn)) {
18816        return out({
18817          status: "error",
18818          action: ACTION,
18819          url: location.href,
18820          validationErrors: [],
18821          confirmation: null,
18822          reason: "The 'Become a solution partner' submit button could not be located inside the form.",
18823          nextStep: "Scroll to the Become a partner section, confirm the application form is rendered, then call this tool again.",
18824          summary: "Could not submit: the submit button was not found in the form."
18825        });
18826      }
18827
18828      var startUrl = location.href;
18829      var btnType = low(btn.getAttribute("type") || "submit");
18830      var formAction = form.getAttribute("action");
18831      if (btnType === "submit" && formAction) {
18832        var dest = startUrl;
18833        try { dest = new URL(formAction, location.href).toString(); } catch (e) { dest = startUrl; }
18834        var navPayload = {
18835          status: "navigated",
18836          action: ACTION,
18837          url: dest,
18838          nextStep: "The browser is leaving this page for the post-submission destination; read the confirmation on the page that loads.",
18839          summary: "Submitted the solution partner application; the browser is navigating to " + dest + "."
18840        };
18841        btn.click();
18842        return out(navPayload);
18843      }
18844
18845      var region = form.closest("section") || form.parentElement || form;
18846      function leafTexts(root) {
18847        var set = {};
18848        if (!root) return set;
18849        var all = root.querySelectorAll("*");
18850        for (var i = 0; i < all.length; i++) {
18851          var el = all[i];
18852          if (el.children && el.children.length) continue;
18853          var tn = el.tagName;
18854          if (tn === "SCRIPT" || tn === "STYLE" || tn === "OPTION" || tn === "svg" || tn === "PATH") continue;
18855          if (el.getAttribute && el.getAttribute("aria-hidden") === "true") continue;
18856          var t = norm(el.textContent);
18857          if (!t || t.length > 240) continue;
18858          if (!visible(el)) continue;
18859          set[t] = true;
18860        }
18861        return set;
18862      }
18863      var before = leafTexts(region);
18864
18865      var unloading = false;
18866      var onHide = function () { unloading = true; };
18867      try {
18868        window.addEventListener("pagehide", onHide);
18869        window.addEventListener("beforeunload", onHide);
18870      } catch (e) { }
18871
18872      try { btn.click(); } catch (clickErr) {
18873        try { window.removeEventListener("pagehide", onHide); window.removeEventListener("beforeunload", onHide); } catch (e) { }
18874        return out({
18875          status: "error",
18876          action: ACTION,
18877          url: location.href,
18878          validationErrors: [],
18879          confirmation: null,
18880          reason: "Clicking the submit button threw: " + (clickErr && clickErr.message ? clickErr.message : String(clickErr)),
18881          nextStep: "Reload the partners page, refill the form with " + FILL_TOOL + ", then call this tool again.",
18882          summary: "The submit button could not be clicked."
18883        });
18884      }
18885
18886      var ERROR_RE = /(required|invalid|please\s|must\s|enter a|valid email|cannot be|not a valid|is missing|fill (in|out))/i;
18887      var CONFIRM_RE = /(thank you|thanks|we have received|received your|success|submitted|be in touch|get back to you|shortly|soon as possible|our team will)/i;
18888      var validationErrors = [];
18889      var otherMessages = [];
18890      var confirmation = null;
18891      var navigated = false;
18892      var deadline = Date.now() + 12000;
18893
18894      while (Date.now() < deadline) {
18895        await sleep(400);
18896        if (unloading) { navigated = true; break; }
18897        if (location.href !== startUrl) { navigated = true; break; }
18898        var scope = (region && region.isConnected) ? region : (document.querySelector("main") || document.body);
18899        var now = leafTexts(scope);
18900        validationErrors = [];
18901        otherMessages = [];
18902        confirmation = null;
18903        for (var key in now) {
18904          if (!Object.prototype.hasOwnProperty.call(now, key)) continue;
18905          if (before[key]) continue;
18906          if (ERROR_RE.test(key) && key.length <= 200) {
18907            if (validationErrors.indexOf(key) === -1) validationErrors.push(key);
18908          } else if (CONFIRM_RE.test(key)) {
18909            if (!confirmation || key.length > confirmation.length) confirmation = key;
18910          } else if (otherMessages.indexOf(key) === -1) {
18911            otherMessages.push(key);
18912          }
18913        }
18914        if (!confirmation && !form.isConnected && otherMessages.length) {
18915          var longest = "";
18916          for (var om = 0; om < otherMessages.length; om++) { if (otherMessages[om].length > longest.length) longest = otherMessages[om]; }
18917          if (longest) confirmation = longest;
18918        }
18919        if (validationErrors.length || confirmation) break;
18920      }
18921
18922      try { window.removeEventListener("pagehide", onHide); window.removeEventListener("beforeunload", onHide); } catch (e) { }
18923
18924      if (navigated) {
18925        return out({
18926          status: "navigated",
18927          action: ACTION,
18928          url: location.href,
18929          nextStep: "The page moved to the post-submission destination; read the confirmation text there rather than on the form page.",
18930          summary: "Clicked the submit button and the page navigated away from the application form."
18931        });
18932      }
18933
18934      var gate = null;
18935      try { gate = document.querySelector('iframe[src*="recaptcha"], iframe[src*="hcaptcha"], iframe[title*="captcha" i], #onetrust-banner-sdk'); } catch (e) { gate = null; }
18936      var gateNote = (gate && visible(gate)) ? " A consent or captcha layer is visible and may be blocking the submission." : "";
18937
18938      if (validationErrors.length) {
18939        return out({
18940          status: "error",
18941          action: ACTION,
18942          url: location.href,
18943          validationErrors: validationErrors.slice(0, 20),
18944          confirmation: null,
18945          otherMessages: otherMessages.slice(0, 10),
18946          reason: "The form rendered validation messages instead of a confirmation." + gateNote,
18947          nextStep: "Correct the listed fields with " + FILL_TOOL + ", then call this tool again.",
18948          summary: "The application was not accepted: " + validationErrors.length + " validation message(s) appeared on the form."
18949        });
18950      }
18951      if (confirmation) {
18952        return out({
18953          status: "ok",
18954          observed: true,
18955          action: ACTION,
18956          validationErrors: [],
18957          confirmation: confirmation,
18958          otherMessages: otherMessages.slice(0, 10),
18959          url: location.href,
18960          summary: "Submitted the solution partner application; the page now shows: " + confirmation
18961        });
18962      }
18963      return out({
18964        status: "partial",
18965        observed: false,
18966        action: ACTION,
18967        validationErrors: [],
18968        confirmation: null,
18969        otherMessages: otherMessages.slice(0, 10),
18970        url: location.href,
18971        reason: "The submit button was clicked but neither a confirmation nor a validation message appeared in the form area within 12 seconds, and the URL did not change." + gateNote,
18972        nextStep: "Look at the Become a partner section in the browser to see whether the application was accepted before submitting again; re-running this tool may send a duplicate application.",
18973        summary: "Clicked submit but could not read any outcome text from the page."
18974      });
18975    } catch (err) {
18976      return out({
18977        status: "error",
18978        action: ACTION,
18979        url: location.href,
18980        validationErrors: [],
18981        confirmation: null,
18982        reason: "The tool hit an unexpected error: " + (err && err.message ? err.message : 
18982String(err)),
18983        nextStep: "Check the form in the browser before retrying, so the application is not sent twice.",
18984        summary: "Could not report the submission outcome because of an unexpected error."
18985      });
18986    }
18987  }
18988});
18989
18990  /* ---- optimizely-open-sales-chat ---- */
18991mc.registerToolIf("/products/**", {
18992  name: "optimizely-open-sales-chat",
18993  description: "Opens the Optimizely live sales chat (the Qualified messenger mounted on this page as an app.qualified.com iframe) so the visitor can talk to a sales representative. Optionally attaches known visitor details (email, name, company) to the chat session first, and can push an opening message onto the chat command queue on the visitor's behalf; the conversation itself is rendered inside the app.qualified.com frame, so the tool reports whether that command was accepted rather than confirming delivery. Returns the messenger state read back after the call: whether the messenger widget is present, whether its panel opened, its measured size before and after, which visitor details were attached, whether the opening message command was accepted, the active cookie consent groups, and the page URL.",
18994  inputSchema: {
18995    type: "object",
18996    properties: {
18997      email: {
18998        type: "string",
18999        description: "Work email of the visitor, attached to the chat session before the messenger opens. Optional."
19000      },
19001      name: {
19002        type: "string",
19003        description: "Full name of the visitor, attached to the chat session before the messenger opens. Optional."
19004      },
19005      company: {
19006        type: "string",
19007        description: "Company name of the visitor, attached to the chat session before the messenger opens. Optional."
19008      },
19009      message: {
19010        type: "string",
19011        description: "Opening message to send to sales on the visitor's behalf. Optional; when omitted the messenger is only opened."
19012      }
19013    },
19014    required: []
19015  },
19016  annotations: { readOnlyHint: false },
19017  execute: async function (args) {
19018    trackWebMCPTool("optimizely-open-sales-chat");
19019    var action = "open the Qualified sales chat messenger";
19020    var urlBefore = location.href;
19021    var reply = function (payload) {
19022      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
19023    };
19024    try {
19025      var a = args || {};
19026      var sleep = function (ms) { return new Promise(function (r) { setTimeout(r, ms); }); };
19027      var str = function (v) { return typeof v === "string" && v.trim() ? v.trim() : null; };
19028
19029      var findFrame = function () {
19030        return document.querySelector('iframe[src*="app.qualified.com"]');
19031      };
19032      var frameState = function (el) {
19033        if (!el) { return { present: false, width: 0, height: 0, visible: false }; }
19034        var r = el.getBoundingClientRect();
19035        var cs = window.getComputedStyle(el);
19036        return {
19037          present: true,
19038          width: Math.round(r.width),
19039          height: Math.round(r.height),
19040          visible: r.width > 1 && r.height > 1 && cs.display !== "none" && cs.visibility !== "hidden" && parseFloat(cs.opacity || "1") > 0.05
19041        };
19042      };
19043
19044      var consentGroups = typeof window.OnetrustActiveGroups === "string" ? window.OnetrustActiveGroups : null;
19045      var before = frameState(findFrame());
19046
19047      // The Qualified snippet may initialise late; poll briefly for the command queue.
19048      var api = null;
19049      for (var i = 0; i < 25; i++) {
19050        if (typeof window.qualified === "function") { api = window.qualified; break; }
19051        await sleep(200);
19052      }
19053
19054      var details = {};
19055      if (str(a.email)) { details.email = str(a.email); }
19056      if (str(a.name)) { details.name = str(a.name); }
19057      if (str(a.company)) { details.company = str(a.company); }
19058      var wantedMessage = str(a.message);
19059
19060      var apiUsed = null;
19061      var identify = { requested: Object.keys(details).length > 0, sent: false, fields: details, error: null };
19062      var openingMessage = wantedMessage
19063        ? { text: wantedMessage, commandAttempted: false, commandAccepted: false, deliveryConfirmed: false, error: null }
19064        : null;
19065      var openError = null;
19066
19067      if (api) {
19068        apiUsed = "qualified-command-queue";
19069        if (identify.requested) {
19070          try { api("identify", details); identify.sent = true; }
19071          catch (e1) { identify.error = String((e1 && e1.message) || e1); }
19072        }
19073        try { api("showChat"); }
19074        catch (e2) { openError = String((e2 && e2.message) || e2); }
19075        if (openingMessage) {
19076          openingMessage.commandAttempted = true;
19077          try { api("sendMessage", wantedMessage); openingMessage.commandAccepted = true; }
19078          catch (e3) { openingMessage.error = String((e3 && e3.message) || e3); }
19079        }
19080      } else {
19081        // No global: look for a host-side launcher by its accessible name before giving up.
19082        var launcher = document.querySelector(
19083          'button[aria-label*="chat" i], button[title*="chat" i], a[aria-label*="chat" i], [role="button"][aria-label*="chat" i], button[aria-label*="messenger" i], [role="button"][aria-label*="messenger" i], button[aria-label*="qualified" i]'
19084        );
19085        if (launcher) {
19086          apiUsed = "launcher-click-fallback";
19087          try { launcher.click(); }
19088          catch (e4) { openError = String((e4 && e4.message) || e4); }
19089        } else {
19090          var noApiState = frameState(findFrame());
19091          return reply({
19092            status: "error",
19093            action: action,
19094            url: location.href,
19095            observed: noApiState.present,
19096            messengerPresent: noApiState.present,
19097            messengerOpen: null,
19098            qualifiedApi: "absent",
19099            consentGroups: consentGroups,
19100            reason: "window.qualified was not defined after waiting about 5 seconds, and no chat launcher control with a chat or messenger accessible name was found in the document.",
19101            nextStep: "Cookie consent may be blocking the chat widget: read window.OnetrustActiveGroups, allow the relevant group (OneTrust.ToggleInfoDisplay() opens the preference centre, OneTrust.AllowAll() accepts all), then call this tool again. Otherwise use the 'Book a demo' link at /get-started/ or the contact form on this page.",
19102            summary: "The live sales chat could not be opened because the Qualified messenger API and launcher were both unavailable on this page."
19103          });
19104        }
19105      }
19106
19107      if (location.href !== urlBefore) {
19108        return reply({
19109          status: "navigated",
19110          action: action,
19111          url: location.href,
19112          nextStep: "The page changed while the chat was being opened; wait for the new page to load and call optimizely-open-sales-chat again there.",
19113          summary: "The page navigated while opening the sales chat, so the messenger state was not read."
19114        });
19115      }
19116
19117      // Let the messenger settle, watching for the panel to appear or expand.
19118      var after = frameState(findFrame());
19119      for (var j = 0; j < 25; j++) {
19120        await sleep(200);
19121        after = frameState(findFrame());
19122        var grew = after.visible && (after.width * after.height) > (before.width * before.height) + 1000;
19123        if (grew || (after.visible && after.width * after.height > 90000)) { break; }
19124      }
19125
19126      var frameEl = findFrame();
19127      if (frameEl) {
19128        try {
19129          var prevOutline = frameEl.style.outline;
19130          frameEl.style.outline = "3px solid #0037ff";
19131          setTimeout(function () { frameEl.style.outline = prevOutline; }, 5000);
19132        } catch (e5) { /* highlight is cosmetic only */ }
19133      }
19134
19135      var beforeArea = before.width * before.height;
19136      var afterArea = after.width * after.height;
19137      var expanded = afterArea > beforeArea + 1000;
19138      var messengerOpen = after.present && after.visible && (expanded || afterArea > 90000);
19139
19140      var payload = {
19141        status: "ok",
19142        action: action,
19143        url: location.href,
19144        observed: after.present,
19145        messengerPresent: after.present,
19146        messengerOpen: after.present ? messengerOpen : null,
19147        messengerSize: {
19148          before: { width: before.width, height: before.height, visible: before.visible },
19149          after: { width: after.width, height: after.height, visible: after.visible }
19150        },
19151        qualifiedApi: apiUsed,
19152        identify: identify,
19153        openingMessage: openingMessage,
19154        consentGroups: consentGroups,
19155        summary: ""
19156      };
19157
19158      if (!after.present) {
19159        payload.status = "partial";
19160        payload.observed = false;
19161        payload.reason = "The Qualified messenger element (iframe[src*=app.qualified.com]) was not in the document after the open command, so no messenger state could be read.";
19162        payload.nextStep = "Wait a few seconds for the chat widget to load, check window.OnetrustActiveGroups for c
19162onsent, then call this tool again; or use the 'Book a demo' link at /get-started/.";
19163        payload.summary = "The open command was issued but no messenger widget could be found on the page afterwards.";
19164        return reply(payload);
19165      }
19166
19167      if (openError) {
19168        payload.status = "partial";
19169        payload.reason = "The open command reported an error: " + openError;
19170        payload.nextStep = "Ask the visitor to click the chat launcher on the page, or use the 'Book a demo' link at /get-started/.";
19171        payload.summary = "The messenger widget is on the page but the open command reported an error.";
19172        return reply(payload);
19173      }
19174
19175      if (!messengerOpen) {
19176        payload.status = "partial";
19177        payload.reason = "The messenger widget is present but its measured size after the open command (" + after.width + "x" + after.height + ") did not grow, so an open panel could not be confirmed.";
19178        payload.nextStep = "Ask the visitor to click the chat launcher on the page and retry, or use the 'Book a demo' link at /get-started/.";
19179        payload.summary = "The sales chat widget is present but the panel was not observed to open.";
19180        return reply(payload);
19181      }
19182
19183      if (openingMessage) {
19184        var messageState = openingMessage.commandAccepted
19185          ? "accepted"
19186          : (openingMessage.commandAttempted ? "rejected" : "not attempted because the chat command queue was unavailable");
19187        payload.status = "partial";
19188        payload.reason = "The conversation itself is rendered inside the app.qualified.com frame, so delivery of the opening message could not be read back from this page.";
19189        payload.nextStep = "Ask the visitor to check the open messenger panel and re-type the opening message if it is not shown there.";
19190        payload.summary = "The sales chat messenger opened (" + after.width + "x" + after.height + "); the opening message command was " + messageState + " and its delivery was not confirmed.";
19191        return reply(payload);
19192      }
19193
19194      payload.summary = "The Optimizely sales chat messenger is open (" + after.width + "x" + after.height + ")" + (identify.sent ? " with the visitor details attached." : ".");
19195      return reply(payload);
19196    } catch (err) {
19197      return reply({
19198        status: "error",
19199        action: action,
19200        url: location.href,
19201        observed: false,
19202        reason: "Unexpected error while opening the sales chat: " + String((err && err.message) || err),
19203        nextStep: "Call this tool again, or use the 'Book a demo' link at /get-started/.",
19204        summary: "The sales chat could not be opened because of an unexpected error."
19205      });
19206    }
19207  }
19208});
19209
19210  /* ---- optimizely-read-product-page-details ---- */
19211mc.registerToolIf("/products/*", {
19212  name: "optimizely-read-product-page-details",
19213  description: "Reads the product page the browser is on and returns its content as structured JSON for side-by-side vendor comparison. It expands each 'Questions you might have' accordion item and returns question and answer pairs, steps the customer-proof carousel and the 'Meet the agents' slider through their slides to collect the quotes (audience, quote, name, role, company) or linked customer stories (headline, attribution line, company, link) and every agent slide (name, link, image) together with the slider's own stated total, and returns the feature blocks (eyebrow, heading, description, image, CTA target), the capability columns with their bullet lists, the highlighted metrics, the in-page link targets, the section headings, and whether the inline demo form is present with its field labels. It restores the accordion and the sliders to their starting state, highlights the regions it read, and only reads: it never fills or submits the form. Returns url, title, faqs, quotes, stories, agents, agentTotal, featureBlocks, capabilityColumns, stats, ctas, pageSections, demoFormPresent, demoFormSelector, counts and a per-section note of what was actually observed.",
19214  inputSchema: {
19215    type: "object",
19216    properties: {
19217      sections: {
19218        type: "array",
19219        items: { type: "string" },
19220        description: "Optional list of sections to read. Known values: faqs, quotes, stories, agents, features, stats, capabilities, ctas, form. Omit to read every section; an unknown value is reported back together with the list that is available."
19221      },
19222      maxItemsPerSection: {
19223        type: "number",
19224        description: "Maximum number of items returned per section. Defaults to 40."
19225      },
19226      restoreState: {
19227        type: "boolean",
19228        description: "Collapse the accordion items and return the sliders to their first slide after reading. Defaults to true."
19229      }
19230    }
19231  },
19232  execute: async function (args) {
19233    trackWebMCPTool("optimizely-read-product-page-details");
19234
19235    var out = function (payload) {
19236      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
19237    };
19238    var ACTION = "expand and read product page content";
19239    var KNOWN = ["faqs", "quotes", "stories", "agents", "features", "stats", "capabilities", "ctas", "form"];
19240    var startUrl = location.href;
19241    var base = startUrl.split("#")[0];
19242
19243    try {
19244      var a = args || {};
19245      var wanted = KNOWN.slice();
19246
19247      if (a.sections !== undefined && a.sections !== null && a.sections !== "") {
19248        var requested = Array.isArray(a.sections) ? a.sections : [a.sections];
19249        var bad = [];
19250        var good = [];
19251        for (var si = 0; si < requested.length; si++) {
19252          var val = String(requested[si] === null || requested[si] === undefined ? "" : requested[si]).trim().toLowerCase();
19253          if (!val) continue;
19254          if (KNOWN.indexOf(val) === -1) bad.push(String(requested[si]));
19255          else if (good.indexOf(val) === -1) good.push(val);
19256        }
19257        if (bad.length) {
19258          return out({
19259            status: "error",
19260            action: ACTION,
19261            url: location.href,
19262            reason: "Unknown section name(s) requested: " + bad.join(", "),
19263            availableOptions: KNOWN,
19264            nextStep: "Call again with sections drawn from availableOptions, or omit the sections argument to read every section.",
19265            summary: "Did not read the page because the requested section name(s) are not ones this tool knows."
19266          });
19267        }
19268        if (good.length) wanted = good;
19269      }
19270
19271      var want = function (name) { return wanted.indexOf(name) !== -1; };
19272      var limit = (typeof a.maxItemsPerSection === "number" && a.maxItemsPerSection > 0) ? Math.floor(a.maxItemsPerSection) : 40;
19273      var restore = a.restoreState === false ? false : true;
19274
19275      var sleep = function (ms) { return new Promise(function (r) { setTimeout(r, ms); }); };
19276
19277      var clean = function (raw) {
19278        var t = (raw === null || raw === undefined) ? "" : String(raw);
19279        t = t.replace(/\s+/g, " ").trim();
19280        if (!t) return "";
19281        for (var n = 6; n >= 2; n--) {
19282          if (t.length % n !== 0) continue;
19283          var unit = t.slice(0, t.length / n);
19284          if (unit.length < 2) continue;
19285          var rebuilt = "";
19286          for (var k = 0; k < n; k++) rebuilt += unit;
19287          if (rebuilt === t) return unit.trim();
19288        }
19289        return t;
19290      };
19291
19292      var visText = function (el) {
19293        if (!el) return "";
19294        if (el.getAttribute && el.getAttribute("aria-hidden") === "true") return "";
19295        var parts = [];
19296        var kids = el.childNodes || [];
19297        for (var i = 0; i < kids.length; i++) {
19298          var node = kids[i];
19299          if (node.nodeType === 3) parts.push(node.nodeValue || "");
19300          else if (node.nodeType === 1) parts.push(visText(node));
19301        }
19302        return clean(parts.join(" "));
19303      };
19304
19305      var highlighted = [];
19306      var scrolledOnce = false;
19307      var mark = function (el) {
19308        if (!el || highlighted.indexOf(el) !== -1) return;
19309        try {
19310          el.style.outline = "3px solid #0037ff";
19311          el.style.outlineOffset = "2px";
19312          highlighted.push(el);
19313        } catch (e) {}
19314        if (!scrolledOnce) {
19315          scrolledOnce = true;
19316          try { el.scrollIntoView({ behavior: "smooth", block: "center" }); }
19317          catch (e2) { try { el.scrollIntoView(); } catch (e3) {} }
19318        }
19319      };
19320
19321      var navigatedAway = function () { return location.href.split("#")[0] !== base; };
19322      var navPayload = function (phase) {
19323        return {
19324          status: "navigated",
19325          action: ACTION,
19326          url: location.href,
19327          nextStep: "The browser left the product page while reading the " + phase + " section. Wait for the new page to finish loading, then call optimizely-read-product-page-details again if it is another product page.",
19328          summary: "The page navigated away while reading the " + phase + " section, so no content was returned."
19329        };
19330      };
19331
19332      var safeExtract = async function (itemSelector, fieldMap, cap) {
19333        try {
19334          var r = await extractItems(itemSelector, fieldMap, cap);
19335          if (!r || !Array.isArray(r.results)) return { resultCount: 0, truncated: false, results: [] };
19336          var cleaned = [];
19337          for (var i = 0; i < r.results.length; i++) {
19338            var item = r.results[i];
19339            var obj = {};
19340            for (var key in item) {
19341              if (!Object.prototype.hasOwnProperty.call(item, key)) continue;
19342              var v = item[key];
19343              obj[key] = (typeof v === "string") ? clean(v) : v;
19344            }
19345            cleaned.push(obj);
19346          }
19347          return { resultCount: r.resultCount, truncated: !!r.truncated, results: cleaned };
19348        } catch (e) {
19349          return { resultCount: 0, truncated: false, results: [] };
19350        }
19351      };
19352
19353      var findControl = function (root, names) {
19354        if (!root) return null;
19355        var nodes = root.querySelectorAll('button, div[role="button"], [role="button"]');
19356        for (var i = 0; i < nodes.length; i++) {
19357          var el = nodes[i];
19358          var label = (el.getAttribute("aria-label") || el.getAttribute("title") || visText(el) || "").toLowerCase().trim();
19359          if (!label) continue;
19360          for (var j = 0; j < names.length; j++) {
19361            if (label === names[j] || label.indexOf(names[j]) === 0) return el;
19362          }
19363        }
19364        return null;
19365      };
19366
19367      var notes = {};
19368      var truncatedAny = false;
19369
19370      /* ---------------- FAQ accordion ---------------- */
19371      var faqs = [];
19372      notes.faqs = "notFoundOnThisPage";
19373      if (want("faqs")) {
19374        var accRoot = document.querySelector(".accordionblock");
19375        var faqItems = document.querySelectorAll('.accordionblock div.overflow-clip');
19376        if (accRoot && faqItems.length) {
19377          mark(accRoot);
19378          var faqMax = Math.min(faqItems.length, limit);
19379          if (faqItems.length > faqMax) truncatedAny = true;
19380          for (var fi = 0; fi < faqMax; fi++) {
19381            var item = faqItems[fi];
19382            var trigger = item.querySelector('h3 > button');
19383            if (!trigger) {
19384              faqs.push({ index: fi, question: "", answer: "", status: "trigger-not-found" });
19385              continue;
19386            }
19387            var question = visText(trigger);
19388            var wasOpen = trigger.getAttribute("aria-expanded") === "true";
19389            var panelId = trigger.getAttribute("aria-controls") || "";
19390            if (!wasOpen) { try { trigger.click(); } catch (eClick) {} }
19391
19392            var answer = "";
19393            var deadline = Date.now() + 3000;
19394            while (Date.now() < deadline) {
19395              await sleep(120);
19396              var panel = item.querySelector('div[role="region"]');
19397              if (!panel && panelId) panel = document.getElementById(panelId);
19398              var isOpen = trigger.getAttribute("aria-expanded") === "true";
19399              var panelText = panel ? visText(panel) : "";
19400              if (isOpen && panelText) { answer = panelText; break; }
19401            }
19402
19403            var state = answer ? "expanded" : (trigger.getAttribute("aria-expanded") === "true" ? "expanded-but-no-text-within-3s" : "did-not-expand");
19404            faqs.push({
19405              index: fi,
19406              question: question,
19407              answer: answer,
19408              status: state,
19409              triggerId: trigger.getAttribute("id") || "",
19410              panelId: panelId
19411            });
19412
19413            if (restore && !wasOpen && trigger.getAttribute("aria-expanded") === "true") {
19414              try { trigger.click(); } catch (eRestore) {}
19415              await sleep(90);
19416            }
19417            if (navigatedAway()) return out(navPayload("FAQ accordion"));
19418          }
19419          var answered = 0;
19420          for (var fa = 0; fa < faqs.length; fa++) if (faqs[fa].answer) answered++;
19421          notes.faqs = "expanded " + faqs.length + " of " + faqItems.length + " accordion items; " + answered + " returned answer text";
19422        }
19423      }
19424
19425      /* ---------------- Portrait quote carousel ---------------- */
19426      var quotes = [];
19427      notes.quotes = "notFoundOnThisPage";
19428      if (want("quotes")) {
19429        var cards = document.querySelectorAll('div[role="button"][aria-label="Next card"]');
19430        if (cards.length) {
19431          var quoteRegion = document.querySelector('div[aria-roledescription="carousel"][aria-label="Portrait card carousel"]') || cards[0].parentElement;
19432          mark(quoteRegion);
19433          var seenQuote = {};
19434          var collectQuotes = async function () {
19435            var r = await safeExtract('div[role="button"][aria-label="Next card"]', {
19436              audience: 'span.font-mono',
19437              quote: 'blockquote',
19438              name: 'div.font-body > span:nth-child(1)',
19439              role: 'div.font-body > span:nth-child(2)',
19440              company: 'img @alt',
19441              image: 'img @src'
19442            }, limit);
19443            if (r.truncated) truncatedAny = true;
19444            for (var qi = 0; qi < r.results.length; qi++) {
19445              var q = r.results[qi];
19446              if (!q.quote && !q.name) continue;
19447              /* some cards print the company on the first attribution line and the person on the second */
19448              if (q.name && q.company && q.name === q.company && q.role) {
19449                q.name = q.role;
19450                q.role = "";
19451              }
19452              var key = (q.quote || "") + "|" + (q.company || "") + "|" + (q.name || "");
19453              if (seenQuote[key]) continue;
19454              seenQuote[key] = true;
19455              quotes.push(q);
19456            }
19457          };
19458          await collectQuotes();
19459          var quoteRounds = Math.min(cards.length, 6);
19460          var advanced = 0;
19461          for (var qr = 0; qr < quoteRounds; qr++) {
19462            var activeCard = document.querySelector('div[role="button"][aria-label="Next card"][tabindex="0"]') ||
19463                             document.querySelector('div[role="button"][aria-label="Next card"]');
19464            if (!activeCard) break;
19465            try { activeCard.click(); } catch (eQ) {}
19466            advanced++;
19467            await sleep(420);
19468            if (navigatedAway()) return out(navPayload("customer quote carousel"));
19469            await collectQuotes();
19470          }
19471          notes.quotes = "read the portrait quote carousel; advanced it " + advanced + " time(s) and collected " + quotes.length + " unique card(s) from " + cards.length + " card node(s)";
19472        }
19473      }
19474
19475      /* ---------------- Stories of success carousel ---------------- */
19476      var stories = [];
19477      notes.stories = "notFoundOnThisPage";
19478      if (want("stories")) {
19479        /* the same data-carousel-item attribute is also used by the brand logo marquee;
19480           the story cards are the ones that also carry data-carousel-set */
19481        var storySelector = 'div[data-carousel-item][data-carousel-set]';
19482        var storyNodes = document.querySelectorAll(storySelector);
19483        if (!storyNodes.length) {
19484          storySelector = 'div[data-carousel-item]';
19485          storyNodes = document.querySelectorAll(storySelector);
19486        }
19487        if (storyNodes.length) {
19488          var storyRegion = document.querySelector('div[role="region"][aria-roledescription="carousel"]');
19489          if (!storyRegion) {
19490            storyRegion = storyNodes[0].closest ? (storyNodes[0].closest('section') || storyNodes[0].parentElement) : storyNodes[0].parentElement;
19491          }
19492          mark(storyRegion);
19493          var seenStory = {};
19494          var collectStories = async function () {
19495            var r = await safeExtract(storySelector, {
19496              storyUrl: 'a @href',
19497              headline: 'h3',
19498              attribution: 'div.font-body p',
19499              company: 'span[role="img"] @aria-label',
19500              image: 'img @src',
19501              imageAlt: 'img @alt',
19502              hidden: '@aria-hidden',
19503              carouselSet: '@data-carousel-set'
19504            }, 200);
19505            if (r.truncated) truncatedAny = true;
19506            var visible = [];
19507            for (var vi = 0; vi < r.results.length; vi++) {
19508              if (r.results[vi].hidden !== "true") visible.push(r.results[vi]);
19509            }
19510            var pool = visible.length ? visible : r.results;
19511            var added = 0;
19512            for (var pi = 0; pi < pool.length; pi++) {
19513              var s = pool[pi];
19514              var skey = s.storyUrl || s.headline || "";
19515              if (!skey) continue;
19516              if (seenStory[skey]) continue;
19517              seenStory[skey] = true;
19518              stories.push({
19519                storyUrl: s.storyUrl || "",
19520                headline: s.headline || "",
19521                attribution: s.attribution || "",
19522                company: s.company || "",
19523                image: s.image || "",
19524                imageAlt: s.imageAlt || ""
19525              });
19526              added++;
19527              if (stories.length >= limit) { truncatedAny = true; break; }
19528            }
19529            return added;
19530          };
19531          await collectStories();
19532          var storyNext = findControl(storyRegion, ["next"]);
19533          var storySteps = 0;
19534          var quiet = 0;
19535          while (storyNext && storySteps < 6 && quiet < 2 && stories.length < limit) {
19536            try { storyNext.click(); } catch (eS) {}
19537            storySteps++;
19538            await sleep(450);
19539            if (navigatedAway()) return out(navPayload("stories of success carousel"));
19540            var gained = await collectStories();
19541            quiet = gained > 0 ? 0 : quiet + 1;
19542          }
19543          notes.stories = "read the stories carousel; " + storyNodes.length + " carousel node(s) matching '" + storySelector + "' collapsed to " + stories.length + " unique story/stories after " + storySteps + " advance click(s)" + (storyNext ? "" : " (no advance control found by accessible name, read the nodes directly)");
19544        }
19545      }
19546
19547      /* ---------------- Meet the agents slider ---------------- */
19548      var agents = [];
19549      var agentTotal = null;
19550      notes.agents = "notFoundOnThisPage";
19551      if (want("agents")) {
19552        var showcase = document.querySelector('section[aria-label="Agent showcase"]');
19553        if (showcase) {
19554          mark(showcase);
19555          var liveSelector = 'section[aria-label="Agent showcase"] div[aria-live="polite"][aria-atomic="true"]';
19556          var liveText = function () {
19557            var el = document.querySelector(liveSelector);
19558            return el ? visText(el) : "";
19559          };
19560          var parseTotal = function () {
19561            var m = liveText().match(/of\s+(\d+)/i);
19562            return m ? parseInt(m[1], 10) : null;
19563          };
19564          agentTotal = parseTotal();
19565          var dots = showcase.querySelectorAll('div[role="tablist"][aria-label="Select agent"] button[role="tab"]');
19566          if (agentTotal === null && dots.length) agentTotal = dots.length;
19567          if (agentTotal === null) {
19568            try {
19569              var stated = await readResultCount(liveSelector, null);
19570              if (typeof stated === "number" && stated > 0) agentTotal = stated;
19571            } catch (eCount) {}
19572          }
19573
19574          var seenAgent = {};
19575          var collectAgents = async function () {
19576            var r = await safeExtract('section[aria-label="Agent showcase"] div[aria-roledescription="slide"]', {
19577              slideLabel: '@aria-label',
19578              agentName: 'h3',
19579              agentUrl: 'a @href',
19580              agentTitle: 'a @title',
19581              image: 'img @src',
19582              imageAlt: 'img @alt'
19583            }, 40);
19584            if (r.truncated) truncatedAny = true;
19585            for (var ai = 0; ai < r.results.length; ai++) {
19586              var ag = r.results[ai];
19587              var akey = ag.agentUrl || ag.agentName || ag.slideLabel || "";
19588              if (!akey) continue;
19589              if (seenAgent[akey]) continue;
19590              if (!ag.agentName && !ag.agentUrl) continue;
19591              seenAgent[akey] = true;
19592              agents.push(ag);
19593            }
19594          };
19595
19596          await collectAgents();
19597          var agentSteps = 0;
19598          if (dots.length) {
19599            var dotMax = Math.min(dots.length, 12);
19600            for (var di = 0; di < dotMax; di++) {
19601              try { dots[di].click(); } catch (eD) {}
19602              agentSteps++;
19603              await sleep(520);
19604              if (navigatedAway()) return out(navPayload("agent slider"));
19605              await collectAgents();
19606            }
19607            if (restore && dots[0]) {
19608              try { dots[0].click(); } catch (eD0) {}
19609              await sleep(200);
19610            }
19611          } else {
19612            var nextAgent = showcase.querySelector('button[aria-label="Next agent"]') || document.querySelector('button[aria-label="Next agent"]');
19613            if (nextAgent) {
19614              var stepMax = Math.min(agentTotal || 6, 12);
19615              for (var ns = 0; ns < stepMax; ns++) {
19616                try { nextAgent.click(); } catch (eN) {}
19617                agentSteps++;
19618                await sleep(520);
19619                if (navigatedAway()) return out(navPayload("agent slider"));
19620                await collectAgents();
19621              }
19622              if (restore) {
19623                var prevAgent = document.querySelector('button[aria-label="Previous agent"]');
19624                var guard = 0;
19625                while (prevAgent && guard < 12 && !/agent\s+1\s+of/i.test(liveText())) {
19626                  try { prevAgent.click(); } catch (eP) {}
19627                  guard++;
19628                  await sleep(260);
19629                }
19630              }
19631            }
19632          }
19633          if (agentTotal === null) agentTotal = parseTotal();
19634          notes.agents = "stepped the agent slider " + agentSteps + " time(s) and collected " + agents.length + " unique slide(s)" +
19635            (agentTotal ? "; the slider states a total of " + agentTotal : "; the slider stated no total, so the total is the number collected");
19636        }
19637      }
19638
19639      /* ---------------- Feature blocks ---------------- */
19640      var featureBlocks = [];
19641      notes.features = "notFoundOnThisPage";
19642      if (want("features")) {
19643        var featureNotes = [];
19644        var itcNode = document.querySelector('div.image-text-container-block');
19645        if (itcNode) {
19646          mark(itcNode);
19647          var itc = await safeExtract('div.image-text-container-block', {
19648            eyebrow: 'p.font-body',
19649            heading: 'h3',
19650            description: 'div[data-epi-edit="Description"] p',
19651            ctaLabel: 'a > div:nth-child(2) > span > span:first-child',
19652            ctaTitle: 'a @title',
19653            ctaUrl: 'a @href',
19654            image: 'img @src',
19655            imageAlt: 'img @alt'
19656          }, limit);
19657          if (itc.truncated) truncatedAny = true;
19658          for (var ii = 0; ii < itc.results.length; ii++) {
19659            var b = itc.results[ii];
19660            if (!b.heading && !b.description) continue;
19661            b.blockType = "image-text-container-block";
19662            featureBlocks.push(b);
19663          }
19664          featureNotes.push(itc.results.length + " image-text block(s)");
19665        }
19666        var zzNode = document.querySelector('div.zigzagblock article');
19667        if (zzNode) {
19668          mark(zzNode.closest ? (zzNode.closest('div.zigzagblock') || zzNode) : zzNode);
19669          var zz = await safeExtract('div.zigzagblock article', {
19670            heading: 'h3',
19671            description: 'p',
19672            image: 'img @src',
19673            imageAlt: 'img @alt'
19674          }, limit);
19675          if (zz.truncated) truncatedAny = true;
19676          for (var zi = 0; zi < zz.results.length; zi++) {
19677            var z = zz.results[zi];
19678            if (!z.heading && !z.description) continue;
19679            z.blockType = "zigzag";
19680            featureBlocks.push(z);
19681          }
19682          featureNotes.push(zz.results.length + " zigzag block(s)");
19683        }
19684        if (featureNotes.length) notes.features = "read " + featureNotes.join(" and ");
19685      }
19686
19687      /* ---------------- Stats ---------------- */
19688      var stats = [];
19689      notes.stats = "notFoundOnThisPage";
19690      if (want("stats")) {
19691        var statProbe = document.querySelector('div.rounded-3xl');
19692        if (statProbe) {
19693          var stx = await safeExtract('div.rounded-3xl', {
19694            metric: 'span[aria-hidden="false"]',
19695            label: 'p.font-body'
19696          }, Math.min(limit, 30));
19697          for (var sti = 0; sti < stx.results.length; sti++) {
19698            var s2 = stx.results[sti];
19699            if (!s2.metric || !s2.label) continue;
19700            stats.push({ metric: s2.metric, label: s2.label });
19701          }
19702          if (stats.length) {
19703            mark(statProbe);
19704            notes.stats = "read " + stats.length + " metric block(s) from the leaf span[aria-hidden=\"false\"] nodes";
19705          } else {
19706            notes.stats = "matched " + stx.results.length + " candidate node(s) but none carried both a metric and a label";
19707          }
19708        }
19709      }
19710
19711      /* ---------------- Capability columns ---------------- */
19712      var capabilityColumns = [];
19713      notes.capabilities = "notFoundOnThisPage";
19714      if (want("capabilities")) {
19715        var scope = null;
19716        var featuresHeading = document.getElementById("features");
19717        if (featuresHeading && featuresHeading.closest) {
19718          scope = featuresHeading.closest("section") || featuresHeading.closest("div") || featuresHeading.parentElement;
19719        }
19720        var columns = (scope || document).querySelectorAll('div.bg-tertiary-2');
19721        if (!columns.length) columns = document.querySelectorAll('div.bg-tertiary-2');
19722        for (var ci = 0; ci < columns.length && capabilityColumns.length < limit; ci++) {
19723          var col = columns[ci];
19724          var colHeading = col.querySelector('h3 > span');
19725          var bullets = col.querySelectorAll('div[data-epi-edit="MainBody"] ul > li');
19726          if (!colHeading || !bullets.length) continue;
19727          var list = [];
19728          for (var bi = 0; bi < bullets.length; bi++) {
19729            var bt = visText(bullets[bi]);
19730            if (bt) list.push(bt);
19731          }
19732          var ch = visText(colHeading);
19733          if (!ch && !list.length) continue;
19734          capabilityColumns.push({ heading: ch, capabilities: list });
19735          mark(col);
19736        }
19737        if (capabilityColumns.length) {
19738          notes.capabilities = "read " + capabilityColumns.length + " capability column(s)" + (scope ? " scoped to the features section" : " (features heading anchor not found, scanned the document and kept only columns carrying a heading and a bullet list)");
19739        }
19740      }
19741
19742      /* ---------------- CTA targets ---------------- */
19743      var ctas = [];
19744      notes.ctas = "notFoundOnThisPage";
19745      if (want("ctas")) {
19746        var mainEl = document.querySelector("main") || document.body;
19747        var anchors = mainEl ? mainEl.querySelectorAll("a[href]") : [];
19748        var seenCta = {};
19749        var ctaCap = Math.min(limit, 60);
19750        for (var ci2 = 0; ci2 < anchors.length && ctas.length < ctaCap; ci2++) {
19751          var an = anchors[ci2];
19752          if (an.closest && (an.closest("nav") || an.closest("footer"))) continue;
19753          var href = an.getAttribute("href") || "";
19754          if (!href || href.toLowerCase().indexOf("javascript:") === 0) continue;
19755          var label = clean(an.getAttribute("aria-label") || "");
19756          if (!label) label = visText(an);
19757          if (!label) label = clean(an.getAttribute("title") || "");
19758          if (!label) continue;
19759          if (label.length > 120) label = label.slice(0, 120);
19760          var ckey = href + "|" + label;
19761          if (seenCta[ckey]) continue;
19762          seenCta[ckey] = true;
19763          ctas.push({ label: label, url: href, title: clean(an.getAttribute("title") || "") });
19764        }
19765        if (anchors.length > ctas.length) truncatedAny = truncatedAny || (ctas.length >= ctaCap);
19766        if (ctas.length) notes.ctas = "collected " + ctas.length + " in-page link target(s) outside the site navigation and footer";
19767      }
19768
19769      /* ---------------- Inline demo form (report only) ---------------- */
19770      var demoFormPresent = false;
19771      var demoFormSelector = null;
19772      var demoFormSubmitSelector = null;
19773      var demoFormFields = [];
19774      notes.form = "notFoundOnThisPage";
19775      if (want("form")) {
19776        var demoForm = document.querySelector("form.opti-form");
19777        if (demoForm) {
19778          demoFormPresent = true;
19779          demoFormSelector = "form.opti-form";
19780          if (demoForm.querySelector('button[type="submit"]')) demoFormSubmitSelector = 'form.opti-form button[type="submit"]';
19781          var controls = demoForm.querySelectorAll("input, textarea, select");
19782          for (var fci = 0; fci < controls.length; fci++) {
19783            var ctl = controls[fci];
19784            var ctype = (ctl.getAttribute("type") || ctl.tagName || "").toLowerCase();
19785            if (ctype === "hidden") continue;
19786            var flabel = clean(ctl.getAttribute("aria-label") || "");
19787            if (!flabel && ctl.labels && ctl.labels.length) flabel = visText(ctl.labels[0]);
19788            if (!flabel && ctl.id) {
19789              var lab = document.querySelector('label[for="' + ctl.id + '"]');
19790              if (lab) flabel = visText(lab);
19791            }
19792            if (!flabel) flabel = clean(ctl.getAttribute("placeholder") || "");
19793            if (!flabel) flabel = clean(ctl.getAttribute("name") || "");
19794            if (!flabel) continue;
19795            demoFormFields.push({
19796              label: flabel,
19797              name: ctl.getAttribute("name") || "",
19798              type: ctype,
19799              required: !!ctl.required
19800            });
19801          }
19802          mark(demoForm);
19803          notes.form = "the inline demo form is present with " + demoFormFields.length + " visible field(s); this tool reported it only and did not fill or submit it";
19804        }
19805      }
19806
19807      /* ---------------- Page section headings (unrecognised blocks) ---------------- */
19808      var pageSections = [];
19809      var mainScope = document.querySelector("main") || document.body;
19810      if (mainScope) {
19811        var heads = mainScope.querySelectorAll("h2");
19812        for (var hi = 0; hi < heads.length && pageSections.length < 25; hi++) {
19813          var ht = visText(heads[hi]);
19814          if (ht && pageSections.indexOf(ht) === -1) pageSections.push(ht);
19815        }
19816      }
19817
19818      if (navigatedAway()) return out(navPayload("final read"));
19819
19820      var counts = {
19821        faqs: faqs.length,
19822        quotes: quotes.length,
19823        stories: stories.length,
19824        agents: agents.length,
19825        featureBlocks: featureBlocks.length,
19826        capabilityColumns: capabilityColumns.length,
19827        stats: stats.length,
19828        ctas: ctas.length
19829      };
19830      var total = counts.faqs + counts.quotes + counts.stories + counts.agents + counts.featureBlocks + counts.capabilityColumns + counts.stats + counts.ctas;
19831      var faqsAnswered = 0;
19832      for (var fq = 0; fq < faqs.length; fq++) if (faqs[fq].answer) faqsAnswered++;
19833
19834      var observed = total > 0;
19835      var payload = {
19836        status: observed ? "ok" : "partial",
19837        observed: observed,
19838        action: ACTION,
19839        url: location.href,
19840        title: clean(document.title),
19841        sectionsRead: wanted,
19842        resultCount: total,
19843        counts: counts,
19844        faqs: faqs,
19845        faqsAnswered: faqsAnswered,
19846        quotes: quotes,
19847        stories: stories,
19848        agents: agents,
19849        agentTotal: agentTotal,
19850        featureBlocks: featureBlocks,
19851        capabilityColumns: capabilityColumns,
19852        stats: stats,
19853        ctas: ctas,
19854        pageSections: pageSections,
19855        demoFormPresent: demoFormPresent,
19856        demoFormSelector: demoFormSelector,
19857        demoFormSubmitSelector: demoFormSubmitSelector,
19858        demoFormFields: demoFormFields,
19859        truncated: truncatedAny,
19860        notes: notes,
19861        summary: observed
19862          ? ("Read " + counts.faqs + " FAQ item(s) (" + faqsAnswered + " with answer text), " + counts.quotes + " quote(s), " + counts.stories + " customer story/stories, " + counts.agents + " agent slide(s), " + counts.featureBlocks + " feature block(s), " + counts.capabilityColumns + " capability column(s), " + counts.stats + " metric(s) and " + counts.ctas + " link target(s) from this product page.")
19863          : "Interacted with the page but could not read any content out of the requested sections."
19864      };
19865
19866      if (!observed) {
19867        payload.reason = "None of the requested sections yielded readable content: no accordion items, carousel cards, agent slides, feature blocks, metrics or link targets were found in the settled DOM.";
19868        payload.nextStep = "Confirm the page has finished loading (this page hydrates client-side) and call optimizely-read-product-page-details again, or request a narrower sections list.";
19869      } else if (demoFormPresent) {
19870        payload.nextStep = "The inline demo form is on this page; hand off to the demo request tool to fill and submit it.";
19871      }
19872
19873      return out(payload);
19874    } catch (err) {
19875      return out({
19876        status: "error",
19877        action: ACTION,
19878        url: location.href,
19879        reason: "The read failed with an unexpected error: " + String((err && err.message) ? err.message : err),
19880        nextStep: "Retry once the page has finished loading, or call again with a narrower sections list such as ['faqs'] to isolate the failing section.",
19881        summary: "Could not read the product page content because of an unexpected error."
19882      });
19883    }
19884  }
19885});
19886
19887  /* ---- optimizely-list-optimizely-products-and-plans ---- */
19888mc.registerToolIf("/p*", {
19889  name: "optimizely-list-optimizely-products-and-plans",
19890  description: "Read the Optimizely product catalogue and plan packages from the page currently open. On the products listing it returns each product card as name, one-line positioning description, absolute URL and card image. On the plans page it returns each plan card with its name, one-line description, the complete feature bullet list and its Read more URL, plus the additional marketer tool cards with name, description and URL, and the page's own pricing statement flagged as quote-only when that statement is present. Also returns which page layout was read, the observed card count, where the data was read from, and the page URL. Read-only: it scrolls to and highlights the card region it read.",
19891  inputSchema: {
19892    type: "object",
19893    properties: {
19894      limit: {
19895        type: "integer",
19896        description: "Maximum number of cards to return per section. Defaults to 50."
19897      }
19898    },
19899    required: []
19900  },
19901  execute: async function (args) {
19902    trackWebMCPTool("optimizely-list-optimizely-products-and-plans");
19903
19904    var startUrl = location.href;
19905
19906    function pack(payload) {
19907      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
19908    }
19909
19910    try {
19911      var a = args || {};
19912      var limit = parseInt(a.limit, 10);
19913      if (!isFinite(limit) || limit <= 0) { limit = 50; }
19914      if (limit > 100) { limit = 100; }
19915
19916      function q(sel) {
19917        try { return Array.prototype.slice.call(document.querySelectorAll(sel)); } catch (e) { return []; }
19918      }
19919      function qIn(root, sel) {
19920        try { return Array.prototype.slice.call(root.querySelectorAll(sel)); } catch (e) { return []; }
19921      }
19922      function txt(node) {
19923        if (!node) { return ""; }
19924        try {
19925          if (node.getAttribute && node.getAttribute("aria-hidden") === "true") { return ""; }
19926        } catch (e) {}
19927        return (node.textContent || "").replace(/\s+/g, " ").trim();
19928      }
19929      function leaf(root, sel) {
19930        try { return txt(root.querySelector(sel)); } catch (e) { return ""; }
19931      }
19932      function abs(href) {
19933        if (!href) { return null; }
19934        try { return new URL(href, location.origin).href; } catch (e) { return href; }
19935      }
19936      function imgSrc(root) {
19937        try {
19938          var im = root.querySelector("img");
19939          return im ? (im.getAttribute("src") || im.src || null) : null;
19940        } catch (e) { return null; }
19941      }
19942      function highlight(els) {
19943        if (!els || !els.length) { return false; }
19944        var anc = els[0];
19945        var last = els[els.length - 1];
19946        var guard = 0;
19947        while (anc && anc.parentElement && !anc.contains(last) && guard < 25) {
19948          anc = anc.parentElement;
19949          guard++;
19950        }
19951        var target = anc || els[0];
19952        try {
19953          target.style.outline = "3px solid #0037ff";
19954          target.style.outlineOffset = "4px";
19955          target.scrollIntoView({ behavior: "smooth", block: "center" });
19956          return true;
19957        } catch (e) { return false; }
19958      }
19959
19960      // Attempt the Next.js data island first, as a fallback source of card content.
19961      function islandProps() {
19962        try {
19963          var el = document.getElementById("__NEXT_DATA__");
19964          if (!el) { return null; }
19965          var data = JSON.parse(el.textContent || "{}");
19966          return (data && data.props && data.props.pageProps) || null;
19967        } catch (e) { return null; }
19968      }
19969      function cardsFromIsland(pp) {
19970        var out = [];
19971        var seen = {};
19972        var visits = 0;
19973        function walk(node, depth) {
19974          if (!node || depth > 8 || visits > 6000) { return; }
19975          visits++;
19976          if (Array.isArray(node)) {
19977            for (var i = 0; i < node.length; i++) { walk(node[i], depth + 1); }
19978            return;
19979          }
19980          if (typeof node !== "object") { return; }
19981          var href = null;
19982          ["url", "href", "link", "path", "slug"].forEach(function (k) {
19983            if (!href && typeof node[k] === "string" && node[k].indexOf("/products/") === 0) { href = node[k]; }
19984          });
19985          var name = null;
19986          ["title", "name", "heading", "label", "eyebrow"].forEach(function (k) {
19987            if (!name && typeof node[k] === "string" && node[k].trim() && node[k].length < 120) { name = node[k].trim(); }
19988          });
19989          var desc = null;
19990          ["description", "subtitle", "summary", "intro", "text"].forEach(function (k) {
19991            if (!desc && typeof node[k] === "string" && node[k].trim() && node[k].length < 400) { desc = node[k].trim(); }
19992          });
19993          if (href && name && !seen[href]) {
19994            seen[href] = 1;
19995            out.push({ name: name, description: desc || null, url: abs(href), image: null });
19996          }
19997          for (var k2 in node) {
19998            if (Object.prototype.hasOwnProperty.call(node, k2)) { walk(node[k2], depth + 1); }
19999          }
20000        }
20001        walk(pp, 0);
20002        return out;
20003      }
20004      var islandCards = cardsFromIsland(islandProps());
20005
20006      // Let the page settle before reading it.
20007      try { await waitForElement('main a[href^="/products/"]', 8000); } catch (e) {}
20008      await new Promise(function (r) { setTimeout(r, 350); });
20009
20010      if (location.href !== startUrl) {
20011        return pack({
20012          status: "navigated",
20013          action: "read the Optimizely product and plan cards",
20014          url: location.href,
20015          nextStep: "The page changed while the tool was reading. Call this tool again once the new page has loaded.",
20016          summary: "The page navigated before the cards could be read."
20017        });
20018      }
20019
20020      var allAnchors = q('main a[href^="/products/"]');
20021
20022      var planEls = q('main a[href^="/products/"]:has(ul)');
20023      if (!planEls.length) {
20024        planEls = allAnchors.filter(function (el) { return !!el.querySelector("ul li"); });
20025      }
20026      var toolEls = q('main a[href^="/products/"]:has(video)');
20027      if (!toolEls.length) {
20028        toolEls = allAnchors.filter(function (el) { return !!el.querySelector("video") && !el.querySelector("ul li"); });
20029      }
20030
20031      var path = location.pathname.replace(/\/+$/, "") || "/";
20032      // Only the products listing itself carries the product card section; other
20033      // paths matching this pattern may link to /products/ pages without being it.
20034      var onProductsListing = /(^|\/)products$/.test(path) ||
20035        q("main h2").some(function (h) { return /one platform, every function/i.test(txt(h)); });
20036      var layout;
20037      if (planEls.length) {
20038        layout = "plans";
20039      } else if (path === "/plans") {
20040        layout = "plans";
20041      } else if (allAnchors.length && onProductsListing) {
20042        layout = "products";
20043      } else {
20044        layout = "unknown";
20045      }
20046
20047      if (layout === "plans" && !toolEls.length && allAnchors.length) {
20048        var planHrefs = {};
20049        planEls.forEach(function (el) { planHrefs[el.getAttribute("href")] = 1; });
20050        toolEls = allAnchors.filter(function (el) { return !planHrefs[el.getAttribute("href")]; });
20051      }
20052
20053      // Pricing statement, read from the page's own copy.
20054      var pricingStatement = null;
20055      q("main p, main li, main h2, main h3").some(function (n) {
20056        var t = txt(n);
20057        if (t && /individually packaged/i.test(t)) { pricingStatement = t; return true; }
20058        return false;
20059      });
20060      var pricing = {
20061        statementObserved: !!pricingStatement,
20062        statement: pricingStatement,
20063        model: pricingStatement ? "quote-only" : null
20064      };
20065
20066      if (layout === "plans") {
20067        var plans = [];
20068        planEls.slice(0, limit).forEach(function (el) {
20069          var bullets = [];
20070          qIn(el, "ul li").forEach(function (li) {
20071            var t = txt(li);
20072            if (t) { bullets.push(t); }
20073          });
20074          plans.push({
20075            name: leaf(el, "h3") || null,
20076            description: leaf(el, "p:nth-of-type(1)") || leaf(el, "p") || null,
20077            features: bullets,
20078            featureCount: bullets.length,
20079            readMoreUrl: abs(el.getAttribute("href")),
20080            image: imgSrc(el)
20081          });
20082        });
20083
20084        var seenTool = {};
20085        var tools = [];
20086        toolEls.forEach(function (el) {
20087          if (tools.length >= limit) { return; }
20088          var u = abs(el.getAttribute("href"));
20089          if (!u || seenTool[u]) { return; }
20090          seenTool[u] = 1;
20091          tools.push({
20092            name: leaf(el, "h3") || null,
20093            description: leaf(el, "p") || null,
20094            url: u
20095          });
20096        });
20097
20098        var readable = plans.some(function (p) { return !!p.name || !!p.description; }) ||
20099                       tools.some(function (t) { return !!t.name || !!t.description; });
20100        var highlighted = highlight(planEls.concat(toolEls));
20101
20102        if (!readable) {
20103          return pack({
20104            status: "partial",
20105            observed: false,
20106            action: "read the Optimizely plan packages",
20107            pageLayout: "plans",
20108            url: location.href,
20109            source: "dom",
20110            resultCount: null,
20111            plans: [],
20112            additionalTools: [],
20113            pricing: pricing,
20114            reason: "No plan cards with readable text were found under main on this page.",
20115            nextStep: "Confirm the plans page has finished loading, then call this tool again.",
20116            summary: "The plans page was open but no plan card text could be read."
20117          });
20118        }
20119
20120        return pack({
20121          status: "ok",
20122          observed: true,
20123          action: "read the Optimizely plan packages",
20124          pageLayout: "plans",
20125          url: location.href,
20126          source: "dom",
20127          highlighted: highlighted,
20128          resultCount: plans.length + tools.length,
20129          planCount: plans.length,
20130          additionalToolCount: tools.length,
20131          truncated: (planEls.length > plans.length) || (toolEls.length > tools.length),
20132          plans: plans,
20133          additionalTools: tools,
20134          pricing: pricing,
20135          summary: "Read " + plans.length + " plan card(s) with their feature bullets and " + tools.length + " additional tool card(s) from the plans page" + (pricingStatement ? ", plus the quote-only pricing statement." : ".")
20136        });
20137      }
20138
20139      if (layout === "products") {
20140        var raw = [];
20141        var truncatedFlag = false;
20142        var source = "dom";
20143        try {
20144          var ex = await extractItems('main a[href^="/products/"]', { name: "p", description: "h3", url: "@href", image: "img @src" }, limit);
20145          if (ex && ex.results && ex.results.length) {
20146            raw = ex.results;
20147            truncatedFlag = !!ex.truncated;
20148          }
20149        } catch (e) {}
20150        if (!raw.length) {
20151          allAnchors.slice(0, limit).forEach(function (el) {
20152            raw.push({
20153              name: leaf(el, "p"),
20154              description: leaf(el, "h3"),
20155              url: el.getAttribute("href"),
20156              image: imgSrc(el)
20157            });
20158          });
20159          truncatedFlag = allAnchors.length > raw.length;
20160        }
20161
20162        var seenP = {};
20163        var products = [];
20164        raw.forEach(function (it) {
20165          var u = abs(it.url);
20166          if (!u || seenP[u]) { return; }
20167          seenP[u] = 1;
20168          products.push({
20169            name: (it.name || "").trim() || null,
20170            description: (it.description || "").trim() || null,
20171            url: u,
20172            image: it.image || null
20173          });
20174        });
20175
20176        var okProducts = products.some(function (p) { return !!p.name || !!p.description; });
20177
20178        if (!okProducts && islandCards.length) {
20179          return pack({
20180            status: "partial",
20181            observed: false,
20182            action: "read the Optimizely product catalogue",
20183            pageLayout: "products",
20184            url: location.href,
20185            source: "next-data",
20186            highlighted: false,
20187            resultCount: islandCards.length,
20188            truncated: islandCards.length > limit,
20189            products: islandCards.slice(0, limit),
20190            pricing: pricing,
20191            reason: "The rendered product cards could not be read, so these entries come from the page's embedded data payload and were not checked against the visible cards.",
20192            nextStep: "Confirm the products listing has finished rendering, then call this tool again to read the visible cards.",
20193            summary: "Returned " + Math.min(islandCards.length, limit) + " product entries from the page data payload because the rendered cards could not be read."
20194          });
20195        }
20196
20197        if (!okProducts) {
20198          return pack({
20199            status: "partial",
20200            observed: false,
20201            action: "read the Optimizely product catalogue",
20202            pageLayout: "products",
20203            url: location.href,
20204            source: "dom",
20205            resultCount: null,
20206            products: [],
20207            pricing: pricing,
20208            reason: "No product cards with readable name or description text were found under main on this page.",
20209            nextStep: "Confirm the products listing has finished loading, then call this tool again.",
20210            summary: "The products page was open but no product card text could be read."
20211          });
20212        }
20213
20214        var hl = highlight(allAnchors);
20215        return pack({
20216          status: "ok",
20217          observed: true,
20218          action: "read the Optimizely product catalogue",
20219          pageLayout: "products",
20220          url: location.href,
20221          source: source,
20222          highlighted: hl,
20223          resultCount: products.length,
20224          truncated: truncatedFlag,
20225          products: products,
20226          pricing: pricing,
20227          nextStep: "Open the plans page and call this tool again to read the plan packages, their feature bullets and the pricing statement.",
20228          summary: "Read " + products.length + " product card(s) with name, description and URL from the products listing."
20229        });
20230      }
20231
20232      return pack({
20233        status: "partial",
20234        observed: false,
20235        action: "read the Optimizely product and plan cards",
20236        pageLayout: "unknown",
20237        url: location.href,
20238        source: "dom",
20239        resultCount: null,
20240        products: [],
20241        plans: [],
20242        additionalTools: [],
20243        pricing: pricing,
20244        reason: allAnchors.length
20245          ? "This page links to product pages but carries neither the product card section nor any plan cards, so it is not the products listing or the plans page."
20246          : "This page carries no product cards (no links to /products/ inside main) and no plan cards.",
20247        nextStep: "Open the products listing or the plans page and call this tool again.",
20248        summary: "No product or plan card section was found on this page."
20249      });
20250    } catch (err) {
20251      return pack({
20252        status: "error",
20253        action: "read the Optimizely product and plan cards",
20254        url: location.href,
20255        reason: "Unexpected failure while reading the page: " + ((err && err.message) ? err.message : 
20255String(err)),
20256        nextStep: "Reload the page and call this tool again.",
20257        summary: "The catalogue could not be read because of an unexpected error."
20258      });
20259    }
20260  }
20261});
20262
20263  /* ---- optimizely-developer-hub-entry-points, optimizely-developer-hub-entry-points-results ---- */
20264mc.registerToolIf("/developers/**", {
20265  name: "optimizely-developer-hub-entry-points",
20266  description: "Lists the developer entry points linked from the current Optimizely developer page and opens one on request. Returns each entry point with its title, URL, path, group (role, product, beta, community destination or other), the section heading it sits under and, where the page carries them, its description and call-to-action text, all read from the live page. Pass destination with an entry point title, section, path or URL to follow that link, and the tool reports the destination it is opening.",
20267  inputSchema: {
20268    type: "object",
20269    properties: {
20270      destination: {
20271        type: "string",
20272        description: "Optional. Title, section heading, path or full URL of the entry point to open, for example 'Front-end devs', '/developers/cms/', 'Betas', or an Academy or community URL. It is matched against the entry points read from the page at call time. Omit it to list the entry points without opening one."
20273      }
20274    },
20275    required: [],
20276    additionalProperties: false
20277  },
20278  execute: async function (args) {
20279    trackWebMCPTool("optimizely-developer-hub-entry-points");
20280    var startUrl = location.href;
20281    try {
20282      var params = args || {};
20283      var requested = typeof params.destination === "string" ? params.destination.trim() : "";
20284
20285      try { await waitForElement("main", 5000); } catch (e) {}
20286
20287      var ICONS = /\b(arrow_forward|arrow_outward|arrow_right_alt|open_in_new|north_east|chevron_right|chevron_left|keyboard_arrow_down|keyboard_arrow_up|keyboard_arrow_right|rocket_launch)\b/g;
20288      function clean(s) { return String(s == null ? "" : s).replace(ICONS, " ").replace(/\s+/g, " ").trim(); }
20289      function qsa(sel, root) {
20290        try { return Array.prototype.slice.call((root || document).querySelectorAll(sel)); } catch (e) { return []; }
20291      }
20292      function leafText(el) {
20293        if (!el) return "";
20294        if (el.getAttribute && el.getAttribute("aria-hidden") === "true") return "";
20295        return clean(el.textContent);
20296      }
20297      function headingText(el) {
20298        if (!el) return "";
20299        var parts = [];
20300        (function walk(n) {
20301          if (!n) return;
20302          if (n.nodeType === 3) { var t = clean(n.nodeValue); if (t) parts.push(t); return; }
20303          if (n.nodeType !== 1) return;
20304          if (n.getAttribute && n.getAttribute("aria-hidden") === "true") return;
20305          for (var i = 0; i < n.childNodes.length; i++) { walk(n.childNodes[i]); }
20306        })(el);
20307        var out = [];
20308        for (var j = 0; j < parts.length; j++) {
20309          if (!out.length || out[out.length - 1].toLowerCase() !== parts[j].toLowerCase()) { out.push(parts[j]); }
20310        }
20311        return clean(out.join(" "));
20312      }
20313      function labelOf(a) {
20314        if (!a) return "";
20315        var al = a.getAttribute && (a.getAttribute("aria-label") || a.getAttribute("title"));
20316        if (al) return clean(al);
20317        var h = a.querySelector ? a.querySelector("h1, h2, h3, h4") : null;
20318        if (h && headingText(h)) return headingText(h);
20319        return headingText(a);
20320      }
20321      function contextOf(a) {
20322        var res = { section: null, description: null };
20323        var el = a && a.parentElement;
20324        for (var d = 0; d < 5 && el; d++) {
20325          var tag = el.tagName;
20326          if (tag === "MAIN" || tag === "BODY" || tag === "FOOTER" || tag === "HTML") break;
20327          var h = null;
20328          try { h = el.querySelector("h2, h3, h4, h5"); } catch (e) { h = null; }
20329          if (h && !(h.closest && h.closest("a")) && headingText(h)) {
20330            res.section = headingText(h);
20331            var ps = qsa("p", el);
20332            for (var i = 0; i < ps.length; i++) {
20333              if (ps[i].querySelector("a")) continue;
20334              var t = leafText(ps[i]);
20335              if (t) { res.description = t; break; }
20336            }
20337            break;
20338          }
20339          el = el.parentElement;
20340        }
20341        return res;
20342      }
20343      function abs(href) {
20344        try { return new URL(href, location.href).href; } catch (e) { return String(href == null ? "" : href); }
20345      }
20346      function pathOf(u) {
20347        try { var x = new URL(u); return x.origin === location.origin ? (x.pathname + x.search + x.hash) : null; } catch (e) { return null; }
20348      }
20349
20350      var entries = [];
20351      var seen = {};
20352      var firstNode = null;
20353      function add(group, title, href, description, cta, node, section) {
20354        if (!href) return;
20355        var url = abs(href);
20356        if (!url || /^(javascript|mailto|tel):/i.test(url)) return;
20357        if (seen[url]) return;
20358        seen[url] = true;
20359        entries.push({
20360          group: group,
20361          title: clean(title) || url,
20362          url: url,
20363          path: pathOf(url),
20364          section: clean(section) || null,
20365          description: clean(description) || null,
20366          cta: clean(cta) || null,
20367          external: pathOf(url) === null
20368        });
20369        if (!firstNode && node) firstNode = node;
20370      }
20371
20372      qsa('main p > a[href^="/developers/"]').forEach(function (a) {
20373        add("role", labelOf(a), a.getAttribute("href"), null, null, a, null);
20374      });
20375
20376      var cardData = null;
20377      try {
20378        cardData = await extractItems('main a[href^="/developers/"]:has(h3)', { url: "@href", title: "h3", description: "p", cta: "div > span > span:nth-child(1)" }, 25);
20379      } catch (e) { cardData = null; }
20380      var cardsTruncated = !!(cardData && cardData.truncated);
20381      if (cardData && cardData.results && cardData.results.length) {
20382        cardData.results.forEach(function (r) { add("product", r.title, r.url, r.description, r.cta, null, null); });
20383      } else {
20384        qsa('main a[href^="/developers/"]').forEach(function (a) {
20385          if (!a.querySelector || !a.querySelector("h3")) return;
20386          add("product", headingText(a.querySelector("h3")), a.getAttribute("href"), leafText(a.querySelector("p")), null, a, null);
20387        });
20388      }
20389
20390      qsa('main a[href^="/developers/optimizely-one"]').forEach(function (a) {
20391        var c = contextOf(a);
20392        add("product", c.section || labelOf(a), a.getAttribute("href"), c.description, labelOf(a), a, c.section);
20393      });
20394
20395      qsa('main a[href^="/beta"]').forEach(function (a) {
20396        var c = contextOf(a);
20397        add("beta", labelOf(a) || "Betas", a.getAttribute("href"), c.description, labelOf(a), a, c.section);
20398      });
20399
20400      [
20401        ['main a[href^="https://world.optimizely.com/"]', "Optimizely World community forum"],
20402        ['main a[href^="https://academy.optimizely.com/"]', "Academy course catalog"],
20403        ['main a[href^="https://join.slack.com/"]', "Community Slack invite"],
20404        ['a[href="https://support.optimizely.com/"], a[href="https://support.optimizely.com"]', "Support"]
20405      ].forEach(function (pair) {
20406        qsa(pair[0]).forEach(function (a) {
20407          if (a.querySelector && a.querySelector("h3")) return;
20408          var c = contextOf(a);
20409          add("community", labelOf(a) || pair[1], a.getAttribute("href"), c.description, labelOf(a), a, c.section);
20410        });
20411      });
20412
20413      qsa('main a[href^="/developers/"]').forEach(function (a) {
20414        var t = labelOf(a);
20415        if (!t) return;
20416        add("other", t, a.getAttribute("href"), null, null, a, null);
20417      });
20418
20419      var grouped = { roles: [], products: [], beta: [], community: [], other: [] };
20420      var groupKey = { role: "roles", product: "products", beta: "beta", community: "community", other: "other" };
20421      entries.forEach(function (e) { (grouped[groupKey[e.group]] || grouped.other).push(e); });
20422
20423      function norm(s) { return String(s == null ? "" : s).toLowerCase().replace(/[^a-z0-9]+/g, " ").trim(); }
20424      function normPath(s) { return String(s == null ? "" : s).toLowerCase().replace(/\/+$/, ""); }
20425
20426      if (requested) {
20427        var wantedUrl = /^https?:\/\//i.test(requested) ? requested : (requested.charAt(0) === "/" ? abs(requested) : "");
20428        var target = null;
20429        var tiers = [
20430          function (e) { return wantedUrl && normPath(e.url) === normPath(wantedUrl); },
20431          function (e) { return e.path && normPath(e.path) === normPath(requested); },
20432          function (e) { return e.title && norm(e.title) === norm(requested); },
20433          function (e) { return e.section && norm(e.section) === norm(requested); },
20434          function (e) { return e.cta && norm(e.cta) === norm(requested); },
20435          function (e) { return e.title && norm(requested).length > 2 && norm(e.title).indexOf(norm(requested)) !== -1; },
20436          function (e) { return e.section && norm(requested).length > 2 && norm(e.section).indexOf(norm(requested)) !== -1; },
20437          function (e) { return e.cta && norm(requested).length > 2 && norm(e.cta).indexOf(norm(requested)) !== -1; },
20438          function (e) { return norm(requested).length > 2 && normPath(e.url).indexOf(normPath(requested)) !== -1; }
20439        ];
20440        for (var t = 0; t < tiers.length && !target; t++) {
20441          for (var i = 0; i < entries.length; i++) {
20442            if (tiers[t](entries[i])) { target = entries[i]; break; }
20443          }
20444        }
20445
20446        if (!target) {
20447          return { content: [{ type: "text", text: JSON.stringify({
20448            status: "error",
20449            action: "open a developer entry point",
20450            url: location.href,
20451            reason: entries.length
20452              ? ("No entry point on this page matches \"" + requested + "\".")
20453              : ("No entry point links could be read from this page, so \"" + requested + "\" could not be matched."),
20454            availableOptions: entries.map(function (e) { return { title: e.title, section: e.section, url: e.url, group: e.group }; }),
20455            nextStep: entries.length
20456              ? "Call this tool again with destination set to one of the titles or URLs in availableOptions."
20457              : "Wait for the developer page to finish rendering, then call this tool again with no destination to list what is on it.",
20458            summary: "Could not match \"" + requested + "\" to a link on this page."
20459          }) }] };
20460        }
20461
20462        var destUrl = target.url;
20463        try {
20464          var built = buildUrl("{destinationUrl}", { destinationUrl: target.url });
20465          if (built && /^https?:\/\//i.test(built) && built.indexOf("%3A") === -1) { destUrl = built; }
20466        } catch (e) {}
20467
20468        var sameOrigin = !target.external;
20469        var readable = sameOrigin && target.path && /^\/developers(\/|$)/.test(target.path);
20470        var payload = {
20471          status: "navigated",
20472          action: "open the " + target.title + " developer entry point",
20473          url: destUrl,
20474          fromUrl: startUrl,
20475          destination: {
20476            title: target.title,
20477            url: destUrl,
20478            path: target.path,
20479            group: target.group,
20480            section: target.section,
20481            description: target.description,
20482            cta: target.cta,
20483            external: target.external
20484          },
20485          nextStep: readable
20486            ? ("The browser is loading " + destUrl + ". Once it has settled, call optimizely-developer-hub-entry-points-results to read that page.")
20487            : (sameOrigin
20488              ? ("The browser is loading " + destUrl + ", which is outside the developer section, so the tools registered there do not read it.")
20489              : ("The browser is loading " + destUrl + ", which is on another domain, so the tools registered on this site do not read it.")),
20490          summary: "Opening " + target.title + " at " + destUrl + "."
20491        };
20492
20493        var moved = false;
20494        if (sameOrigin && window.next && window.next.router && typeof window.next.router.push === "function") {
20495          try { window.next.router.push(target.path || destUrl); moved = true; } catch (e) { moved = false; }
20496        }
20497        if (!moved) { location.assign(destUrl); }
20498        return { content: [{ type: "text", text: JSON.stringify(payload) }] };
20499      }
20500
20501      var region = null;
20502      if (firstNode && firstNode.closest) { region = firstNode.closest("section") || firstNode.parentElement; }
20503      if (!region) { region = document.querySelector("main"); }
20504      if (region && region.style) {
20505        var previousOutline = region.style.outline;
20506        region.style.outline = "3px solid #0037ff";
20507        try { region.scrollIntoView({ behavior: "smooth", block: "center" }); } catch (e) { region.scrollIntoView(); }
20508        setTimeout(function () { region.style.outline = previousOutline; }, 6000);
20509      }
20510
20511      if (!entries.length) {
20512        return { content: [{ type: "text", text: JSON.stringify({
20513          status: "partial",
20514          action: "list the developer entry points on this page",
20515          url: location.href,
20516          observed: false,
20517          resultCount: null,
20518          entryPoints: grouped,
20519          results: [],
20520          reason: "No entry point anchors were present in the DOM when the page was read, so nothing could be confirmed.",
20521          nextStep: "Wait for the page to finish rendering and call this tool again, or open /developers directly.",
20522          summary: "The developer entry point links could not be read from this page."
20523        }) }] };
20524      }
20525
20526      return { content: [{ type: "text", text: JSON.stringify({
20527        status: "ok",
20528        action: "list the developer entry points on this page",
20529        url: location.href,
20530        observed: true,
20531        resultCount: entries.length,
20532        truncated: cardsTruncated,
20533        counts: {
20534          roles: grouped.roles.length,
20535          products: grouped.products.length,
20536          beta: grouped.beta.length,
20537          community: grouped.community.length,
20538          other: grouped.other.length
20539        },
20540        entryPoints: grouped,
20541        results: entries,
20542        nextStep: "Call this tool again with destination set to one of the returned titles or URLs to open that entry point.",
20543        summary: "Read " + entries.length + " developer entry points from this page: " + grouped.roles.length + " by role, " + grouped.products.length + " by product, " + grouped.community.length + " community destinations."
20544      }) }] };
20545    } catch (err) {
20546      return { content: [{ type: "text", text: JSON.stringify({
20547        status: "error",
20548        action: "read or open a developer entry point",
20549        url: location.href,
20550        reason: "The tool failed while working with the page: " + (err && err.message ? err.message : 
20550String(err)),
20551        nextStep: "Load the developer page again and call the tool again.",
20552        summary: "Could not read the developer entry points on this page."
20553      }) }] };
20554    }
20555  }
20556});
20557
20558mc.registerToolIf("/developers/**", {
20559  name: "optimizely-developer-hub-entry-points-results",
20560  description: "Reads the Optimizely developer page the browser has landed on and returns its main heading plus the links and cards listed in its main content, each with title, URL, the section heading it sits under and the description and call-to-action text where the page carries them, along with how many were found. Scrolls to and highlights the region the links were read from.",
20561  inputSchema: {
20562    type: "object",
20563    properties: {
20564      limit: {
20565        type: "integer",
20566        description: "Optional maximum number of links to return. Defaults to 40."
20567      }
20568    },
20569    required: [],
20570    additionalProperties: false
20571  },
20572  execute: async function (args) {
20573    trackWebMCPTool("optimizely-developer-hub-entry-points-results");
20574    try {
20575      var limit = (args && typeof args.limit === "number" && args.limit > 0) ? Math.floor(args.limit) : 40;
20576      try { await waitForElement("main", 6000); } catch (e) {}
20577
20578      var ICONS = /\b(arrow_forward|arrow_outward|arrow_right_alt|open_in_new|north_east|chevron_right|chevron_left|keyboard_arrow_down|keyboard_arrow_up|keyboard_arrow_right|rocket_launch)\b/g;
20579      function clean(s) { return String(s == null ? "" : s).replace(ICONS, " ").replace(/\s+/g, " ").trim(); }
20580      function qsa(sel, root) {
20581        try { return Array.prototype.slice.call((root || document).querySelectorAll(sel)); } catch (e) { return []; }
20582      }
20583      function leafText(el) {
20584        if (!el) return "";
20585        if (el.getAttribute && el.getAttribute("aria-hidden") === "true") return "";
20586        return clean(el.textContent);
20587      }
20588      function headingText(el) {
20589        if (!el) return "";
20590        var parts = [];
20591        (function walk(n) {
20592          if (!n) return;
20593          if (n.nodeType === 3) { var t = clean(n.nodeValue); if (t) parts.push(t); return; }
20594          if (n.nodeType !== 1) return;
20595          if (n.getAttribute && n.getAttribute("aria-hidden") === "true") return;
20596          for (var i = 0; i < n.childNodes.length; i++) { walk(n.childNodes[i]); }
20597        })(el);
20598        var out = [];
20599        for (var j = 0; j < parts.length; j++) {
20600          if (!out.length || out[out.length - 1].toLowerCase() !== parts[j].toLowerCase()) { out.push(parts[j]); }
20601        }
20602        return clean(out.join(" "));
20603      }
20604      function labelOf(a) {
20605        if (!a) return "";
20606        var al = a.getAttribute && (a.getAttribute("aria-label") || a.getAttribute("title"));
20607        if (al) return clean(al);
20608        var h = a.querySelector ? a.querySelector("h1, h2, h3, h4") : null;
20609        if (h && headingText(h)) return headingText(h);
20610        return headingText(a);
20611      }
20612      function contextOf(a) {
20613        var res = { section: null, description: null };
20614        var el = a && a.parentElement;
20615        for (var d = 0; d < 5 && el; d++) {
20616          var tag = el.tagName;
20617          if (tag === "MAIN" || tag === "BODY" || tag === "FOOTER" || tag === "HTML") break;
20618          var h = null;
20619          try { h = el.querySelector("h2, h3, h4, h5"); } catch (e) { h = null; }
20620          if (h && !(h.closest && h.closest("a")) && headingText(h)) {
20621            res.section = headingText(h);
20622            var ps = qsa("p", el);
20623            for (var i = 0; i < ps.length; i++) {
20624              if (ps[i].querySelector("a")) continue;
20625              var t = leafText(ps[i]);
20626              if (t) { res.description = t; break; }
20627            }
20628            break;
20629          }
20630          el = el.parentElement;
20631        }
20632        return res;
20633      }
20634      function abs(href) {
20635        try { return new URL(href, location.href).href; } catch (e) { return String(href == null ? "" : href); }
20636      }
20637
20638      var results = [];
20639      var seen = {};
20640      var firstNode = null;
20641      function add(title, href, description, cta, node, section) {
20642        if (!href) return;
20643        var url = abs(href);
20644        if (!url || /^(javascript|mailto|tel):/i.test(url) || seen[url]) return;
20645        seen[url] = true;
20646        results.push({
20647          title: clean(title) || url,
20648          url: url,
20649          section: clean(section) || null,
20650          description: clean(description) || null,
20651          cta: clean(cta) || null
20652        });
20653        if (!firstNode && node) firstNode = node;
20654      }
20655
20656      var cardData = null;
20657      try {
20658        cardData = await extractItems('main a[href^="/developers/"]:has(h3)', { url: "@href", title: "h3", description: "p", cta: "div > span > span:nth-child(1)" }, limit);
20659      } catch (e) { cardData = null; }
20660      if (cardData && cardData.results) {
20661        cardData.results.forEach(function (r) { add(r.title, r.url, r.description, r.cta, null, null); });
20662      }
20663
20664      qsa('main p > a[href^="/developers/"]').forEach(function (a) { add(labelOf(a), a.getAttribute("href"), null, null, a, null); });
20665      qsa('main a[href^="/developers/"]').forEach(function (a) {
20666        var h3 = a.querySelector ? a.querySelector("h3") : null;
20667        var c = h3 ? { section: null, description: null } : contextOf(a);
20668        add(labelOf(a), a.getAttribute("href"), h3 ? leafText(a.querySelector("p")) : c.description, null, a, c.section);
20669      });
20670      qsa('main a[href^="/beta"]').forEach(function (a) {
20671        var c = contextOf(a);
20672        add(labelOf(a) || "Betas", a.getAttribute("href"), c.description, labelOf(a), a, c.section);
20673      });
20674      [
20675        'main a[href^="https://world.optimizely.com/"]',
20676        'main a[href^="https://academy.optimizely.com/"]',
20677        'main a[href^="https://join.slack.com/"]',
20678        'a[href="https://support.optimizely.com/"], a[href="https://support.optimizely.com"]'
20679      ].forEach(function (sel) {
20680        qsa(sel).forEach(function (a) {
20681          if (a.querySelector && a.querySelector("h3")) return;
20682          var c = contextOf(a);
20683          add(labelOf(a), a.getAttribute("href"), c.description, labelOf(a), a, c.section);
20684        });
20685      });
20686      qsa("main p > a[href]").forEach(function (a) {
20687        var c = contextOf(a);
20688        add(labelOf(a), a.getAttribute("href"), c.description, labelOf(a), a, c.section);
20689      });
20690
20691      var truncated = results.length > limit || !!(cardData && cardData.truncated);
20692      if (results.length > limit) { results = results.slice(0, limit); }
20693
20694      var heading = headingText(document.querySelector("main h1")) || clean(document.title) || null;
20695
20696      var region = null;
20697      if (firstNode && firstNode.closest) { region = firstNode.closest("section") || firstNode.parentElement; }
20698      if (!region) { region = document.querySelector("main"); }
20699      if (region && region.style) {
20700        var previousOutline = region.style.outline;
20701        region.style.outline = "3px solid #0037ff";
20702        try { region.scrollIntoView({ behavior: "smooth", block: "center" }); } catch (e) { region.scrollIntoView(); }
20703        setTimeout(function () { region.style.outline = previousOutline; }, 6000);
20704      }
20705
20706      if (!results.length) {
20707        return { content: [{ type: "text", text: JSON.stringify({
20708          status: "partial",
20709          action: "read the developer page this browser landed on",
20710          url: location.href,
20711          observed: false,
20712          heading: heading,
20713          resultCount: null,
20714          results: [],
20715          truncated: false,
20716          reason: "No links were present in the main content when the page was read.",
20717          nextStep: "Wait for the page to finish rendering and call this tool again.",
20718          summary: "Landed on " + (heading || location.href) + " but no links could be read from it yet."
20719        }) }] };
20720      }
20721
20722      return { content: [{ type: "text", text: JSON.stringify({
20723        status: "ok",
20724        action: "read the developer page this browser landed on",
20725        url: location.href,
20726        observed: true,
20727        heading: heading,
20728        resultCount: results.length,
20729        results: results,
20730        truncated: truncated,
20731        summary: "Read " + results.length + " links from " + (heading || location.href) + "."
20732      }) }] };
20733    } catch (err) {
20734      return { content: [{ type: "text", text: JSON.stringify({
20735        status: "error",
20736        action: "read the developer page this browser landed on",
20737        url: location.href,
20738        reason: "The tool failed while reading the page: " + (err && err.message ? err.message : String(err)),
20739        nextStep: "Load the page again and call this tool again.",
20740        summary: "Could not read this developer page."
20741      }) }] };
20742    }
20743  }
20744});
20745
20746  /* ---- optimizely-manage-cookie-consent ---- */
20747mc.registerTool({
20748  name: "optimizely-manage-cookie-consent",
20749  description: "Manages cookie consent on this page through the OneTrust consent SDK: reads the current consent state, opens or closes the cookie preference centre, allows every category, rejects the optional categories, or updates named consent groups. Returns the action performed, the mechanism it used, the raw OnetrustActiveGroups value, the parsed list of active consent group IDs, any consent groups and their labels that can be read from the preference centre while it is open, whether the preference centre is open, which consent-gated globals (chat, analytics) are present afterwards, and the 'Do not sell or share my personal information' privacy portal URL read from the page footer.",
20750  inputSchema: {
20751    type: "object",
20752    properties: {
20753      action: {
20754        type: "string",
20755        description: "What to do: status (read only, the default), open, close, accept-all, reject-all, or update-groups. The value is validated at call time and an unsupported one comes back as an error listing availableOptions."
20756      },
20757      allowGroups: {
20758        type: "array",
20759        items: { type: "string" },
20760        description: "Consent group IDs to allow, used with action update-groups. The IDs are read off the preference centre at call time, so an unknown one comes back as an error listing the IDs that actually exist."
20761      },
20762      denyGroups: {
20763        type: "array",
20764        items: { type: "string" },
20765        description: "Consent group IDs to withdraw consent from, used with action update-groups. Validated against the same live list of group IDs."
20766      }
20767    },
20768    required: []
20769  },
20770  execute: async function (args) {
20771    trackWebMCPTool("optimizely-manage-cookie-consent");
20772
20773    var SUPPORTED = ["status", "open", "close", "accept-all", "reject-all", "update-groups"];
20774    var DSAR_SELECTOR = 'footer a[href="https://privacyportal-cdn.onetrust.com/dsarwebform/1b599339-d045-4e2a-bde8-37ce346ab896/f6b0dc19-39c9-4553-bae5-b0306087d29e.html"]';
20775    var startUrl = location.href;
20776
20777    function reply(payload) {
20778      return { content: [{ type: "text", text: JSON.stringify(payload) }] };
20779    }
20780    function sleep(ms) {
20781      return new Promise(function (resolve) { setTimeout(resolve, ms); });
20782    }
20783    function clean(s) {
20784      return (s || "").replace(/\s+/g, " ").trim();
20785    }
20786    function isVisible(el) {
20787      if (!el) return false;
20788      try {
20789        var r = el.getBoundingClientRect();
20790        if (!r.width || !r.height) return false;
20791        var s = window.getComputedStyle(el);
20792        return s.display !== "none" && s.visibility !== "hidden" && s.opacity !== "0";
20793      } catch (e) {
20794        return false;
20795      }
20796    }
20797    function getApi() {
20798      try {
20799        var o = window.OneTrust;
20800        if (o && (typeof o.ToggleInfoDisplay === "function" || typeof o.AllowAll === "function" || typeof o.RejectAll === "function")) {
20801          return { api: o, name: "window.OneTrust" };
20802        }
20803        var l = window.Optanon;
20804        if (l && (typeof l.ToggleInfoDisplay === "function" || typeof l.AllowAll === "function" || typeof l.RejectAll === "function")) {
20805          return { api: l, name: "window.Optanon" };
20806        }
20807      } catch (e) {}
20808      return null;
20809    }
20810    async function waitForApi(timeout) {
20811      var deadline = Date.now() + timeout;
20812      var f = getApi();
20813      while (!f && Date.now() < deadline) {
20814        await sleep(200);
20815        f = getApi();
20816      }
20817      return f;
20818    }
20819    function readActiveGroups() {
20820      var raw = null;
20821      try {
20822        if (typeof window.OnetrustActiveGroups === "string") raw = window.OnetrustActiveGroups;
20823        else if (typeof window.OptanonActiveGroups === "string") raw = window.OptanonActiveGroups;
20824      } catch (e) {
20825        raw = null;
20826      }
20827      var list = [];
20828      if (raw) {
20829        var parts = raw.split(",");
20830        for (var i = 0; i < parts.length; i++) {
20831          var p = clean(parts[i]);
20832          if (p && list.indexOf(p) === -1) list.push(p);
20833        }
20834      }
20835      return { raw: raw, list: list };
20836    }
20837    function findModal() {
20838      var pc = document.getElementById("onetrust-pc-sdk");
20839      if (pc && isVisible(pc)) return pc;
20840      var nodes = document.querySelectorAll('[role="dialog"], [role="alertdialog"]');
20841      for (var i = 0; i < nodes.length; i++) {
20842        var n = nodes[i];
20843        if (!isVisible(n)) continue;
20844        if (n.id === "onetrust-banner-sdk") continue;
20845        if (n.closest && n.closest("#onetrust-banner-sdk")) continue;
20846        var t = clean(n.textContent).toLowerCase();
20847        if (t.indexOf("cookie") > -1 || t.indexOf("consent") > -1 || t.indexOf("privacy preference") > -1) return n;
20848      }
20849      return null;
20850    }
20851    async function waitForModal(timeout) {
20852      var deadline = Date.now() + timeout;
20853      var m = findModal();
20854      while (!m && Date.now() < deadline) {
20855        await sleep(150);
20856        m = findModal();
20857      }
20858      return m;
20859    }
20860    async function waitForModalGone(timeout) {
20861      var deadline = Date.now() + timeout;
20862      while (findModal() && Date.now() < deadline) {
20863        await sleep(150);
20864      }
20865      return !findModal();
20866    }
20867    function labelFor(el) {
20868      var txt = "";
20869      try {
20870        var aria = clean(el.getAttribute("aria-label"));
20871        if (aria) return aria.slice(0, 120);
20872        var labelledBy = el.getAttribute("aria-labelledby");
20873        if (labelledBy) {
20874          var ids = labelledBy.split(/\s+/);
20875          for (var i = 0; i < ids.length; i++) {
20876            var ref = document.getElementById(ids[i]);
20877            if (ref && ref.getAttribute("aria-hidden") !== "true") txt += " " + clean(ref.textContent);
20878          }
20879          txt = clean(txt);
20880          if (txt) return txt.slice(0, 120);
20881        }
20882        var id = el.getAttribute("id");
20883        if (id) {
20884          var sel = 'label[for="' + (window.CSS && CSS.escape ? CSS.escape(id) : id) + '"]';
20885          var lab = document.querySelector(sel);
20886          if (lab && lab.getAttribute("aria-hidden") !== "true") {
20887            txt = clean(lab.textContent);
20888            if (txt) return txt.slice(0, 120);
20889          }
20890        }
20891        var wrap = el.closest ? el.closest("label") : null;
20892        if (wrap && wrap.getAttribute("aria-hidden") !== "true") {
20893          txt = clean(wrap.textContent);
20894          if (txt) return txt.slice(0, 120);
20895        }
20896        var title = clean(el.getAttribute("title"));
20897        if (title) return title.slice(0, 120);
20898        var host = el.closest ? el.closest("li, div") : null;
20899        if (host) {
20900          var head = host.querySelector("h2, h3, h4, h5");
20901          if (head && head.getAttribute("aria-hidden") !== "true") {
20902            txt = clean(head.textContent);
20903            if (txt) return txt.slice(0, 120);
20904          }
20905        }
20906      } catch (e) {}
20907      return null;
20908    }
20909    function groupIdFor(box) {
20910      try {
20911        var id = box.getAttribute("id") || "";
20912        var m = id.match(/ot-group-id-(.+)$/);
20913        if (m && m[1]) return m[1];
20914        var direct = box.getAttribute("data-optanongroupid");
20915        if (direct) return direct;
20916        var host = box.closest ? box.closest("[data-optanongroupid]") : null;
20917        if (host) return host.getAttribute("data-optanongroupid");
20918        var named = box.getAttribute("name") || "";
20919        var n = named.match(/^(C\d{4}|BG\w+|STACK\d+)$/i);
20920        if (n) return n[1];
20921      } catch (e) {}
20922      return null;
20923    }
20924    function discoverGroups(scopeEl) {
20925      var out = [];
20926      var seen = {};
20927      var scope = scopeEl || document;
20928      var boxes = [];
20929      try {
20930        boxes = scope.querySelectorAll('input[type="checkbox"], [role="switch"], [role="checkbox"]');
20931      } catch (e) {
20932        boxes = [];
20933      }
20934      for (var i = 0; i < boxes.length; i++) {
20935        var box = boxes[i];
20936        var gid = groupIdFor(box);
20937        if (!gid) continue;
20938        var key = gid.toUpperCase();
20939        if (seen[key]) continue;
20940        seen[key] = true;
20941        var checked = false;
20942        try {
20943          checked = box.checked === true || box.getAttribute("aria-checked") === "true";
20944        } catch (e) {}
20945        out.push({ id: gid, label: labelFor(box), allowed: checked });
20946      }
20947      return out;
20948    }
20949    function findCookieSettingsLink() {
20950      var anchors = document.querySelectorAll("footer a");
20951      for (var i = 0; i < anchors.length; i++) {
20952        var t = clean(anchors[i].textContent).toLowerCase();
20953        if (t === "cookie settings" || t.indexOf("cookie setting") > -1 || t.indexOf("cookie preference") > -1) return anchors[i];
20954      }
20955      return null;
20956    }
20957    function findDsarLink() {
20958      var el = document.querySelector(DSAR_SELECTOR);
20959      if (el) return el;
20960      var anchors = document.querySelectorAll("footer a");
20961      for (var i = 0; i < anchors.length; i++) {
20962        var href = anchors[i].getAttribute("href") || "";
20963        var t = clean(anchors[i].textContent).toLowerCase();
20964        if (href.indexOf("dsarwebform") > -1 || href.indexOf("privacyportal") > -1) return anchors[i];
20965        if (t.indexOf("do not sell") > -1) return anchors[i];
20966      }
20967      return null;
20968    }
20969    function highlight(el, scroll) {
20970      if (!el) return false;
20971      try {
20972        if (scroll && el.scrollIntoView) el.scrollIntoView({ behavior: "smooth", block: "center" });
20973        var prevOutline = el.style.outline;
20974        var prevOffset = el.style.outlineOffset;
20975        el.style.outline = "3px solid #0037ff";
20976        el.style.outlineOffset = "2px";
20977        setTimeout(function () {
20978          try {
20979            el.style.outline = prevOutline;
20980            el.style.outlineOffset = prevOffset;
20981          } catch (e) {}
20982        }, 6000);
20983        return true;
20984      } catch (e) {
20985        return false;
20986      }
20987    }
20988    function gatedGlobals() {
20989      var out = { qualifiedChat: false, googleAnalytics: false, microsoftUet: false, optimizelyWeb: false };
20990      try { out.qualifiedChat = typeof window.qualified !== "undefined"; } catch (e) {}
20991      try { out.googleAnalytics = typeof window.gtag === "function" || typeof window.dataLayer !== "undefined"; } catch (e) {}
20992      try { out.microsoftUet = typeof window.uetq !== "undefined"; } catch (e) {}
20993      try { out.optimizelyWeb = typeof window.optimizely !== "undefined"; } catch (e) {}
20994      return out;
20995    }
20996    function navigatedPayload(actionLabel) {
20997      return reply({
20998        status: "navigated",
20999        action: actionLabel,
21000        url: location.href,
21001        nextStep: "The page is loading a new document. Call optimizely-manage-cookie-consent again once it has settled to read the consent state.",
21002        summary: "The interaction navigated the browser, so the consent state was not read on this page."
21003      });
21004    }
21005
21006    try {
21007      var a = args || {};
21008      var requested = clean(a.action || "status").toLowerCase();
21009      var actionLabels = {
21010        "status": "read the cookie consent state",
21011        "open": "open the cookie preference centre",
21012        "close": "close the cookie preference centre",
21013        "accept-all": "accept all cookie categories",
21014        "reject-all": "reject the optional cookie categories",
21015        "update-groups": "update individual cookie consent groups"
21016      };
21017
21018      if (SUPPORTED.indexOf(requested) === -1) {
21019        return reply({
21020          status: "error",
21021          action: "validate the requested consent action",
21022          requestedAction: a.action === undefined || a.action === null ? null : String(a.action),
21023          url: location.href,
21024          reason: "The action '" + requested + "' is not one this tool performs.",
21025          availableOptions: SUPPORTED,
21026          nextStep: "Call again with one of the actions listed in availableOptions, for example status or accept-all.",
21027          summary: "Unsupported action; the supported actions are listed in availableOptions."
21028        });
21029      }
21030
21031      var actionLabel = actionLabels[requested];
21032      var before = readActiveGroups();
21033      var found = await waitForApi(requested === "status" ? 1500 : 4000);
21034      var api = found ? found.api : null;
21035      var apiName = found ? found.name : null;
21036
21037      var mechanism = "none";
21038      var performed = false;
21039      var failure = null;
21040
21041      if (requested === "status") {
21042        performed = true;
21043        mechanism = "read window.OnetrustActiveGroups";
21044      } else if (requested === "open") {
21045        var openModal = findModal();
21046        if (openModal) {
21047          performed = true;
21048          mechanism = "preference centre was already open";
21049        } else if (api && typeof api.ToggleInfoDisplay === "function") {
21050          api.ToggleInfoDisplay();
21051          mechanism = apiName + ".ToggleInfoDisplay()";
21052          await waitForModal(3000);
21053          performed = !!findModal();
21054          if (!performed) {
21055            failure = {
21056              reason: "ToggleInfoDisplay() was called but no visible cookie preference dialog appeared within 3 seconds.",
21057              nextStep: "Retry once the consent SDK has finished loading, or hand the visitor the privacyPortalUrl."
21058            };
21059          }
21060        } else {
21061          var link = findCookieSettingsLink();
21062          if (link) {
21063            var guard = function (ev) { ev.preventDefault(); };
21064            document.addEventListener("click", guard, true);
21065            link.addEventListener("click", guard, { once: true });
21066            link.click();
21067            try { document.removeEventListener("click", guard, true); } catch (e) {}
21068            try { link.removeEventListener("click", guard); } catch (e) {}
21069            mechanism = "clicked the footer Cookie Settings link with default navigation cancelled";
21070            if (location.href !== startUrl) return navigatedPayload(actionLabel);
21071            await waitForModal(3000);
21072            performed = !!findModal();
21073            if (!performed) {
21074              failure = {
21075                reason: "Neither window.OneTrust nor window.Optanon resolved, and clicking the footer Cookie Settings link did not open a visible cookie preference dialog.",
21076                nextStep: "Retry once the consent SDK has loaded, or hand the visitor the privacyPortalUrl to manage their data directly."
21077              };
21078            }
21079          } else {
21080            failure = {
21081              reason: "The OneTrust consent SDK globals did not resolve and no footer link labelled Cookie Settings could be found.",
21082              nextStep: "Retry once the consent SDK has loaded, or hand the visitor the privacyPortalUrl."
21083            };
21084          }
21085        }
21086      } else if (requested === "close") {
21087        var toClose = findModal();
21088        if (!toClose) {
21089          performed = true;
21090          mechanism = "preference centre was already closed";
21091        } else if (api && typeof api.ToggleInfoDisplay === "function") {
21092          api.ToggleInfoDisplay();
21093          mechanism = apiName + ".ToggleInfoDisplay()";
21094          await waitForModalGone(2500);
21095          performed = !findModal();
21096          if (!performed && api && typeof api.Close === "function") {
21097            api.Close();
21098            mechanism = apiName + ".ToggleInfoDisplay() then " + apiName + ".Close()";
21099            await waitForModalGone(1500);
21100            performed = !findModal();
21101          }
21102          if (!performed) {
21103            failure = {
21104              reason: "The consent SDK close call ran but a cookie preference dialog is still visible.",
21105              nextStep: "Ask the visitor to close the dialog, or call this tool again with action close."
21106            };
21107          }
21108        } else {
21109          var closeBtn = null;
21110          var btns = toClose.querySelectorAll('button, [role="button"]');
21111          for (var ci = 0; ci < btns.length; ci++) {
21112            var lbl = clean(btns[ci].getAttribute("aria-label") || btns[ci].getAttribute("title") || btns[ci].textContent).toLowerCase();
21113            if (lbl === "close" || lbl.indexOf("close") > -1) { closeBtn = btns[ci]; break; }
21114          }
21115          if (closeBtn) {
21116            closeBtn.click();
21117            mechanism = "clicked the dialog close control";
21118            await waitForModalGone(2000);
21119            performed = !findModal();
21120          }
21121          if (!performed) {
21122            failure = {
21123              reason: "No consent SDK global resolved and no close control could be located inside the visible cookie dialog.",
21124              nextStep: "Ask the visitor to close the dialog manually."
21125            };
21126          }
21127        }
21128      } else if (requested === "accept-all" || requested === "reject-all") {
21129        var fnName = requested === "accept-all" ? "AllowAll" : "RejectAll";
21130        if (api && typeof api[fnName] === "function") {
21131          api[fnName]();
21132          mechanism = apiName + "." + fnName + "()";
21133          performed = true;
21134          var deadlineA = Date.now() + 2500;
21135          while (Date.now() < deadlineA) {
21136            await sleep(200);
21137            if (readActiveGroups().raw !== before.raw) break;
21138          }
21139          await sleep(400);
21140        } else {
21141          failure = {
21142            reason: "The consent SDK function " + fnName + "() is not available on window.OneTrust or window.Optanon at call time.",
21143            nextStep: "Call this tool with action open to show the preference centre so the visitor can set the categories, or hand them the privacyPortalUrl."
21144          };
21145        }
21146      } else if (requested === "update-groups") {
21147        var allow = Array.isArray(a.allowGroups) ? a.allowGroups.map(function (g) { return clean(String(g)); }
21147).filter(Boolean) : [];
21148        var deny = Array.isArray(a.denyGroups) ? a.denyGroups.map(function (g) { return clean(String(g)); }).filter(Boolean) : [];
21149        if (!allow.length && !deny.length) {
21150          return reply({
21151            status: "error",
21152            action: actionLabel,
21153            requestedAction: requested,
21154            url: location.href,
21155            reason: "action update-groups was requested but neither allowGroups nor denyGroups contained a consent group ID.",
21156            availableOptions: discoverGroups(findModal()).map(function (g) { return g.id; }),
21157            nextStep: "Call with action open first to enumerate the consent groups, then call update-groups with the IDs you want, or use accept-all / reject-all.",
21158            summary: "No consent group IDs were supplied for update-groups."
21159          });
21160        }
21161        var modalForGroups = findModal();
21162        var known = discoverGroups(modalForGroups);
21163        if (!known.length && api && typeof api.ToggleInfoDisplay === "function") {
21164          api.ToggleInfoDisplay();
21165          modalForGroups = await waitForModal(3000);
21166          await sleep(300);
21167          known = discoverGroups(modalForGroups);
21168        }
21169        if (!known.length) {
21170          return reply({
21171            status: "error",
21172            action: actionLabel,
21173            requestedAction: requested,
21174            mechanism: "none",
21175            observed: before.raw !== null,
21176            preferenceCentreOpen: !!findModal(),
21177            activeGroupsRaw: before.raw,
21178            activeGroups: before.list,
21179            groups: [],
21180            consentGatedGlobals: gatedGlobals(),
21181            privacyPortalUrl: (function () { var d = findDsarLink(); return d ? d.href : null; })(),
21182            url: location.href,
21183            reason: "The individual consent groups could not be read from the preference centre at call time, so the requested group IDs cannot be checked or toggled.",
21184            availableOptions: before.list,
21185            nextStep: "Use action accept-all or reject-all, or call action open and let the visitor set the categories.",
21186            summary: "Per-group consent could not be applied because the preference centre groups were not readable."
21187          });
21188        }
21189        var knownIds = known.map(function (g) { return g.id; });
21190        var knownUpper = knownIds.map(function (g) { return g.toUpperCase(); });
21191        var unknown = [];
21192        var wanted = allow.concat(deny);
21193        for (var wi = 0; wi < wanted.length; wi++) {
21194          if (knownUpper.indexOf(wanted[wi].toUpperCase()) === -1) unknown.push(wanted[wi]);
21195        }
21196        if (unknown.length) {
21197          return reply({
21198            status: "error",
21199            action: actionLabel,
21200            requestedAction: requested,
21201            mechanism: "none",
21202            observed: true,
21203            preferenceCentreOpen: !!findModal(),
21204            activeGroupsRaw: before.raw,
21205            activeGroups: before.list,
21206            groups: known,
21207            consentGatedGlobals: gatedGlobals(),
21208            privacyPortalUrl: (function () { var d = findDsarLink(); return d ? d.href : null; })(),
21209            url: location.href,
21210            reason: "These consent group IDs are not on this page: " + unknown.join(", ") + ".",
21211            availableOptions: knownIds,
21212            nextStep: "Call again using the group IDs listed in availableOptions, or use accept-all / reject-all.",
21213            summary: "The requested consent group IDs do not exist here; the live IDs are in availableOptions."
21214          });
21215        }
21216        var pairs = [];
21217        for (var ai = 0; ai < allow.length; ai++) pairs.push(allow[ai] + ":1");
21218        for (var di = 0; di < deny.length; di++) pairs.push(deny[di] + ":0");
21219        if (api && typeof api.UpdateConsent === "function") {
21220          api.UpdateConsent("Category", pairs.join(","));
21221          mechanism = apiName + ".UpdateConsent('Category', '" + pairs.join(",") + "')";
21222          performed = true;
21223          await sleep(900);
21224        } else {
21225          var toggled = 0;
21226          var boxesAll = (findModal() || document).querySelectorAll('input[type="checkbox"], [role="switch"], [role="checkbox"]');
21227          for (var bi = 0; bi < boxesAll.length; bi++) {
21228            var bx = boxesAll[bi];
21229            var bid = groupIdFor(bx);
21230            if (!bid) continue;
21231            var up = bid.toUpperCase();
21232            var wantOn = allow.some(function (g) { return g.toUpperCase() === up; });
21233            var wantOff = deny.some(function (g) { return g.toUpperCase() === up; });
21234            if (!wantOn && !wantOff) continue;
21235            var isOn = bx.checked === true || bx.getAttribute("aria-checked") === "true";
21236            if ((wantOn && !isOn) || (wantOff && isOn)) {
21237              bx.click();
21238              toggled++;
21239              await sleep(120);
21240            }
21241          }
21242          var saveBtn = null;
21243          var saveScope = findModal() || document;
21244          var sbtns = saveScope.querySelectorAll('button, [role="button"]');
21245          for (var si = 0; si < sbtns.length; si++) {
21246            var stxt = clean(sbtns[si].getAttribute("aria-label") || sbtns[si].textContent).toLowerCase();
21247            if (/confirm my choices|save (and )?(exit|close)?|save my choices|save settings|save changes/.test(stxt)) { saveBtn = sbtns[si]; break; }
21248          }
21249          if (saveBtn) {
21250            saveBtn.click();
21251            mechanism = "toggled " + toggled + " preference centre group control(s) and clicked the save control";
21252            performed = true;
21253            await sleep(1000);
21254          } else if (toggled > 0) {
21255            mechanism = "toggled " + toggled + " preference centre group control(s)";
21256            performed = true;
21257            failure = {
21258              reason: "The group toggles were set but no save or confirm control could be located in the preference centre, so the choices may not be persisted.",
21259              nextStep: "Ask the visitor to confirm their choices in the open dialog, or use accept-all / reject-all."
21260            };
21261            await sleep(500);
21262          } else {
21263            failure = {
21264              reason: "No granular update API was exposed and no matching group control could be toggled in the preference centre.",
21265              nextStep: "Use action accept-all or reject-all, or call action open and let the visitor set the categories."
21266            };
21267          }
21268        }
21269        if (location.href !== startUrl) return navigatedPayload(actionLabel);
21270      }
21271
21272      if (location.href !== startUrl) return navigatedPayload(actionLabel);
21273
21274      if (performed && requested !== "status") {
21275        var deadlineB = Date.now() + 1200;
21276        while (Date.now() < deadlineB) {
21277          if (typeof window.qualified !== "undefined") break;
21278          await sleep(200);
21279        }
21280      }
21281
21282      var after = readActiveGroups();
21283      var modalNow = findModal();
21284      var groupsNow = discoverGroups(modalNow);
21285      var dsarEl = findDsarLink();
21286      var dsarUrl = dsarEl ? dsarEl.href : null;
21287      var highlighted = false;
21288      if (dsarEl) highlighted = highlight(dsarEl.parentElement || dsarEl, !modalNow);
21289
21290      var observed = after.raw !== null || groupsNow.length > 0 || (requested === "open" && !!modalNow);
21291
21292      var status;
21293      if (failure) status = "error";
21294      else if (!observed) status = "partial";
21295      else status = "ok";
21296
21297      var summary;
21298      if (status === "error") {
21299        summary = "Could not " + actionLabel + "; see reason and nextStep.";
21300      } else if (status === "partial") {
21301        summary = "Ran " + mechanism + " but the consent state could not be read back from this page.";
21302      } else if (requested === "status") {
21303        summary = "Consent state read: " + (after.list.length ? after.list.length + " active group(s) - " + after.list.join(", ") : "no active consent groups reported") + ".";
21304      } else {
21305        summary = "Performed " + actionLabel + "; active consent groups are now " + (after.list.length ? after.list.join(", ") : "none reported") + ".";
21306      }
21307
21308      var payload = {
21309        status: status,
21310        action: actionLabel,
21311        requestedAction: requested,
21312        mechanism: mechanism,
21313        consentSdk: apiName,
21314        observed: observed,
21315        preferenceCentreOpen: !!modalNow,
21316        activeGroupsRaw: after.raw,
21317        activeGroups: after.list,
21318        activeGroupsBefore: before.list,
21319        groups: groupsNow,
21320        consentGatedGlobals: gatedGlobals(),
21321        privacyPortalUrl: dsarUrl,
21322        privacyPortalLabel: dsarEl ? clean(dsarEl.textContent) : null,
21323        highlightedFooterPrivacyLinks: highlighted,
21324        url: location.href,
21325        summary: summary
21326      };
21327
21328      if (status === "error") {
21329        payload.reason = failure.reason;
21330        payload.nextStep = failure.nextStep;
21331        if (failure.availableOptions) payload.availableOptions = failure.availableOptions;
21332      } else if (status === "partial") {
21333        payload.reason = "Neither window.OnetrustActiveGroups nor any preference centre group control could be read after the action, so the consent state is unknown.";
21334        payload.nextStep = "Retry once the consent SDK has loaded, or hand the visitor the privacyPortalUrl to manage their data directly.";
21335      }
21336
21337      return reply(payload);
21338    } catch (err) {
21339      return reply({
21340        status: "error",
21341        action: "manage cookie consent",
21342        url: location.href,
21343        reason: "The consent tool threw while running: " + (err && err.message ? err.message : String(err)),
21344        nextStep: "Retry with action status to read the consent state before attempting a change.",
21345        summary: "Cookie consent could not be managed because of a runtime error."
21346      });
21347    }
21348  }
21349});
21350
21351  /* ---- optimizely-discover-site-capabilities ----
21352     Registered on every page. WebMCP registrations live and die with the
21353     document, so page-scoped tools only exist on their own pages; this tool
21354     gives an agent the full site capability map from anywhere and can carry
21355     it to the right page, where the snippet re-registers that page's tools. */
21356mc.registerTool({
21357  name: "optimizely-discover-site-capabilities",
21358  description: "Lists every visitor capability the Optimizely WebMCP toolset offers across the whole site - not just the tools registered on the current page - as a map of intent, the page it lives on, and the tool names that become available there. Tools register per page, so a capability's tools only appear once its page is loaded; pass goTo with a capability id (or a site path) and this tool navigates the browser there, and the toolset for that page registers when the new document loads. Use this from any page to answer 'what can I do on this site?' and to reach the page where a capability's tools live.",
21359  inputSchema: {
21360    type: "object",
21361    properties: {
21362      goTo: {
21363        type: "string",
21364        description: "Optional capability id from the returned map (for example site-search, find-partner, register-beta-interest) or a site path (for example /connectors). When given, the browser navigates to that capability's page; call the capability's tools once the page has loaded and its toolset has registered. Omit to just read the map."
21365      }
21366    },
21367    required: []
21368  },
21369  execute: async function (args) {
21370    trackWebMCPTool("optimizely-discover-site-capabilities");
21371    var a = args || {};
21372    function out(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
21373    var CAPABILITIES = [
21374      { id: "request-demo", intent: "Request a demo, pricing or a custom plan from the sales team", path: "/get-started/", tools: ["optimizely-request-demo-lead-form", "optimizely-request-demo-lead-form-submit"], availableEverywhere: true, note: "The lead form tools are registered on every page; the form itself lives on /get-started/ and other pages carrying the contact form." },
21375      { id: "site-search", intent: "Search the site for articles, guides, videos and customer stories", path: "/search", tools: ["optimizely-search-optimizely-site", "optimizely-search-optimizely-site-results", "optimizely-filter-search-facets"] },
21376      { id: "connectors-directory", intent: "Search and filter the integrations/connectors directory", path: "/connectors", tools: ["optimizely-search-connectors-directory", "optimizely-extract-connector-listing"] },
21377      { id: "connector-detail", intent: "Read a connector's detail page and contact its publisher", path: "/connectors", tools: ["optimizely-extract-connector-listing", "optimizely-request-connector-contact-publisher", "optimizely-request-connector-contact-publisher-submit"], note: "The contact-publisher tools register on individual connector pages under /connectors/<slug>." },
21378      { id: "field-notes", intent: "Filter and page through the Field Notes resource hub", path: "/field-notes", tools: ["optimizely-browse-field-notes-library", "optimizely-browse-field-notes-library-results"] },
21379      { id: "events", intent: "Find Optimizely events and webinars", path: "/field-notes/events", tools: ["optimizely-find-optimizely-events", "optimizely-find-optimizely-events-results", "optimizely-find-optimizely-events-open"] },
21380      { id: "opticon", intent: "Explore and register for the Opticon 26 conference", path: "/field-notes/opticon", tools: ["optimizely-get-opticon-overview", "optimizely-open-opticon-registration", "optimizely-register-opticon-online", "optimizely-register-opticon-online-submit"], note: "Online registration is drivable end to end; New York and London hand off to an external Cvent wizard." },
21381      { id: "agents-directory", intent: "Search the Opal AI agent directory", path: "/agents/", tools: ["optimizely-search-opal-agent-directory", "optimizely-search-opal-agent-directory-results"] },
21382      { id: "find-partner", intent: "Find an implementation partner by country, specialization and industry", path: "/partners", tools: ["optimizely-find-implementation-partner"] },
21383      { id: "become-partner", intent: "Apply to become an Optimizely solution partner", path: "/partners", tools: ["optimizely-apply-become-solution-partner", "optimizely-apply-become-solution-partner-submit"] },
21384      { id: "products", intent: "Read the product catalogue and product page details, or open the sales chat", path: "/products", tools: ["optimizely-list-optimizely-products-and-plans", "optimizely-read-product-page-details", "optimizely-open-sales-chat"], note: "The product-detail and sales-chat tools register on individual product pages under /products/<slug>; the catalogue/plans tool registers on /products and /plans." },
21385      { id: "plans", intent: "Read the plan packages and pricing statement", path: "/plans", tools: ["optimizely-list-optimizely-products-and-plans"] },
21386      { id: "developers", intent: "Browse the developer hub entry points", path: "/developers", tools: ["optimizely-developer-hub-entry-points", "optimizely-developer-hub-entry-points-results"] },
21387      { id: "register-beta-interest", intent: "Register interest in Optimizely beta programmes", path: "/beta", tools: ["optimizely-list-beta-programs", "optimizely-register-beta-interest", "optimizely-register-beta-interest-submit"] },
21388      { id: "cookie-consent", intent: "Read or change cookie consent", path: null, tools: ["optimizely-manage-cookie-consent"], availableEverywhere: true }
21389    ];
21390    try {
21391      var wanted = typeof a.goTo === "string" ? a.goTo.trim() : "";
21392      if (!wanted) {
21393        return out({
21394          status: "ok",
21395          action: "list the site's WebMCP capabilities",
21396          url: location.href,
21397          currentPath: location.pathname,
21398          registrationModel: "Tools register per page when the snippet runs on that page's document; a capability's page-scoped tools exist only while its page is loaded. Use goTo to reach a capability's page.",
21399          capabilities: CAPABILITIES,
21400          summary: "The site offers " + CAPABILITIES.length + " capabilities; page-scoped toolsets register on their own pages, and goTo navigates there."
21401        });
21402      }
21403      var lowWant = wanted.toLowerCase();
21404      var hit = null;
21405      var i;
21406      for (i = 0; i < CAPABILITIES.length && !hit; i++) {
21407        if (CAPABILITIES[i].id === lowWant) hit = CAPABILITIES[i];
21408      }
21409      if (!hit && wanted.charAt(0) === "/") {
21410        var wantPath = wanted.replace(/\/+$/, "").toLowerCase();
21411        for (i = 0; i < CAPABILITIES.length && !hit; i++) {
21412          var p = CAPABILITIES[i].path ? CAPABILITIES[i].path.replace(/\/+$/, "").toLowerCase() : null;
21413          if (p && p === wantPath) hit = CAPABILITIES[i];
21414        }
21415        if (!hit) hit = { id: null, intent: "open the requested path", path: wanted, tools: [] };
21416      }
21417      if (!hit) {
21418        for (i = 0; i < CAPABILITIES.length && !hit; i++) {
21419          if (CAPABILITIES[i].id.indexOf(lowWant) !== -1 || CAPABILITIES[i].intent.toLowerCase().indexOf(lowWant) !== -1 || CAPABILITIES[i].tools.join(" ").indexOf(lowWant) !== -1) hit = CAPABILITIES[i];
21420        }
21421      }
21422      if (!hit) {
21423        return out({
21424          status: "error",
21425          action: "open a capability's page",
21426          url: location.href,
21427          reason: "'" + wanted + "' does not match any capability id, intent or path.",
21428          availableOptions: CAPABILITIES.map(function (c) { return c.id; }),
21429          nextStep: "Call this tool again with goTo set to one of availableOptions, or to a site path starting with /.",
21430          summary: "The requested capability was not recognised."
21431        });
21432      }
21433      if (!hit.path) {
21434        return out({
21435          status: "ok",
21436          action: "open a capability's page",
21437          url: location.href,
21438          capability: hit,
21439          nextStep: "This capability's tools are registered on every page - call them directly, no navigation needed.",
21440          summary: "The '" + hit.id + "' tools are available on every page, including this one."
21441        });
21442      }
21443      var destAbs = hit.path;
21444      try { destAbs = new URL(hit.path, location.origin).href; } catch (e0) {}
21445      if (location.pathname.replace(/\/+$/, "") === hit.path.replace(/\/+$/, "")) {
21446        return out({
21447          status: "ok",
21448          action: "open a capability's page",
21449          url: location.href,
21450          capability: hit,
21451          nextStep: "Already on this capability's page; its tools (" + hit.tools.join(", ") + ") are registered here - call them directly.",
21452          summary: "Already on " + hit.path + "; the capability's tools are available now."
21453        });
21454      }
21455      var payload = {
21456        status: "navigated",
21457        action: "open the page for the '" + (hit.id || wanted) + "' capability",
21458        url: destAbs,
21459        capability: hit,
21460        nextStep: "The page is loading; once it settles the snippet registers that page's toolset" + (hit.tools.length ? " (" + hit.tools.join(", ") + ")" : "") + " - call those tools there.",
21461        summary: "Navigating to " + hit.path + (hit.id ? " for the '" + hit.id + "' capability" : "") + "."
21462      };
21463      location.assign(hit.path);
21464      return out(payload);
21465    } catch (err) {
21466      return out({
21467        status: "error",
21468        action: "discover the site's WebMCP capabilities",
21469        url: location.href,
21470        reason: "The discovery tool threw: " + (err && err.message ? err.message : String(err)),
21471        nextStep: "Call this tool again with no arguments to read the capability map.",
21472        summary: "The capability map could not be produced."
21473      });
21474    }
21475  }
21476});
21477
21478  /* ---- optimizely-list-beta-programs, optimizely-register-beta-interest, optimizely-register-beta-interest-submit ---- */
21479mc.registerToolIf("/beta", {
21480  name: "optimizely-list-beta-programs",
21481  description: "Reads the Optimizely beta hub at /beta and returns the beta programmes it lists, grouped the way the page groups them: Open betas, Closed betas, and the 'Other features you can try out right now' list. Each entry is the accordion card title read from the live page. Pass expandBeta with a programme name to open that card's accordion and return its description text as well; the card is collapsed again afterwards unless it was already open. Also returns the anchor of the shared 'Register interest for betas' form and the name of the tool that fills it.",
21482  inputSchema: {
21483    type: "object",
21484    properties: {
21485      expandBeta: {
21486        type: "string",
21487        description: "Name of one listed programme whose accordion should be opened so its description can be read, matched case-insensitively against the card titles on the page at call time. An unmatched name comes back as an error listing the titles that are actually there."
21488      }
21489    },
21490    required: []
21491  },
21492  execute: async function (args) {
21493    trackWebMCPTool("optimizely-list-beta-programs");
21494    var a = args || {};
21495    function out(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
21496    function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
21497    function collapseRepeat(s) { return String(s || "").replace(/(.{4,}?)\1{2,}/g, "$1"); }
21498    function cleanText(el) {
21499      var t = "";
21500      (function walk(n) {
21501        if (!n) return;
21502        if (n.nodeType === 3) { t += n.nodeValue; return; }
21503        if (n.nodeType !== 1) return;
21504        if (n.getAttribute && n.getAttribute("aria-hidden") === "true") return;
21505        if (/material-symbols/.test(String(n.className || ""))) return;
21506        for (var i = 0; i < n.childNodes.length; i++) walk(n.childNodes[i]);
21507      })(el);
21508      return collapseRepeat(t.replace(/\s+/g, " ").trim()).trim();
21509    }
21510    try {
21511      try { await waitForElement("main h3 button", 10000); } catch (e) {}
21512      await sleep(300);
21513      var main = document.querySelector("main") || document.body;
21514      var sections = { openBetas: [], closedBetas: [], otherAvailableFeatures: [] };
21515      var cards = [];
21516      var current = null;
21517      var heads = main.querySelectorAll("h1, h2, h3");
21518      for (var i = 0; i < heads.length; i++) {
21519        var h = heads[i];
21520        var trigger = h.querySelector("button");
21521        if (!trigger) {
21522          var t = cleanText(h).toLowerCase();
21523          if (t.indexOf("open betas") === 0) current = "openBetas";
21524          else if (t.indexOf("closed betas") === 0) current = "closedBetas";
21525          else if (t.indexOf("other features") === 0) current = "otherAvailableFeatures";
21526          else if (t.indexOf("register interest") === 0 || t.indexOf("currently available") === 0) current = current;
21527          continue;
21528        }
21529        if (!current) continue;
21530        var name = cleanText(trigger);
21531        if (!name) continue;
21532        sections[current].push(name);
21533        cards.push({ name: name, heading: h, trigger: trigger, section: current });
21534      }
21535
21536      if (!cards.length) {
21537        return out({
21538          status: "partial",
21539          observed: false,
21540          action: "list the beta programmes on the beta hub",
21541          url: location.href,
21542          resultCount: null,
21543          openBetas: [],
21544          closedBetas: [],
21545          otherAvailableFeatures: [],
21546          reason: "No accordion cards (main h3 > button) were readable after waiting for the page to settle, so no programmes could be listed.",
21547          nextStep: "Let https://www.optimizely.com/beta finish hydrating and call this tool again.",
21548          summary: "The beta hub was open but no programme cards could be read from it."
21549        });
21550      }
21551
21552      var expanded = null;
21553      if (typeof a.expandBeta === "string" && a.expandBeta.trim()) {
21554        var want = a.expandBeta.replace(/\s+/g, " ").trim().toLowerCase();
21555        var hit = null;
21556        for (var ci = 0; ci < cards.length; ci++) {
21557          if (cards[ci].name.toLowerCase() === want) { hit = cards[ci]; break; }
21558        }
21559        if (!hit) {
21560          var partial = [];
21561          for (var cj = 0; cj < cards.length; cj++) {
21562            if (cards[cj].name.toLowerCase().indexOf(want) !== -1) partial.push(cards[cj]);
21563          }
21564          if (partial.length === 1) hit = partial[0];
21565        }
21566        if (!hit) {
21567          return out({
21568            status: "error",
21569            action: "expand a beta programme card",
21570            url: location.href,
21571            reason: "'" + a.expandBeta + "' does not match any programme card title on the page right now.",
21572            availableOptions: cards.map(function (c) { return c.name; }),
21573            nextStep: "Call this tool again with expandBeta set to one of the titles in availableOptions.",
21574            summary: "The requested programme name was not found among the cards on the beta hub."
21575          });
21576        }
21577        var wasOpen = hit.trigger.getAttribute("aria-expanded") === "true";
21578        if (!wasOpen) { try { hit.trigger.click(); } catch (e1) {} }
21579        var region = null;
21580        var deadline = Date.now() + 3500;
21581        while (Date.now() < deadline) {
21582          await sleep(150);
21583          var wrap = hit.heading.parentElement;
21584          region = wrap ? wrap.querySelector('div[role="region"]') : null;
21585          if (region && region.getAttribute("data-state") === "open" && cleanText(region)) break;
21586        }
21587        var description = region ? cleanText(region) : "";
21588        expanded = { name: hit.name, section: hit.section, description: description || null };
21589        try {
21590          hit.heading.scrollIntoView({ behavior: "smooth", block: "center" });
21591          hit.heading.style.outline = "3px solid #0037ff";
21592          setTimeout(function () { try { hit.heading.style.outline = ""; } catch (e2) {} }, 5000);
21593        } catch (e3) {}
21594        if (!wasOpen && hit.trigger.getAttribute("aria-expanded") === "true") {
21595          try { hit.trigger.click(); } catch (e4) {}
21596        }
21597      } else {
21598        try {
21599          var first = cards[0].heading;
21600          first.scrollIntoView({ behavior: "smooth", block: "center" });
21601        } catch (e5) {}
21602      }
21603
21604      var total = cards.length;
21605      var payload = {
21606        status: "ok",
21607        observed: true,
21608        action: "list the beta programmes on the beta hub",
21609        url: location.href,
21610        resultCount: total,
21611        openBetas: sections.openBetas,
21612        closedBetas: sections.closedBetas,
21613        otherAvailableFeatures: sections.otherAvailableFeatures,
21614        expanded: expanded,
21615        registerFormAnchor: "/beta/#register-interest",
21616        registerToolName: "optimizely-register-beta-interest",
21617        summary: "Read " + total + " cards from the beta hub: " + sections.openBetas.length + " open beta(s), " + sections.closedBetas.length + " closed beta(s) and " + sections.otherAvailableFeatures.length + " already-available feature(s)" + (expanded ? "; expanded '" + expanded.name + "'" + (expanded.description ? " and read its description" : " but its description text did not render") : "") + "."
21618      };
21619      if (expanded && !expanded.description) {
21620        payload.status = "partial";
21621        payload.reason = "The card '" + expanded.name + "' was toggled but its accordion body showed no readable text within 3.5 seconds.";
21622        payload.nextStep = "Call this tool again with the same expandBeta value once the page has settled.";
21623      }
21624      return out(payload);
21625    } catch (err) {
21626      return out({
21627        status: "error",
21628        action: "list the beta programmes on the beta hub",
21629        url: location.href,
21630        reason: "The read failed: " + (err && err.message ? err.message : String(err)),
21631        nextStep: "Reload https://www.optimizely.com/beta and call this tool again.",
21632        summary: "The beta programme list could not be read."
21633      });
21634    }
21635  }
21636});
21637
21638mc.registerToolIf("/beta", {
21639  name: "optimizely-register-beta-interest",
21640  description: "Fills the shared 'Register interest for betas' form on the Optimizely beta hub (
21640/beta, anchor #register-interest). Ticks the checkbox for each requested beta programme in the 'Which betas are you interested in?' group (names are matched against the checkbox labels on the page at call time), writes Full name, Work email, Company name and the optional Partner name, sets the Job role through the form's own option list, ticks any 'Which Optimizely products do you currently use?' checkboxes requested, writes the Additional information note, and sets the design-feedback opt-in checkbox (which the page defaults to ticked). All requested option names are validated against the live page before anything is written; an unknown name comes back as an error carrying availableOptions. Returns the values read back out of the form, the betas and products actually ticked, the required items still outstanding, a ready flag and the name of the companion submit tool. Nothing is sent until that submit tool is called. Note the page's static disclaimer: submitting the form itself constitutes agreement to the beta terms (/beta/beta-terms2/) - there is no separate consent checkbox.",
21641  inputSchema: {
21642    type: "object",
21643    properties: {
21644      betas: {
21645        type: "array",
21646        items: { type: "string" },
21647        description: "One or more beta programme names to tick in the 'Which betas are you interested in?' group, written as they read on the page, for example 'Outlier management in Experimentation'. Matched case-insensitively against the checkbox labels present at call time."
21648      },
21649      fullName: { type: "string", description: "Value for the Full name field." },
21650      email: { type: "string", description: "Value for the Work email field." },
21651      company: { type: "string", description: "Value for the Company name field." },
21652      partnerName: { type: "string", description: "Optional value for the 'Partner name (optional)' field." },
21653      jobRole: { type: "string", description: "Job role to choose, matched against the options the form offers at call time (for example Developer, Product Manager, Executive). An unknown value comes back with the live option list." },
21654      productsUsed: {
21655        type: "array",
21656        items: { type: "string" },
21657        description: "Optional list of names to tick in the 'Which Optimizely products do you currently use?' group, matched against the checkbox labels present at call time."
21658      },
21659      additionalInformation: { type: "string", description: "Optional free text for the 'Additional information' box, for example what the beta will be used for." },
21660      designFeedbackOptIn: { type: "boolean", description: "Whether the 'Yes, you can ask me for design feedback' checkbox should be ticked. The page defaults it to ticked; pass false to untick it." }
21661    },
21662    required: ["betas", "fullName", "email", "company"]
21663  },
21664  execute: async function (args) {
21665    trackWebMCPTool("optimizely-register-beta-interest");
21666    var a = args || {};
21667    var ACTION = "fill the beta register-interest form";
21668    var FORM_SEL = "form.opti-form";
21669    function out(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
21670    function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
21671    function norm(s) { return String(s == null ? "" : s).replace(/\s+/g, " ").trim(); }
21672    function low(s) { return norm(s).toLowerCase(); }
21673    function labelName(l) {
21674      var t = "";
21675      (function walk(n) {
21676        if (!n) return;
21677        if (n.nodeType === 3) { t += n.nodeValue; return; }
21678        if (n.nodeType !== 1) return;
21679        if (n.getAttribute && n.getAttribute("aria-hidden") === "true") return;
21680        if (/material-symbols/.test(String(n.className || ""))) return;
21681        for (var i = 0; i < n.childNodes.length; i++) walk(n.childNodes[i]);
21682      })(l);
21683      return norm(t);
21684    }
21685    function nativeSet(el, value) {
21686      var proto = window.HTMLInputElement.prototype;
21687      if (el.tagName === "TEXTAREA") proto = window.HTMLTextAreaElement.prototype;
21688      else if (el.tagName === "SELECT") proto = window.HTMLSelectElement.prototype;
21689      var d = Object.getOwnPropertyDescriptor(proto, "value");
21690      if (d && d.set) { d.set.call(el, value); } else { el.value = value; }
21691      el.dispatchEvent(new Event("input", { bubbles: true }));
21692      el.dispatchEvent(new Event("change", { bubbles: true }));
21693    }
21694    function toList(v) {
21695      if (Array.isArray(v)) return v.map(norm).filter(Boolean);
21696      if (typeof v === "string" && v.trim()) return [norm(v)];
21697      return [];
21698    }
21699    /* The two checkbox groups share one markup pattern; each option's clickable
21700       element is the button[role="checkbox"], not the shadow input. Group
21701       membership is decided by the last group legend seen in document order. */
21702    function readGroups(form) {
21703      var res = { betas: [], products: [], feedback: null };
21704      var all = form.querySelectorAll("*");
21705      var cur = null;
21706      var seen = [];
21707      for (var i = 0; i < all.length; i++) {
21708        var el = all[i];
21709        if (!el.children.length) {
21710          var t = low(el.textContent || "");
21711          if (t.indexOf("which betas are you interested") !== -1) cur = "betas";
21712          else if (t.indexOf("which optimizely products") !== -1) cur = "products";
21713        }
21714        if (el.tagName !== "LABEL") continue;
21715        var btn = el.querySelector('button[role="checkbox"]');
21716        if (!btn || seen.indexOf(btn) !== -1) continue;
21717        seen.push(btn);
21718        var name = labelName(el);
21719        if (!name) continue;
21720        if (/design feedback/i.test(name)) { res.feedback = { name: name, btn: btn }; continue; }
21721        if (cur === "betas") res.betas.push({ name: name, btn: btn });
21722        else if (cur === "products") res.products.push({ name: name, btn: btn });
21723      }
21724      return res;
21725    }
21726    function isChecked(btn) { return btn.getAttribute("aria-checked") === "true"; }
21727    function matchEntry(list, wanted) {
21728      var w = low(wanted);
21729      var i;
21730      for (i = 0; i < list.length; i++) { if (low(list[i].name) === w) return list[i]; }
21731      var partial = [];
21732      for (i = 0; i < list.length; i++) { if (low(list[i].name).indexOf(w) !== -1) partial.push(list[i]); }
21733      return partial.length === 1 ? partial[0] : null;
21734    }
21735    function names(list) { return list.map(function (e) { return e.name; }); }
21736    function checkedNames(list) {
21737      return list.filter(function (e) { return isChecked(e.btn); }).map(function (e) { return e.name; });
21738    }
21739    async function setCheckbox(btn, desired) {
21740      if (isChecked(btn) === desired) return true;
21741      try { btn.click(); } catch (e) {}
21742      await sleep(120);
21743      return isChecked(btn) === desired;
21744    }
21745
21746    try {
21747      var form = null;
21748      try { form = await waitForElement(FORM_SEL, 12000); } catch (e0) { form = document.querySelector(FORM_SEL); }
21749      if (!form) {
21750        return out({
21751          status: "error",
21752          action: ACTION,
21753          url: location.href,
21754          reason: "No form matching " + FORM_SEL + " is present on this page.",
21755          nextStep: "Open https://www.optimizely.com/beta and call this tool again once the register-interest form has rendered.",
21756          summary: "The beta register-interest form was not found on this page."
21757        });
21758      }
21759      try {
21760        form.scrollIntoView({ behavior: "smooth", block: "center" });
21761        form.style.outline = "3px solid #0037ff";
21762        form.style.outlineOffset = "4px";
21763      } catch (e1) {}
21764      await sleep(300);
21765
21766      var groups = readGroups(form);
21767      if (!groups.betas.length) {
21768        return out({
21769          status: "error",
21770          action: ACTION,
21771          url: location.href,
21772          reason: "The 'Which betas are you interested in?' checkbox group could not be read from the form, so the requested betas cannot be matched.",
21773          nextStep: "Let the page finish hydrating and call this tool again.",
21774          summary: "The beta checkbox group was not readable, so nothing was written."
21775        });
21776      }
21777
21778      var wantBetas = toList(a.betas);
21779      var wantProducts = toList(a.productsUsed);
21780      if (!wantBetas.length) {
21781        return out({
21782          status: "error",
21783          action: ACTION,
21784          url: location.href,
21785          reason: "The betas argument was empty; at least one beta programme must be named.",
21786          availableOptions: names(groups.betas),
21787          nextStep: "Call this tool again with betas set to one or more names from availableOptions.",
21788          summary: "No beta programme was requested, so nothing was written."
21789        });
21790      }
21791
21792      /* Validate every requested option before writing anything. */
21793      var unknownBetas = [];
21794      var betaHits = [];
21795      var i;
21796      for (i = 0; i < wantBetas.length; i++) {
21797        var bHit = matchEntry(groups.betas, wantBetas[i]);
21798        if (bHit) betaHits.push(bHit); else unknownBetas.push(wantBetas[i]);
21799      }
21800      if (unknownBetas.length) {
21801        return out({
21802          status: "error",
21803          action: ACTION,
21804          url: location.href,
21805          reason: "These beta names are not among the checkboxes on the page right now: " + unknownBetas.join(", ") + ". Nothing was written to the form.",
21806          availableOptions: names(groups.betas),
21807          nextStep: "Call this tool again using names from availableOptions.",
21808          summary: "One or more requested betas do not exist on the form, so nothing was written."
21809        });
21810      }
21811      var unknownProducts = [];
21812      var productHits = [];
21813      for (i = 0; i < wantProducts.length; i++) {
21814        var pHit = matchEntry(groups.products, wantProducts[i]);
21815        if (pHit) productHits.push(pHit); else unknownProducts.push(wantProducts[i]);
21816      }
21817      if (unknownProducts.length) {
21818        return out({
21819          status: "error",
21820          action: ACTION,
21821          url: location.href,
21822          reason: "These product names are not among the 'Which Optimizely products do you currently use?' checkboxes: " + unknownProducts.join(", ") + ". Nothing was written to the form.",
21823          availableOptions: names(groups.products),
21824          nextStep: "Call this tool again using names from availableOptions, or omit productsUsed.",
21825          summary: "One or more requested products do not exist on the form, so nothing was written."
21826        });
21827      }
21828      var roleSelect = form.querySelector('select[name="job_role_specifics"]');
21829      var roleChoice = null;
21830      if (a.jobRole !== undefined && a.jobRole !== null && norm(a.jobRole) !== "") {
21831        if (!roleSelect) {
21832          return out({
21833            status: "error",
21834            action: ACTION,
21835            url: location.href,
21836            reason: "A jobRole was requested but the Job role select (select[name=\"job_role_specifics\"]) is not in the form.",
21837            nextStep: "Let the page finish hydrating and call this tool again, or omit jobRole.",
21838            summary: "The Job role control was not found, so nothing was written."
21839          });
21840        }
21841        var opts = [];
21842        for (i = 0; i < roleSelect.options.length; i++) {
21843          var lbl = norm(roleSelect.options[i].textContent) || norm(roleSelect.options[i].value);
21844          if (lbl) opts.push({ value: roleSelect.options[i].value, label: lbl });
21845        }
21846        var wantRole = low(a.jobRole);
21847        for (i = 0; i < opts.length; i++) { if (low(opts[i].label) === wantRole || low(opts[i].value) === wantRole) { roleChoice = opts[i]; break; } }
21848        if (!roleChoice) {
21849          for (i = 0; i < opts.length; i++) { if (low(opts[i].label).indexOf(wantRole) === 0) { roleChoice = opts[i]; break; } }
21850        }
21851        if (!roleChoice) {
21852          return out({
21853            status: "error",
21854            action: ACTION,
21855            url: location.href,
21856            reason: "The job role '" + a.jobRole + "' is not one of the options the form offers. Nothing was written to the form.",
21857            availableOptions: opts.map(function (o) { return o.label; }),
21858            nextStep: "Call this tool again with jobRole set to one of the values in availableOptions.",
21859            summary: "The requested job role does not exist on the form, so nothing was written."
21860          });
21861        }
21862      }
21863
21864      /* Everything validated - now write. Text fields target stable name
21865         attributes; the Radix-generated ids regenerate every load. */
21866      var notes = [];
21867      var textPlan = [
21868        { key: "fullName", sel: 'form.opti-form input[name="firstName"]', value: a.fullName, required: true, label: "Full name" },
21869        { key: "email", sel: 'form.opti-form input[name="email"]', value: a.email, required: true, label: "Work email" },
21870        { key: "company", sel: 'form.opti-form input[name="company"]', value: a.company, required: true, label: "Company name" },
21871        { key: "partnerName", sel: 'form.opti-form input[name="partner_name"]', value: a.partnerName, required: false, label: "Partner name" },
21872        { key: "additionalInformation", sel: 'form.opti-form textarea[name="Other_Additional_Information__c"]', value: a.additionalInformation, required: false, label: "Additional information" }
21873      ];
21874      var fields = {};
21875      var missing = [];
21876      for (i = 0; i < textPlan.length; i++) {
21877        var spec = textPlan[i];
21878        var el = document.querySelector(spec.sel);
21879        if (!el) {
21880          fields[spec.key] = null;
21881          if (spec.required) missing.push(spec.label);
21882          notes.push("The '" + spec.label + "' control was not found in the form.");
21883          continue;
21884        }
21885        if (spec.value !== undefined && spec.value !== null && String(spec.value) !== "") {
21886          var wanted = String(spec.value);
21887          var filled = false;
21888          try { await fillInput(spec.sel, wanted); filled = true; } catch (e2) { filled = false; }
21889          if (!filled || norm(el.value) !== norm(wanted)) nativeSet(el, wanted);
21890          await sleep(80);
21891        }
21892        fields[spec.key] = norm(el.value);
21893        if (spec.required && !fields[spec.key]) missing.push(spec.label);
21894      }
21895
21896      if (roleChoice && roleSelect) {
21897        nativeSet(roleSelect, roleChoice.value);
21898        await sleep(250);
21899        /* Radix's hidden native select listens for change (autofill support);
21900           verify the visible combobox trigger picked the value up, and drive
21901           the trigger UI as a fallback if it did not. */
21902        var trigger = form.querySelector('button[role="combobox"]');
21903        var triggerOk = trigger ? low(trigger.textContent || "").indexOf(low(roleChoice.label)) !== -1 : false;
21904        if (roleSelect.value !== roleChoice.value || (trigger && !triggerOk)) {
21905          if (trigger) {
21906            try { trigger.click(); } catch (e3) {}
21907            var opt = null;
21908            var deadline = Date.now() + 3000;
21909            while (Date.now() < deadline && !opt) {
21910              await sleep(150);
21911              var rendered = document.querySelectorAll('[role="option"]');
21912              for (var oi = 0; oi < rendered.length; oi++) {
21913                if (low(rendered[oi].textContent) === low(roleChoice.label)) { opt = rendered[oi]; break; }
21914              }
21915            }
21916            if (opt) {
21917              try { opt.dispatchEvent(new MouseEvent("mousedown", { bubbles: true })); } catch (e4) {}
21918              try { opt.click(); } catch (e5) {}
21919              await sleep(250);
21920            } else {
21921              try { trigger.click(); } catch (e6) {}
21922              notes.push("The Job role option list did not render, so the role may not be applied; check fields.jobRole.");
21923            }
21924          }
21925        }
21926        fields.jobRole = roleSelect ? norm(roleSelect.value) : null;
21927      } else {
21928        fields.jobRole = roleSelect ? norm(roleSelect.value) : null;
21929      }
21930
21931      for (i = 0; i < betaHits.length; i++) {
21932        var okB = await setCheckbox(betaHits[i].btn, true);
21933        if (!okB) notes.push("The checkbox for '" + betaHits[i].name + "' did not report aria-checked=\"true\" after clicking.");
21934      }
21935      for (i = 0; i < productHits.length; i++) {
21936        var okP = await setCheckbox(productHits[i].btn, true);
21937        if (!okP) notes.push("The checkbox for '" + productHits[i].name + "' did not report aria-checked=\"true\" after clicking.");
21938      }
21939      var feedbackState = null;
21940      if (groups.feedback) {
21941        if (typeof a.designFeedbackOptIn === "boolean") {
21942          var okF = await setCheckbox(groups.feedback.btn, a.designFeedbackOptIn);
21943          if (!okF) notes.push("The design-feedback checkbox did not settle into the requested state.");
21944        }
21945        feedbackState = isChecked(groups.feedback.btn);
21946      }
21947
21948      await sleep(300);
21949      var liveGroups = readGroups(form);
21950      var betasChecked = checkedNames(liveGroups.betas);
21951      var productsChecked = checkedNames(liveGroups.products);
21952      if (!betasChecked.length) missing.push("Which betas are you interested in");
21953      fields.betasChecked = betasChecked;
21954      fields.productsChecked = productsChecked;
21955      fields.designFeedbackOptIn = feedbackState;
21956
21957      function hiddenVal(name) {
21958        var h = form.querySelector('input[name="' + name + '"]');
21959        return h ? String(h.value || "") : null;
21960      }
21961      var ready = missing.length === 0;
21962      var payload = {
21963        status: ready && !notes.length ? "ok" : "partial",
21964        observed: true,
21965        action: ACTION,
21966        url: location.href,
21967        fields: fields,
21968        missing: missing,
21969        ready: ready,
21970        trackingKeysPresent: { pageKey: !!hiddenVal("page_key"), formKey: !!hiddenVal("form_key") },
21971        termsNote: "Submitting this form constitutes agreement to the beta terms at /beta/beta-terms2/ per the page's own disclaimer; there is no separate consent checkbox.",
21972        submitToolName: "optimizely-register-beta-interest-submit",
21973        notes: notes,
21974        summary: ""
21975      };
21976      if (ready && !notes.length) {
21977        payload.nextStep = "Review the values, then call optimizely-register-beta-interest-submit to send the registration.";
21978        payload.summary = "Filled the beta register-interest form for " + (fields.email || "the supplied contact") + " with " + betasChecked.length + " beta(s) ticked; it is ready to submit.";
21979      } else if (!ready) {
21980        payload.reason = "These required items are still outstanding: " + missing.join(", ") + ".";
21981        payload.nextStep = "Supply the missing values and call this tool again before submitting.";
21982        payload.summary = "Filled the form but " + missing.length + " required item(s) are still outstanding.";
21983      } else {
21984        payload.reason = notes.join(" ");
21985        payload.nextStep = "Check the flagged items in notes, re-run this tool if needed, then call optimizely-register-beta-interest-submit.";
21986        payload.summary = "Filled the form with " + betasChecked.length + " beta(s) ticked, but some controls could not be fully verified.";
21987      }
21988      return out(payload);
21989    } catch (err) {
21990      return out({
21991        status: "error",
21992        action: ACTION,
21993        url: location.href,
21994        reason: "The fill attempt threw an error: " + (err && err.message ? err.message : String(err)),
21995        nextStep: "Let the form finish loading and call this tool again.",
21996        summary: "Could not complete filling the beta register-interest form."
21997      });
21998    }
21999  }
22000});
22001
22002mc.registerToolIf("/beta", {
22003  name: "optimizely-register-beta-interest-submit",
22004  description: "Clicks the Submit button on the beta register-interest form that optimizely-register-beta-interest filled, waits for the page to respond, and reports what it observes: the confirmation text if the registration was accepted (including anything announced in the form's aria-live status region), any validation messages the form renders, and the URL after the click. The form submits via JavaScript, so the page normally stays put. It refuses to click when no beta checkbox is ticked or the Work email field is empty, since the submission would be pointless. This sends the registration to Optimizely - fill and review the values first.",
22005  inputSchema: {
22006    type: "object",
22007    properties: {
22008      confirm: { type: "boolean", description: "Set true to confirm the reviewed form should be sent. Defaults to true; set false to abort without clicking." }
22009    },
22010    required: []
22011  },
22012  execute: async function (args) {
22013    trackWebMCPTool("optimizely-register-beta-interest-submit");
22014    var a = args || {};
22015    var ACTION = "submit the beta register-interest form";
22016    var FORM_SEL = "form.opti-form";
22017    function out(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
22018    function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
22019    function norm(s) { return String(s == null ? "" : s).replace(/\s+/g, " ").trim(); }
22020    function visible(el) {
22021      if (!el) return false;
22022      try { if (el.closest && el.closest('[aria-hidden="true"]')) return false; } catch (e) {}
22023      var r = el.getBoundingClientRect ? el.getBoundingClientRect() : { width: 0, height: 0 };
22024      return !!(el.offsetParent || r.width || r.height);
22025    }
22026    function leafTexts(root) {
22027      var set = {};
22028      if (!root) return set;
22029      var all = root.querySelectorAll("*");
22030      for (var i = 0; i < all.length; i++) {
22031        var el = all[i];
22032        if (el.children && el.children.length) continue;
22033        var tag = el.tagName;
22034        if (tag === "SCRIPT" || tag === "STYLE" || tag === "OPTION") continue;
22035        if (el.getAttribute && el.getAttribute("aria-hidden") === "true") continue;
22036        var t = norm(el.textContent);
22037        if (!t || t.length > 240) continue;
22038        if (!visible(el)) continue;
22039        set[t] = true;
22040      }
22041      return set;
22042    }
22043    try {
22044      if (a.confirm === false) {
22045        return out({
22046          status: "error",
22047          action: ACTION,
22048          url: location.href,
22049          validationErrors: [],
22050          confirmation: null,
22051          reason: "confirm was false, so the form was not clicked and nothing was sent.",
22052          nextStep: "Call this tool again with confirm set to true when the reviewed values should be sent.",
22053          summary: "Submission aborted at the caller's request; nothing was sent."
22054        });
22055      }
22056      var form = document.querySelector(FORM_SEL);
22057      if (!form) {
22058        return out({
22059          status: "error",
22060          action: ACTION,
22061          url: location.href,
22062          validationErrors: [],
22063          confirmation: null,
22064          reason: "No form matching " + FORM_SEL + " is present on this page, so there is nothing to submit.",
22065          nextStep: "Open /beta, fill the form with optimizely-register-beta-interest, then call this tool.",
22066          summary: "The beta register-interest form is not on this page."
22067        });
22068      }
22069      var emailEl = form.querySelector('input[name="email"]');
22070      var anyBeta = false;
22071      var boxes = form.querySelectorAll('button[role="checkbox"][aria-checked="true"]');
22072      for (var bi = 0; bi < boxes.length; bi++) {
22073        var wrap = boxes[bi].closest ? boxes[bi].closest("label") : null;
22074        var t = wrap ? norm(wrap.textContent) : "";
22075        if (t && !/design feedback/i.test(t)) { anyBeta = true; break; }
22076      }
22077      if (!anyBeta || !emailEl || !norm(emailEl.value)) {
22078        return out({
22079          status: "error",
22080          action: ACTION,
22081          url: location.href,
22082          validationErrors: [],
22083          confirmation: null,
22084          reason: !anyBeta
22085            ? "No beta checkbox is ticked, so the submission would carry no programme selection."
22086            : "The Work email field is empty.",
22087          nextStep: "Call optimizely-register-beta-interest to complete the form, then call this tool again.",
22088          summary: "Did not submit: the form is not complete."
22089        });
22090      }
22091      var btn = form.querySelector('button[type="submit"]');
22092      if (!btn) {
22093        return out({
22094          status: "error",
22095          action: ACTION,
22096          url: location.href,
22097          validationErrors: [],
22098          confirmation: null,
22099          reason: "The form's Submit button (form.opti-form button[type=\"submit\"]) was not found.",
22100          nextStep: "Let the form finish rendering and call this tool again.",
22101          summary: "Could not find the Submit button on the form."
22102        });
22103      }
22104
22105      var container = form.parentElement || document.querySelector("main") || document.body;
22106      var before = leafTexts(container);
22107      var liveRegion = document.querySelector('div[aria-live]');
22108      var liveBefore = liveRegion ? norm(liveRegion.textContent) : "";
22109      var urlBefore = location.href;
22110
22111      try { btn.scrollIntoView({ behavior: "smooth", block: "center" }); } catch (e0) {}
22112      btn.click();
22113
22114      var ERROR_RE = /(required|invalid|valid email|please\s|must\s|enter a|cannot be|is missing|fill (in|out)|try again)/i;
22115      var CONFIRM_RE = /(thank|thanks|received|success|submitted|in touch|be notified|we will|confirmation)/i;
22116      var validationErrors = [];
22117      var confirmation = null;
22118      var navigated = false;
22119      var formGone = false;
22120      var deadline = Date.now() + 15000;
22121      while (Date.now() < deadline) {
22122        await sleep(400);
22123        if (location.href !== urlBefore) { navigated = true; break; }
22124        var liveNow = liveRegion && liveRegion.isConnected ? norm(liveRegion.textContent) : "";
22125        if (liveNow && liveNow !== liveBefore) {
22126          if (ERROR_RE.test(liveNow)) validationErrors.push(liveNow);
22127          else confirmation = liveNow;
22128        }
22129        var scope = (container && container.isConnected) ? container : (document.querySelector("main") || document.body);
22130        var now = leafTexts(scope);
22131        for (var key in now) {
22132          if (!Object.prototype.hasOwnProperty.call(now, key)) continue;
22133          if (before[key]) continue;
22134          if (ERROR_RE.test(key)) {
22135            if (validationErrors.indexOf(key) === -1) validationErrors.push(key);
22136          } else if (CONFIRM_RE.test(key)) {
22137            if (!confirmation || key.length > confirmation.length) confirmation = key;
22138          }
22139        }
22140        formGone = !document.querySelector(FORM_SEL);
22141        if (formGone && !confirmation) {
22142          for (var k2 in now) {
22143            if (Object.prototype.hasOwnProperty.call(now, k2) && !before[k2] && (!confirmation || k2.length > confirmation.length)) confirmation = k2;
22144          }
22145        }
22146        if (validationErrors.length || confirmation) break;
22147      }
22148
22149      if (navigated) {
22150        return out({
22151          status: "navigated",
22152          action: ACTION,
22153          url: location.href,
22154          validationErrors: [],
22155          confirmation: null,
22156          nextStep: "The page moved after the click; read the confirmation on the page that is now loaded.",
22157          summary: "Clicked Submit and the browser navigated to " + location.href + "."
22158        });
22159      }
22160      if (validationErrors.length) {
22161        return out({
22162          status: "error",
22163          action: ACTION,
22164          url: location.href,
22165          validationErrors: validationErrors.slice(0, 15),
22166          confirmation: null,
22167          submitted: false,
22168          reason: "The form rendered validation messages instead of a confirmation.",
22169          nextStep: "Correct the flagged values with optimizely-register-beta-interest, then call this tool again.",
22170          summary: "The registration was not accepted; the form shows " + validationErrors.length + " validation message(s)."
22171        });
22172      }
22173      if (confirmation) {
22174        try { form.style.outline = ""; form.style.outlineOffset = ""; } catch (e1) {}
22175        return out({
22176          status: "ok",
22177          action: ACTION,
22178          url: location.href,
22179          validationErrors: [],
22180          confirmation: confirmation,
22181          submitted: true,
22182          summary: "Submitted the beta registration; the page responded: " + confirmation
22183        });
22184      }
22185      return out({
22186        status: "partial",
22187        action: ACTION,
22188        url: location.href,
22189        observed: false,
22190        validationErrors: [],
22191        confirmation: null,
22192        submitted: null,
22193        formStillPresent: !formGone,
22194        reason: formGone
22195          ? "The form left the page after the click but no confirmation text could be read, so the outcome wording is unknown."
22196          : "No confirmation and no validation message appeared within 15 seconds of the click, so the outcome could not be read.",
22197        nextStep: "Look at the register-interest section on the page; only resend if the form is clearly still showing unsent values, since re-clicking may register the interest twice.",
22198        summary: "Clicked Submit but could not read a confirmation or an error from the page."
22199      });
22200    } catch (err) {
22201      return out({
22202        status: "error",
22203        action: ACTION,
22204        url: location.href,
22205        validationErrors: [],
22206        confirmation: null,
22207        reason: "The submit attempt threw an error: " + (err && err.message ? err.message : 
22207String(err)),
22208        nextStep: "Check the page for a confirmation before retrying, so the interest is not registered twice.",
22209        summary: "Could not complete submitting the beta register-interest form."
22210      });
22211    }
22212  }
22213});
22214
22215  /* ---- optimizely-get-opticon-overview, optimizely-open-opticon-registration,
22216          optimizely-register-opticon-online, optimizely-register-opticon-online-submit ---- */
22217mc.registerToolIf("/field-notes/opticon**", {
22218  name: "optimizely-get-opticon-overview",
22219  description: "Reads the Camp Opticon 26 conference hub at /field-notes/opticon and returns the three registration destinations (New York, London, Online) with their dates and registration URLs, the venue and date shown on the location tab that is currently active, that tab's agenda rows (time and session title), the unique speaker cards from the speaker carousel, and the FAQ question list. Pass tab to switch the hub to another location tab (the tabs are URL-driven, so this loads ?tab=new-york, ?tab=london or ?tab=online). Pass expandFaq with a question's wording to open that FAQ accordion item and return its answer text - pricing lives in the 'What's the cost?' item. Note the accordion only holds one item open at a time, so expanding one closes whichever was open.",
22220  inputSchema: {
22221    type: "object",
22222    properties: {
22223      tab: {
22224        type: "string",
22225        description: "Location tab to show: new-york, london or online. When it differs from the active tab the tool loads the hub with that ?tab= parameter and returns status navigated; call it again on the loaded page to read that tab's venue and agenda."
22226      },
22227      expandFaq: {
22228        type: "string",
22229        description: "Wording of one FAQ question to expand and read, matched case-insensitively against the questions on the page at call time, for example 'What's the cost?'. An unmatched value comes back with the questions that are actually there."
22230      }
22231    },
22232    required: []
22233  },
22234  execute: async function (args) {
22235    trackWebMCPTool("optimizely-get-opticon-overview");
22236    var a = args || {};
22237    var ACTION = "read the Camp Opticon hub";
22238    function out(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
22239    function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
22240    /* The hub's marquee headings repeat their text (block-level x5, and
22241       per-letter on some tabs) purely for visual effect - collapse both. */
22242    function collapseRepeat(s) {
22243      var t = String(s || "").replace(/\s+/g, " ").trim();
22244      for (var guard = 0; guard < 4; guard++) {
22245        var next = t.replace(/(.{4,}?)\1{1,}/g, "$1");
22246        if (next === t) break;
22247        t = next;
22248      }
22249      if (t.length >= 6 && t.length % 2 === 0) {
22250        var halves = true;
22251        for (var i = 0; i < t.length; i += 2) { if (t.charAt(i) !== t.charAt(i + 1)) { halves = false; break; } }
22252        if (halves) {
22253          var un = "";
22254          for (var j = 0; j < t.length; j += 2) un += t.charAt(j);
22255          t = un;
22256        }
22257      }
22258      return t.trim();
22259    }
22260    function txt(el) { return el && el.textContent ? collapseRepeat(el.textContent) : ""; }
22261    function stripIcon(s) { return String(s || "").replace(/\b(keyboard_arrow_(down|up)|arrow_forward|arrow_outward)\b/g, "").replace(/\s+/g, " ").trim(); }
22262    var DATE_RE = /\b\d{1,2}\s+(January|February|March|April|May|June|July|August|September|October|November|December)\s+\d{4}\b/i;
22263    try {
22264      try { await waitForElement('[role="tablist"] [role="tab"]', 10000); } catch (e0) {}
22265      await sleep(300);
22266
22267      var tabs = Array.prototype.slice.call(document.querySelectorAll('[role="tablist"] [role="tab"]'));
22268      var tabNames = tabs.map(function (t) { return txt(t); });
22269      var activeTabEl = null;
22270      for (var ti = 0; ti < tabs.length; ti++) {
22271        if (tabs[ti].getAttribute("data-state") === "active" || tabs[ti].getAttribute("aria-selected") === "true") { activeTabEl = tabs[ti]; break; }
22272      }
22273      var activeTab = activeTabEl ? txt(activeTabEl) : null;
22274
22275      if (typeof a.tab === "string" && a.tab.trim()) {
22276        var slug = a.tab.trim().toLowerCase().replace(/\s+/g, "-");
22277        var VALID = ["new-york", "london", "online"];
22278        if (VALID.indexOf(slug) === -1) {
22279          return out({
22280            status: "error",
22281            action: "switch the Opticon location tab",
22282            url: location.href,
22283            reason: "'" + a.tab + "' is not one of the location tabs.",
22284            availableOptions: VALID,
22285            nextStep: "Call this tool again with tab set to new-york, london or online.",
22286            summary: "The requested tab does not exist on the Opticon hub."
22287          });
22288        }
22289        var activeSlug = activeTab ? activeTab.toLowerCase().replace(/\s+/g, "-") : "";
22290        if (activeSlug !== slug) {
22291          var target = "/field-notes/opticon?tab=" + slug;
22292          var payloadNav = {
22293            status: "navigated",
22294            action: "switch the Opticon location tab to " + slug,
22295            url: location.origin + target,
22296            nextStep: "The hub is loading with the " + slug + " tab active; call optimizely-get-opticon-overview again on the loaded page to read that tab's venue, date and agenda.",
22297            summary: "Loading the Opticon hub with the " + slug + " tab active."
22298          };
22299          location.assign(target);
22300          return out(payloadNav);
22301        }
22302      }
22303
22304      /* Registration destinations: read the three bottom cards, fall back to
22305         the canonical URLs the page's own FAQ lists. */
22306      var destinations = [];
22307      var seenDest = {};
22308      function pushDest(name, dateText, href, external, note) {
22309        if (!href || seenDest[name]) return;
22310        seenDest[name] = 1;
22311        destinations.push({ destination: name, date: dateText || null, registrationUrl: href, external: !!external, note: n
22311ote });
22312      }
22313      var cards = Array.prototype.slice.call(document.querySelectorAll(".image-text-container-block > a"));
22314      for (var ci = 0; ci < cards.length; ci++) {
22315        var cardText = txt(cards[ci]);
22316        var href = cards[ci].getAttribute("href") || "";
22317        var abs = href;
22318        try { abs = new URL(href, location.origin).href; } catch (e1) {}
22319        var m = cardText.match(DATE_RE);
22320        var dateText = m ? m[0] : null;
22321        if (/new york/i.test(cardText)) pushDest("New York", dateText, abs, /events\.optimizely\.com/i.test(abs), "External Cvent registration wizard - hand the visitor this URL; the WebMCP tools cannot drive it.");
22322        else if (/london/i.test(cardText)) pushDest("London", dateText, abs, /events\.optimizely\.com/i.test(abs), "External Cvent registration wizard - hand the visitor this URL; the WebMCP tools cannot drive it.");
22323        else if (/online/i.test(cardText)) pushDest("Online", dateText, abs, false, "Native on-site form - drive it with optimizely-register-opticon-online.");
22324      }
22325      if (!destinations.length) {
22326        pushDest("New York", null, "https://events.optimizely.com/Opticon26NY", true, "External Cvent registration wizard (URL from the page's FAQ) - hand off to the visitor.");
22327        pushDest("London", null, "https://events.optimizely.com/Opticon26London", true, "External Cvent registration wizard (URL from the page's FAQ) - hand off to the visitor.");
22328        pushDest("Online", null, location.origin + "/field-notes/opticon/online-registration", false, "Native on-site form - drive it with optimizely-register-opticon-online.");
22329      }
22330
22331      /* Active tab venue, date and agenda. */
22332      var panel = document.querySelector('[role="tabpanel"][data-state="active"]');
22333      var venue = null, tabDate = null;
22334      var agenda = [];
22335      if (panel) {
22336        var vh = panel.querySelector("h2");
22337        venue = vh ? txt(vh) : null;
22338        var pm = (panel.textContent || "").match(DATE_RE);
22339        tabDate = pm ? pm[0] : null;
22340        var rows = panel.querySelectorAll("div.container.mx-auto.pb-4");
22341        for (var ri = 0; ri < rows.length && agenda.length < 30; ri++) {
22342          var timeEl = rows[ri].querySelector("p.font-body-bold");
22343          var titleEl = rows[ri].querySelector("h3");
22344          var time = timeEl ? txt(timeEl) : "";
22345          var title = titleEl ? txt(titleEl) : "";
22346          if (time || title) agenda.push({ time: time || null, session: title || null });
22347        }
22348      }
22349
22350      /* Speakers: the carousel duplicates its slide set - dedupe by name and
22351         read textContent, because off-screen duplicates are inert. */
22352      var speakers = [];
22353      var seenSpeaker = {};
22354      var slides = document.querySelectorAll("[data-carousel-item]");
22355      for (var si = 0; si < slides.length; si++) {
22356        var nameEl = slides[si].querySelector("h3");
22357        if (!nameEl) continue;
22358        var sname = collapseRepeat(nameEl.textContent || "");
22359        if (!sname || seenSpeaker[sname]) continue;
22360        seenSpeaker[sname] = 1;
22361        var roleEl = nameEl.nextElementSibling;
22362        var role = roleEl ? collapseRepeat(roleEl.textContent || "") : "";
22363        speakers.push({ name: sname, roleAndCompany: role || null });
22364      }
22365
22366      /* FAQ accordion (single-open Radix accordion). */
22367      var faqTriggers = [];
22368      var allExpandables = document.querySelectorAll("button[aria-expanded]");
22369      for (var fi = 0; fi < allExpandables.length; fi++) {
22370        var q = stripIcon(collapseRepeat(allExpandables[fi].textContent || ""));
22371        if (!q || q.length > 160) continue;
22372        if (allExpandables[fi].getAttribute("role") === "tab") continue;
22373        faqTriggers.push({ question: q, btn: allExpandables[fi] });
22374      }
22375      var faqQuestions = faqTriggers.map(function (f) { return f.question; });
22376      var expandedFaq = null;
22377      if (typeof a.expandFaq === "string" && a.expandFaq.trim()) {
22378        var wantQ = a.expandFaq.replace(/\s+/g, " ").trim().toLowerCase();
22379        var hitQ = null;
22380        for (var qi = 0; qi < faqTriggers.length; qi++) {
22381          if (faqTriggers[qi].question.toLowerCase() === wantQ) { hitQ = faqTriggers[qi]; break; }
22382        }
22383        if (!hitQ) {
22384          for (var qj = 0; qj < faqTriggers.length; qj++) {
22385            if (faqTriggers[qj].question.toLowerCase().indexOf(wantQ) !== -1) { hitQ = faqTriggers[qj]; break; }
22386          }
22387        }
22388        if (!hitQ) {
22389          return out({
22390            status: "error",
22391            action: "expand an Opticon FAQ item",
22392            url: location.href,
22393            reason: "'" + a.expandFaq + "' does not match any FAQ question on this page.",
22394            availableOptions: faqQuestions,
22395            nextStep: "Call this tool again with expandFaq set to one of the questions in availableOptions.",
22396            summary: "The requested FAQ question was not found on the Opticon hub."
22397          });
22398        }
22399        if (hitQ.btn.getAttribute("aria-expanded") !== "true") { try { hitQ.btn.click(); } catch (e2) {} }
22400        var body = null;
22401        var deadline = Date.now() + 3500;
22402        while (Date.now() < deadline) {
22403          await sleep(150);
22404          var cid = hitQ.btn.getAttribute("aria-controls");
22405          body = cid ? document.getElementById(cid) : null;
22406          if (body && collapseRepeat(body.textContent || "")) break;
22407        }
22408        expandedFaq = {
22409          question: hitQ.question,
22410          answer: body ? collapseRepeat(body.textContent || "").slice(0, 2500) : null,
22411          note: "The accordion holds one item open at a time, so this item is now the open one."
22412        };
22413        try { hitQ.btn.scrollIntoView({ behavior: "smooth", block: "center" }); } catch (e3) {}
22414      }
22415
22416      var observed = destinations.length > 0 || !!venue || speakers.length > 0;
22417      var payload = {
22418        status: observed ? "ok" : "partial",
22419        observed: observed,
22420        action: ACTION,
22421        url: location.href,
22422        locationTabs: tabNames,
22423        activeTab: activeTab,
22424        destinations: destinations,
22425        activeTabVenue: venue,
22426        activeTabDate: tabDate,
22427        agenda: agenda,
22428        speakers: speakers,
22429        faqQuestions: faqQuestions,
22430        expandedFaq: expandedFaq,
22431        onlineRegistrationToolName: "optimizely-register-opticon-online",
22432        openRegistrationToolName: "optimizely-open-opticon-registration",
22433        summary: ""
22434      };
22435      if (observed) {
22436        payload.summary = "Read the Opticon hub: " + destinations.length + " registration destination(s)" +
22437          (activeTab ? ", active tab " + activeTab : "") +
22438          (venue ? " (" + venue + (tabDate ? ", " + tabDate : "") + ")" : "") +
22439          ", " + agenda.length + " agenda row(s), " + speakers.length + " speaker(s), " + faqQuestions.length + " FAQ question(s)" +
22440          (expandedFaq ? "; expanded '" + expandedFaq.question + "'" : "") + ".";
22441      } else {
22442        payload.reason = "Neither the destination cards, the location tabs, nor the speaker carousel could be read after the page settled.";
22443        payload.nextStep = "Let https://www.optimizely.com/field-notes/opticon finish hydrating and call this tool again.";
22444        payload.summary = "The Opticon hub was open but its content could not be read yet.";
22445      }
22446      return out(payload);
22447    } catch (err) {
22448      return out({
22449        status: "error",
22450        action: ACTION,
22451        url: location.href,
22452        reason: "The read failed: " + (err && err.message ? err.message : String(err)),
22453        nextStep: "Reload the Opticon hub and call this tool again.",
22454        summary: "The Opticon hub could not be read."
22455      });
22456    }
22457  }
22458});
22459
22460mc.registerToolIf("/field-notes/opticon**", {
22461  name: "optimizely-open-opticon-registration",
22462  description: "Opens the registration flow for one Opticon 26 destination. For Online it loads the native registration page at /field-notes/opticon/online-registration, which the companion tool optimizely-register-opticon-online can then fill. For New York and L
22462ondon it loads the external Cvent registration wizard on events.optimizely.com - that wizard is a third-party multi-step form the WebMCP tools cannot drive, so the visitor completes it themselves; the tool says so in its reply. The destination URLs are read from the links on the live page where possible.",
22463  inputSchema: {
22464    type: "object",
22465    properties: {
22466      destination: {
22467        type: "string",
22468        description: "Which registration to open: online, new-york or london."
22469      }
22470    },
22471    required: ["destination"]
22472  },
22473  execute: async function (args) {
22474    trackWebMCPTool("optimizely-open-opticon-registration");
22475    var a = args || {};
22476    function out(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
22477    try {
22478      var want = String(a.destination || "").trim().toLowerCase().replace(/\s+/g, "-");
22479      if (want === "ny") want = "new-york";
22480      var VALID = ["online", "new-york", "london"];
22481      if (VALID.indexOf(want) === -1) {
22482        return out({
22483          status: "error",
22484          action: "open an Opticon registration",
22485          url: location.href,
22486          reason: "'" + a.destination + "' is not an Opticon destination.",
22487          availableOptions: VALID,
22488          nextStep: "Call this tool again with destination set to online, new-york or london.",
22489          summary: "The requested destination does not exist."
22490        });
22491      }
22492
22493      if (want === "online") {
22494        var onlineHref = "/field-notes/opticon/online-registration";
22495        var onlineLink = document.querySelector('a[href*="/opticon/online-registration"]');
22496        if (onlineLink) onlineHref = onlineLink.getAttribute("href") || onlineHref;
22497        var onlineAbs = onlineHref;
22498        try { onlineAbs = new URL(onlineHref, location.origin).href; } catch (e0) {}
22499        if (location.pathname.replace(/\/+$/, "") === "/field-notes/opticon/online-registration") {
22500          return out({
22501            status: "ok",
22502            action: "open the Opticon Online registration",
22503            url: location.href,
22504            observed: true,
22505            nextStep: "The Online registration page is already open; call optimizely-register-opticon-online to fill the form.",
22506            summary: "Already on the Opticon Online registration page."
22507          });
22508        }
22509        var payloadOnline = {
22510          status: "navigated",
22511          action: "open the Opticon Online registration",
22512          url: onlineAbs,
22513          fillToolName: "optimizely-register-opticon-online",
22514          nextStep: "The Online registration page is loading; call optimizely-register-opticon-online on it to fill the form.",
22515          summary: "Opening the Opticon Online registration page."
22516        };
22517        location.assign(onlineHref);
22518        return out(payloadOnline);
22519      }
22520
22521      var FALLBACK = { "new-york": "https://events.optimizely.com/Opticon26NY", "london": "https://events.optimizely.com/Opticon26London" };
22522      var externalHref = null;
22523      var anchors = document.querySelectorAll('a[href*="events.optimizely.com"]');
22524      var token = want === "new-york" ? /opticon26ny/i : /opticon26london/i;
22525      for (var i = 0; i < anchors.length; i++) {
22526        var h = anchors[i].getAttribute("href") || "";
22527        if (token.test(h)) { externalHref = h; break; }
22528      }
22529      if (!externalHref) externalHref = FALLBACK[want];
22530      var payloadExt = {
22531        status: "navigated",
22532        action: "open the Opticon " + (want === "new-york" ? "New York" : "London") + " registration",
22533        url: externalHref,
22534        external: true,
22535        handOff: true,
22536        reason: "This destination registers through an external Cvent wizard on events.optimizely.com - a third-party multi-step form the WebMCP tools cannot drive.",
22537        nextStep: "The Cvent registration wizard is loading; the visitor completes it themselves. No WebMCP tool reads or fills that page.",
22538        summary: "Opening the external Cvent registration wizard for Opticon " + (want === "new-york" ? "New York" : "London") + "; the visitor takes it from there."
22539      };
22540      location.assign(externalHref);
22541      return out(payloadExt);
22542    } catch (err) {
22543      return out({
22544        status: "error",
22545        action: "open an Opticon registration",
22546        url: location.href,
22547        reason: "The tool failed while opening the destination: " + (err && err.message ? err.message : 
22547String(err)),
22548        nextStep: "Call optimizely-get-opticon-overview to re-read the destinations, then try again.",
22549        summary: "The registration destination could not be opened."
22550      });
22551    }
22552  }
22553});
22554
22555mc.registerToolIf("/field-notes/opticon**", {
22556  name: "optimizely-register-opticon-online",
22557  description: "Fills the 'Get your pass for Opticon Online' registration form (the native form on /field-notes/opticon/online-registration, also mounted in the hub's Online tab). Writes First name, Last name, Work email, Company and Job title into the form's named inputs and resolves the Country through the form's own type-ahead combobox, committing the choice and verifying it against the hidden country input the form actually submits. If the form is not on the current page it loads the online-registration page instead and says so. Returns the values read back out of the form, any per-field validation messages the form is already showing, the required items still outstanding, a ready flag and the name of the companion submit tool. Nothing is sent until that submit tool is called; the page has no consent checkbox, only a static privacy notice.",
22558  inputSchema: {
22559    type: "object",
22560    properties: {
22561      firstName: { type: "string", description: "Value for the First name field." },
22562      lastName: { type: "string", description: "Value for the Last name field." },
22563      email: { type: "string", description: "Value for the Work email field. The form's own guidance asks for a work address rather than a personal one." },
22564      company: { type: "string", description: "Value for the Company field." },
22565      jobTitle: { type: "string", description: "Value for the Job title field, for example 'Marketing Manager'." },
22566      country: { type: "string", description: "Country name to resolve in the Country type-ahead, written as a full country name, for example 'United States' or 'Sweden'. The tool types it, commits the highlighted match and verifies the value the form will submit; when nothing commits it reports that back." }
22567    },
22568    required: ["firstName", "lastName", "email", "company"]
22569  },
22570  execute: async function (args) {
22571    trackWebMCPTool("optimizely-register-opticon-online");
22572    var a = args || {};
22573    var ACTION = "fill the Opticon Online registration form";
22574    var FORM_SEL = "form.opti-form";
22575    function out(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
22576    function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
22577    function norm(s) { return String(s == null ? "" : s).replace(/\s+/g, " ").trim(); }
22578    function nativeSet(el, value) {
22579      var proto = el.tagName === "TEXTAREA" ? window.HTMLTextAreaElement.prototype : window.HTMLInputElement.prototype;
22580      var d = Object.getOwnPropertyDescriptor(proto, "value");
22581      if (d && d.set) { d.set.call(el, value); } else { el.value = value; }
22582      el.dispatchEvent(new Event("input", { bubbles: true }));
22583      el.dispatchEvent(new Event("change", { bubbles: true }));
22584    }
22585    function fieldError(input) {
22586      if (!input) return null;
22587      var id = input.getAttribute("aria-describedby");
22588      var span = id ? document.getElementById(id) : null;
22589      if (!span || (span.classList && span.classList.contains("hidden"))) return null;
22590      var t = norm(span.textContent).replace(/ /g, "").trim();
22591      return t || null;
22592    }
22593    function key(el, type, keyName, keyCode) {
22594      try {
22595        el.dispatchEvent(new KeyboardEvent(type, { key: keyName, code: keyName, keyCode: keyCode, which: keyCode, bubbles: true, cancelable: true }));
22596      } catch (e) {}
22597    }
22598    try {
22599      var form = document.querySelector(FORM_SEL);
22600      if (!form) { try { form = await waitForElement(FORM_SEL, 6000); } catch (e0) { form = null; } }
22601      if (!form) {
22602        var dest = "/field-notes/opticon/online-registration";
22603        var payloadNav = {
22604          status: "navigated",
22605          action: ACTION,
22606          url: location.origin + dest,
22607          reason: "The registration form is not mounted on this page (on the hub it only exists inside the Online tab), so the dedicated registration page is being loaded instead.",
22608          nextStep: "Call optimizely-register-opticon-online again once /field-notes/opticon/online-registration has loaded.",
22609          summary: "Loading the Opticon Online registration page, where the form lives."
22610        };
22611        location.assign(dest);
22612        return out(payloadNav);
22613      }
22614
22615      try {
22616        form.scrollIntoView({ behavior: "smooth", block: "center" });
22617        form.style.outline = "3px solid #0037ff";
22618        form.style.outlineOffset = "4px";
22619      } catch (e1) {}
22620      await sleep(300);
22621
22622      /* Text inputs: the name attributes are the only stable handles - the
22623         Radix ids regenerate on every render. */
22624      var plan = [
22625        { key: "firstName", label: "First name", sel: 'form.opti-form input[name="firstName"]', value: a.firstName, required: true },
22626        { key: "lastName", label: "Last name", sel: 'form.opti-form input[name="lastName"]', value: a.lastName, required: true },
22627        { key: "email", label: "Work email", sel: 'form.opti-form input[name="email"]', value: a.email, required: true },
22628        { key: "company", label: "Company", sel: 'form.opti-form input[name="company"]', value: a.company, required: true },
22629        { key: "jobTitle", label: "Job title", sel: 'form.opti-form input[name="title"]', value: a.jobTitle, required: false }
22630      ];
22631      var fields = {};
22632      var missing = [];
22633      var validationErrors = [];
22634      var notes = [];
22635      for (var i = 0; i < plan.length; i++) {
22636        var spec = plan[i];
22637        var el = document.querySelector(spec.sel);
22638        if (!el) {
22639          fields[spec.key] = null;
22640          if (spec.required) missing.push(spec.label);
22641          notes.push("The '" + spec.label + "' control was not found in the form.");
22642          continue;
22643        }
22644        if (spec.value !== undefined && spec.value !== null && String(spec.value) !== "") {
22645          var wanted = String(spec.value);
22646          var ok = false;
22647          try { await fillInput(spec.sel, wanted); ok = true; } catch (e2) { ok = false; }
22648          if (!ok || norm(el.value) !== norm(wanted)) nativeSet(el, wanted);
22649          await sleep(80);
22650        }
22651        fields[spec.key] = norm(el.value);
22652        if (spec.required && !fields[spec.key]) missing.push(spec.label);
22653        var fe = fieldError(el);
22654        if (fe) validationErrors.push({ field: spec.label, message: fe });
22655      }
22656
22657      /* Country: a virtualized type-ahead. Type to filter, commit with
22658         ArrowDown+Enter (or a rendered option), then trust only the hidden
22659         input the form submits. */
22660      var countryInfo = { requested: a.country ? norm(a.country) : null, applied: null, committed: false };
22661      var countryHiddenSel = 'form.opti-form input[type="hidden"][name="country"]';
22662      if (a.country !== undefined && a.country !== null && norm(a.country) !== "") {
22663        var cInput = form.querySelector("#country-input") || document.querySelector("#country-input");
22664        if (!cInput) {
22665          notes.push("The Country combobox (#country-input) was not found in the form.");
22666        } else {
22667          var wrap = cInput.closest ? cInput.closest(".optiformsselectionelement") : null;
22668          try { (wrap || cInput).scrollIntoView({ behavior: "smooth", block: "center" }); } catch (e3) {}
22669          try { if (wrap) wrap.click(); } catch (e4) {}
22670          try { cInput.focus(); } catch (e5) {}
22671          try { cInput.click(); } catch (e6) {}
22672          await sleep(250);
22673          nativeSet(cInput, norm(a.country));
22674          key(cInput, "keyup", "a", 65);
22675          var menu = document.getElementById("country-menu");
22676          var deadlineC = Date.now() + 3000;
22677          while (Date.now() < deadlineC) {
22678            await sleep(150);
22679            if (cInput.getAttribute("aria-expanded") === "true") break;
22680            if (menu && menu.classList && !menu.classList.contains("hidden")) break;
22681          }
22682          var option = null;
22683          var rendered = document.querySelectorAll('#country-menu [role="option"]');
22684          for (var oi = 0; oi < rendered.length; oi++) {
22685            if (norm(rendered[oi].textContent).toLowerCase() === norm(a.country).toLowerCase()) { option = rendered[oi]; break; }
22686          }
22687          if (!option && rendered.length === 1) option = rendered[0];
22688          if (option) {
22689            try { option.dispatchEvent(new MouseEvent("mousedown", { bubbles: true })); } catch (e7) {}
22690            try { option.click(); } catch (e8) {}
22691          } else {
22692            key(cInput, "keydown", "ArrowDown", 40);
22693            await sleep(150);
22694            key(cInput, "keydown", "Enter", 13);
22695          }
22696          await sleep(400);
22697          var cHidden = document.querySelector(countryHiddenSel);
22698          countryInfo.applied = cHidden ? norm(cHidden.value) : null;
22699          countryInfo.committed = !!countryInfo.applied;
22700          if (!countryInfo.committed) {
22701            notes.push("The country '" + norm(a.country) + "' did not commit into the form's hidden country input; the value the form would submit is still empty. Try the full country name exactly as commonly written (for example '
22701United States').");
22702          } else if (countryInfo.applied.toLowerCase() !== norm(a.country).toLowerCase()) {
22703            notes.push("The country combobox committed '" + countryInfo.applied + "' for the requested '" + norm(a.country) + "'.");
22704          }
22705        }
22706      } else {
22707        var cHidden0 = document.querySelector(countryHiddenSel);
22708        countryInfo.applied = cHidden0 ? norm(cHidden0.value) : null;
22709        countryInfo.committed = !!countryInfo.applied;
22710      }
22711      fields.country = countryInfo.applied;
22712
22713      var ready = missing.length === 0 && validationErrors.length === 0;
22714      var payload = {
22715        status: ready && !notes.length ? "ok" : "partial",
22716        observed: true,
22717        action: ACTION,
22718        url: location.href,
22719        fields: fields,
22720        country: countryInfo,
22721        missing: missing,
22722        validationErrors: validationErrors,
22723        ready: ready,
22724        consentNote: "The form carries no consent checkbox - only a static notice linking the privacy notice at /legal/privacy-notice/.",
22725        submitToolName: "optimizely-register-opticon-online-submit",
22726        notes: notes,
22727        summary: ""
22728      };
22729      if (ready && !notes.length) {
22730        payload.nextStep = "Review the values, then call optimizely-register-opticon-online-submit to send the registration.";
22731        payload.summary = "Filled the Opticon Online registration form for " + (fields.email || "the supplied contact") + "; it is ready to submit.";
22732      } else if (missing.length || validationErrors.length) {
22733        payload.reason = (missing.length ? "Required fields still empty: " + missing.join(", ") + ". " : "") +
22734          (validationErrors.length ? "The form is showing " + validationErrors.length + " validation message(s)." : "");
22735        payload.nextStep = "Supply or correct the listed values and call this tool again before submitting.";
22736        payload.summary = "Filled the form, but it is not ready to submit yet.";
22737      } else {
22738        payload.reason = notes.join(" ");
22739        payload.nextStep = "Check the flagged items, re-run this tool if needed, then call optimizely-register-opticon-online-submit.";
22740        payload.summary = "Filled the form, but some controls could not be fully verified.";
22741      }
22742      return out(payload);
22743    } catch (err) {
22744      return out({
22745        status: "error",
22746        action: ACTION,
22747        url: location.href,
22748        reason: "The fill attempt threw an error: " + (err && err.message ? err.message : String(err)),
22749        nextStep: "Let the form finish loading and call this tool again.",
22750        summary: "Could not complete filling the Opticon Online registration form."
22751      });
22752    }
22753  }
22754});
22755
22756mc.registerToolIf("/field-notes/opticon**", {
22757  name: "optimizely-register-opticon-online-submit",
22758  description: "Clicks the Submit button on the Opticon Online registration form that optimizely-register-opticon-online filled, waits for the form to respond, and reports the outcome. Success is detected by the form's own success panel (.form-success-page) appearing or the form fieldset leaving the page - the thank-you content on this form can legitimately be empty, so the panel's presence counts as confirmation even without text. Failures are read from the per-field validation spans the form renders. It refuses to click while any required field is empty. This sends the registration to Optimizely - fill and review the values first.",
22759  inputSchema: {
22760    type: "object",
22761    properties: {
22762      confirm: { type: "boolean", description: "Set true to confirm the reviewed form should be sent. Defaults to true; set false to abort without clicking." }
22763    },
22764    required: []
22765  },
22766  execute: async function (args) {
22767    trackWebMCPTool("optimizely-register-opticon-online-submit");
22768    var a = args || {};
22769    var ACTION = "submit the Opticon Online registration form";
22770    var FORM_SEL = "form.opti-form";
22771    function out(p) { return { content: [{ type: "text", text: JSON.stringify(p) }] }; }
22772    function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
22773    function norm(s) { return String(s == null ? "" : s).replace(/\s+/g, " ").trim(); }
22774    function readFieldErrors(form) {
22775      var errs = [];
22776      if (!form || !form.isConnected) return errs;
22777      var inputs = form.querySelectorAll("input:not([type=hidden])");
22778      for (var i = 0; i < inputs.length; i++) {
22779        var id = inputs[i].getAttribute("aria-describedby");
22780        var span = id ? document.getElementById(id) : null;
22781        if (!span || (span.classList && span.classList.contains("hidden"))) continue;
22782        var t = norm(span.textContent).replace(/ /g, "").trim();
22783        if (!t) continue;
22784        var labEl = null;
22785        var inputId = inputs[i].getAttribute("id");
22786        if (inputId) { try { labEl = document.querySelector('label[for="' + inputId + '"]'); } catch (e) { labEl = null; } }
22787        errs.push({ field: labEl ? norm(labEl.textContent) : (inputs[i].getAttribute("name") || inputs[i].getAttribute("aria-label") || "field"), message: t });
22788      }
22789      return errs;
22790    }
22791    try {
22792      if (a.confirm === false) {
22793        return out({
22794          status: "error",
22795          action: ACTION,
22796          url: location.href,
22797          validationErrors: [],
22798          confirmation: null,
22799          reason: "confirm was false, so the form was not clicked and nothing was sent.",
22800          nextStep: "Call this tool again with confirm set to true when the reviewed values should be sent.",
22801          summary: "Submission aborted at the caller's request; nothing was sent."
22802        });
22803      }
22804      var form = document.querySelector(FORM_SEL);
22805      if (!form) {
22806        return out({
22807          status: "error",
22808          action: ACTION,
22809          url: location.href,
22810          validationErrors: [],
22811          confirmation: null,
22812          reason: "No form matching " + FORM_SEL + " is on this page, so there is nothing to submit.",
22813          nextStep: "Open /field-notes/opticon/online-registration, fill the form with optimizely-register-opticon-online, then call this tool.",
22814          summary: "The Opticon Online registration form is not on this page."
22815        });
22816      }
22817      var required = [
22818        { label: "First name", sel: 'input[name="firstName"]' },
22819        { label: "Last name", sel: 'input[name="lastName"]' },
22820        { label: "Work email", sel: 'input[name="email"]' },
22821        { label: "Company", sel: 'input[name="company"]' }
22822      ];
22823      var empty = [];
22824      for (var ri = 0; ri < required.length; ri++) {
22825        var rEl = form.querySelector(required[ri].sel);
22826        if (!rEl || !norm(rEl.value)) empty.push(required[ri].label);
22827      }
22828      if (empty.length) {
22829        return out({
22830          status: "error",
22831          action: ACTION,
22832          url: location.href,
22833          validationErrors: [],
22834          confirmation: null,
22835          reason: "These fields are still empty: " + empty.join(", ") + ".",
22836          nextStep: "Call optimizely-register-opticon-online to complete the form, then call this tool again.",
22837          summary: "Did not submit: the form is not complete."
22838        });
22839      }
22840      var btn = form.querySelector('.optiformssubmitelement button[type="submit"]') || form.querySelector('button[type="submit"]');
22841      if (!btn) {
22842        return out({
22843          status: "error",
22844          action: ACTION,
22845          url: location.href,
22846          validationErrors: [],
22847          confirmation: null,
22848          reason: "The form's Submit button was not found.",
22849          nextStep: "Let the form finish rendering and call this tool again.",
22850          summary: "Could not find the Submit button on the form."
22851        });
22852      }
22853
22854      var urlBefore = location.href;
22855      try { btn.scrollIntoView({ behavior: "smooth", block: "center" }); } catch (e0) {}
22856      btn.click();
22857
22858      var confirmation = null;
22859      var successPanel = false;
22860      var formGone = false;
22861      var errs = [];
22862      var navigated = false;
22863      var deadline = Date.now() + 18000;
22864      while (Date.now() < deadline) {
22865        await sleep(400);
22866        if (location.href !== urlBefore) { navigated = true; break; }
22867        var panel = document.querySelector(".form-success-page, .form-confirmation");
22868        if (panel) {
22869          successPanel = true;
22870          confirmation = norm(panel.textContent) || null;
22871          break;
22872        }
22873        formGone = !document.querySelector(FORM_SEL + " fieldset");
22874        if (formGone) break;
22875        var liveForm = document.querySelector(FORM_SEL);
22876        errs = readFieldErrors(liveForm);
22877        if (errs.length) break;
22878        /* the button flips to a busy label while the POST is in flight */
22879      }
22880
22881      if (navigated) {
22882        return out({
22883          status: "navigated",
22884          action: ACTION,
22885          url: location.href,
22886          validationErrors: [],
22887          confirmation: null,
22888          nextStep: "The page moved after the click; read the confirmation on the page that is now loaded.",
22889          summary: "Clicked Submit and the browser navigated to " + location.href + "."
22890        });
22891      }
22892      if (errs.length) {
22893        return out({
22894          status: "error",
22895          action: ACTION,
22896          url: location.href,
22897          validationErrors: errs,
22898          confirmation: null,
22899          submitted: false,
22900          reason: "The form rendered validation messages on " + errs.length + " field(s) instead of a confirmation.",
22901          nextStep: "Correct the flagged values with optimizely-register-opticon-online, then call this tool again.",
22902          summary: "The registration was not accepted; the form shows validation messages."
22903        });
22904      }
22905      if (successPanel || formGone) {
22906        return out({
22907          status: "ok",
22908          action: ACTION,
22909          url: location.href,
22910          validationErrors: [],
22911          confirmation: confirmation,
22912          submitted: true,
22913          confirmationNote: confirmation ? null : "The form's success state appeared without visible thank-you text, which is how this form is configured - the submission went through.",
22914          summary: confirmation
22915            ? "Submitted the Opticon Online registration; the page responded: " + confirmation
22916            : "Submitted the Opticon Online registration; the form's success state replaced the form (its thank-you content is empty by design)."
22917        });
22918      }
22919      return out({
22920        status: "partial",
22921        action: ACTION,
22922        url: location.href,
22923        observed: false,
22924        validationErrors: [],
22925        confirmation: null,
22926        submitted: null,
22927        reason: "No success panel, no validation message and no form removal was observed within 18 seconds of the click, so the outcome could not be read.",
22928        nextStep: "Look at the form on the page; only resend if it clearly still shows the unsent values, since re-clicking may register the visitor twice.",
22929        summary: "Clicked Submit but could not read a confirmation or an error from the page."
22930      });
22931    } catch (err) {
22932      return out({
22933        status: "error",
22934        action: ACTION,
22935        url: location.href,
22936        validationErrors: [],
22937        confirmation: null,
22938        reason: "The submit attempt threw an error: " + (err && err.message ? err.message : 
22938String(err)),
22939        nextStep: "Check the page for a confirmation before retrying, so the registration is not sent twice.",
22940        summary: "Could not complete submitting the Opticon Online registration form."
22941      });
22942    }
22943  }
22944});
22945})();
22946}, "dependencies": []}]}, {"viewId": "5808189738844160", "changes": [{"type": "custom_code", "value": function($){(function(){var s=document.createElement('script');s.type='text/javascript';s.text=decodeURIComponent(escape(atob('LyogV2ViTUNQIHRvb2xzIGZvciBvcHRpbWl6ZWx5IC0gZ2VuZXJhdGVkIGJ5IHRoZSBPcGFsIFdlYk1DUCBTaXRlIEFuYWx5emVyICovCihmdW5jdGlvbiAoKSB7CiAgJ3VzZSBzdHJpY3QnOwogIHZhciBfbWNSYXcgPSAodHlwZW9mIGRvY3VtZW50ICE9PSAndW5kZWZpbmVkJyAmJiBkb2N1bWVudC5tb2RlbENvbnRleHQpIHx8CiAgICAgICAgICAgICAgICh0eXBlb2YgbmF2aWdhdG9yICE9PSAndW5kZWZpbmVkJyAmJiBuYXZpZ2F0b3IubW9kZWxDb250ZXh0KTsKICBpZiAoIV9tY1JhdyB8fCB0eXBlb2YgX21jUmF3LnJlZ2lzdGVyVG9vbCAhPT0gJ2Z1bmN0aW9uJykgewogICAgY29uc29sZS53YXJuKCdbV2ViTUNQXSBObyBtb2RlbENvbnRleHQgYXZhaWxhYmxlOyB0b29scyBub3QgcmVnaXN0ZXJlZC4nKTsKICAgIHJldHVybjsKICB9CgogIGZ1bmN0aW9uIHRyYWNrV2ViTUNQVG9vbChuYW1lKSB7CiAgICB0cnkgewogICAgICB3aW5kb3cub3B0aW1pemVseSA9IHdpbmRvdy5vcHRpbWl6ZWx5IHx8IFtdOwogICAgICB3aW5kb3cub3B0aW1pemVseS5wdXNoKHsKICAgICAgICB0eXBlOiAnZXZlbnQnLCBldmVudE5hbWU6ICdXZWJNQ1BUb29sJywKICAgICAgICBwcm9wZXJ0aWVzOiB7IFdlYk1DUFRvb2w6IG5hbWUgfHwgJ3Vua25vd24nIH0KICAgICAgfSk7CiAgICB9IGNhdGNoIChlKSB7IC8qIHRyYWNraW5nIG11c3QgbmV2ZXIgYnJlYWsgYSB0b29sICovIH0KICB9CgogIGZ1bmN0aW9uIF93cmFwSGFuZGxlcihmbiwgdG9vbE5hbWUpIHsKICAgIHJldHVybiBmdW5jdGlvbiAoKSB7IHRyYWNrV2ViTUNQVG9vbCh0b29sTmFtZSk7IHJldHVybiBmbi5hcHBseSh0aGlzLCBhcmd1bWVudHMpOyB9OwogIH0KCiAgZnVuY3Rpb24gX3BhdGhNYXRjaGVzKHBhdHRlcm4pIHsKICAgIGlmICghcGF0dGVybiB8fCBwYXR0ZXJuID09PSAnLyoqJykgeyByZXR1cm4gdHJ1ZTsgfQogICAgdmFyIHBhdGggPSBsb2NhdGlvbi5wYXRobmFtZS5yZXBsYWNlKC9cLyskLywgJycpIHx8ICcvJzsKICAgIHZhciBwYXQgID0gU3RyaW5nKHBhdHRlcm4pLnJlcGxhY2UoL1wvKyQvLCAnJykgfHwgJy8nOwogICAgaWYgKHBhdCA9PT0gJy8nKSB7IHJldHVybiBwYXRoID09PSAnLyc7IH0KICAgIHZhciByeCA9IHBhdC5zcGxpdCgnKionKS5tYXAoZnVuY3Rpb24gKGNodW5rKSB7CiAgICAgIHJldHVybiBjaHVuay5zcGxpdCgnKicpLm1hcChmdW5jdGlvbiAobGl0KSB7CiAgICAgICAgcmV0dXJuIGxpdC5yZXBsYWNlKC9bLiorP14ke30oKXxbXF1cXF0vZywgJ1xcJCYnKTsKICAgICAgfSkuam9pbignW14vXSonKTsKICAgIH0pLmpvaW4oJy4qJyk7CiAgICB0cnkgeyByZXR1cm4gbmV3IFJlZ0V4cCgnXicgKyByeCArICckJykudGVzdChwYXRoKTsgfSBjYXRjaCAoZSkgeyByZXR1cm4gdHJ1ZTsgfQogIH0KCiAgLyogUm91dGUtc2NvcGVkIHRvb2xzIGFyZSBSRUNPUkRFRCBoZXJlLCBub3QgcmVnaXN0ZXJlZCBlYWdlcmx5LCBzbyBhIHJlYWwgU1BBCiAgICAgKGNsaWVudC1zaWRlIHJvdXRlIGNoYW5nZXMgdmlhIGhpc3RvcnkucHVzaFN0YXRlL3JlcGxhY2VTdGF0ZSwgV0lUSE9VVCBhCiAgICAgZnVsbCBwYWdlIHJlbG9hZCkgY2FuIHJlZ2lzdGVyIGEgcm91dGUncyB0b29scyB0aGUgbW9tZW50IGl0cyBwYXRoIGJlY29tZXMKICAgICBhY3RpdmUuIFRoZSBtb2RlbENvbnRleHQgQVBJIGV4cG9zZXMgb25seSByZWdpc3RlclRvb2wgLS0gdGhlcmUgaXMgTk8KICAgICB1bnJlZ2lzdGVyIC0tIHNvIHJlZ2lzdHJhdGlvbiBpcyByZWdpc3Rlci1vbmNlLWFjY3VtdWxhdGU6IF9zeW5jUm91dGUgaXMKICAgICBpZGVtcG90ZW50IHZpYSBlYWNoIGVudHJ5J3MgYHJlZ2lzdGVyZWRgIGZsYWcsIGFuZCB0b29scyBmcm9tIGFuCiAgICAgYWxyZWFkeS12aXNpdGVkIHJvdXRlIHN0YXkgcmVnaXN0ZXJlZCBhZnRlciBhIHNvZnQgbmF2aWdhdGlvbiAoaGFybWxlc3M6CiAgICAgZXZlcnkgaGFuZGxlciBndWFyZHMgaXRzIERPTSB3b3JrIHdpdGggd2FpdEZvckVsZW1lbnQgKyB0aW1lb3V0KS4gKi8KICB2YXIgX3JvdXRlUmVnaXN0cnkgPSBbXTsKCiAgZnVuY3Rpb24gX3N5bmNSb3V0ZSgpIHsKICAgIHZhciBjaGFuZ2VkID0gZmFsc2U7CiAgICBmb3IgKHZhciBpID0gMDsgaSA8IF9yb3V0ZVJlZ2lzdHJ5Lmxlbmd0aDsgaSsrKSB7CiAgICAgIHZhciBlbnRyeSA9IF9yb3V0ZVJlZ2lzdHJ5W2ldOwogICAgICBpZiAoIWVudHJ5LnJlZ2lzdGVyZWQgJiYgX3BhdGhNYXRjaGVzKGVudHJ5LnBhdHRlcm4pKSB7CiAgICAgICAgdHJ5IHsKICAgICAgICAgIG1jLnJlZ2lzdGVyVG9vbChlbnRyeS5kZXNjcmlwdG9yKTsKICAgICAgICAgIGVudHJ5LnJlZ2lzdGVyZWQgPSB0cnVlOwogICAgICAgICAgY2hhbmdlZCA9IHRydWU7CiAgICAgICAgfSBjYXRjaCAoZSkgeyAvKiBvbmUgYmFkIHRvb2wgbXVzdCBuZXZlciBicmVhayB0aGUgcmVzdCAqLyB9CiAgICAgIH0KICAgIH0KICAgIHJldHVybiBjaGFuZ2VkOwogIH0KCiAgLyogVGVsbCBhIGxpc3RlbmluZyBhZ2VudCB0aGUgcGFnZSdzIHRvb2wgc2V0IGNoYW5nZWQgc28gaXQgcmUtcmVhZHMgdGhlIG1vZGVsCiAgICAgY29udGV4dC4gRmlyZWQgYWZ0ZXIgdGhlIGluaXRpYWwgcmVnaXN0cmF0aW9uIEFORCBhZnRlciBldmVyeSBzb2Z0IHJvdXRlCiAgICAgY2hhbmdlLiBCZXN0LWVmZm9ydDsgbXVzdCBuZXZlciBicmVhayByZWdpc3RyYXRpb24uICovCiAgZnVuY3Rpb24gX2Rpc3BhdGNoVG9vbGNoYW5nZSgpIHsKICAgIHRyeSB7CiAgICAgIGlmICh0eXBlb2YgZG9jdW1lbnQgIT09ICd1bmRlZmluZWQnICYmIHR5cGVvZiBFdmVudCA9PT0gJ2Z1bmN0aW9uJykgewogICAgICAgIGRvY3VtZW50LmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCd0b29sY2hhbmdlJykpOwogICAgICB9CiAgICB9IGNhdGNoIChlKSB7IC8qIG5ldmVyIGJyZWFrIHJlZ2lzdHJhdGlvbiAqLyB9CiAgfQoKICAvKiBXaXJlIHRoZSBTUEEgcm91dGVyIGhvb2sgT05D
22946RTogd3JhcCBoaXN0b3J5LnB1c2hTdGF0ZS9yZXBsYWNlU3RhdGUgYW5kCiAgICAgbGlzdGVuIGZvciBwb3BzdGF0ZS9oYXNoY2hhbmdlLCBzbyBhIGNsaWVudC1zaWRlIHJvdXRlIGNoYW5nZSByZS1ydW5zCiAgICAgX3N5bmNSb3V0ZSAocmVnaXN0ZXJpbmcgYW55IHRvb2xzIHdob3NlIHBhdHRlcm4gbm93IG1hdGNoZXMpIGFuZCByZS1zaWduYWxzCiAgICAgdG9vbGNoYW5nZS4gT24gYW4gTVBBIHRoaXMgc2ltcGx5IG5ldmVyIGZpcmVzIC0tIHRoZSBmdWxsIHJlbG9hZCByZS1ydW5zIHRoZQogICAgIHdob2xlIElJRkUgaW5zdGVhZC4gKi8KICB2YXIgX3JvdXRlckluc3RhbGxlZCA9IGZhbHNlOwogIGZ1bmN0aW9uIF9pbnN0YWxsUm91dGVyKCkgewogICAgaWYgKF9yb3V0ZXJJbnN0YWxsZWQgfHwgdHlwZW9mIHdpbmRvdyA9PT0gJ3VuZGVmaW5lZCcgfHwgIXdpbmRvdy5oaXN0b3J5KSB7IHJldHVybjsgfQogICAgX3JvdXRlckluc3RhbGxlZCA9IHRydWU7CiAgICBmdW5jdGlvbiBfb25Sb3V0ZUNoYW5nZSgpIHsKICAgICAgdHJ5IHsgX3N5bmNSb3V0ZSgpOyB9IGNhdGNoIChlKSB7IC8qIG5ldmVyIGJyZWFrICovIH0KICAgICAgX2Rpc3BhdGNoVG9vbGNoYW5nZSgpOwogICAgfQogICAgWydwdXNoU3RhdGUnLCAncmVwbGFjZVN0YXRlJ10uZm9yRWFjaChmdW5jdGlvbiAobSkgewogICAgICB2YXIgb3JpZyA9IHdpbmRvdy5oaXN0b3J5W21dOwogICAgICBpZiAodHlwZW9mIG9yaWcgIT09ICdmdW5jdGlvbicpIHsgcmV0dXJuOyB9CiAgICAgIHdpbmRvdy5oaXN0b3J5W21dID0gZnVuY3Rpb24gKCkgewogICAgICAgIHZhciByID0gb3JpZy5hcHBseSh0aGlzLCBhcmd1bWVudHMpOwogICAgICAgIHRyeSB7IF9vblJvdXRlQ2hhbmdlKCk7IH0gY2F0Y2ggKGUpIHsgLyogbmV2ZXIgYnJlYWsgbmF2aWdhdGlvbiAqLyB9CiAgICAgICAgcmV0dXJuIHI7CiAgICAgIH07CiAgICB9KTsKICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdwb3BzdGF0ZScsIF9vblJvdXRlQ2hhbmdlKTsKICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdoYXNoY2hhbmdlJywgX29uUm91dGVDaGFuZ2UpOwogIH0KCiAgdmFyIG1jID0gewogICAgcmVnaXN0ZXJUb29sOiBmdW5jdGlvbiAoZGVzY3JpcHRvcikgewogICAgICBpZiAoZGVzY3JpcHRvciAmJiB0eXBlb2YgZGVzY3JpcHRvciA9PT0gJ29iamVjdCcpIHsKICAgICAgICB2Y
22946XIgX25hbWUgPSBkZXNjcmlwdG9yLm5hbWU7CiAgICAgICAgT2JqZWN0LmtleXMoZGVzY3JpcHRvcikuZm9yRWFjaChmdW5jdGlvbiAoaykgewogICAgICAgICAgaWYgKHR5cGVvZiBkZXNjcmlwdG9yW2tdID09PSAnZnVuY3Rpb24nKSB7CiAgICAgICAgICAgIGRlc2NyaXB0b3Jba10gPSBfd3JhcEhhbmRsZXIoZGVzY3JpcHRvcltrXSwgX25hbWUpOwogICAgICAgICAgfQogICAgICAgIH0pOwogICAgICB9CiAgICAgIHJldHVybiBfbWNSYXcucmVnaXN0ZXJUb29sKGRlc2NyaXB0b3IpOwogICAgfSwKICAgIC8qIFJlY29yZCBhIHJvdXRlLXNjb3BlZCB0b29sLCB0aGVuIHJlZ2lzdGVyIGl0IGlmIGl0cyBwYXRoIGlzIGFscmVhZHkKICAgICAgIGFjdGl2ZS4gTGF0ZXIgc29mdC1uYXZpZ2F0aW9ucyBwaWNrIGl0IHVwIHZpYSBfc3luY1JvdXRlIChTUEEgc3VwcG9ydCkuICovCiAgICByZWdpc3RlclRvb2xJZjogZnVuY3Rpb24gKHBhdHRlcm4sIGRlc2NyaXB0b3IpIHsKICAgICAgX3JvdXRlUmVnaXN0cnkucHVzaCh7IHBhdHRlcm46IHBhdHRlcm4sIGRlc2NyaXB0b3I6IGRlc2NyaXB0b3IsIHJlZ2lzdGVyZWQ6IGZhbHNlIH0pOwogICAgICBfc3luY1JvdXRlKCk7CiAgICAgIHJldHVybiBudWxsOwogICAgfQogIH07CgogIGZ1bmN0aW9uIHdhaXRGb3JFbGVtZW50KHNlbGVjdG9yLCB0aW1lb3V0KSB7CiAgICB0aW1lb3V0ID0gdGltZW91dCB8fCA1MDAwOwogICAgcmV0dXJuIG5ldyBQcm9taXNlKGZ1bmN0aW9uIChyZXNvbHZlLCByZWplY3QpIHsKICAgICAgdmFyIGZvdW5kID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihzZWxlY3Rvcik7CiAgICAgIGlmIChmb3VuZCkgeyByZXR1cm4gcmVzb2x2ZShmb3VuZCk7IH0KICAgICAgdmFyIG9icyA9IG5ldyBNdXRhdGlvbk9ic2VydmVyKGZ1bmN0aW9uICgpIHsKICAgICAgICB2Y
22946XIgZWwgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKHNlbGVjdG9yKTsKICAgICAgICBpZiAoZWwpIHsgb2JzLmRpc2Nvbm5lY3QoKTsgY2xlYXJUaW1lb3V0KHRpbWVyKTsgcmVzb2x2ZShlbCk7IH0KICAgICAgfSk7CiAgICAgIG9icy5vYnNlcnZlKGRvY3VtZW50LmRvY3VtZW50RWxlbWVudCwgeyBjaGlsZExpc3Q6IHRydWUsIHN1YnRyZWU6IHRydWUgfSk7CiAgICAgIHZhciB0aW1lciA9IHNldFRpbWVvdXQoZnVuY3Rpb24gKCkgewogICAgICAgIG9icy5kaXNjb25uZWN0KCk7CiAgICAgICAgcmVqZWN0KG5ldyBFcnJvcignW1dlYk1DUF0gdGltZW91dCB3YWl0aW5nIGZvciAnICsgc2VsZWN0b3IpKTsKICAgICAgfSwgdGltZW91dCk7CiAgICB9KTsKICB9CgogIGZ1bmN0aW9uIHNldE5hdGl2ZVZhbHVlKGVsLCB2YWx1ZSkgewogICAgdmFyIGRlc2MgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKE9iamVjdC5nZXRQcm90b3R5cGVPZihlbCksICd2YWx1ZScpOwogICAgaWYgKGRlc2MgJiYgZGVzYy5zZXQpIHsgZGVzYy5zZXQuY2FsbChlbCwgdmFsdWUpOyB9IGVsc2UgeyBlbC52YWx1ZSA9IHZhbHVlOyB9CiAgfQoKICBhc3luYyBmdW5jdGlvbiBmaWxsSW5wdXQoc2VsZWN0b3IsIHZhbHVlKSB7CiAgICB2YXIgZWwgPSBhd2FpdCB3YWl0Rm9yRWxlbWVudChzZWxlY3Rvcik7CiAgICBlbC5mb2N1cygpOwogICAgc2V0TmF0aXZlVmFsdWUoZWwsIHZhbHVlKTsKICAgIGVsLmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCdpbnB1dCcsICB7IGJ1YmJsZXM6IHRydWUgfSkpOwogICAgZWwuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoJ2NoYW5nZScsIHsgYnViYmxlczogdHJ1ZSB9KSk7CiAgICByZXR1cm4gZWw7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBmaWxsU2VsZWN0KHNlbGVjdG9yLCB2YWx1ZSkgewogICAgdmFyIGVsID0gYXdhaXQgd2FpdEZvckVsZW1lbnQoc2VsZWN0b3IpOwogICAgZWwudmFsdWUgPSB2YWx1ZTsKICAgIGlmIChlbC5zZWxlY3RlZEluZGV4ID09PSAtMSkgewogICAgICB2YXIgb3B0ID0gQXJyYXkucHJvdG90eXBlLmZpbmQuY2FsbChlbC5vcHRpb25zLCBmdW5jdGlvbiAobykgewogICAgICAgIHJldHVybiBvLnRleHRDb250ZW50LnRyaW0oKS50b0xvd2VyQ2FzZSgpID09PSBTdHJpbmcodmFsdWUpLnRyaW0oKS50b0xvd2VyQ2FzZSgpOwogICAgICB9KTsKICAgICAgaWYgKG9wdCkgeyBlbC52YWx1ZSA9IG9wdC52YWx1ZTsgfQogICAgfQogICAgZWwuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoJ2NoYW5nZScsIHsgYnViYmxlczogdHJ1ZSB9KSk7CiAgICByZXR1cm4gZWw7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBmaWxsQ2hlY2tib3goc2VsZWN0b3IsIGNoZWNrZWQpIHsKICAgIHZhciBlbCA9IGF3YWl0IHdhaXRGb3JFbGVtZW50KHNlbGVjdG9yKTsKICAgIGlmIChlbC5jaGVja2VkICE9PSAhIWNoZWNrZWQpIHsgZWwuY2xpY2soKTsgfQogICAgZWwuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoJ2NoYW5nZScsIHsgYnViYmxlczogdHJ1ZSB9KSk7CiAgICByZXR1cm4gZWw7CiAgfQoKICAvKiBSZWFkIE9ORSBmaWVsZCBvZmYgT05FIGxlYWYgbm9kZS4gJ0BhdHRyJyByZWFkcyBhbiBhdHRyaWJ1dGUgb2YgdGhlIGl0ZW0KICAgICBpdHNlbGY7ICdzZWwgQGF0dHInIHJlYWRzIGFuIGF0dHJpYnV0ZSBvZiBhIGRlc2NlbmRhbnQuIE5ldmVyIHJldHVybnMgdGhlCiAgICAgY29uY2F0ZW5hdGVkIHRleHQgb2YgYSB3aG9sZSBjb250YWluZXIuICovCiAgZnVuY3Rpb24gX2ZpZWxkKHJvb3QsIHNwZWMpIHsKICAgIGlmICghc3BlYykgeyByZXR1cm4gbnVsbDsgfQogICAgc3BlYyA9IFN0cmluZyhzcGVjKS50cmltKCk7CiAgICB2YXIgYXQgPSBzcGVjLmluZGV4T2YoJ0AnKTsKICAgIGlmIChhdCA9PT0gMCkgeyByZXR1cm4gcm9vdC5nZXRBdHRyaWJ1dGUoc3BlYy5zbGljZSgxKSk7IH0KICAgIGlmIChhdCA+IDApIHsKICAgICAgdmFyIGVsMCA9IHJvb3QucXVlcnlTZWxlY3RvcihzcGVjLnNsaWNlKDAsIGF0KS50cmltKCkpOwogICAgICByZXR1cm4gZWwwID8gZWwwLmdldEF0dHJpYnV0ZShzcGVjLnNsaWNlKGF0ICsgMSkpIDogbnVsbDsKICAgIH0KICAgIHZhciBlbCA9IHJvb3QucXVlcnlTZWxlY3RvcihzcGVjKTsKICAgIGlmICghZWwpIHsgcmV0dXJuIG51bGw7IH0KICAgIHJldHVybiAoZWwudGV4dENvbnRlbnQgfHwgJycpLnJlcGxhY2UoL1xzKy9nLCAnICcpLnRyaW0oKTsKICB9CgogIGZ1bmN0aW9uIGV4dHJhY3RJdGVtcyhpdGVtU2VsZWN0b3IsIGZpZWxkTWFwLCBsaW1pdCkgewogICAgbGltaXQgPSBsaW1pdCB8fCAxMDsKICAgIHZhciBub2RlcyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoaXRlbVNlbGVjdG9yKSk7CiAgICByZXR1cm4gewogICAgICByZXN1bHRDb3VudDogbm9kZXMubGVuZ3RoLAogICAgICB0cnVuY2F0ZWQ6IG5vZGVzLmxlbmd0aCA+IGxpbWl0LAogICAgICByZXN1bHRzOiBub2Rlcy5zbGljZSgwLCBsaW1pdCkubWFwKGZ1bmN0aW9uIChuKSB7CiAgICAgICAgdmFyIG8gPSB7fTsKICAgICAgICBPYmplY3Qua2V5cyhmaWVsZE1hcCB8fCB7fSkuZm9yRWFjaChmdW5jdGlvbiAoaykgewogICAgICAgICAgdmFyIHYgPSBfZmllbGQobiwgZmllbGRNYXBba10pOwogICAgICAgICAgaWYgKHYgIT09IG51bGwgJiYgdiAhPT0gdW5kZWZpbmVkICYmIHYgIT09ICcnKSB7IG9ba10gPSBTdHJpbmcodikuc2xpY2UoMCwgMzAwKTsgfQogICAgICAgIH0pOwogICAgICAgIHJldHVybiBvOwogICAgICB9KQogICAgfTsKICB9CgogIC8qIFBhcnNlIHRoZSBpbnRlZ2VyIHRoZSBwYWdlIGl0c2VsZiBzdGF0ZXMsIGUuZy4gIjUgaXRlbXMiIC0+IDUuICovCiAgZnVuY3Rpb24gcmVhZFJlc3VsdENvdW50KHNlbGVjdG9yLCBmYWxsYmFjaykgewogICAgaWYgKCFzZWxlY3RvcikgeyByZXR1cm4gZmFsbGJhY2s7IH0KICAgIHZhciBlbCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3Ioc2VsZWN0b3IpOwogICAgaWYgKCFlbCkgeyByZXR1cm4gZmFsbGJhY2s7IH0KICAgIHZhciBtID0gKGVsLnRleHRDb250ZW50IHx8ICcnKS5yZXBsYWNlKC8sL2csICcnKS5tYXRjaCgvXGQrLyk7CiAgICByZXR1cm4gbSA/IHBhcnNlSW50KG1bMF0sIDEwKSA6IGZhbGxiYWNrOwogIH0KCiAgZnVuY3Rpb24gYnVpbGRVcmwodGVtcGxhdGUsIGFyZ3MpIHsKICAgIHJldHVybi
22946B0ZW1wbGF0ZS5yZXBsYWNlKC9ceyhcdyspXH0vZywgZnVuY3Rpb24gKF8sIGspIHsKICAgICAgcmV0dXJuIGFyZ3MgJiYgYXJnc1trXSAhPSBudWxsID8gZW5jb2RlVVJJQ29tcG9uZW50KGFyZ3Nba10pIDogJyc7CiAgICB9KS5yZXBsYWNlKC9bPyZdXHcrPSg/PSZ8JCkvZywgJycpLnJlcGxhY2UoL1w/Ji8sICc/JykucmVwbGFjZSgvWz8mXSQvLCAnJyk7CiAgfQoKICAvKiBXaG9sZS1zaXRlIGNhcGFiaWxpdHkgbWFwLCBkZXJpdmVkIG9uY2UgYXQgYXNzZW1ibGUgdGltZSBhbmQgZW1iZWRkZWQgYXMgYQogICAgIHN0YXRpYyBsaXRlcmFsIHNvIEVWRVJZIHBhZ2UgY2FycmllcyB0aGUgRlVMTCBtYXAgKG5vdCBqdXN0IHRoZSB0b29scyB0aGF0CiAgICAgcmVnaXN0ZXJlZCBvbiB0aGUgY3VycmVudCBwYXRoKS4gU2FtZSBieXRlcyBhcyB0aGUgZW1pdHRlZAogICAgIHdlYm1jcC1jYXBhYmlsaXRpZXMtPHByZWZpeD4uanNvbjogb25lIHNvdXJjZSBvZiB0cnV0aCwgdHdvIGRpc2NvdmVyeQogICAgIHN1cmZhY2VzLiAqLwogIHZhciBfV0VCTUNQX0NBUFMgPSB7InNpdGUiOiAiIiwgImdlbmVyYXRlZF9hdCI6ICIyMDI2LTA4LTIwVDIyOjE5OjU1LjQ0NjU0OFoiLCAidG9vbF9wcmVmaXgiOiAib3B0aW1pemVseSIsICJ0b29sX2NvdW50IjogNywgImNhcGFiaWxpdGllcyI6IFt7Im5hbWUiOiAib3B0aW1pemVseS1zdWJtaXQtb3B0aW1pemVseS1sZWFkLWZvcm0iLCAiZGVzY3JpcHRpb24iOiAiIiwgInVybFBhdHRlcm4iOiAiLyJ9LCB7Im5hbWUiOiAib3B0aW1pemVseS1zdWJtaXQtb3B0aW1pemVseS1sZWFkLWZvcm0tc3VibWl0IiwgImRlc2NyaXB0aW9uIjogIiIsICJ1cmxQYXR0ZXJuIjogIi8ifSwgeyJuYW1lIjogIm9wdGltaXplbHktc3VibWl0LWRlbW8tcmVxdWVzdCIsICJkZXNjcmlwdGlvbiI6ICIiLCAidXJsUGF0dGVybiI6ICIvZ2V0LXN0YXJ0ZWQvIn0sIHsibmFtZSI6ICJvcHRpbWl6ZWx5LXN1Ym1pdC1kZW1vLXJlcXVlc3Qtc3VibWl0IiwgImRlc2NyaXB0aW9uIjogIiIsICJ1cmxQYXR0ZXJuIjogIi9nZXQtc3RhcnRlZC8ifSwgeyJuYW1lIjogIm9wdGltaXplbHktc2VhcmNoLW9wdGltaXplbHktcmVzb3VyY2VzIiwgImRlc2NyaXB0aW9uIjogIiIsICJ1cmxQYXR0ZXJuIjogIi9zZWFyY2gvIn0sIHsibmFtZSI6ICJvcHRpbWl6ZWx5LXN1Ym1pdC1iZWNvbWUtYS1wYXJ0bmVyLWlucXVpcnkiLCAiZGVzY3JpcHRpb24iOiAiIiwgInVybFBhdHRlcm4iOiAiL3BhcnRuZXJzLyJ9LCB7Im5hbWUiOiAib3B0aW1pemVseS1zdWJtaXQtYmVjb21lLWEtcGFydG5lci1pbnF1aXJ5LXN1Ym1pdCIsICJkZXNjcmlwdGlvbiI6ICIiLCAidXJsUGF0dGVybiI6ICIvcGFydG5lcnMvIn1dLCAiYnlQYWdlIjogeyIvIjogWyJvcHRpbWl6ZWx5LXN1Ym1pdC1vcHRpbWl6ZWx5LWxlYWQtZm9ybSIsICJvcHRpbWl6ZWx5LXN1Ym1pdC1vcHRpbWl6ZWx5LWxlYWQtZm9ybS1zdWJtaXQiXSwgIi9nZXQtc3RhcnRlZC8iOiBbIm9wdGltaXplbHktc3VibWl0LWRlbW8tcmVxdWVzdCIsICJvcHRpbWl6ZWx5LXN1Ym1pdC1kZW1vLXJlcXVlc3Qtc3VibWl0Il0sICIvc2VhcmNoLyI6IFsib3B0aW1pemVseS1zZWFyY2gtb3B0aW1pemVseS1yZXNvdXJjZXMiXSwgIi9wYXJ0bmVycy8iOiBbIm9wdGltaXplbHktc3VibWl0LWJlY29tZS1hLXBhcnRuZXItaW5xdWlyeSIsICJvcHRpbWl6ZWx5LXN1Ym1pdC1iZWNvbWUtYS1wYXJ0bmVyLWlucXVpcnktc3VibWl0Il19fTsKCiAgLyogT25lIGdsb2JhbGx5LXJlZ2lzdGVyZWQgZGlzY292ZXJ5IHRvb2w6IGFuIGFnZW50IG9uIEFOWSBwYWdlIGNhbiBzZWUgdGhlCiAgICAgd2hvbGUgc2l0ZSdzIGNhcGFiaWxpdGllcyAoaW50ZW50IC0+IHBhZ2UgLT4gdG9vbHMpIGFuZCBqdW1wIHRvIHRoZSBwYWdlCiAgICAgdGhhdCBob3N0cyBhIGNhcGFiaWxpdHkuIFJlZ2lzdGVyZWQgd2l0aCB0aGUgYmFyZSByZWdpc3RlclRvb2wgKHBhdHRlcm4KICAgICAvKiopLCBzbyBpdCBzdXJ2aXZlcyBldmVyeSBmdWxsLXBhZ2UgbmF2aWdhdGlvbi4gUjkgdHJhY2tpbmcgaXMgYXBwbGllZCBieQogICAgIHRoZSByZWdpc3RlclRvb2wgd3JhcHBlciwgc28gZG8gTk9UIGNhbGwgdHJhY2tXZWJNQ1BUb29sIGhlcmUuICovCiAgbWMucmVnaXN0ZXJUb29sKHsKICAgIG5hbWU6ICdvcHRpbWl6ZWx5LWRpc2NvdmVyLWNhcGFiaWxpdGllcycsCiAgICBkZXNjcmlwdGlvbjogJ0xpc3QgZXZlcnkgV2ViTUNQIGNhcGFiaWxpdHkgYWNyb3NzIHRoaXMgd2hvbGUgc2l0ZTogdGhlIGludGVudCwgdGhlIHBhZ2UgVVJMIHBhdHRlcm4gdGhhdCBob3N0cyBpdCwgYW5kIHRoZSB0b29sIG5hbWVzIHRvIGNhbGwgdGhlcmUuIE9wdGlvbmFsbHkgcGFzcyBuYXZpZ2F0ZVRvIHRvIGp1bXAgdG8gdGhlIHBhZ2UgdGhhdCBob3N0cyBhIGNhcGFiaWxpdHkgKHRoZSBmdWxsIHBhZ2UgcmVsb2FkczsgY2FsbCB0aGUgdGFyZ2V0IHRvb2wgYWZ0ZXIgaXQgbG9hZHMpLiBVc2UgdGhpcyBmaXJzdCB3aGVuIHRoZSB0b29sIHlvdSBuZWVkIGlzIG5vdCByZWdpc3RlcmVkIG9uIHRoZSBjdXJyZW50IHBhZ2UuJywKICAgIGlucHV0U2NoZW1hOiB7CiAgICAgIHR5cGU6ICdvYmplY3QnLAogICAgICBwcm9wZXJ0aWVzOiB7CiAgICAgICAgbmF2aWdhdGVUbzogewogICAgICAgICAgdHlwZTogJ3N0cmluZycsCiAgICAgICAgICBkZXNjcmlwdGlvbjogJ09wdGlvbmFsIHNhbWUtb3JpZ2luIHBhdGggb3IgVVJMIHRvIG5hdmlnYXRlIHRvLCBlLmcuIGEgY2FwYWJpbGl0eSB1cmxQYXR0ZXJuIHdpdGggYW55ICogcmVwbGFjZWQgYnkgYSByZWFsIHZhbHVlLiBDcm9zcy1vcmlnaW4gZGVzdGluYXRpb25zIGFyZSByZWZ1c2VkLicKICAgICAgICB9CiAgICAgIH0KICAgIH0sCiAgICBleGVjdXRlOiBmdW5jdGlvbiAoYXJncykgewogICAgICB2YXIgcGF5bG9hZCA9IHsKICAgICAgICBzaXRlOiBfV0VCTUNQX0NBUFMuc2l0ZSB8fCBsb2NhdGlvbi5vcmlnaW4sCiAgICAgICAgY3VycmVudFBhdGg6IGxvY2F0aW9uLnBhdGhuYW1lLAogICAgICAgIHRvb2xfY291bnQ6IChfV0VCTUNQX0NBUFMuY2FwYWJpbGl0aWVzIHx8IFtdKS5sZW5ndGgsCiAgICAgICAgY2FwYWJpbGl0aWVzOiBfV0VCTUNQX0NBUFMuY2FwYWJpbGl0aWVzIHx8IFtdLAogICAgICAgIGJ5UGFnZTogX1dFQk1DUF9DQVBTLmJ5UGFnZSB8fCB7fQogICAgICB9OwogICAgICB2Y
22946XIgZGVzdCA9IChhcmdzICYmIGFyZ3MubmF2aWdhdGVUbykgPyBTdHJpbmcoYXJncy5uYXZpZ2F0ZVRvKS50cmltKCkgOiAnJzsKICAgICAgaWYgKGRlc3QpIHsKICAgICAgICB2YXIgdXJsID0gbnVsbDsKICAgICAgICB0cnkgeyB1cmwgPSBuZXcgVVJMKGRlc3QsIGxvY2F0aW9uLm9yaWdpbik7IH0KICAgICAgICBjYXRjaCAoZSkgeyBwYXlsb2FkLm5hdmlnYXRpb24gPSB7IHN0YXR1czogJ2ludmFsaWQnLCBuYXZpZ2F0ZVRvOiBkZXN0IH07IH0KICAgICAgICBpZiAodXJsKSB7CiAgICAgICAgICBpZiAodXJsLm9yaWdpbiAhPT0gbG9jYXRpb24ub3JpZ2luKSB7CiAgICAgICAgICAgIHBheWxvYWQubmF2aWdhdGlvbiA9IHsgc3RhdHVzOiAncmVmdXNlZCcsIHJlYXNvbjogJ2Nyb3NzLW9yaWdpbicsIG5hdmlnYXRlVG86IGRlc3QgfTsKICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIHBheWxvYWQubmF2aWdhdGlvbiA9IHsgc3RhdHVzOiAnbmF2aWdhdGVkJywgdG86IHVybC5wYXRobmFtZSArIHVybC5zZWFyY2ggfTsKICAgICAgICAgICAgdmFyIG91dCA9IHsgY29udGVudDogW3sgdHlwZTogJ3RleHQnLCB0ZXh0OiBKU09OLnN0cmluZ2lmeShwYXlsb2FkKSB9XSB9OwogICAgICAgICAgICBsb2NhdGlvbi5hc3NpZ24odXJsLmhyZWYpOwogICAgICAgICAgICByZXR1cm4gb3V0OwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfQogICAgICByZXR1cm4geyBjb250ZW50OiBbeyB0eXBlOiAndGV4dCcsIHRleHQ6IEpTT04uc3RyaW5naWZ5KHBheWxvYWQpIH1dIH07CiAgICB9CiAgfSk7CgogIC8qIC0tLS0gb3B0aW1pemVseS1zdWJtaXQtZGVtby1yZXF1ZXN0LCBvcHRpbWl6ZWx5LXN1Ym1pdC1kZW1vLXJlcXVlc3Qtc3VibWl0IC0tLS0gKi8KaWYgKCF3aW5kb3cuX193bWNwUmVnKSB7CiAgd2luZG93Ll9fd21jcFJlZyA9IChmdW5jdGlvbiAoKSB7CiAgICB2YXIgbGl2ZSA9IHt9OwogICAgZnVuY3Rpb24gZ2xvYlRvUmUoZ2xvYikgewogICAgICB2YXIgcmUgPSBnbG9iCiAgICAgICAgLnJlcGxhY2UoL1suK14ke30oKXxbXF1cXF0vZywgIlxcJCYiKQogICAgICAgIC5yZXBsYWNlKC9cL1wqXCovZywgIlx1MDAwMCIpCiAgICAgICAgLnJlcGxhY2UoL1wqL2csICJbXi9dKyIpCiAgICAgICAgLnNwbGl0KCJcdTAwMDAiKQogICAgICAgIC5qb2luKCIoLy4qKT8iKTsKICAgICAgcmV0dXJuIG5ldyBSZWdFeHAoIl4iICsgcmUgKyAiJCIpOwogICAgfQogICAgZnVuY3Rpb24gcmVjb25jaWxlKCkgewogICAgICB2YXIgcGF0aCA9IGxvY2F0aW9uLnBhdGhuYW1lOwogICAgICBPYmplY3Qua2V5cyhsaXZlKS5mb3JFYWNoKGZ1bmN0aW9uIChuYW1lKSB7CiAgICAgICAgdmFyIGUgPSBsaXZlW25hbWVdOwogICAgICAgIHZhciBtYXRjaCA9IGUucmUudGVzdChwYXRoKTsKICAgICAgICBpZiAobWF0Y2ggJiYgIWUub24pIHsgbWMucmVnaXN0ZXJUb29sKGUuZGVmKTsgZS5vbiA9IHRydWU7IH0KICAgICAgICBlbHNlIGlmICghbWF0Y2ggJiYgZS5vbiAmJiBtYy51bnJlZ2lzdGVyVG9vbCkgeyBtYy51bnJlZ2lzdGVyVG9vbChuYW1lKTsgZS5vbiA9IGZhbHNlOyB9CiAgICAgIH0pOwogICAgfQogICAgZnVuY3Rpb24gcmVnaXN0ZXJUb29sSWYoZ2xvYiwgZGVmKSB7CiAgICAgIGxpdmVbZGVmLm5hbWVdID0geyByZTogZ2xvYlRvUmUoZ2xvYiksIGRlZjogZGVmLCBvbjogZmFsc2UgfTsKICAgICAgcmVjb25jaWxlKCk7CiAgICB9CiAgICBpZiAoIXdpbmRvdy5fX3dtY3BSb3V0ZSkgewogICAgICB3aW5kb3cuX193bWNwUm91dGUgPSB0cnVlOwogICAgICBbInB1c2hTdGF0ZSIsICJyZXBsYWNlU3RhdGUiXS5mb3JFYWNoKGZ1bmN0aW9uIChtKSB7CiAgICAgICAgdmFyIG9yaWcgPSBoaXN0b3J5W21dOwogICAgICAgIGhpc3RvcnlbbV0gPSBmdW5jdGlvbiAoKSB7CiAgICAgICAgICB2Y
22946XIgciA9IG9yaWcuYXBwbHkodGhpcywgYXJndW1lbnRzKTsKICAgICAgICAgIHdpbmRvdy5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgid21jcDpyb3V0ZSIpKTsKICAgICAgICAgIHJldHVybiByOwogICAgICAgIH07CiAgICAgIH0pOwogICAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcigicG9wc3RhdGUiLCBmdW5jdGlvbiAoKSB7CiAgICAgICAgd2luZG93LmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCJ3bWNwOnJvdXRlIikpOwogICAgICB9KTsKICAgICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoIndtY3A6cm91dGUiLCByZWNvbmNpbGUpOwogICAgfQogICAgcmV0dXJuIHsgcmVnaXN0ZXJUb29sSWY6IHJlZ2lzdGVyVG9vbElmLCByZWNvbmNpbGU6IHJlY29uY2lsZSB9OwogIH0pKCk7Cn0KCmlmICghd2luZG93Ll9fb3B0aURlbW9Gb3JtKSB7CiAgd2luZG93Ll9fb3B0aURlbW9Gb3JtID0gKGZ1bmN0aW9uICgpIHsKICAgIHZhciBGT1JNX1NFTCA9ICJmb3JtLm9wdGktZm9ybSI7CgogICAgZnVuY3Rpb24gc2xlZXAobXMpIHsgcmV0dXJuIG5ldyBQcm9taXNlKGZ1bmN0aW9uIChyKSB7IHNldFRpbWVvdXQociwgbXMpOyB9KTsgfQoKICAgIGZ1bmN0aW9uIHRydW5jKHMsIG4pIHsKICAgICAgbiA9IG4gfHwgMzAwOwogICAgICBzID0gKHMgPT09IG51bGwgfHwgcyA9PT0gdW5kZWZpbmVkKSA/ICIiIDogU3RyaW5nKHMpOwogICAgICBzID0gcy5yZXBsYWNlKC9ccysvZywgIiAiKS50cmltKCk7CiAgICAgIHJldHVybiBzLmxlbmd0aCA+IG4gPyBzLnNsaWNlKDAsIG4pICsgIi4uLiIgOiBzOwogICAgfQoKICAgIGZ1bmN0aW9uIG91dChwYXlsb2FkKSB7CiAgICAgIHJldHVybiB7IGNvbnRlbnQ6IFt7IHR5cGU6ICJ0ZXh0IiwgdGV4dDogSlNPTi5zdHJpbmdpZnkocGF5bG9hZCkgfV0gfTsKICAgIH0KCiAgICBmdW5jdGlvbiBnZXRGb3JtKCkgeyByZXR1cm4gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihGT1JNX1NFTCk7IH0KCiAgICBmdW5jdGlvbiBzZXROYXRpdmUoZWwsIHZhbHVlKSB7CiAgICAgIHRyeSB7IGVsLmZvY3VzKCk7IH0gY2F0Y2ggKGUpIHt9CiAgICAgIHRyeSB7CiAgICAgICAgdmFyIHByb3RvID0gKGVsLnRhZ05hbWUgPT09ICJURVhUQVJFQSIpCiAgICAgICAgICA/IHdpbmRvdy5IVE1MVGV4dEFyZWFFbGVtZW50LnByb3RvdHlwZQogICAgICAgICAgOiB3aW5kb3cuSFRNTElucHV0RWxlbWVudC5wcm90b3R5cGU7CiAgICAgICAgdmFyIGQgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHByb3RvLCAidmFsdWUiKTsKICAgICAgICBpZiAoZCAmJiBkLnNldCkgeyBkLnNldC5jYWxsKGVsLCB2YWx1ZSk7IH0gZWxzZSB7IGVsLnZhbHVlID0gdmFsdWU7IH0KICAgICAgfSBjYXRjaCAoZSkgeyB0cnkgeyBlbC52YWx1ZSA9IHZhbHVlOyB9IGNhdGNoIChlMikge30gfQogICAgICB0cnkgeyBlbC5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgiaW5wdXQiLCB7IGJ1YmJsZXM6IHRydWUgfSkpOyB9IGNhdGNoIChlKSB7fQogICAgICB0cnkgeyBlbC5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgiY2hhbmdlIiwgeyBidWJibGVzOiB0cnVlIH0pKTsgfSBjYXRjaCAoZSkge30KICAgIH0KCiAgICBmdW5jdGlvbiBmaWxsRmllbGQoc2VsZWN0b3IsIHZhbHVlKSB7CiAgICAgIHZhciBlbCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3Ioc2VsZWN0b3IpOwogICAgICBpZiAoIWVsKSB7IHJldHVybiBmYWxzZTsgfQogICAgICB0cnkgeyBpZiAodHlwZW9mIGZpbGxJbnB1dCA9PT0gImZ1bmN0aW9uIikgeyBmaWxsSW5wdXQoc2VsZWN0b3IsIHZhbHVlKTsgfSB9IGNhdGNoIChlKSB7fQogICAgICBzZXROYXRpdmUoZWwsIHZhbHVlKTsKICAgICAgcmV0dXJuIHRydWU7CiAgICB9CgogICAgYXN5bmMgZnVuY3Rpb24gcG9sbEZvcihmbiwgdGltZW91dCwgc3RlcCkgewogICAgICB0aW1lb3V0ID0gdGltZW91dCB8fCA4MDAwOwogICAgICBzdGVwID0gc3RlcCB8fCAyMDA7CiAgICAgIHZhciBzdGFydCA9IERhdGUubm93KCk7CiAgICAgIHdoaWxlIChEYXRlLm5vdygpIC0gc3RhcnQgPCB0aW1lb3V0KSB7CiAgICAgICAgdmFyIHYgPSBudWxsOwogICAgICAgIHRyeSB7IHYgPSBmbigpOyB9IGNhdGNoIChlKSB7IHYgPSBudWxsOyB9CiAgICAgICAgaWYgKHYpIHsgcmV0dXJuIHY7IH0KICAgICAgICBhd2FpdCBzbGVlcChzdGVwKTsKICAgICAgfQogICAgICByZXR1cm4gbnVsbDsKICAgIH0KCiAgICBmdW5jdGlvbiByZWFkRXJyb3JzKCkgewogICAgICB2YXIgZiA9IGdldEZvcm0oKTsKICAgICAgaWYgKCFmKSB7IHJldHVybiBbXTsgfQogICAgICB2YXIgc2VlbiA9IHt9OwogICAgICB2YXIgbGlzdCA9IFtdOwogICAgICBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChmLnF1ZXJ5U2VsZWN0b3JBbGwoJ1thcmlhLWludmFsaWQ9InRydWUiXScpKS5mb3JFYWNoKGZ1bmN0aW9uIChlbCkgewogICAgICAgIHZhciBtc2cgPSAiIjsKICAgICAgICB2Y
22946XIgaWRzID0gKGVsLmdldEF0dHJpYnV0ZSgiYXJpYS1kZXNjcmliZWRieSIpIHx8ICIiKS5zcGxpdCgvXHMrLyk7CiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBpZHMubGVuZ3RoOyBpKyspIHsKICAgICAgICAgIGlmICghaWRzW2ldKSB7IGNvbnRpbnVlOyB9CiAgICAgICAgICB2YXIgcyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGlkc1tpXSk7CiAgICAgICAgICBpZiAocyAmJiAocy50ZXh0Q29udGVudCB8fCAiIikudHJpbSgpKSB7IG1zZyA9IChzLnRleHRDb250ZW50IHx8ICIiKS50cmltKCk7IGJyZWFrOyB9CiAgICAgICAgfQogICAgICAgIGlmICghbXNnICYmIGVsLmNsb3Nlc3QpIHsKICAgICAgICAgIHZhciB3ID0gZWwuY2xvc2VzdCgiLm9wdGlmb3Jtc3RleHRib3hlbGVtZW50LCAub3B0aWZvcm1zc2VsZWN0aW9uZWxlbWVudCwgLm9wdGlmb3Jtc3RleHRhcmVhZWxlbWVudCIpOwogICAgICAgICAgaWYgKHcpIHsKICAgICAgICAgICAgdmFyIHNwID0gdy5xdWVyeVNlbGVjdG9yKCJzcGFuIik7CiAgICAgICAgICAgIGlmIChzcCAmJiAhc3AuY2xhc3NMaXN0LmNvbnRhaW5zKCJoaWRkZW4iKSkgeyBtc2cgPSAoc3AudGV4dENvbnRlbnQgfHwgIiIpLnRyaW0oKTsgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgICB2YXIgbmFtZSA9IGVsLmdldEF0dHJpYnV0ZSgibmFtZSIpIHx8IGVsLmlkIHx8ICJmaWVsZCI7CiAgICAgICAgdmFyIGtleSA9IG5hbWUgKyAifCIgKyBtc2c7CiAgICAgICAgaWYgKG1zZyAmJiAhc2VlbltrZXldKSB7IHNlZW5ba2V5XSA9IDE7IGxpc3QucHVzaCh0cnVuYyhuYW1lICsgIjogIiArIG1zZywgMzAwKSk7IH0KICAgICAgfSk7CiAgICAgIEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGYucXVlcnlTZWxlY3RvckFsbCgKICAgICAgICAiLm9wdGlmb3Jtc3RleHRib3hlbGVtZW50ID4gc3BhbiwgLm9wdGlmb3Jtc3NlbGVjdGlvbmVsZW1lbnQgPiBzcGFuLCAub3B0aWZvcm1zdGV4dGFyZWFlbGVtZW50ID4gc3BhbiIKICAgICAgKSkuZm9yRWFjaChmdW5jdGlvbiAoc3ApIHsKICAgICAgICBpZiAoc3AuY2xhc3NMaXN0LmNvbnRhaW5zKCJoaWRkZW4iKSkgeyByZXR1cm47IH0KICAgICAgICB2Y
22946XIgdCA9IChzcC50ZXh0Q29udGVudCB8fCAiIikudHJpbSgpOwogICAgICAgIGlmICh0ICYmICFzZWVuWyJ8IiArIHRdKSB7IHNlZW5bInwiICsgdF0gPSAxOyBsaXN0LnB1c2godHJ1bmModCwgMzAwKSk7IH0KICAgICAgfSk7CiAgICAgIHJldHVybiBsaXN0LnNsaWNlKDAsIDEwKTsKICAgIH0KCiAgICBmdW5jdGlvbiBvcHRpb25UZXh0cyhub2RlcykgewogICAgICByZXR1cm4gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwobm9kZXMpLnNsaWNlKDAsIDEwKS5tYXAoZnVuY3Rpb24gKG8pIHsKICAgICAgICByZXR1cm4gdHJ1bmMoby50ZXh0Q29udGVudCwgNjApOwogICAgICB9KTsKICAgIH0KCiAgICBhc3luYyBmdW5jdGlvbiBzZWxlY3RDb3VudHJ5KG5hbWUpIHsKICAgICAgdmFyIGlucHV0ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiI2NvdW50cnktaW5wdXQiKTsKICAgICAgdmFyIGhpZGRlbiA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoRk9STV9TRUwgKyAnIGlucHV0W25hbWU9ImNvdW50cnkiXScpOwogICAgICBpZiAoIWlucHV0KSB7CiAgICAgICAgcmV0dXJuIHsgb2s6IGZhbHNlLCBvYnNlcnZlZDogZmFsc2UsIHJlYXNvbjogImNvdW50cnkgY29tYm9ib3ggI2NvdW50cnktaW5wdXQgd2FzIG5vdCBmb3VuZCBpbiB0aGUgZm9ybSIgfTsKICAgICAgfQogICAgICB0cnkgeyBpbnB1dC5mb2N1cygpOyB9IGNhdGNoIChlKSB7fQogICAgICB0cnkgeyBpbnB1dC5jbGljaygpOyB9IGNhdGNoIChlKSB7fQogICAgICBzZXROYXRpdmUoaW5wdXQsICIiKTsKICAgICAgYXdhaXQgc2xlZXAoMTIwKTsKICAgICAgc2V0TmF0aXZlKGlucHV0LCBuYW1lKTsKICAgICAgdHJ5IHsKICAgICAgICBpbnB1dC5kaXNwYXRjaEV2ZW50KG5ldyBLZXlib2FyZEV2ZW50KCJrZXlkb3duIiwgeyBidWJibGVzOiB0cnVlLCBrZXk6IG5hbWUuc2xpY2UoLTEpIH0pKTsKICAgICAgICBpbnB1dC5kaXNwYXRjaEV2ZW50KG5ldyBLZXlib2FyZEV2ZW50KCJrZXl1cCIsIHsgYnViYmxlczogdHJ1ZSwga2V5OiBuYW1lLnNsaWNlKC0xKSB9KSk7CiAgICAgIH0gY2F0Y2ggKGUpIHt9CiAgICAgIHZhciBub2RlcyA9IGF3YWl0IHBvbGxGb3IoZnVuY3Rpb24gKCkgewogICAgICAgIHZhciBuID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnI2NvdW50cnktbWVudSBbcm9sZT0ib3B0aW9uIl0nKTsKICAgICAgICBpZiAoIW4ubGVuZ3RoKSB7IG4gPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCdbcm9sZT0ibGlzdGJveCJdIFtyb2xlPSJvcHRpb24iXScpOyB9CiAgICAgICAgcmV0dXJuIG4ubGVuZ3RoID8gbiA6IG51bGw7CiAgICAgIH0sIDYwMDApOwogICAgICBpZiAoIW5vZGVzKSB7CiAgICAgICAgdHJ5IHsKICAgICAgICAgIHZhciB0b2dnbGUgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCIjY291bnRyeS10b2dnbGUtYnV0dG9uIik7CiAgICAgICAgICBpZiAodG9nZ2xlKSB7IHRvZ2dsZS5jbGljaygpOyB9CiAgICAgICAgfSBjYXRjaCAoZSkge30KICAgICAgICBub2RlcyA9IGF3YWl0IHBvbGxGb3IoZnVuY3Rpb24gKCkgewogICAgICAgICAgdmFyIG4gPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcjY291bnRyeS1tZW51IFtyb2xlPSJvcHRpb24iXScpOwogICAgICAgICAgcmV0dXJuIG4ubGVuZ3RoID8gbiA6IG51bGw7CiAgICAgICAgfSwgNDAwMCk7CiAgICAgIH0KICAgICAgaWYgKCFub2RlcykgewogICAgICAgIHJldHVybiB7IG9rOiBmYWxzZSwgb2JzZXJ2ZWQ6IGZhbHNlLCByZWFzb246ICJubyBbcm9sZT1vcHRpb25dIGl0ZW1zIGV2ZXIgcmVuZGVyZWQgaW4gI2NvdW50cnktbWVudSBhZnRlciB0eXBpbmcgJyIgKyB0cnVuYyhuYW1lLCA2MCkgKyAiJyIgfTsKICAgICAgfQogICAgICB2YXIgYXJyID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwobm9kZXMpOwogICAgICB2YXIgd2FudCA9IFN0cmluZyhuYW1lKS50cmltKCkudG9Mb3dlckNhc2UoKTsKICAgICAgZnVuY3Rpb24gdHh0KG8pIHsgcmV0dXJuIChvLnRleHRDb250ZW50IHx8ICIiKS50cmltKCkudG9Mb3dlckNhc2UoKTsgfQogICAgICB2YXIgcGljayA9IGFyci5maWx0ZXIoZnVuY3Rpb24gKG8pIHsgcmV0dXJuIHR4dChvKSA9PT0gd2FudDsgfSlbMF0KICAgICAgICB8fCBhcnIuZmlsdGVyKGZ1bmN0aW9uIChvKSB7IHJldHVybiB0eHQobykuaW5kZXhPZih3YW50KSA9PT0gMDsgfSlbMF0KICAgICAgICB8fCBhcnIuZmlsdGVyKGZ1bmN0aW9uIChvKSB7IHJldHVybiB0eHQobykuaW5kZXhPZih3YW50KSA+IC0xOyB9KVswXTsKICAgICAgaWYgKCFwaWNrKSB7CiAgICAgICAgcmV0dXJuIHsKICAgICAgICAgIG9rOiBmYWxzZSwgb2JzZXJ2ZWQ6IHRydWUsCiAgICAgICAgICByZWFzb246ICJubyBjb3VudHJ5IG9wdGlvbiBtYXRjaGVkICciICsgdHJ1bmMobmFtZSwgNjApICsgIiciLAogICAgICAgICAgYXZhaWxhYmxlT3B0aW9uczogb3B0aW9uVGV4dHMoYXJyKQogICAgICAgIH07CiAgICAgIH0KICAgICAgdmFyIGxhYmVsID0gdHJ1bmMocGljay50ZXh0Q29udGVudCwgNjApOwogICAgICB0cnkgeyBwaWNrLmRpc3BhdGNoRXZlbnQobmV3IE1vdXNlRXZlbnQoIm1vdXNlZG93biIsIHsgYnViYmxlczogdHJ1ZSB9KSk7IH0gY2F0Y2ggKGUpIHt9CiAgICAgIHRyeSB7IHBpY2suZGlzcGF0Y2hFdmVudChuZXcgTW91c2VFdmVudCgibW91c2V1cCIsIHsgYnViYmxlczogdHJ1ZSB9KSk7IH0gY2F0Y2ggKGUpIHt9CiAgICAgIHRyeSB7IHBpY2suY2xpY2soKTsgfSBjYXRjaCAoZSkge30KICAgICAgdmFyIHZhbCA9IGF3YWl0IHBvbGxGb3IoZnVuY3Rpb24gKCkgewogICAgICAgIHZhciBoID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihGT1JNX1NFTCArICcgaW5wdXRbbmFtZT0iY291bnRyeSJdJyk7CiAgICAgICAgcmV0dXJuIChoICYmIGgudmFsdWUpID8gaC52YWx1ZSA6IG51bGw7CiAgICAgIH0sIDMwMDAsIDE1MCk7CiAgICAgIGhpZGRlbiA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoRk9STV9TRUwgKyAnIGlucHV0W25hbWU9ImNvdW50cnkiXScpOwogICAgICByZXR1cm4gewogICAgICAgIG9rOiAhIXZhbCwKICAgICAgICBvYnNlcnZlZDogdHJ1ZSwKICAgICAgICBwaWNrZWQ6IGxhYmVsLAogICAgICAgIGhpZGRlblZhbHVlOiB2YWwgfHwgKGhpZGRlbiA/IGhpZGRlbi52YWx1ZSA6ICIiKSwKICAgICAgICB2aXNpYmxlVmFsdWU6IGlucHV0LnZhbHVlIHx8ICIiLAogICAgICAgI
22946HJlYXNvbjogdmFsID8gdW5kZWZpbmVkIDogImNsaWNrZWQgb3B0aW9uICciICsgbGFiZWwgKyAiJyBidXQgdGhlIGhpZGRlbiBpbnB1dFtuYW1lPWNvdW50cnldIHN0YXllZCBlbXB0eSIKICAgICAgfTsKICAgIH0KCiAgICByZXR1cm4gewogICAgICBGT1JNX1NFTDogRk9STV9TRUwsCiAgICAgIHNsZWVwOiBzbGVlcCwKICAgICAgdHJ1bmM6IHRydW5jLAogICAgICBvdXQ6IG91dCwKICAgICAgZ2V0Rm9ybTogZ2V0Rm9ybSwKICAgICAgc2V0TmF0aXZlOiBzZXROYXRpdmUsCiAgICAgIGZpbGxGaWVsZDogZmlsbEZpZWxkLAogICAgICBwb2xsRm9yOiBwb2xsRm9yLAogICAgICByZWFkRXJyb3JzOiByZWFkRXJyb3JzLAogICAgICBzZWxlY3RDb3VudHJ5OiBzZWxlY3RDb3VudHJ5CiAgICB9OwogIH0pKCk7Cn0KCndpbmRvdy5fX3dtY3BSZWcucmVnaXN0ZXJUb29sSWYoIi8qKi9nZXQtc3RhcnRlZC8qKiIsIHsKICBuYW1lOiAib3B0aW1pemVseS1zdWJtaXQtZGVtby1yZXF1ZXN0IiwKICBkZXNjcmlwdGlvbjogIkZpbGxzIHRoZSBPcHRpbWl6ZWx5ICdTdGFydCB5b3VyIGpvdXJuZXkgd2l0aCBPcHRpbWl6ZWx5JyBkZW1vIC8gZ2V0LXN0YXJ0ZWQgbGVhZCBmb3JtIChmb3JtLm9wdGktZm9ybSkgb24gaHR0cHM6Ly93d3cub3B0aW1pemVseS5jb20vZ2V0LXN0YXJ0ZWQvIDogaXQgc2Nyb2xscyB0byB0aGUgZm9ybSwgaGlnaGxpZ2h0cyBpdCwgYW5kIGRyaXZlcyB0aGUgUmVhY3QtY29udHJvbGxlZCBpbnB1dHMgYW5kIHRoZSB0eXBlLXRvLWZpbHRlciBDb3VudHJ5IGNvbWJvYm94ICgjY291bnRyeS1pbnB1dCAtPiAjY291bnRyeS1tZW51KSBleGFjdGx5IGxpa2UgYSB1c2VyIHdvdWxkLiBJdCBkb2VzIE5PVCBzdWJtaXQuIFJldHVybnMgdGhlIHZhbHVlcyBpdCByZWFkcyBCQUNLIG91dCBvZiB0aGUgbGl2ZSBET006IHtzdGF0dXMsIGFjdGlvbiwgZmllbGRzIHtmaXJzdE5hbWUsIGxhc3ROYW1lLCBlbWFpbCwgY29tcGFueSwgdGl0bGUsIHBob25lLCBjb3VudHJ5IChoaWRkZW4gYXV0aG9yaXRhdGl2ZSB2YWx1ZSksIGNvdW50cnlJbnB1dCwgcHJvZHVjdEludGVyZXN0fSwgbWlzc2luZyBbcmVxdWlyZWQgZmllbGRzIHN0aWxsIGVtcHR5XSwgY291bnRyeVNlbGVjdGlvbiB7cGlja2VkLCBoaWRkZW5WYWx1ZSwgYXZhaWxhYmxlT3B0aW9uc30sIHJlYWR5LCBzdWJtaXRUb29sTmFtZSwgdXJsLCBzdW1tYXJ5fS4gQ2FsbCBvcHRpbWl6ZWx5LXN1Ym1pdC1kZW1vLXJlcXVlc3Qtc3VibWl0IGFmdGVyd2FyZHMgdG8gYWN0dWFsbHkgc2VuZCBpdC4iLAogIGlucHV0U2NoZW1hOiB7CiAgICB0eXBlOiAib2JqZWN0IiwKICAgIHByb3BlcnRpZXM6IHsKICAgICAgZmlyc3ROYW1lOiB7IHR5cGU6ICJzdHJpbmciLCBkZXNjcmlwdGlvbjogIlZpc2l0b3IncyBmaXJzdCBuYW1lIChyZXF1aXJlZCBieSB0aGUgZm9ybSdzIEpTIHZhbGlkYXRpb24pLiIgfSwKICAgICAgbGFzdE5hbWU6IHsgdHlwZTogInN0cmluZyIsIGRlc2NyaXB0aW9uOiAiVmlzaXRvcidzIGxhc3QgbmFtZSAocmVxdWlyZWQgYnkgdGhlIGZvcm0ncyBKUyB2YWxpZGF0aW9uKS4iIH0sCiAgICAgIGVtYWlsOiB7IHR5cGU6ICJzdHJpbmciLCBkZXNjcmlwdGlvbjogIldvcmsgZW1haWwgYWRkcmVzcywgZS5nLiBqYW5lLmRvZUBjb21wYW55LmNvbSAocmVxdWlyZWQpLiIgfSwKICAgICAgY29tcGFueTogeyB0eXBlOiAic3RyaW5nIiwgZGVzY3JpcHRpb246ICJDb21wYW55IC8gb3JnYW5pemF0aW9uIG5hbWUgKHJlcXVpcmVkKS4iIH0sCiAgICAgIHRpdGxlOiB7IHR5cGU6ICJzdHJpbmciLCBkZXNjcmlwdGlvbjogIkpvYiB0aXRsZSwgZS5nLiAnRGlyZWN0b3Igb2YgRGlnaXRhbCBNYXJrZXRpbmcnIChvcHRpb25hbCkuIiB9LAogICAgICBwaG9uZTogeyB0eXBlOiAic3RyaW5nIiwgZGVzY3JpcHRpb246ICJQaG9uZSBudW1iZXIgaW4gaW50ZXJuYXRpb25hbCBmb3JtYXQsIGUuZy4gKzEgMjM0IDU2NyA4OTAxIChvcHRpb25hbCkuIiB9LAogICAgICBjb3VudHJ5OiB7IHR5cGU6ICJzdHJpbmciLCBkZXNjcmlwdGlvbjogIkNvdW50cnkgbmFtZSBhcyBpdCBhcHBlYXJzIGluIHRoZSBDb3VudHJ5IGNvbWJvYm94LCBlLmcuICdVbml0ZWQgU3RhdGVzJywgJ1VuaXRlZCBLaW5nZG9tJywgJ0dlcm1hbnknLiBUaGUgb3B0aW9uIGxpc3QgaXMgcmVuZGVyZWQgb25seSBhZnRlciB0eXBpbmcsIHNvIHBhc3MgdGhlIGZ1bGwgRW5nbGlzaCBjb3VudHJ5IG5hbWU7IGlmIGl0IGRvZXMgbm90IG1hdGNoLCB0aGUgdG9vbCByZXR1cm5zIGF2YWlsYWJsZU9wdGlvbnMgcmVhZCBmcm9tIHRoZSBvcGVuIG1lbnUuIiB9LAogICAgICBwcm9kdWN0SW50ZXJlc3Q6IHsgdHlwZTogInN0cmluZyIsIGRlc2NyaXB0aW9uOiAiRnJlZSB0ZXh0IGZvciAnV2hhdCBwcm9kdWN0cyBhcmUgeW91IGludGVyZXN0ZWQgaW4/JyAob3B0aW9uYWwpLCBlLmcuICdXZWIgRXhwZXJpbWVudGF0aW9uIGFuZCBDb250ZW50IE1hbmFnZW1lbnQnLiIgfQogICAgfSwKICAgIHJlcXVpcmVkOiBbImZpcnN0TmFtZSIsICJsYXN0TmFtZSIsICJlbWFpbCIsICJjb21wYW55IiwgImNvdW50cnkiXQogIH0sCiAgYXN5bmMgZXhlY3V0ZShhcmdzKSB7CiAgICB2YXIgSCA9IHdpbmRvdy5fX29wdGlEZW1vRm9ybTsKICAgIGFyZ3MgPSBhcmdzIHx8IHt9OwogICAgdHJ5IHsKICAgICAgdmFyIGZvcm0gPSBILmdldEZvcm0oKTsKICAgICAgaWYgKCFmb3JtKSB7CiAgICAgICAgdHJ5IHsgYXdhaXQgd2FpdEZvckVsZW1lbnQoImZvcm0ub3B0aS1mb3JtIik7IH0gY2F0Y2ggKGUpIHt9CiAgICAgICAgZm9ybSA9IEguZ2V0Rm9ybSgpOwogICAgICB9CiAgICAgIGlmICghZm9ybSkgewogICAgICAgIHJldHVybiBILm91dCh7CiAgICAgICAgICBzdGF0dXM6ICJlcnJvciIsCiAgICAgICAgICBhY3Rpb246ICJsb2NhdGUgdGhlIGRlbW8gcmVxdWVzdCBmb3JtIiwKICAgICAgICAgIHJlYXNvbjogIk5vIGVsZW1lbnQgbWF0Y2hpbmcgZm9ybS5vcHRpLWZvcm0gZXhpc3RzIG9uIHRoaXMgcGFnZSIsCiAgICAgICAgICBuZXh0U3RlcDogIm5hdmlnYXRlIHRvIGh0dHBzOi8vd3d3Lm9wdGltaXplbHkuY29tL2dldC1zdGFydGVkLyBhbmQgY2FsbCBvcHRpbWl6ZWx5LXN1Ym1pdC1kZW1vLXJlcXVlc3QgYWdhaW4iLAogICAgICAgICAgb2JzZXJ2ZWQ6IGZhbHNlLAogICAgICAgICAgdXJsOiBsb2NhdGlvbi5ocmVmLAogICAgICAgICAgc3VtbWFyeTogIkRlbW8gcmVxdWVzdCBmb3JtIG5vdCBwcmVzZW50IG9uIHRoaXMgcGFnZSIKICAgICAgICB9KTsKICAgICAgfQoKICAgICAgdHJ5IHsgZm9ybS5zY3JvbGxJbnRvVmlldyh7IGJlaGF2aW9yOiAic21vb3RoIiwgYmxvY2s6ICJjZW50ZXIiIH0pOyB9IGNhdGNoIChlKSB7fQogICAgICB0cnkgeyBmb3JtLnN0eWxlLm91dGxpbmUgPSAiM3B4IHNvbGlkICMwMDM3ZmYiOyBmb3JtLnN0eWxlLm91dGxpbmVPZmZzZXQgPSAiNHB4IjsgfSBjYXRjaCAoZSkge30KICAgICAgYXdhaXQgSC5zbGVlcCgyNTApOwoKICAgICAgdmFyIHRleHRNYXAgPSB7CiAgICAgICAgZmlyc3ROYW1lOiAnZm9ybS5vcHRpLWZvcm0gaW5wdXRbbmFtZT0iZmlyc3ROYW1lIl0nLAogICAgICAgIGxhc3ROYW1lOiAnZm9ybS5vcHRpLWZvcm0gaW5wdXRbbmFtZT0ibGFzdE5hbWUiXScsCiAgICAgICAgZW1haWw6ICdmb3JtLm9wdGktZm9ybSBpbnB1dFtuYW1lPSJlbWFpbCJdJywKICAgICAgICBjb21wYW5
229465OiAnZm9ybS5vcHRpLWZvcm0gaW5wdXRbbmFtZT0iY29tcGFueSJdJywKICAgICAgICB0aXRsZTogJ2Zvcm0ub3B0aS1mb3JtIGlucHV0W25hbWU9InRpdGxlIl0nLAogICAgICAgIHBob25lOiAnZm9ybS5vcHRpLWZvcm0gaW5wdXRbbmFtZT0icGhvbmUiXScKICAgICAgfTsKICAgICAgdmFyIG5vdEZvdW5kID0gW107CiAgICAgIE9iamVjdC5rZXlzKHRleHRNYXApLmZvckVhY2goZnVuY3Rpb24gKGtleSkgewogICAgICAgIHZhciB2ID0gYXJnc1trZXldOwogICAgICAgIGlmICh2ID09PSB1bmRlZmluZWQgfHwgdiA9PT0gbnVsbCB8fCB2ID09PSAiIikgeyByZXR1cm47IH0KICAgICAgICBpZiAoIUguZmlsbEZpZWxkKHRleHRNYXBba2V5XSwgU3RyaW5nKHYpKSkgeyBub3RGb3VuZC5wdXNoKGtleSk7IH0KICAgICAgfSk7CgogICAgICBpZiAoYXJncy5wcm9kdWN0SW50ZXJlc3QpIHsKICAgICAgICBpZiAoIUguZmlsbEZpZWxkKCdmb3JtLm9wdGktZm9ybSB0ZXh0YXJlYVtuYW1lPSJPdGhlcl9BZGRpdGlvbmFsX0luZm9ybWF0aW9uX19jIl0nLCBTdHJpbmcoYXJncy5wcm9kdWN0SW50ZXJlc3QpKSkgewogICAgICAgICAgbm90Rm91bmQucHVzaCgicHJvZHVjdEludGVyZXN0Iik7CiAgICAgICAgfQogICAgICB9CgogICAgICB2YXIgY291bnRyeVNlbGVjdGlvbiA9IG51bGw7CiAgICAgIGlmIChhcmdzLmNvdW50cnkpIHsKICAgICAgICBjb3VudHJ5U2VsZWN0aW9uID0gYXdhaXQgSC5zZWxlY3RDb3VudHJ5KFN0cmluZyhhcmdzLmNvdW50cnkpKTsKICAgICAgICBpZiAoY291bnRyeVNlbGVjdGlvbiAmJiBjb3VudHJ5U2VsZWN0aW9uLmF2YWlsYWJsZU9wdGlvbnMpIHsKICAgICAgICAgIHJldHVybiBILm91dCh7CiAgICAgICAgICAgIHN0YXR1czogImVycm9yIiwKICAgICAgICAgICAgYWN0aW9uOiAic2VsZWN0IGEgY291bnRyeSBpbiB0aGUgY29tYm9ib3giLAogICAgICAgICAgICByZWFzb246IGNvdW50cnlTZWxlY3Rpb24ucmVhc29uLAogICAgICAgICAgICBhdmFpbGFibGVPcHRpb25zOiB7IGNvdW50cnk6IGNvdW50cnlTZWxlY3Rpb24uYXZhaWxhYmxlT3B0aW9ucyB9LAogICAgICAgICAgICBuZXh0U3RlcDogImNhbGwgb3B0aW1pemVseS1zdWJtaXQtZGVtby1yZXF1ZXN0IGFnYWluIHdpdGggY291bnRyeSBzZXQgdG8gb25lIG9mIGF2YWlsYWJsZU9wdGlvbnMuY291bnRyeSIsCiAgICAgICAgICAgIG9ic2VydmVkOiB0cnVlLAogICAgICAgICAgICB1cmw6IGxvY2F0aW9uLmhyZWYsCiAgICAgICAgICAgIHN1bW1hcnk6ICJDb3VudHJ5ICciICsgSC50cnVuYyhhcmdzLmNvdW50cnksIDYwKSArICInIGRpZCBub3QgbWF0Y2ggYW55IG9wdGlvbiBpbiB0aGUgb3BlbiBtZW51IgogICAgICAgICAgfSk7CiAgICAgICAgfQogICAgICB9CgogICAgICB0cnkgeyBhd2FpdCB3YWl0Rm9yRWxlbWVudCgnZm9ybS5vcHRpLWZvcm0gaW5wdXRbbmFtZT0iZW1haWwiXScpOyB9IGNhdGNoIChlKSB7fQogICAgICBhd2FpdCBILnNsZWVwKDIwMCk7CgogICAgICBmdW5jdGlvbiByZWFkVmFsKHNlbCkgewogICAgICAgIHZhciBlbCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3Ioc2VsKTsKICAgICAgICByZXR1cm4gZWwgPyBILnRydW5jKGVsLnZhbHVlLCAzMDApIDogbnVsbDsKICAgICAgfQogICAgICB2YXIgZmllbGRzID0gewogICAgICAgIGZpcnN0TmFtZTogcmVhZFZhbCgnZm9ybS5vcHRpLWZvcm0gaW5wdXRbbmFtZT0iZmlyc3ROYW1lIl0nKSwKICAgICAgICBsYXN0TmFtZTogcmVhZFZhbCgnZm9ybS5vcHRpLWZvcm0gaW5wdXRbbmFtZT0ibGFzdE5hbWUiXScpLAogICAgICAgIGVtYWlsOiByZWFkVmFsKCdmb3JtLm9wdGktZm9ybSBpbnB1dFtuYW1lPSJlbWFpbCJdJyksCiAgICAgICAgY29tcGFueTogcmVhZFZhbCgnZm9ybS5vcHRpLWZvcm0gaW5wdXRbbmFtZT0iY29tcGFueSJdJyksCiAgICAgICAgdGl0bGU6IHJlYWRWYWwoJ2Zvcm0ub3B0aS1mb3JtIGlucHV0W25hbWU9InRpdGxlIl0nKSwKICAgICAgICBwaG9uZTogcmVhZFZhbCgnZm9ybS5vcHRpLWZvcm0gaW5wdXRbbmFtZT0icGhvbmUiXScpLAogICAgICAgIGNvdW50cnk6IHJlYWRWYWwoJ2Zvcm0ub3B0aS1mb3JtIGlucHV0W25hbWU9ImNvdW50cnkiXScpLAogICAgICAgIGNvdW50cnlJbnB1dDogcmVhZFZhbCgiI2NvdW50cnktaW5wdXQiKSwKICAgICAgICBwcm9kdWN0SW50ZXJlc3Q6IHJlYWRWYWwoJ2Zvcm0ub3B0aS1mb3JtIHRleHRhcmVhW25hbWU9Ik90aGVyX0FkZGl0aW9uYWxfSW5mb3JtYXRpb25fX2MiXScpCiAgICAgIH07CgogICAgICB2YXIgcmVxdWlyZWRGaWVsZHMgPSBbImZpcnN0TmFtZSIsICJsYXN0TmFtZSIsICJlbWFpbCIsICJjb21wYW55IiwgImNvdW50cnkiXTsKICAgICAgdmFyIG1pc3NpbmcgPSByZXF1aXJlZEZpZWxkcy5maWx0ZXIoZnVuY3Rpb24gKGspIHsKICAgICAgICByZXR1cm4gIWZpZWxkc1trXTsKICAgICAgfSk7CiAgICAgIG5vdEZvdW5kLmZvckVhY2goZnVuY3Rpb24gKGspIHsgaWYgKG1pc3NpbmcuaW5kZXhPZihrKSA9PT0gLTEpIHsgbWlzc2luZy5wdXNoKGspOyB9IH0pOwoKICAgICAgdmFyIHJlYWR5ID0gbWlzc2luZy5sZW5ndGggPT09IDA7CiAgICAgIHZhciBwYXlsb2FkID0gewogICAgICAgIHN0YXR1czogcmVhZHkgPyAib2siIDogInBhcnRpYWwiLAogICAgICAgIGFjdGlvbjogImZpbGxlZCBhbmQgaGlnaGxpZ2h0ZWQgdGhlIE9wdGltaXplbHkgZ2V0LXN0YXJ0ZWQgZGVtbyByZXF1ZXN0IGZvcm0iLAogICAgICAgIG9ic2VydmVkOiB0cnVlLAogICAgICAgIGZpZWxkczogZmllbGRzLAogICAgICAgIG1pc3Npbmc6IG1pc3NpbmcsCiAgICAgICAgcmVhZHk6IHJlYWR5LAogICAgICAgIHN1Ym1pdFRvb2xOYW1lOiAib3B0aW1pemVseS1zdWJtaXQtZGVtby1yZXF1ZXN0LXN1Ym1pdCIsCiAgICAgICAgdmFsaWRhdGlvbkVycm9yczogSC5yZWFkRXJyb3JzKCksCiAgICAgICAgdXJsOiBsb2NhdGlvbi5ocmVmLAogICAgICAgIHN1bW1hcnk6IHJlYWR5CiAgICAgICAgICA/ICJBbGwgcmVxdWlyZWQgZGVtby1yZXF1ZXN0IGZpZWxkcyBhcmUgcG9wdWxhdGVkOyBjYWxsIG9wdGltaXplbHktc3VibWl0LWRlbW8tcmVxdWVzdC1zdWJtaXQgdG8gc2VuZCBpdC4iCiAgICAgICAgICA6ICJGb3JtIGZpbGxlZCBidXQgc3RpbGwgbWlzc2luZzogIiArIG1pc3Npbmcuam9pbigiLCAiKQogICAgICB9OwogICAgICBpZiAoY291bnRyeVNlbGVjdGlvbikgewogICAgICAgI
22946HBheWxvYWQuY291bnRyeVNlbGVjdGlvbiA9IHsKICAgICAgICAgIG9rOiAhIWNvdW50cnlTZWxlY3Rpb24ub2ssCiAgICAgICAgICBwaWNrZWQ6IGNvdW50cnlTZWxlY3Rpb24ucGlja2VkIHx8IG51bGwsCiAgICAgICAgICBoaWRkZW5WYWx1ZTogY291bnRyeVNlbGVjdGlvbi5oaWRkZW5WYWx1ZSB8fCAiIiwKICAgICAgICAgIHJlYXNvbjogY291bnRyeVNlbGVjdGlvbi5yZWFzb24gfHwgbnVsbAogICAgICAgIH07CiAgICAgIH0KICAgICAgcmV0dXJuIEgub3V0KHBheWxvYWQpOwogICAgfSBjYXRjaCAoZXJyKSB7CiAgICAgIHJldHVybiBILm91dCh7CiAgICAgICAgc3RhdHVzOiAiZXJyb3IiLAogICAgICAgIGFjdGlvbjogImZpbGwgdGhlIGRlbW8gcmVxdWVzdCBmb3JtIiwKICAgICAgICByZWFzb246IEgudHJ1bmMoZXJyICYmIGVyci5tZXNzYWdlID8gZXJyLm1lc3NhZ2UgOiBTdHJpbmcoZXJyKSwgMzAwKSwKICAgICAgICBuZXh0U3RlcDogInJlbG9hZCBodHRwczovL3d3dy5vcHRpbWl6ZWx5LmNvbS9nZXQtc3RhcnRlZC8gYW5kIHRyeSBvcHRpbWl6ZWx5LXN1Ym1pdC1kZW1vLXJlcXVlc3QgYWdhaW4iLAogICAgICAgIG9ic2VydmVkOiBmYWxzZSwKICAgICAgICB1cmw6IGxvY2F0aW9uLmhyZWYsCiAgICAgICAgc3VtbWFyeTogIlVuZXhwZWN0ZWQgZXJyb3Igd2hpbGUgZmlsbGluZyB0aGUgZGVtbyByZXF1ZXN0IGZvcm0iCiAgICAgIH0pOwogICAgfQogIH0KfSk7Cgp3aW5kb3cuX193bWNwUmVnLnJlZ2lzdGVyVG9vbElmKCIvKiovZ2V0LXN0YXJ0ZWQvKioiLCB7CiAgbmFtZTogIm9wdGltaXplbHktc3VibWl0LWRlbW8tcmVxdWVzdC1zdWJtaXQiLAogIGRlc2NyaXB0aW9uOiAiU3VibWl0cyB0aGUgYWxyZWFkeS1maWxsZWQgT3B0aW1pemVseSBkZW1vIC8gZ2V0LXN0YXJ0ZWQgbGVhZCBmb3JtIG9uIGh0dHBzOi8vd3d3Lm9wdGltaXplbHkuY29tL2dldC1zdGFydGVkLyBieSBjbGlja2luZyBpdHMgcmVhbCBzdWJtaXQgYnV0dG9uIChtYXRjaGVkIHN0cnVjdHVyYWxseSBhcyBmb3JtLm9wdGktZm9ybSAub3B0aWZvcm1zc3VibWl0ZWxlbWVudCBidXR0b25bdHlwZT1zdWJtaXRdLCBuZXZlciBieSBsYWJlbCwgYmVjYXVzZSB0aGUgbGFiZWwgaXMgdW5kZXIgYW4gQS9CIHRlc3QpLCB0aGVuIHdhaXRzIGFuZCBvYnNlcnZlcyB0aGUgcGFnZSBvdXRjb21lLiBSZXR1cm5zIHtzdGF0dXMsIGFjdGlvbiwgY2xpY2tlZCwgdmFsaWRhdGlvbkVycm9ycyBbbWVzc2FnZXMgcmVhZCBvdXQgb2YgdGhlIGZvcm1dLCBjb25maXJtYXRpb24gW3RoYW5rLXlvdSB0ZXh0IGlmIHRoZSBmb3JtIHdhcyByZXBsYWNlZF0sIGZvcm1TdGlsbFByZXNlbnQsIG9ic2VydmVkLCB1cmwsIHN1bW1hcnl9LiBzdGF0dXMgaXMgJ2Vycm9yJyB3aGVuZXZlciB2YWxpZGF0aW9uRXJyb3JzIGlzIG5vbi1lbXB0eS4gUnVuIG9wdGltaXplbHktc3VibWl0LWRlbW8tcmVxdWVzdCBmaXJzdCB0byBwb3B1bGF0ZSB0aGUgZmllbGRzLiIsCiAgaW5wdXRTY2hlbWE6IHsKICAgIHR5cGU6ICJvYmplY3QiLAogICAgcHJvcGVydGllczogewogICAgICBjb25maXJtOiB7IHR5cGU6ICJib29sZWFuIiwgZGVzY3JpcHRpb246ICJPcHRpb25hbCBhY2tub3dsZWRnZW1lbnQgdGhhdCB0aGUgdmlzaXRvcidzIGRldGFpbHMgYXJlIGNvcnJlY3QgYW5kIHRoZSBsZWFkIG1heSBiZSBzZW50IHRvIE9wdGltaXplbHkncyBzYWxlcyB0ZWFtLiBTdWJtaXNzaW9uIHByb2NlZWRzIGVpdGhlciB3YXkuIiB9CiAgICB9CiAgfSwKICBhc3luYyBleGVjdXRlKGFyZ3MpIHsKICAgIHZhciBIID0gd2luZG93Ll9fb3B0aURlbW9Gb3JtOwogICAgdHJ5IHsKICAgICAgdmFyIGZvcm0gPSBILmdldEZvcm0oKTsKICAgICAgaWYgKCFmb3JtKSB7CiAgICAgICAgcmV0dXJuIEgub3V0KHsKICAgICAgICAgIHN0YXR1czogImVycm9yIiwKICAgICAgICAgIGFjdGlvbjogImxvY2F0ZSB0aGUgZGVtbyByZXF1ZXN0IGZvcm0gdG8gc3VibWl0IiwKICAgICAgICAgIHJlYXNvbjogIk5vIGVsZW1lbnQgbWF0Y2hpbmcgZm9ybS5vcHRpLWZvcm0gZXhpc3RzIG9uIHRoaXMgcGFnZSAoaXQgbWF5IGFscmVhZHkgaGF2ZSBiZWVuIHN1Ym1pdHRlZCwgb3IgeW91IGFyZSBvbiBhbm90aGVyIHBhZ2UpIiwKICAgICAgICAgIG5leHRTdGVwOiAibmF2aWdhdGUgdG8gaHR0cHM6Ly93d3cub3B0aW1pemVseS5jb20vZ2V0LXN0YXJ0ZWQvLCBydW4gb3B0aW1pemVseS1zdWJtaXQtZGVtby1yZXF1ZXN0LCB0aGVuIHN1Ym1pdCIsCiAgICAgICAgICBvYnNlcnZlZDogZmFsc2UsCiAgICAgICAgICB1cmw6IGxvY2F0aW9uLmhyZWYsCiAgICAgICAgICBzdW1tYXJ5OiAiRGVtbyByZXF1ZXN0IGZvcm0gbm90IHByZXNlbnQsIG5vdGhpbmcgc3VibWl0dGVkIgogICAgICAgIH0pOwogICAgICB9CiAgICAgIHZhciBidG4gPSBmb3JtLnF1ZXJ5U2VsZWN0b3IoJy5vcHRpZm9ybXNzdWJtaXRlbGVtZW50IGJ1dHRvblt0eXBlPSJzdWJtaXQiXScpCiAgICAgICAgfHwgZm9ybS5xdWVyeVNlbGVjdG9yKCdidXR0b25bdHlwZT0ic3VibWl0Il0nKQogICAgICAgIHx8IGZvcm0ucXVlcnlTZWxlY3RvcignaW5wdXRbdHlwZT0ic3VibWl0Il0nKTsKICAgICAgaWYgKCFidG4pIHsKICAgICAgICByZXR1cm4gSC5vdXQoewogICAgICAgICAgc3RhdHVzOiAiZXJyb3IiLAogICAgICAgICAgYWN0aW9uOiAibG9jYXRlIHRoZSBzdWJtaXQgYnV0dG9uIiwKICAgICAgICAgIHJlYXNvbjogIk5vIGJ1dHRvblt0eXBlPXN1Ym1pdF0gZm91bmQgaW5zaWRlIGZvcm0ub3B0aS1mb3JtIC5vcHRpZm9ybXNzdWJtaXRlbGVtZW50IiwKICAgICAgICAgIG5leHRTdGVwOiAicmVsb2FkIGh0dHBzOi8vd3d3Lm9wdGltaXplbHkuY29tL2dldC1zdGFydGVkLyBhbmQgdHJ5IGFnYWluIG9uY2UgdGhlIGZvcm0gaGFzIGZ1bGx5IHJlbmRlcmVkIiwKICAgICAgICAgIG9ic2VydmVkOiB0cnVlLAogICAgICAgICAgdXJsOiBsb2NhdGlvbi5ocmVmLAogICAgICAgICAgc3VtbWFyeTogIlN1Ym1pdCBidXR0b24gbm90IGZvdW5kOyBmb3JtIHdhcyBub3Qgc3VibWl0dGVkIgogICAgICAgIH0pOwogICAgICB9CgogICAgICB2YXIgY29udGFpbmVyID0gZm9ybS5wYXJlbnRFbGVtZW50OwogICAgICB2Y
22946XIgcHJlRmlsbGVkID0ge307CiAgICAgIFsiZmlyc3ROYW1lIiwgImxhc3ROYW1lIiwgImVtYWlsIiwgImNvbXBhbnkiLCAiY291bnRyeSJdLmZvckVhY2goZnVuY3Rpb24gKG4pIHsKICAgICAgICB2YXIgZWwgPSBmb3JtLnF1ZXJ5U2VsZWN0b3IoJ2lucHV0W25hbWU9IicgKyBuICsgJyJdJyk7CiAgICAgICAgcHJlRmlsbGVkW25dID0gZWwgPyAoZWwudmFsdWUgPyB0cnVlIDogZmFsc2UpIDogZmFsc2U7CiAgICAgIH0pOwoKICAgICAgdHJ5IHsgYnRuLnNjcm9sbEludG9WaWV3KHsgYmVoYXZpb3I6ICJzbW9vdGgiLCBibG9jazogImNlbnRlciIgfSk7IH0gY2F0Y2ggKGUpIHt9CiAgICAgIGF3YWl0IEguc2xlZXAoMjAwKTsKICAgICAgYnRuLmNsaWNrKCk7CgogICAgICB0cnkgewogICAgICAgIGF3YWl0IHdhaXRGb3JFbGVtZW50KCdmb3JtLm9wdGktZm9ybSBbYXJpYS1pbnZhbGlkPSJ0cnVlIl0nKTsKICAgICAgfSBjYXRjaCAoZSkge30KCiAgICAgIHZhciBvdXRjb21lID0gYXdhaXQgSC5wb2xsRm9yKGZ1bmN0aW9uICgpIHsKICAgICAgICB2YXIgZXJycyA9IEgucmVhZEVycm9ycygpOwogICAgICAgIGlmIChlcnJzLmxlbmd0aCkgeyByZXR1cm4geyBraW5kOiAiZXJyb3JzIiwgZXJyb3JzOiBlcnJzIH07IH0KICAgICAgICB2YXIgZiA9IEguZ2V0Rm9ybSgpOwogICAgICAgIGlmICghZiB8fCAhZG9jdW1lbnQuY29udGFpbnMoZikgfHwgZi5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS5oZWlnaHQgPT09IDApIHsKICAgICAgICAgIHJldHVybiB7IGtpbmQ6ICJnb25lIiB9OwogICAgICAgIH0KICAgICAgICB2Y
22946XIgdHh0ID0gKGNvbnRhaW5lciAmJiBjb250YWluZXIudGV4dENvbnRlbnQgPyBjb250YWluZXIudGV4dENvbnRlbnQgOiAiIikudG9Mb3dlckNhc2UoKTsKICAgICAgICBpZiAodHh0LmluZGV4T2YoInRoYW5rIHlvdSIpID4gLTEgfHwgdHh0LmluZGV4T2YoIndlIGhhdmUgcmVjZWl2ZWQiKSA+IC0xIHx8IHR4dC5pbmRleE9mKCJiZSBpbiB0b3VjaCIpID4gLTEpIHsKICAgICAgICAgIHJldHVybiB7IGtpbmQ6ICJjb25maXJtZWQiIH07CiAgICAgICAgfQogICAgICAgIHJldHVybiBudWxsOwogICAgICB9LCAxNTAwMCwgMzAwKTsKCiAgICAgIHZhciB2YWxpZGF0aW9uRXJyb3JzID0gSC5yZWFkRXJyb3JzKCk7CiAgICAgIHZhciBzdGlsbFByZXNlbnQgPSAhIUguZ2V0Rm9ybSgpICYmIGRvY3VtZW50LmNvbnRhaW5zKEguZ2V0Rm9ybSgpKTsKICAgICAgdmFyIGNvbmZpcm1hdGlvbiA9ICIiOwogICAgICBpZiAob3V0Y29tZSAmJiAob3V0Y29tZS5raW5kID09PSAiZ29uZSIgfHwgb3V0Y29tZS5raW5kID09PSAiY29uZmlybWVkIikpIHsKICAgICAgICBjb25maXJtYXRpb24gPSBILnRydW5jKGNvbnRhaW5lciAmJiBjb250YWluZXIuaW5uZXJUZXh0ID8gY29udGFpbmVyLmlubmVyVGV4dCA6IGRvY3VtZW50LmJvZHkuaW5uZXJUZXh0LCAzMDApOwogICAgICB9CgogICAgICBpZiAodmFsaWRhdGlvbkVycm9ycy5sZW5ndGgpIHsKICAgICAgICByZXR1cm4gSC5vdXQoewogICAgICAgICAgc3RhdHVzOiAiZXJyb3IiLAogICAgICAgICAgYWN0aW9uOiAiY2xpY2tlZCB0aGUgZGVtbyByZXF1ZXN0IHN1Ym1pdCBidXR0b24gYW5kIHJlYWQgdGhlIGZvcm0ncyB2YWxpZGF0aW9uIG91dHB1dCIsCiAgICAgICAgICBjbGlja2VkOiB0cnVlLAogICAgICAgICAgdmFsaWRhdGlvbkVycm9yczogdmFsaWRhdGlvbkVycm9ycywKICAgICAgICAgIGNvbmZpcm1hdGlvbjogIiIsCiAgICAgICAgICBmb3JtU3RpbGxQcmVzZW50OiBzdGlsbFByZXNlbnQsCiAgICAgICAgICBmaWVsZHNQb3B1bGF0ZWRCZWZvcmVTdWJtaXQ6IHByZUZpbGxlZCwKICAgICAgICAgIG9ic2VydmVkOiB0cnVlLAogICAgICAgICAgcmVhc29uOiAidGhlIGZvcm0gcmVqZWN0ZWQgdGhlIHN1Ym1pc3Npb24gY2xpZW50LXNpZGUiLAogICAgICAgICAgbmV4dFN0ZXA6ICJjYWxsIG9wdGltaXplbHktc3VibWl0LWRlbW8tcmVxdWVzdCBhZ2FpbiB3aXRoIGNvcnJlY3RlZCB2YWx1ZXMgZm9yIHRoZSBmaWVsZHMgbmFtZWQgaW4gdmFsaWRhdGlvbkVycm9ycywgdGhlbiBzdWJtaXQiLAogICAgICAgICAgdXJsOiBsb2NhdGlvbi5ocmVmLAogICAgICAgICAgc3VtbWFyeTogdmFsaWRhdGlvbkVycm9ycy5sZW5ndGggKyAiIHZhbGlkYXRpb24gZXJyb3IocykgYmxvY2tlZCB0aGUgZGVtbyByZXF1ZXN0IgogICAgICAgIH0pOwogICAgICB9CgogICAgICBpZiAoIW91dGNvbWUpIHsKICAgICAgICByZXR1cm4gSC5vdXQoewogICAgICAgICAgc3RhdHVzOiAicGFydGlhbCIsCiAgICAgICAgICBhY3Rpb246ICJjbGlja2VkIHRoZSBkZW1vIHJlcXVlc3Qgc3VibWl0IGJ1dHRvbiBidXQgbmV2ZXIgc2F3IGEgY29uZmlybWF0aW9uIG9yIGFuIGVycm9yIiwKICAgICAgICAgIGNsaWNrZWQ6IHRydWUsCiAgICAgICAgICB2YWxpZGF0aW9uRXJyb3JzOiBbXSwKICAgICAgICAgIGNvbmZpcm1hdGlvbjogIiIsCiAgICAgICAgICBmb3JtU3RpbGxQcmVzZW50OiBzdGlsbFByZXNlbnQsCiAgICAgICAgICBmaWVsZHNQb3B1bGF0ZWRCZWZvcmVTdWJtaXQ6IHByZUZpbGxlZCwKICAgICAgICAgIG9ic2VydmVkOiBmYWxzZSwKICAgICAgICAgIG5leHRTdGVwOiAiY2hlY2sgdGhlIHBhZ2UgbWFudWFsbHk7IHRoZSByZXF1ZXN0IG1heSBzdGlsbCBiZSBwcm9jZXNzaW5nIiwKICAgICAgICAgIHVybDogbG9jYXRpb24uaHJlZiwKICAgICAgICAgIHN1bW1hcnk6ICJTdWJtaXQgY2xpY2tlZCwgYnV0IG5vIGNvbmZpcm1hdGlvbiBvciB2YWxpZGF0aW9uIGVycm9yIGFwcGVhcmVkIHdpdGhpbiAxNXMiCiAgICAgICAgfSk7CiAgICAgIH0KCiAgICAgIHJldHVybiBILm91dCh7CiAgICAgICAgc3RhdHVzOiAib2siLAogICAgICAgIGFjdGlvbjogImNsaWNrZWQgdGhlIGRlbW8gcmVxdWVzdCBzdWJtaXQgYnV0dG9uIGFuZCBvYnNlcnZlZCB0aGUgY29uZmlybWF0aW9uIHN0YXRlIiwKICAgICAgICBjbGlja2VkOiB0cnVlLAogICAgICAgIHZhbGlkYXRpb25FcnJvcnM6IFtdLAogICAgICAgIGNvbmZpcm1hdGlvbjogY29uZmlybWF0aW9uLAogICAgICAgIGZvcm1TdGlsbFByZXNlbnQ6IHN0aWxsUHJlc2VudCwKICAgICAgICBmaWVsZHNQb3B1bGF0ZWRCZWZvcmVTdWJtaXQ6IHByZUZpbGxlZCwKICAgICAgICBvYnNlcnZlZDogdHJ1ZSwKICAgICAgICB1cmw6IGxvY2F0aW9uLmhyZWYsCiAgICAgICAgc3VtbWFyeTogIkRlbW8gcmVxdWVzdCBzdWJtaXR0ZWQ7IHRoZSBmb3JtIHdhcyByZXBsYWNlZCBieSBhIGNvbmZpcm1hdGlvbiBzdGF0ZSIKICAgICAgfSk7CiAgICB9IGNhdGNoIChlcnIpIHsKICAgICAgcmV0dXJuIEgub3V0KHsKICAgICAgICBzdGF0dXM6ICJlcnJvciIsCiAgICAgICAgYWN0aW9uOiAic3VibWl0IHRoZSBkZW1vIHJlcXVlc3QgZm9ybSIsCiAgICAgICAgcmVhc29uOiBILnRydW5jKGVyciAmJiBlcnIubWVzc2FnZSA/IGVyci5tZXNzYWdlIDogU3RyaW5nKGVyciksIDMwMCksCiAgICAgICAgbmV4dFN0ZXA6ICJyZWxvYWQgaHR0cHM6Ly93d3cub3B0aW1pemVseS5jb20vZ2V0LXN0YXJ0ZWQvLCByZS1maWxsIHdpdGggb3B0aW1pemVseS1zdWJtaXQtZGVtby1yZXF1ZXN0IGFuZCBzdWJtaXQgYWdhaW4iLAogICAgICAgIG9ic2VydmVkOiBmYWxzZSwKICAgICAgICB1cmw6IGxvY2F0aW9uLmhyZWYsCiAgICAgICAgc3VtbWFyeTogIlVuZXhwZWN0ZWQgZXJyb3Igd2hpbGUgc3VibWl0dGluZyB0aGUgZGVtbyByZXF1ZXN0IGZvcm0iCiAgICAgIH0pOwogICAgfQogIH0KfSk7CgogIC8qIFJlZ2lzdGVyIHRoZSBhY3RpdmUgcm91dGUncyB0b29scywgdGhlbiB3YXRjaCBmb3IgU1BBIHJvdXRlIGNoYW5nZXMuICovCiAgdHJ5IHsgX3N5bmNSb3V0ZSgpOyB9IGNhdGNoIChlKSB7IC8qIG5ldmVyIGJyZWFrIHJlZ2lzdHJhdGlvbiAqLyB9CiAgdHJ5IHsgX2luc3RhbGxSb3V0ZXIoKTsgfSBjYXRjaCAoZSkgeyAvKiBuZXZlciBicmVhayByZWdpc3RyYXRpb24gKi8gfQogIC8qIFNpZ25hbCB0aGF0IHR
22946oaXMgcGFnZSdzIHRvb2wgc2V0IGlzIG5vdyByZWdpc3RlcmVkLiAqLwogIF9kaXNwYXRjaFRvb2xjaGFuZ2UoKTsKfSkoKTsK')));document.head.appendChild(s);})();
22947}, "id": "8a7e4ad8-e2eb-4142-a0e7-60f6f27c6a1e", "dependencies": []}]}, {"viewId": "4506880884604928", "changes": [{"type": "custom_code", "value": function($){(function(){var s=document.createElement('script');s.type='text/javascript';s.text=decodeURIComponent(escape(atob('LyogV2ViTUNQIHRvb2xzIGZvciBvcHRpbWl6ZWx5IC0gZ2VuZXJhdGVkIGJ5IHRoZSBPcGFsIFdlYk1DUCBTaXRlIEFuYWx5emVyICovCihmdW5jdGlvbiAoKSB7CiAgJ3VzZSBzdHJpY3QnOwogIHZhciBfbWNSYXcgPSAodHlwZW9mIGRvY3VtZW50ICE9PSAndW5kZWZpbmVkJyAmJiBkb2N1bWVudC5tb2RlbENvbnRleHQpIHx8CiAgICAgICAgICAgICAgICh0eXBlb2YgbmF2aWdhdG9yICE9PSAndW5kZWZpbmVkJyAmJiBuYXZpZ2F0b3IubW9kZWxDb250ZXh0KTsKICBpZiAoIV9tY1JhdyB8fCB0eXBlb2YgX21jUmF3LnJlZ2lzdGVyVG9vbCAhPT0gJ2Z1bmN0aW9uJykgewogICAgY29uc29sZS53YXJuKCdbV2ViTUNQXSBObyBtb2RlbENvbnRleHQgYXZhaWxhYmxlOyB0b29scyBub3QgcmVnaXN0ZXJlZC4nKTsKICAgIHJldHVybjsKICB9CgogIGZ1bmN0aW9uIHRyYWNrV2ViTUNQVG9vbChuYW1lKSB7CiAgICB0cnkgewogICAgICB3aW5kb3cub3B0aW1pemVseSA9IHdpbmRvdy5vcHRpbWl6ZWx5IHx8IFtdOwogICAgICB3aW5kb3cub3B0aW1pemVseS5wdXNoKHsKICAgICAgICB0eXBlOiAnZXZlbnQnLCBldmVudE5hbWU6ICdXZWJNQ1BUb29sJywKICAgICAgICBwcm9wZXJ0aWVzOiB7IFdlYk1DUFRvb2w6IG5hbWUgfHwgJ3Vua25vd24nIH0KICAgICAgfSk7CiAgICB9IGNhdGNoIChlKSB7IC8qIHRyYWNraW5nIG11c3QgbmV2ZXIgYnJlYWsgYSB0b29sICovIH0KICB9CgogIGZ1bmN0aW9uIF93cmFwSGFuZGxlcihmbiwgdG9vbE5hbWUpIHsKICAgIHJldHVybiBmdW5jdGlvbiAoKSB7IHRyYWNrV2ViTUNQVG9vbCh0b29sTmFtZSk7IHJldHVybiBmbi5hcHBseSh0aGlzLCBhcmd1bWVudHMpOyB9OwogIH0KCiAgZnVuY3Rpb24gX3BhdGhNYXRjaGVzKHBhdHRlcm4pIHsKICAgIGlmICghcGF0dGVybiB8fCBwYXR0ZXJuID09PSAnLyoqJykgeyByZXR1cm4gdHJ1ZTsgfQogICAgdmFyIHBhdGggPSBsb2NhdGlvbi5wYXRobmFtZS5yZXBsYWNlKC9cLyskLywgJycpIHx8ICcvJzsKICAgIHZhciBwYXQgID0gU3RyaW5nKHBhdHRlcm4pLnJlcGxhY2UoL1wvKyQvLCAnJykgfHwgJy8nOwogICAgaWYgKHBhdCA9PT0gJy8nKSB7IHJldHVybiBwYXRoID09PSAnLyc7IH0KICAgIHZhciByeCA9IHBhdC5zcGxpdCgnKionKS5tYXAoZnVuY3Rpb24gKGNodW5rKSB7CiAgICAgIHJldHVybiBjaHVuay5zcGxpdCgnKicpLm1hcChmdW5jdGlvbiAobGl0KSB7CiAgICAgICAgcmV0dXJuIGxpdC5yZXBsYWNlKC9bLiorP14ke30oKXxbXF1cXF0vZywgJ1xcJCYnKTsKICAgICAgfSkuam9pbignW14vXSonKTsKICAgIH0pLmpvaW4oJy4qJyk7CiAgICB0cnkgeyByZXR1cm4gbmV3IFJlZ0V4cCgnXicgKyByeCArICckJykudGVzdChwYXRoKTsgfSBjYXRjaCAoZSkgeyByZXR1cm4gdHJ1ZTsgfQogIH0KCiAgLyogUm91dGUtc2NvcGVkIHRvb2xzIGFyZSBSRUNPUkRFRCBoZXJlLCBub3QgcmVnaXN0ZXJlZCBlYWdlcmx5LCBzbyBhIHJlYWwgU1BBCiAgICAgKGNsaWVudC1zaWRlIHJvdXRlIGNoYW5nZXMgdmlhIGhpc3RvcnkucHVzaFN0YXRlL3JlcGxhY2VTdGF0ZSwgV0lUSE9VVCBhCiAgICAgZnVsbCBwYWdlIHJlbG9hZCkgY2FuIHJlZ2lzdGVyIGEgcm91dGUncyB0b29scyB0aGUgbW9tZW50IGl0cyBwYXRoIGJlY29tZXMKICAgICBhY3RpdmUuIFRoZSBtb2RlbENvbnRleHQgQVBJIGV4cG9zZXMgb25seSByZWdpc3RlclRvb2wgLS0gdGhlcmUgaXMgTk8KICAgICB1bnJlZ2lzdGVyIC0tIHNvIHJlZ2lzdHJhdGlvbiBpcyByZWdpc3Rlci1vbmNlLWFjY3VtdWxhdGU6IF9zeW5jUm91dGUgaXMKICAgICBpZGVtcG90ZW50IHZpYSBlYWNoIGVudHJ5J3MgYHJlZ2lzdGVyZWRgIGZsYWcsIGFuZCB0b29scyBmcm9tIGFuCiAgICAgYWxyZWFkeS12aXNpdGVkIHJvdXRlIHN0YXkgcmVnaXN0ZXJlZCBhZnRlciBhIHNvZnQgbmF2aWdhdGlvbiAoaGFybWxlc3M6CiAgICAgZXZlcnkgaGFuZGxlciBndWFyZHMgaXRzIERPTSB3b3JrIHdpdGggd2FpdEZvckVsZW1lbnQgKyB0aW1lb3V0KS4gKi8KICB2YXIgX3JvdXRlUmVnaXN0cnkgPSBbXTsKCiAgZnVuY3Rpb24gX3N5bmNSb3V0ZSgpIHsKICAgIHZhciBjaGFuZ2VkID0gZmFsc2U7CiAgICBmb3IgKHZhciBpID0gMDsgaSA8IF9yb3V0ZVJlZ2lzdHJ5Lmxlbmd0aDsgaSsrKSB7CiAgICAgIHZhciBlbnRyeSA9IF9yb3V0ZVJlZ2lzdHJ5W2ldOwogICAgICBpZiAoIWVudHJ5LnJlZ2lzdGVyZWQgJiYgX3BhdGhNYXRjaGVzKGVudHJ5LnBhdHRlcm4pKSB7CiAgICAgICAgdHJ5IHsKICAgICAgICAgIG1jLnJlZ2lzdGVyVG9vbChlbnRyeS5kZXNjcmlwdG9yKTsKICAgICAgICAgIGVudHJ5LnJlZ2lzdGVyZWQgPSB0cnVlOwogICAgICAgICAgY2hhbmdlZCA9IHRydWU7CiAgICAgICAgfSBjYXRjaCAoZSkgeyAvKiBvbmUgYmFkIHRvb2wgbXVzdCBuZXZlciBicmVhayB0aGUgcmVzdCAqLyB9CiAgICAgIH0KICAgIH0KICAgIHJldHVybiBjaGFuZ2VkOwogIH0KCiAgLyogVGVsbCBhIGxpc3RlbmluZyBhZ2VudCB0aGUgcGFnZSdzIHRvb2wgc2V0IGNoYW5nZWQgc28gaXQgcmUtcmVhZHMgdGhlIG1vZGVsCiAgICAgY29udGV4dC4gRmlyZWQgYWZ0ZXIgdGhlIGluaXRpYWwgcmVnaXN0cmF0aW9uIEFORCBhZnRlciBldmVyeSBzb2Z0IHJvdXRlCiAgICAgY2hhbmdlLiBCZXN0LWVmZm9ydDsgbXVzdCBuZXZlciBicmVhayByZWdpc3RyYXRpb24uICovCiAgZnVuY3Rpb24gX2Rpc3BhdGNoVG9vbGNoYW5nZSgpIHsKICAgIHRyeSB7CiAgICAgIGlmICh0eXBlb2YgZG9jdW1lbnQgIT09ICd1bmRlZmluZWQnICYmIHR5cGVvZiBFdmVudCA9PT0gJ2Z1bmN0aW9uJykgewogICAgICAgIGRvY3VtZW50LmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCd0b29sY2hhbmdlJykpOwogICAgICB9CiAgICB9IGNhdGNoIChlKSB7IC8qIG5ldmVyIGJyZWFrIHJlZ2lzdHJhdGlvbiAqLyB9CiAgfQoKICAvKiBXaXJlIHRoZSBTUEEgcm91dGVyIGhvb2sgT05D
22947RTogd3JhcCBoaXN0b3J5LnB1c2hTdGF0ZS9yZXBsYWNlU3RhdGUgYW5kCiAgICAgbGlzdGVuIGZvciBwb3BzdGF0ZS9oYXNoY2hhbmdlLCBzbyBhIGNsaWVudC1zaWRlIHJvdXRlIGNoYW5nZSByZS1ydW5zCiAgICAgX3N5bmNSb3V0ZSAocmVnaXN0ZXJpbmcgYW55IHRvb2xzIHdob3NlIHBhdHRlcm4gbm93IG1hdGNoZXMpIGFuZCByZS1zaWduYWxzCiAgICAgdG9vbGNoYW5nZS4gT24gYW4gTVBBIHRoaXMgc2ltcGx5IG5ldmVyIGZpcmVzIC0tIHRoZSBmdWxsIHJlbG9hZCByZS1ydW5zIHRoZQogICAgIHdob2xlIElJRkUgaW5zdGVhZC4gKi8KICB2YXIgX3JvdXRlckluc3RhbGxlZCA9IGZhbHNlOwogIGZ1bmN0aW9uIF9pbnN0YWxsUm91dGVyKCkgewogICAgaWYgKF9yb3V0ZXJJbnN0YWxsZWQgfHwgdHlwZW9mIHdpbmRvdyA9PT0gJ3VuZGVmaW5lZCcgfHwgIXdpbmRvdy5oaXN0b3J5KSB7IHJldHVybjsgfQogICAgX3JvdXRlckluc3RhbGxlZCA9IHRydWU7CiAgICBmdW5jdGlvbiBfb25Sb3V0ZUNoYW5nZSgpIHsKICAgICAgdHJ5IHsgX3N5bmNSb3V0ZSgpOyB9IGNhdGNoIChlKSB7IC8qIG5ldmVyIGJyZWFrICovIH0KICAgICAgX2Rpc3BhdGNoVG9vbGNoYW5nZSgpOwogICAgfQogICAgWydwdXNoU3RhdGUnLCAncmVwbGFjZVN0YXRlJ10uZm9yRWFjaChmdW5jdGlvbiAobSkgewogICAgICB2YXIgb3JpZyA9IHdpbmRvdy5oaXN0b3J5W21dOwogICAgICBpZiAodHlwZW9mIG9yaWcgIT09ICdmdW5jdGlvbicpIHsgcmV0dXJuOyB9CiAgICAgIHdpbmRvdy5oaXN0b3J5W21dID0gZnVuY3Rpb24gKCkgewogICAgICAgIHZhciByID0gb3JpZy5hcHBseSh0aGlzLCBhcmd1bWVudHMpOwogICAgICAgIHRyeSB7IF9vblJvdXRlQ2hhbmdlKCk7IH0gY2F0Y2ggKGUpIHsgLyogbmV2ZXIgYnJlYWsgbmF2aWdhdGlvbiAqLyB9CiAgICAgICAgcmV0dXJuIHI7CiAgICAgIH07CiAgICB9KTsKICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdwb3BzdGF0ZScsIF9vblJvdXRlQ2hhbmdlKTsKICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdoYXNoY2hhbmdlJywgX29uUm91dGVDaGFuZ2UpOwogIH0KCiAgdmFyIG1jID0gewogICAgcmVnaXN0ZXJUb29sOiBmdW5jdGlvbiAoZGVzY3JpcHRvcikgewogICAgICBpZiAoZGVzY3JpcHRvciAmJiB0eXBlb2YgZGVzY3JpcHRvciA9PT0gJ29iamVjdCcpIHsKICAgICAgICB2Y
22947XIgX25hbWUgPSBkZXNjcmlwdG9yLm5hbWU7CiAgICAgICAgT2JqZWN0LmtleXMoZGVzY3JpcHRvcikuZm9yRWFjaChmdW5jdGlvbiAoaykgewogICAgICAgICAgaWYgKHR5cGVvZiBkZXNjcmlwdG9yW2tdID09PSAnZnVuY3Rpb24nKSB7CiAgICAgICAgICAgIGRlc2NyaXB0b3Jba10gPSBfd3JhcEhhbmRsZXIoZGVzY3JpcHRvcltrXSwgX25hbWUpOwogICAgICAgICAgfQogICAgICAgIH0pOwogICAgICB9CiAgICAgIHJldHVybiBfbWNSYXcucmVnaXN0ZXJUb29sKGRlc2NyaXB0b3IpOwogICAgfSwKICAgIC8qIFJlY29yZCBhIHJvdXRlLXNjb3BlZCB0b29sLCB0aGVuIHJlZ2lzdGVyIGl0IGlmIGl0cyBwYXRoIGlzIGFscmVhZHkKICAgICAgIGFjdGl2ZS4gTGF0ZXIgc29mdC1uYXZpZ2F0aW9ucyBwaWNrIGl0IHVwIHZpYSBfc3luY1JvdXRlIChTUEEgc3VwcG9ydCkuICovCiAgICByZWdpc3RlclRvb2xJZjogZnVuY3Rpb24gKHBhdHRlcm4sIGRlc2NyaXB0b3IpIHsKICAgICAgX3JvdXRlUmVnaXN0cnkucHVzaCh7IHBhdHRlcm46IHBhdHRlcm4sIGRlc2NyaXB0b3I6IGRlc2NyaXB0b3IsIHJlZ2lzdGVyZWQ6IGZhbHNlIH0pOwogICAgICBfc3luY1JvdXRlKCk7CiAgICAgIHJldHVybiBudWxsOwogICAgfQogIH07CgogIGZ1bmN0aW9uIHdhaXRGb3JFbGVtZW50KHNlbGVjdG9yLCB0aW1lb3V0KSB7CiAgICB0aW1lb3V0ID0gdGltZW91dCB8fCA1MDAwOwogICAgcmV0dXJuIG5ldyBQcm9taXNlKGZ1bmN0aW9uIChyZXNvbHZlLCByZWplY3QpIHsKICAgICAgdmFyIGZvdW5kID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihzZWxlY3Rvcik7CiAgICAgIGlmIChmb3VuZCkgeyByZXR1cm4gcmVzb2x2ZShmb3VuZCk7IH0KICAgICAgdmFyIG9icyA9IG5ldyBNdXRhdGlvbk9ic2VydmVyKGZ1bmN0aW9uICgpIHsKICAgICAgICB2Y
22947XIgZWwgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKHNlbGVjdG9yKTsKICAgICAgICBpZiAoZWwpIHsgb2JzLmRpc2Nvbm5lY3QoKTsgY2xlYXJUaW1lb3V0KHRpbWVyKTsgcmVzb2x2ZShlbCk7IH0KICAgICAgfSk7CiAgICAgIG9icy5vYnNlcnZlKGRvY3VtZW50LmRvY3VtZW50RWxlbWVudCwgeyBjaGlsZExpc3Q6IHRydWUsIHN1YnRyZWU6IHRydWUgfSk7CiAgICAgIHZhciB0aW1lciA9IHNldFRpbWVvdXQoZnVuY3Rpb24gKCkgewogICAgICAgIG9icy5kaXNjb25uZWN0KCk7CiAgICAgICAgcmVqZWN0KG5ldyBFcnJvcignW1dlYk1DUF0gdGltZW91dCB3YWl0aW5nIGZvciAnICsgc2VsZWN0b3IpKTsKICAgICAgfSwgdGltZW91dCk7CiAgICB9KTsKICB9CgogIGZ1bmN0aW9uIHNldE5hdGl2ZVZhbHVlKGVsLCB2YWx1ZSkgewogICAgdmFyIGRlc2MgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKE9iamVjdC5nZXRQcm90b3R5cGVPZihlbCksICd2YWx1ZScpOwogICAgaWYgKGRlc2MgJiYgZGVzYy5zZXQpIHsgZGVzYy5zZXQuY2FsbChlbCwgdmFsdWUpOyB9IGVsc2UgeyBlbC52YWx1ZSA9IHZhbHVlOyB9CiAgfQoKICBhc3luYyBmdW5jdGlvbiBmaWxsSW5wdXQoc2VsZWN0b3IsIHZhbHVlKSB7CiAgICB2YXIgZWwgPSBhd2FpdCB3YWl0Rm9yRWxlbWVudChzZWxlY3Rvcik7CiAgICBlbC5mb2N1cygpOwogICAgc2V0TmF0aXZlVmFsdWUoZWwsIHZhbHVlKTsKICAgIGVsLmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCdpbnB1dCcsICB7IGJ1YmJsZXM6IHRydWUgfSkpOwogICAgZWwuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoJ2NoYW5nZScsIHsgYnViYmxlczogdHJ1ZSB9KSk7CiAgICByZXR1cm4gZWw7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBmaWxsU2VsZWN0KHNlbGVjdG9yLCB2YWx1ZSkgewogICAgdmFyIGVsID0gYXdhaXQgd2FpdEZvckVsZW1lbnQoc2VsZWN0b3IpOwogICAgZWwudmFsdWUgPSB2YWx1ZTsKICAgIGlmIChlbC5zZWxlY3RlZEluZGV4ID09PSAtMSkgewogICAgICB2YXIgb3B0ID0gQXJyYXkucHJvdG90eXBlLmZpbmQuY2FsbChlbC5vcHRpb25zLCBmdW5jdGlvbiAobykgewogICAgICAgIHJldHVybiBvLnRleHRDb250ZW50LnRyaW0oKS50b0xvd2VyQ2FzZSgpID09PSBTdHJpbmcodmFsdWUpLnRyaW0oKS50b0xvd2VyQ2FzZSgpOwogICAgICB9KTsKICAgICAgaWYgKG9wdCkgeyBlbC52YWx1ZSA9IG9wdC52YWx1ZTsgfQogICAgfQogICAgZWwuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoJ2NoYW5nZScsIHsgYnViYmxlczogdHJ1ZSB9KSk7CiAgICByZXR1cm4gZWw7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBmaWxsQ2hlY2tib3goc2VsZWN0b3IsIGNoZWNrZWQpIHsKICAgIHZhciBlbCA9IGF3YWl0IHdhaXRGb3JFbGVtZW50KHNlbGVjdG9yKTsKICAgIGlmIChlbC5jaGVja2VkICE9PSAhIWNoZWNrZWQpIHsgZWwuY2xpY2soKTsgfQogICAgZWwuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoJ2NoYW5nZScsIHsgYnViYmxlczogdHJ1ZSB9KSk7CiAgICByZXR1cm4gZWw7CiAgfQoKICAvKiBSZWFkIE9ORSBmaWVsZCBvZmYgT05FIGxlYWYgbm9kZS4gJ0BhdHRyJyByZWFkcyBhbiBhdHRyaWJ1dGUgb2YgdGhlIGl0ZW0KICAgICBpdHNlbGY7ICdzZWwgQGF0dHInIHJlYWRzIGFuIGF0dHJpYnV0ZSBvZiBhIGRlc2NlbmRhbnQuIE5ldmVyIHJldHVybnMgdGhlCiAgICAgY29uY2F0ZW5hdGVkIHRleHQgb2YgYSB3aG9sZSBjb250YWluZXIuICovCiAgZnVuY3Rpb24gX2ZpZWxkKHJvb3QsIHNwZWMpIHsKICAgIGlmICghc3BlYykgeyByZXR1cm4gbnVsbDsgfQogICAgc3BlYyA9IFN0cmluZyhzcGVjKS50cmltKCk7CiAgICB2YXIgYXQgPSBzcGVjLmluZGV4T2YoJ0AnKTsKICAgIGlmIChhdCA9PT0gMCkgeyByZXR1cm4gcm9vdC5nZXRBdHRyaWJ1dGUoc3BlYy5zbGljZSgxKSk7IH0KICAgIGlmIChhdCA+IDApIHsKICAgICAgdmFyIGVsMCA9IHJvb3QucXVlcnlTZWxlY3RvcihzcGVjLnNsaWNlKDAsIGF0KS50cmltKCkpOwogICAgICByZXR1cm4gZWwwID8gZWwwLmdldEF0dHJpYnV0ZShzcGVjLnNsaWNlKGF0ICsgMSkpIDogbnVsbDsKICAgIH0KICAgIHZhciBlbCA9IHJvb3QucXVlcnlTZWxlY3RvcihzcGVjKTsKICAgIGlmICghZWwpIHsgcmV0dXJuIG51bGw7IH0KICAgIHJldHVybiAoZWwudGV4dENvbnRlbnQgfHwgJycpLnJlcGxhY2UoL1xzKy9nLCAnICcpLnRyaW0oKTsKICB9CgogIGZ1bmN0aW9uIGV4dHJhY3RJdGVtcyhpdGVtU2VsZWN0b3IsIGZpZWxkTWFwLCBsaW1pdCkgewogICAgbGltaXQgPSBsaW1pdCB8fCAxMDsKICAgIHZhciBub2RlcyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoaXRlbVNlbGVjdG9yKSk7CiAgICByZXR1cm4gewogICAgICByZXN1bHRDb3VudDogbm9kZXMubGVuZ3RoLAogICAgICB0cnVuY2F0ZWQ6IG5vZGVzLmxlbmd0aCA+IGxpbWl0LAogICAgICByZXN1bHRzOiBub2Rlcy5zbGljZSgwLCBsaW1pdCkubWFwKGZ1bmN0aW9uIChuKSB7CiAgICAgICAgdmFyIG8gPSB7fTsKICAgICAgICBPYmplY3Qua2V5cyhmaWVsZE1hcCB8fCB7fSkuZm9yRWFjaChmdW5jdGlvbiAoaykgewogICAgICAgICAgdmFyIHYgPSBfZmllbGQobiwgZmllbGRNYXBba10pOwogICAgICAgICAgaWYgKHYgIT09IG51bGwgJiYgdiAhPT0gdW5kZWZpbmVkICYmIHYgIT09ICcnKSB7IG9ba10gPSBTdHJpbmcodikuc2xpY2UoMCwgMzAwKTsgfQogICAgICAgIH0pOwogICAgICAgIHJldHVybiBvOwogICAgICB9KQogICAgfTsKICB9CgogIC8qIFBhcnNlIHRoZSBpbnRlZ2VyIHRoZSBwYWdlIGl0c2VsZiBzdGF0ZXMsIGUuZy4gIjUgaXRlbXMiIC0+IDUuICovCiAgZnVuY3Rpb24gcmVhZFJlc3VsdENvdW50KHNlbGVjdG9yLCBmYWxsYmFjaykgewogICAgaWYgKCFzZWxlY3RvcikgeyByZXR1cm4gZmFsbGJhY2s7IH0KICAgIHZhciBlbCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3Ioc2VsZWN0b3IpOwogICAgaWYgKCFlbCkgeyByZXR1cm4gZmFsbGJhY2s7IH0KICAgIHZhciBtID0gKGVsLnRleHRDb250ZW50IHx8ICcnKS5yZXBsYWNlKC8sL2csICcnKS5tYXRjaCgvXGQrLyk7CiAgICByZXR1cm4gbSA/IHBhcnNlSW50KG1bMF0sIDEwKSA6IGZhbGxiYWNrOwogIH0KCiAgZnVuY3Rpb24gYnVpbGRVcmwodGVtcGxhdGUsIGFyZ3MpIHsKICAgIHJldHVybi
22947B0ZW1wbGF0ZS5yZXBsYWNlKC9ceyhcdyspXH0vZywgZnVuY3Rpb24gKF8sIGspIHsKICAgICAgcmV0dXJuIGFyZ3MgJiYgYXJnc1trXSAhPSBudWxsID8gZW5jb2RlVVJJQ29tcG9uZW50KGFyZ3Nba10pIDogJyc7CiAgICB9KS5yZXBsYWNlKC9bPyZdXHcrPSg/PSZ8JCkvZywgJycpLnJlcGxhY2UoL1w/Ji8sICc/JykucmVwbGFjZSgvWz8mXSQvLCAnJyk7CiAgfQoKICAvKiBXaG9sZS1zaXRlIGNhcGFiaWxpdHkgbWFwLCBkZXJpdmVkIG9uY2UgYXQgYXNzZW1ibGUgdGltZSBhbmQgZW1iZWRkZWQgYXMgYQogICAgIHN0YXRpYyBsaXRlcmFsIHNvIEVWRVJZIHBhZ2UgY2FycmllcyB0aGUgRlVMTCBtYXAgKG5vdCBqdXN0IHRoZSB0b29scyB0aGF0CiAgICAgcmVnaXN0ZXJlZCBvbiB0aGUgY3VycmVudCBwYXRoKS4gU2FtZSBieXRlcyBhcyB0aGUgZW1pdHRlZAogICAgIHdlYm1jcC1jYXBhYmlsaXRpZXMtPHByZWZpeD4uanNvbjogb25lIHNvdXJjZSBvZiB0cnV0aCwgdHdvIGRpc2NvdmVyeQogICAgIHN1cmZhY2VzLiAqLwogIHZhciBfV0VCTUNQX0NBUFMgPSB7InNpdGUiOiAiIiwgImdlbmVyYXRlZF9hdCI6ICIyMDI2LTA4LTIwVDIyOjE5OjU1LjQ0NjU0OFoiLCAidG9vbF9wcmVmaXgiOiAib3B0aW1pemVseSIsICJ0b29sX2NvdW50IjogNywgImNhcGFiaWxpdGllcyI6IFt7Im5hbWUiOiAib3B0aW1pemVseS1zdWJtaXQtb3B0aW1pemVseS1sZWFkLWZvcm0iLCAiZGVzY3JpcHRpb24iOiAiIiwgInVybFBhdHRlcm4iOiAiLyJ9LCB7Im5hbWUiOiAib3B0aW1pemVseS1zdWJtaXQtb3B0aW1pemVseS1sZWFkLWZvcm0tc3VibWl0IiwgImRlc2NyaXB0aW9uIjogIiIsICJ1cmxQYXR0ZXJuIjogIi8ifSwgeyJuYW1lIjogIm9wdGltaXplbHktc3VibWl0LWRlbW8tcmVxdWVzdCIsICJkZXNjcmlwdGlvbiI6ICIiLCAidXJsUGF0dGVybiI6ICIvZ2V0LXN0YXJ0ZWQvIn0sIHsibmFtZSI6ICJvcHRpbWl6ZWx5LXN1Ym1pdC1kZW1vLXJlcXVlc3Qtc3VibWl0IiwgImRlc2NyaXB0aW9uIjogIiIsICJ1cmxQYXR0ZXJuIjogIi9nZXQtc3RhcnRlZC8ifSwgeyJuYW1lIjogIm9wdGltaXplbHktc2VhcmNoLW9wdGltaXplbHktcmVzb3VyY2VzIiwgImRlc2NyaXB0aW9uIjogIiIsICJ1cmxQYXR0ZXJuIjogIi9zZWFyY2gvIn0sIHsibmFtZSI6ICJvcHRpbWl6ZWx5LXN1Ym1pdC1iZWNvbWUtYS1wYXJ0bmVyLWlucXVpcnkiLCAiZGVzY3JpcHRpb24iOiAiIiwgInVybFBhdHRlcm4iOiAiL3BhcnRuZXJzLyJ9LCB7Im5hbWUiOiAib3B0aW1pemVseS1zdWJtaXQtYmVjb21lLWEtcGFydG5lci1pbnF1aXJ5LXN1Ym1pdCIsICJkZXNjcmlwdGlvbiI6ICIiLCAidXJsUGF0dGVybiI6ICIvcGFydG5lcnMvIn1dLCAiYnlQYWdlIjogeyIvIjogWyJvcHRpbWl6ZWx5LXN1Ym1pdC1vcHRpbWl6ZWx5LWxlYWQtZm9ybSIsICJvcHRpbWl6ZWx5LXN1Ym1pdC1vcHRpbWl6ZWx5LWxlYWQtZm9ybS1zdWJtaXQiXSwgIi9nZXQtc3RhcnRlZC8iOiBbIm9wdGltaXplbHktc3VibWl0LWRlbW8tcmVxdWVzdCIsICJvcHRpbWl6ZWx5LXN1Ym1pdC1kZW1vLXJlcXVlc3Qtc3VibWl0Il0sICIvc2VhcmNoLyI6IFsib3B0aW1pemVseS1zZWFyY2gtb3B0aW1pemVseS1yZXNvdXJjZXMiXSwgIi9wYXJ0bmVycy8iOiBbIm9wdGltaXplbHktc3VibWl0LWJlY29tZS1hLXBhcnRuZXItaW5xdWlyeSIsICJvcHRpbWl6ZWx5LXN1Ym1pdC1iZWNvbWUtYS1wYXJ0bmVyLWlucXVpcnktc3VibWl0Il19fTsKCiAgLyogT25lIGdsb2JhbGx5LXJlZ2lzdGVyZWQgZGlzY292ZXJ5IHRvb2w6IGFuIGFnZW50IG9uIEFOWSBwYWdlIGNhbiBzZWUgdGhlCiAgICAgd2hvbGUgc2l0ZSdzIGNhcGFiaWxpdGllcyAoaW50ZW50IC0+IHBhZ2UgLT4gdG9vbHMpIGFuZCBqdW1wIHRvIHRoZSBwYWdlCiAgICAgdGhhdCBob3N0cyBhIGNhcGFiaWxpdHkuIFJlZ2lzdGVyZWQgd2l0aCB0aGUgYmFyZSByZWdpc3RlclRvb2wgKHBhdHRlcm4KICAgICAvKiopLCBzbyBpdCBzdXJ2aXZlcyBldmVyeSBmdWxsLXBhZ2UgbmF2aWdhdGlvbi4gUjkgdHJhY2tpbmcgaXMgYXBwbGllZCBieQogICAgIHRoZSByZWdpc3RlclRvb2wgd3JhcHBlciwgc28gZG8gTk9UIGNhbGwgdHJhY2tXZWJNQ1BUb29sIGhlcmUuICovCiAgbWMucmVnaXN0ZXJUb29sKHsKICAgIG5hbWU6ICdvcHRpbWl6ZWx5LWRpc2NvdmVyLWNhcGFiaWxpdGllcycsCiAgICBkZXNjcmlwdGlvbjogJ0xpc3QgZXZlcnkgV2ViTUNQIGNhcGFiaWxpdHkgYWNyb3NzIHRoaXMgd2hvbGUgc2l0ZTogdGhlIGludGVudCwgdGhlIHBhZ2UgVVJMIHBhdHRlcm4gdGhhdCBob3N0cyBpdCwgYW5kIHRoZSB0b29sIG5hbWVzIHRvIGNhbGwgdGhlcmUuIE9wdGlvbmFsbHkgcGFzcyBuYXZpZ2F0ZVRvIHRvIGp1bXAgdG8gdGhlIHBhZ2UgdGhhdCBob3N0cyBhIGNhcGFiaWxpdHkgKHRoZSBmdWxsIHBhZ2UgcmVsb2FkczsgY2FsbCB0aGUgdGFyZ2V0IHRvb2wgYWZ0ZXIgaXQgbG9hZHMpLiBVc2UgdGhpcyBmaXJzdCB3aGVuIHRoZSB0b29sIHlvdSBuZWVkIGlzIG5vdCByZWdpc3RlcmVkIG9uIHRoZSBjdXJyZW50IHBhZ2UuJywKICAgIGlucHV0U2NoZW1hOiB7CiAgICAgIHR5cGU6ICdvYmplY3QnLAogICAgICBwcm9wZXJ0aWVzOiB7CiAgICAgICAgbmF2aWdhdGVUbzogewogICAgICAgICAgdHlwZTogJ3N0cmluZycsCiAgICAgICAgICBkZXNjcmlwdGlvbjogJ09wdGlvbmFsIHNhbWUtb3JpZ2luIHBhdGggb3IgVVJMIHRvIG5hdmlnYXRlIHRvLCBlLmcuIGEgY2FwYWJpbGl0eSB1cmxQYXR0ZXJuIHdpdGggYW55ICogcmVwbGFjZWQgYnkgYSByZWFsIHZhbHVlLiBDcm9zcy1vcmlnaW4gZGVzdGluYXRpb25zIGFyZSByZWZ1c2VkLicKICAgICAgICB9CiAgICAgIH0KICAgIH0sCiAgICBleGVjdXRlOiBmdW5jdGlvbiAoYXJncykgewogICAgICB2YXIgcGF5bG9hZCA9IHsKICAgICAgICBzaXRlOiBfV0VCTUNQX0NBUFMuc2l0ZSB8fCBsb2NhdGlvbi5vcmlnaW4sCiAgICAgICAgY3VycmVudFBhdGg6IGxvY2F0aW9uLnBhdGhuYW1lLAogICAgICAgIHRvb2xfY291bnQ6IChfV0VCTUNQX0NBUFMuY2FwYWJpbGl0aWVzIHx8IFtdKS5sZW5ndGgsCiAgICAgICAgY2FwYWJpbGl0aWVzOiBfV0VCTUNQX0NBUFMuY2FwYWJpbGl0aWVzIHx8IFtdLAogICAgICAgIGJ5UGFnZTogX1dFQk1DUF9DQVBTLmJ5UGFnZSB8fCB7fQogICAgICB9OwogICAgICB2Y
22947XIgZGVzdCA9IChhcmdzICYmIGFyZ3MubmF2aWdhdGVUbykgPyBTdHJpbmcoYXJncy5uYXZpZ2F0ZVRvKS50cmltKCkgOiAnJzsKICAgICAgaWYgKGRlc3QpIHsKICAgICAgICB2YXIgdXJsID0gbnVsbDsKICAgICAgICB0cnkgeyB1cmwgPSBuZXcgVVJMKGRlc3QsIGxvY2F0aW9uLm9yaWdpbik7IH0KICAgICAgICBjYXRjaCAoZSkgeyBwYXlsb2FkLm5hdmlnYXRpb24gPSB7IHN0YXR1czogJ2ludmFsaWQnLCBuYXZpZ2F0ZVRvOiBkZXN0IH07IH0KICAgICAgICBpZiAodXJsKSB7CiAgICAgICAgICBpZiAodXJsLm9yaWdpbiAhPT0gbG9jYXRpb24ub3JpZ2luKSB7CiAgICAgICAgICAgIHBheWxvYWQubmF2aWdhdGlvbiA9IHsgc3RhdHVzOiAncmVmdXNlZCcsIHJlYXNvbjogJ2Nyb3NzLW9yaWdpbicsIG5hdmlnYXRlVG86IGRlc3QgfTsKICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIHBheWxvYWQubmF2aWdhdGlvbiA9IHsgc3RhdHVzOiAnbmF2aWdhdGVkJywgdG86IHVybC5wYXRobmFtZSArIHVybC5zZWFyY2ggfTsKICAgICAgICAgICAgdmFyIG91dCA9IHsgY29udGVudDogW3sgdHlwZTogJ3RleHQnLCB0ZXh0OiBKU09OLnN0cmluZ2lmeShwYXlsb2FkKSB9XSB9OwogICAgICAgICAgICBsb2NhdGlvbi5hc3NpZ24odXJsLmhyZWYpOwogICAgICAgICAgICByZXR1cm4gb3V0OwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfQogICAgICByZXR1cm4geyBjb250ZW50OiBbeyB0eXBlOiAndGV4dCcsIHRleHQ6IEpTT04uc3RyaW5naWZ5KHBheWxvYWQpIH1dIH07CiAgICB9CiAgfSk7CgogIC8qIC0tLS0gb3B0aW1pemVseS1zZWFyY2gtb3B0aW1pemVseS1yZXNvdXJjZXMgLS0tLSAqLwppZiAoIXdpbmRvdy5fX3dtY3BSZWcpIHsKICB3aW5kb3cuX193bWNwUmVnID0gKGZ1bmN0aW9uICgpIHsKICAgIHZhciBsaXZlID0ge307CiAgICBmdW5jdGlvbiBnbG9iVG9SZShnbG9iKSB7CiAgICAgIHZhciByZSA9IGdsb2IKICAgICAgICAucmVwbGFjZSgvWy4rXiR7fSgpfFtcXVxcXS9nLCAiXFwkJiIpCiAgICAgICAgLnJlcGxhY2UoL1wvXCpcKi9nLCAiKC8uKik/IikKICAgICAgICAucmVwbGFjZSgvXCovZywgIlteL10rIik7CiAgICAgIHJldHVybiBuZXcgUmVnRXhwKCJeIiArIHJlICsgIiQiKTsKICAgIH0KICAgIGZ1bmN0aW9uIHJlY29uY2lsZSgpIHsKICAgICAgdmFyIHBhdGggPSBsb2NhdGlvbi5wYXRobmFtZTsKICAgICAgT2JqZWN0LmtleXMobGl2ZSkuZm9yRWFjaChmdW5jdGlvbiAobmFtZSkgewogICAgICAgIHZhciBlID0gbGl2ZVtuYW1lXTsKICAgICAgICB2Y
22947XIgbWF0Y2ggPSBlLnJlLnRlc3QocGF0aCk7CiAgICAgICAgaWYgKG1hdGNoICYmICFlLm9uKSB7IG1jLnJlZ2lzdGVyVG9vbChlLmRlZik7IGUub24gPSB0cnVlOyB9CiAgICAgICAgZWxzZSBpZiAoIW1hdGNoICYmIGUub24gJiYgbWMudW5yZWdpc3RlclRvb2wpIHsgbWMudW5yZWdpc3RlclRvb2wobmFtZSk7IGUub24gPSBmYWxzZTsgfQogICAgICB9KTsKICAgIH0KICAgIGZ1bmN0aW9uIHJlZ2lzdGVyVG9vbElmKGdsb2IsIGRlZikgewogICAgICBsaXZlW2RlZi5uYW1lXSA9IHsgcmU6IGdsb2JUb1JlKGdsb2IpLCBkZWY6IGRlZiwgb246IGZhbHNlIH07CiAgICAgIHJlY29uY2lsZSgpOwogICAgfQogICAgaWYgKCF3aW5kb3cuX193bWNwUm91dGUpIHsKICAgICAgd2luZG93Ll9fd21jcFJvdXRlID0gdHJ1ZTsKICAgICAgWyJwdXNoU3RhdGUiLCAicmVwbGFjZVN0YXRlIl0uZm9yRWFjaChmdW5jdGlvbiAobSkgewogICAgICAgIHZhciBvcmlnID0gaGlzdG9yeVttXTsKICAgICAgICBoaXN0b3J5W21dID0gZnVuY3Rpb24gKCkgewogICAgICAgICAgdmFyIHIgPSBvcmlnLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7CiAgICAgICAgICB3aW5kb3cuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoIndtY3A6cm91dGUiKSk7CiAgICAgICAgICByZXR1cm4gcjsKICAgICAgICB9OwogICAgICB9KTsKICAgICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoInBvcHN0YXRlIiwgZnVuY3Rpb24gKCkgewogICAgICAgIHdpbmRvdy5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgid21jcDpyb3V0ZSIpKTsKICAgICAgfSk7CiAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCJ3bWNwOnJvdXRlIiwgcmVjb25jaWxlKTsKICAgIH0KICAgIHJldHVybiB7IHJlZ2lzdGVyVG9vbElmOiByZWdpc3RlclRvb2xJZiwgcmVjb25jaWxlOiByZWNvbmNpbGUgfTsKICB9KSgpOwp9Cgp3aW5kb3cuX193bWNwUmVnLnJlZ2lzdGVyVG9vbElmKCIvc2VhcmNoLyoqIiwgewogIG5hbWU6ICJvcHRpbWl6ZWx5LXNlYXJjaC1vcHRpbWl6ZWx5LXJlc291cmNlcyIsCiAgZGVzY3JpcHRpb246ICJTZWFyY2hlcyBhbmQgZmlsdGVycyBPcHRpbWl6ZWx5J3MgcmVzb3VyY2UgbGlicmFyeSBvbiB0aGUgL3NlYXJjaC8gcGFnZSBieSBkcml2aW5nIHRoZSBwYWdlJ3Mgb3duIFVJOiBpdCBzY3JvbGxzIHRvIGFuZCBoaWdobGlnaHRzIHRoZSBzaWRlYmFyIHNlYXJjaCBmb3JtLCB0eXBlcyB0aGUga2V5d29yZCBpbnRvIHRoZSBzZWFyY2ggYm94IGFuZCBwcmVzc2VzIEVudGVyLCB0b2dnbGVzIGEgVG9waWMgYW5kL29yIFByb2R1Y3QgZmFjZXQgaW4gdGhlIGxlZnQgc2lkZWJhciAoZXhwYW5kaW5nIHRoZSAnLi4uJyBsaXN0IGFuZCB1c2luZyAnQ2xlYXInIHdoZW4gbmVlZGVkKSwgb3B0aW9uYWxseSBtb3ZlcyB0byBhIHJlc3VsdHMgcGFnZSB2aWEgdGhlIHBhZ2luYXRpb24gYnV0dG9ucywgdGhlbiB3YWl0cyBmb3IgdGhlIFJlYWN0IHJlc3VsdHMgZ3JpZCB0byByZS1yZW5kZXIuIElUIFJFVFVSTlMgVEhFIERBVEEgSVQgUkVBRFMgQkFDSyBPRkYgVEhFIFBBR0UsIHNvIHRoZSBjYWxsZXIgZ2V0cyB0aGUgYWN0dWFsIHJlc3VsdCBjYXJkczoge3N0YXR1cywgYWN0aW9uLCBxdWVyeSwgdG9waWMsIHByb2R1Y3QsIHBhZ2UsIGFwcGxpZWRGaWx0ZXJzLCBzaG93aW5nICh0aGUgcGFnZSdzICdTaG93aW5nIFgtWSBvZiBOIHJlc3VsdHMnIGxpbmUpLCByZXN1bHRDb3VudCAodG90YWwgdGhlIHBhZ2UgcmVwb3J0cyksIHJlc3VsdHM6IFt7dGl0bGUsIHVybCAoYWJzb2x1dGUpLCBzdW1tYXJ5LCByZXNvdXJjZVR5cGV9XSBjYXBwZWQgYXQgMTAgaXRlbXMsIHRydW5jYXRlZCwgb2JzZXJ2ZWQsIHVybCwgc3VtbWFyeX0uIE9uIGEgbWlzc2luZyBzZWFyY2ggYm94LCB1bmtub3duIGZhY2V0IHZhbHVlLCBvciByZXN1bHRzIHRoYXQgbmV2ZXIgcmVuZGVyIGl0IHJldHVybnMgc3RhdHVzICdlcnJvcicvJ3BhcnRpYWwnIHdpdGggcmVhc29uLCBhdmFpbGFibGVPcHRpb25zIGFuZCBuZXh0U3RlcCBpbnN0ZWFkIG9mIHRocm93aW5nLiBVc2UgaXQgdG8gZXh0cmFjdCBhbmQgZmlsdGVyIGFydGljbGVzLCBndWlkZXMsIHZpZGVvcyBhbmQgY3VzdG9tZXIgc3RvcmllcyB3aXRob3V0IGxlYXZpbmcgdGhlIHBhZ2UuIiwKICBpbnB1dFNjaGVtYTogewogICAgdHlwZTogIm9iamVjdCIsCiAgICBwcm9wZXJ0aWVzOiB7CiAgICAgIHF1ZXJ5OiB7CiAgICAgICAgdHlwZTogInN0cmluZyIsCiAgICAgICAgZGVzY3JpcHRpb246ICJGcmVlLXRleHQga2V5d29yZCB0eXBlZCBpbnRvIHRoZSBzaWRlYmFyIHNlYXJjaCBib3gsIGUuZy4gJ3BlcnNvbmFsaXphdGlvbicuIE9taXQgdG8ga2VlcCB3aGF0ZXZlciBxdWVyeSBpcyBjdXJyZW50bHkgYXBwbGllZC4iCiAgICAgIH0sCiAgICAgIHRvcGljOiB7CiAgICAgICAgdHlwZTogInN0cmluZyIsCiAgICAgICAgZGVzY3JpcHRpb246ICJUb3BpYyBmYWNldCB0byBzd2l0Y2ggb24gaW4gdGhlIGxlZnQgc2lkZWJhci4gTXVzdCBiZSBvbmUgb2YgdGhlIGxpdmUgZmFjZXQgbGFiZWxzLiIsCiAgICAgICAgZW51bTogWyJBL0IgVGVzdGluZyIsICJBSSIsICJBbmFseXRpY3MiLCAiQW5ub3VuY2VtZW50cyIsICJCMkIiLCAiQmV0YXMiLCAiQ29tbWVyY2UiLCAiQ29udGVudCBtYW5hZ2VtZW50IiwgIkNvbnZlcnNpb24gcmF0ZSBvcHRpbWl6YXRpb24iLCAiQ3VzdG9tZXJzIiwgIkRhdGEiLCAiRGV2ZWxvcGVycyIsICJEaWdpdGFsIGV4cGVyaWVuY2UgcGxhdGZvcm0iLCAiRW1haWwiLCAiRXhwZXJpbWVudGF0aW9uIiwgIkZlYXR1cmUgbWFuYWdlbWVudCIsICJIZWFkbGVzcyIsICJIb3cgd2Ugb3B0aW1pemUiLCAiTWFya2V0aW5nIiwgIk1vYmlsZSIsICJPcHRpY29uIiwgIlBlcnNvbmFsaXphdGlvbiIsICJQcm9kdWN0IHVwZGF0ZXMiXQogICAgICB9LAogICAgICBwcm9kdWN0OiB7CiAgICAgICAgdHlwZTogInN0cmluZyIsCiAgICAgICAgZGVzY3JpcHRpb246ICJQcm9kdWN0IGZhY2V0IHRvIHN3aXRjaCBvbiBpbiB0aGUgbGVmdCBzaWRlYmFyLiBNdXN0IGJlIG9uZSBvZiB0aGUgbGl2ZSBmYWNldCBsYWJlbHMuIiwKICAgICAgICBlbnVtOiBbIkFuYWx5dGljcyIsICJBbnkgd2Vic2l0ZSIsICJDYW1wYWlnbiIsICJDb21tZXJjZSBDb25uZWN0IiwgIkNvbmZpZ3VyZWQgQ29tbWVyY2UiLCAiQ29udGVudCBNYW5hZ2VtZW50IFN5c3RlbSIsICJDb250ZW50IE1hcmtldGluZyBQbGF0Zm9ybSIsICJEYXRhIFBsYXRmb3JtIiwgIkRpZ2l0YWwgQXNzZXQgTWFuYWdlbWVudCIsICJFbWFpbCIsICJGZWF0dXJlIEV4cGVyaW1lbn
22947RhdGlvbiIsICJPcGFsIiwgIk9wdGltaXplbHkgT25lIiwgIlBlcnNvbmFsaXphdGlvbiIsICJSZWNvbW1lbmRhdGlvbnMiLCAiV2FyZWhvdXNlLU5hdGl2ZSBBbmFseXRpY3MiLCAiV2ViIEV4cGVyaW1lbnRhdGlvbiJdCiAgICAgIH0sCiAgICAgIHBhZ2U6IHsKICAgICAgICB0eXBlOiAiaW50ZWdlciIsCiAgICAgICAgbWluaW11bTogMSwKICAgICAgICBkZXNjcmlwdGlvbjogIjEtYmFzZWQgcmVzdWx0cyBwYWdlIHRvIG9wZW4gKDEyIGNhcmRzIHBlciBwYWdlKS4gVGhlIHRvb2wgY2xpY2tzIHRoZSBudW1iZXJlZCBwYWdpbmF0aW9uIGJ1dHRvbiwgb3Igc3RlcHMgd2l0aCBOZXh0L1ByZXZpb3VzIHdoZW4gdGhlIG51bWJlciBpcyBub3QgcmVuZGVyZWQuIgogICAgICB9LAogICAgICBjbGVhckZpbHRlcnM6IHsKICAgICAgICB0eXBlOiAiYm9vbGVhbiIsCiAgICAgICAgZGVzY3JpcHRpb246ICJDbGljayB0aGUgc2lkZWJhciAnQ2xlYXInIGJ1dHRvbiBiZWZvcmUgYXBwbHlpbmcgYW55dGhpbmcuIERlZmF1bHRzIHRvIHRydWUgd2hlbiB0b3BpYyBvciBwcm9kdWN0IGlzIHN1cHBsaWVkLCBvdGhlcndpc2UgZmFsc2UuIgogICAgICB9CiAgICB9CiAgfSwKICBhc3luYyBleGVjdXRlKGFyZ3MpIHsKICAgIGFyZ3MgPSBhcmdzIHx8IHt9OwogICAgdmFyIFRPUElDUyA9IFsiQS9CIFRlc3RpbmciLCAiQUkiLCAiQW5hbHl0aWNzIiwgIkFubm91bmNlbWVudHMiLCAiQjJCIiwgIkJldGFzIiwgIkNvbW1lcmNlIiwgIkNvbnRlbnQgbWFuYWdlbWVudCIsICJDb252ZXJzaW9uIHJhdGUgb3B0aW1pemF0aW9uIiwgIkN1c3RvbWVycyIsICJEYXRhIiwgIkRldmVsb3BlcnMiLCAiRGlnaXRhbCBleHBlcmllbmNlIHBsYXRmb3JtIiwgIkVtYWlsIiwgIkV4cGVyaW1lbnRhdGlvbiIsICJGZWF0dXJlIG1hbmFnZW1lbnQiLCAiSGVhZGxlc3MiLCAiSG93IHdlIG9wdGltaXplIiwgIk1hcmtldGluZyIsICJNb2JpbGUiLCAiT3B0aWNvbiIsICJQZXJzb25hbGl6YXRpb24iLCAiUHJvZHVjdCB1cGRhdGVzIl07CiAgICB2YXIgUFJPRFVDVFMgPSBbIkFuYWx5dGljcyIsICJBbnkgd2Vic2l0ZSIsICJDYW1wYWlnbiIsICJDb21tZXJjZSBDb25uZWN0IiwgIkNvbmZpZ3VyZWQgQ29tbWVyY2UiLCAiQ29udGVudCBNYW5hZ2VtZW50IFN5c3RlbSIsICJDb250ZW50IE1hcmtldGluZyBQbGF0Zm9ybSIsICJEYXRhIFBsYXRmb3JtIiwgIkRpZ2l0YWwgQXNzZXQgTWFuYWdlbWVudCIsICJFbWFpbCIsICJGZWF0dXJlIEV4cGVyaW1lbnRhdGlvbiIsICJPcGFsIiwgIk9wdGltaXplbHkgT25lIiwgIlBlcnNvbmFsaXphdGlvbiIsICJSZWNvbW1lbmRhdGlvbnMiLCAiV2FyZWhvdXNlLU5hdGl2ZSBBbmFseXRpY3MiLCAiV2ViIEV4cGVyaW1lbnRhdGlvbiJdOwogICAgdmFyIFJFU1VMVFNfU0VMRUNUT1IgPSAnbWFpbiBhW2hyZWZePSIvZmllbGQtbm90ZXMvIl0sIG1haW4gYVtocmVmXj0iL2NhbXBhaWducy8iXSc7CiAgICB2YXIgUEFHRV9TSVpFID0gMTI7CgogICAgZnVuY3Rpb24gb3V0KHBheWxvYWQpIHsKICAgICAgcmV0dXJuIHsgY29udGVudDogW3sgdHlwZTogInRleHQiLCB0ZXh0OiBKU09OLnN0cmluZ2lmeShwYXlsb2FkKSB9XSB9OwogICAgfQogICAgZnVuY3Rpb24gdHJ1bmMocywgbikgewogICAgICBuID0gbiB8fCAzMDA7CiAgICAgIHMgPSAocyA9PSBudWxsID8gIiIgOiBTdHJpbmcocykpLnJlcGxhY2UoL1xzKy9nLCAiICIpLnRyaW0oKTsKICAgICAgcmV0dXJuIHMubGVuZ3RoID4gbiA/IHMuc2xpY2UoMCwgbikgKyAiLi4uIiA6IHM7CiAgICB9CiAgICBmdW5jdGlvbiBzbGVlcChtcykgeyByZXR1cm4gbmV3IFByb21pc2UoZnVuY3Rpb24gKHIpIHsgc2V0VGltZW91dChyLCBtcyk7IH0pOyB9CiAgICBmdW5jdGlvbiBsb2NhbFdhaXQoc2VsLCB0aW1lb3V0KSB7CiAgICAgIHRpbWVvdXQgPSB0aW1lb3V0IHx8IDEwMDAwOwogICAgICB2YXIgc3RhcnQgPSBEYXRlLm5vdygpOwogICAgICByZXR1cm4gbmV3IFByb21pc2UoZnVuY3Rpb24gKHJlc29sdmUsIHJlamVjdCkgewogICAgICAgIChmdW5jdGlvbiBwb2xsKCkgewogICAgICAgICAgdmFyIGVsID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihzZWwpOwogICAgICAgICAgaWYgKGVsKSB7IHJlc29sdmUoZWwpOyByZXR1cm47IH0KICAgICAgICAgIGlmIChEYXRlLm5vdygpIC0gc3RhcnQgPiB0aW1lb3V0KSB7IHJlamVjdChuZXcgRXJyb3IoInRpbWVvdXQgd2FpdGluZyBmb3IgIiArIHNlbCkpOyByZXR1cm47IH0KICAgICAgICAgIHNldFRpbWVvdXQocG9sbCwgMjAwKTsKICAgICAgICB9KSgpOwogICAgICB9KTsKICAgIH0KICAgIGZ1bmN0aW9uIHdhaXRGb3Ioc2VsLCB0aW1lb3V0KSB7CiAgICAgIGlmICh0eXBlb2Ygd2FpdEZvckVsZW1lbnQgPT09ICJmdW5jdGlvbiIpIHsKICAgICAgICB0cnkgeyByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKHdhaXRGb3JFbGVtZW50KHNlbCwgdGltZW91dCB8fCAxMDAwMCkpOyB9CiAgICAgICAgY2F0Y2ggKGUpIHsgcmV0dXJuIGxvY2FsV2FpdChzZWwsIHRpbWVvdXQpOyB9CiAgICAgIH0KICAgICAgcmV0dXJuIGxvY2FsV2FpdChzZWwsIHRpbWVvdXQpOwogICAgfQogICAgZnVuY3Rpb24gaGlnaGxpZ2h0KGVsKSB7CiAgICAgIGlmICghZWwgfHwgIWVsLnN0eWxlKSByZXR1cm47CiAgICAgIHZhciBwcmV2T3V0bGluZSA9IGVsLnN0eWxlLm91dGxpbmU7CiAgICAgIHZhciBwcmV2T2Zmc2V0ID0gZWwuc3R5bGUub3V0bGluZU9mZnNldDsKICAgICAgZWwuc3R5bGUub3V0bGluZSA9ICIzcHggc29saWQgIzAwMzdmZiI7CiAgICAgIGVsLnN0eWxlLm91dGxpbmVPZmZzZXQgPSAiMnB4IjsKICAgICAgc2V0VGltZW91dChmdW5jdGlvbiAoKSB7IGVsLnN0eWxlLm91dGxpbmUgPSBwcmV2T3V0bGluZTsgZWwuc3R5bGUub3V0bGluZU9mZnNldCA9IHByZXZPZmZzZXQ7IH0sIDYwMDApOwogICAgfQogICAgZnVuY3Rpb24gc2Nyb2xsVG8oZWwpIHsKICAgICAgaWYgKCFlbCB8fCB0eXBlb2YgZWwuc2Nyb2xsSW50b1ZpZXcgIT09ICJmdW5jdGlvbiIpIHJldHVybjsKICAgICAgdHJ5IHsgZWwuc2Nyb2xsSW50b1ZpZXcoeyBiZWhhdmlvcjogInNtb290aCIsIGJsb2NrOiAiY2VudGVyIiB9KTsgfSBjYXRjaCAoZSkgeyBlbC5zY3JvbGxJbnRvVmlldygpOyB9CiAgICB9CiAgICBmdW5jdGlvbiB0eHQoZWwpIHsgcmV0dXJuICgoZWwgJiYgZWwudGV4dENvbnRlbnQpIHx8ICIiKS5yZXBsYWNlKC9ccysvZywgIiAiKS50cmltKCk7IH0KCiAgICBmdW5jdGlvbiByZWFkQ291bnQoKSB7CiAgICAgI
22947HZhciBub2RlcyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoIm1haW4gcCwgbWFpbiBzcGFuLCBtYWluIGRpdiwgbmF2IHAsIG5hdiBzcGFuIik7CiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbm9kZXMubGVuZ3RoOyBpKyspIHsKICAgICAgICBpZiAobm9kZXNbaV0uY2hpbGRyZW4ubGVuZ3RoID4gMikgY29udGludWU7CiAgICAgICAgdmFyIHQgPSB0eHQobm9kZXNbaV0pOwogICAgICAgIHZhciBtID0gL1Nob3dpbmdccysoW1xkLF0rKVxzKlstXHUyMDEzXHUyMDE0XVxzKihbXGQsXSspXHMrb2ZccysoW1xkLF0rKVxzK3Jlc3VsdHMvaS5leGVjKHQpOwogICAgICAgIGlmIChtKSB7CiAgICAgICAgICByZXR1cm4gewogICAgICAgICAgICBzaG93aW5nOiBtWzBdLAogICAgICAgICAgICBmcm9tOiBwYXJzZUludChtWzFdLnJlcGxhY2UoLywvZywgIiIpLCAxMCksCiAgICAgICAgICAgIHRvOiBwYXJzZUludChtWzJdLnJlcGxhY2UoLywvZywgIiIpLCAxMCksCiAgICAgICAgICAgIHRvdGFsOiBwYXJzZUludChtWzNdLnJlcGxhY2UoLywvZywgIiIpLCAxMCkKICAgICAgICAgIH07CiAgICAgICAgfQogICAgICB9CiAgICAgIHJldHVybiBudWxsOwogICAgfQogICAgZnVuY3Rpb24gcmVhZENhcmRzKCkgewogICAgICB2YXIgYW5jaG9ycyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoUkVTVUxUU19TRUxFQ1RPUik7CiAgICAgIHZhciBzZWVuID0ge30sIGxpc3QgPSBbXTsKICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBhbmNob3JzLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgdmFyIGEgPSBhbmNob3JzW2ldOwogICAgICAgIHZhciBocmVmID0gYS5nZXRBdHRyaWJ1dGUoImhyZWYiKSB8fCAiIjsKICAgICAgICBpZiAoIWhyZWYpIGNvbnRpbnVlOwogICAgICAgIHZhciB0aXRsZSA9IHRydW5jKHR4dChhLnF1ZXJ5U2VsZWN0b3IoImgyIikpLCAyMDApOwogICAgICAgIGlmICghdGl0bGUpIGNvbnRpbnVlOwogICAgICAgIGlmIChzZWVuW2hyZWZdKSBjb250aW51ZTsKICAgICAgICBzZWVuW2hyZWZdID0gdHJ1ZTsKICAgICAgICB2Y
22947XIgcGFydHMgPSBocmVmLnNwbGl0KCIvIikuZmlsdGVyKGZ1bmN0aW9uIChwKSB7IHJldHVybiBwOyB9KTsKICAgICAgICB2YXIga2luZCA9IGhyZWYuaW5kZXhPZigiL2ZpZWxkLW5vdGVzLyIpID09PSAwID8gKHBhcnRzWzFdIHx8ICJmaWVsZC1ub3RlIikgOiAiY2FtcGFpZ24iOwogICAgICAgIGxpc3QucHVzaCh7CiAgICAgICAgICB0aXRsZTogdGl0bGUsCiAgICAgICAgICB1cmw6IGhyZWYuaW5kZXhPZigiaHR0cCIpID09PSAwID8gaHJlZiA6ICJodHRwczovL3d3dy5vcHRpbWl6ZWx5LmNvbSIgKyBocmVmLAogICAgICAgICAgc3VtbWFyeTogdHJ1bmModHh0KGEucXVlcnlTZWxlY3RvcigicCIpKSwgMzAwKSwKICAgICAgICAgIHJlc291cmNlVHlwZTogdHJ1bmMoa2luZCwgNDApCiAgICAgICAgfSk7CiAgICAgIH0KICAgICAgcmV0dXJuIGxpc3Q7CiAgICB9CiAgICBmdW5jdGlvbiBzbmFwc2hvdCgpIHsKICAgICAgdmFyIGMgPSByZWFkQ291bnQoKTsKICAgICAgdmFyIGNhcmRzID0gcmVhZENhcmRzKCk7CiAgICAgIHJldHVybiAoYyA/IGMuc2hvd2luZyA6ICJub25lIikgKyAifCIgKyBsb2NhdGlvbi5zZWFyY2ggKyAifCIgKyBjYXJkcy5sZW5ndGggKyAifCIgKwogICAgICAgIGNhcmRzLnNsaWNlKDAsIDMpLm1hcChmdW5jdGlvbiAoeCkgeyByZXR1cm4geC51cmw7IH0pLmpvaW4oIiwiKTsKICAgIH0KICAgIGFzeW5jIGZ1bmN0aW9uIHNldHRsZShiZWZvcmUsIHRpbWVvdXQpIHsKICAgICAgdmFyIHN0YXJ0ID0gRGF0ZS5ub3coKTsKICAgICAgdGltZW91dCA9IHRpbWVvdXQgfHwgOTAwMDsKICAgICAgd2hpbGUgKERhdGUubm93KCkgLSBzdGFydCA8IHRpbWVvdXQpIHsKICAgICAgICBhd2FpdCBzbGVlcCgyNTApOwogICAgICAgIGlmIChzbmFwc2hvdCgpICE9PSBiZWZvcmUpIHsKICAgICAgICAgIGF3YWl0IHNsZWVwKDUwMCk7CiAgICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgICB9CiAgICAgIH0KICAgICAgcmV0dXJuIGZhbHNlOwogICAgfQoKICAgIGZ1bmN0aW9uIHNpZGViYXIoKSB7IHJldHVybiBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCJhc2lkZSIpIHx8IGRvY3VtZW50LmJvZHk7IH0KICAgIGZ1bmN0aW9uIGZpbmRIZWFkaW5nKG5hbWUpIHsKICAgICAgdmFyIGhlYWRzID0gc2lkZWJhcigpLnF1ZXJ5U2VsZWN0b3JBbGwoImg0LCBoMywgaDUiKTsKICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBoZWFkcy5sZW5ndGg7IGkrKykgewogICAgICAgIHZhciB0ID0gdHh0KGhlYWRzW2ldKS50b0xvd2VyQ2FzZSgpLnJlcGxhY2UoL1woXGQrXCkvZywgIiIpLnRyaW0oKTsKICAgICAgICBpZiAodCA9PT0gbmFtZS50b0xvd2VyQ2FzZSgpKSByZXR1cm4gaGVhZHNbaV07CiAgICAgIH0KICAgICAgcmV0dXJuIG51bGw7CiAgICB9CiAgICBmdW5jdGlvbiBncm91cENvbnRhaW5lcihoKSB7CiAgICAgIHZhciBub2RlID0gaC5wYXJlbnRFbGVtZW50LCBkZXB0aCA9IDAsIGJlc3QgPSBoLnBhcmVudEVsZW1lbnQ7CiAgICAgIHdoaWxlIChub2RlICYmIGRlcHRoIDwgNSAmJiBub2RlLnRhZ05hbWUgIT09ICJBU0lERSIgJiYgbm9kZS50YWdOYW1lICE9PSAiQk9EWSIpIHsKICAgICAgICBpZiAobm9kZS5xdWVyeVNlbGVjdG9yKCJsYWJlbCIpKSB7IGJlc3QgPSBub2RlOyBicmVhazsgfQogICAgICAgIG5vZGUgPSBub2RlLnBhcmVudEVsZW1lbnQ7CiAgICAgICAgZGVwdGgrKzsKICAgICAgfQogICAgICByZXR1cm4gYmVzdCB8fCBzaWRlYmFyKCk7CiAgICB9CiAgICBmdW5jdGlvbiBmaW5kTGFiZWwoY29udGFpbmVyLCB2YWx1ZSkgewogICAgICB2YXIgbGFiZWxzID0gY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3JBbGwoImxhYmVsIik7CiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbGFiZWxzLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgdmFyIHNwYW5zID0gbGFiZWxzW2ldLnF1ZXJ5U2VsZWN0b3JBbGwoInNwYW4iKTsKICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IHNwYW5zLmxlbmd0aDsgaisrKSB7CiAgICAgICAgICB2Y
22947XIgc3QgPSB0eHQoc3BhbnNbal0pLnJlcGxhY2UoL1xzKlwoXGQrXClccyokLywgIiIpOwogICAgICAgICAgaWYgKHN0LnRvTG93ZXJDYXNlKCkgPT09IHZhbHVlLnRvTG93ZXJDYXNlKCkpIHJldHVybiBsYWJlbHNbaV07CiAgICAgICAgfQogICAgICAgIHZhciBsdCA9IHR4dChsYWJlbHNbaV0pLnJlcGxhY2UoL1xzKlwoXGQrXClccyokLywgIiIpOwogICAgICAgIGlmIChsdC50b0xvd2VyQ2FzZSgpID09PSB2YWx1ZS50b0xvd2VyQ2FzZSgpKSByZXR1cm4gbGFiZWxzW2ldOwogICAgICB9CiAgICAgIHJldHVybiBudWxsOwogICAgfQogICAgZnVuY3Rpb24gZXhwYW5kR3JvdXAoY29udGFpbmVyKSB7CiAgICAgIHZhciBidG5zID0gY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3JBbGwoImJ1dHRvbiIpOwogICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGJ0bnMubGVuZ3RoOyBpKyspIHsKICAgICAgICB2YXIgdCA9IHR4dChidG5zW2ldKTsKICAgICAgICBpZiAodCA9PT0gIi4uLiIgfHwgdCA9PT0gIlx1MjAyNiIgfHwgL15zaG93IG1vcmUvaS50ZXN0KHQpIHx8IC9ebW9yZSQvaS50ZXN0KHQpKSB7CiAgICAgICAgICBidG5zW2ldLmNsaWNrKCk7CiAgICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgICB9CiAgICAgIH0KICAgICAgcmV0dXJuIGZhbHNlOwogICAgfQogICAgZnVuY3Rpb24gdXJsSGFzRmFjZXQocGFyYW0sIHZhbHVlKSB7CiAgICAgIHRyeSB7CiAgICAgICAgdmFyIHNwID0gbmV3IFVSTFNlYXJjaFBhcmFtcyhsb2NhdGlvbi5zZWFyY2gpOwogICAgICAgIHZhciBhbGwgPSBzcC5nZXRBbGwocGFyYW0pOwogICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYWxsLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICB2Y
22947XIgcGFydHMgPSBTdHJpbmcoYWxsW2ldKS5zcGxpdCgiLCIpOwogICAgICAgICAgZm9yICh2YXIgaiA9IDA7IGogPCBwYXJ0cy5sZW5ndGg7IGorKykgewogICAgICAgICAgICBpZiAocGFydHNbal0udHJpbSgpLnRvTG93ZXJDYXNlKCkgPT09IHZhbHVlLnRvTG93ZXJDYXNlKCkpIHJldHVybiB0cnVlOwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSBjYXRjaCAoZSkge30KICAgICAgcmV0dXJuIGZhbHNlOwogICAgfQogICAgZnVuY3Rpb24gYW55RmlsdGVyQWN0aXZlKCkgewogICAgICBpZiAoL1s/Jl0odG9waWN8cHJvZHVjdCk9Ly50ZXN0KGxvY2F0aW9uLnNlYXJjaCkpIHJldHVybiB0cnVlOwogICAgICB2YXIgYnRucyA9IHNpZGViYXIoKS5xdWVyeVNlbGVjdG9yQWxsKCJidXR0b24sIHAsIHNwYW4sIGg0Iik7CiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYnRucy5sZW5ndGg7IGkrKykgewogICAgICAgIGlmICgvXkZpbHRlcnNccypcKFxzKlsxLTldXGQqXHMqXCkkL2kudGVzdCh0eHQoYnRuc1tpXSkpKSByZXR1cm4gdHJ1ZTsKICAgICAgfQogICAgICByZXR1cm4gZmFsc2U7CiAgICB9CiAgICBmdW5jdGlvbiBmaW5kQ2xlYXJCdXR0b24oKSB7CiAgICAgIHZhciBidG5zID0gc2lkZWJhcigpLnF1ZXJ5U2VsZWN0b3JBbGwoImJ1dHRvbiIpOwogICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGJ0bnMubGVuZ3RoOyBpKyspIHsKICAgICAgICBpZiAoL15jbGVhciggYWxsKT8kL2kudGVzdCh0eHQoYnRuc1tpXSkpKSByZXR1cm4gYnRuc1tpXTsKICAgICAgfQogICAgICByZXR1cm4gbnVsbDsKICAgIH0KICAgIGFzeW5jIGZ1bmN0aW9uIGFwcGx5RmFjZXQoZ3JvdXBOYW1lLCBwYXJhbSwgdmFsdWUpIHsKICAgICAgaWYgKHVybEhhc0ZhY2V0KHBhcmFtLCB2YWx1ZSkpIHJldHVybiB7IG9rOiB0cnVlLCBhbHJlYWR5QWN0aXZlOiB0cnVlIH07CiAgICAgIHZhciBoID0gZmluZEhlYWRpbmcoZ3JvdXBOYW1lKTsKICAgICAgaWYgKCFoKSByZXR1cm4geyBvazogZmFsc2UsIHJlYXNvbjogInRoZSAnIiArIGdyb3VwTmFtZSArICInIGZhY2V0IGdyb3VwIHdhcyBub3QgZm91bmQgaW4gdGhlIHNpZGViYXIiIH07CiAgICAgIHZhciBjb250YWluZXIgPSBncm91cENvbnRhaW5lcihoKTsKICAgICAgdmFyIGxhYmVsID0gZmluZExhYmVsKGNvbnRhaW5lciwgdmFsdWUpOwogICAgICBpZiAoIWxhYmVsKSB7CiAgICAgICAgZXhwYW5kR3JvdXAoY29udGFpbmVyKTsKICAgICAgICBhd2FpdCBzbGVlcCg2MDApOwogICAgICAgIGxhYmVsID0gZmluZExhYmVsKGNvbnRhaW5lciwgdmFsdWUpOwogICAgICB9CiAgICAgIGlmICghbGFiZWwpIHJldHVybiB7IG9rOiBmYWxzZSwgcmVhc29uOiAiZmFjZXQgb3B0aW9uICciICsgdmFsdWUgKyAiJyB3YXMgbm90IGZvdW5kIHVuZGVyICciICsgZ3JvdXBOYW1lICsgIiciIH07CiAgICAgIHNjcm9sbFRvKGxhYmVsKTsKICAgICAgaGlnaGxpZ2h0KGxhYmVsKTsKICAgICAgdmFyIGJ0biA9IGxhYmVsLnF1ZXJ5U2VsZWN0b3IoImJ1dHRvbiIpIHx8IGxhYmVsOwogICAgICB2YXIgYmVmb3JlID0gc25hcHNob3QoKTsKICAgICAgYnRuLmNsaWNrKCk7CiAgICAgIHZhciBjaGFuZ2VkID0gYXdhaXQgc2V0dGxlKGJlZm9yZSwgOTAwMCk7CiAgICAgIHJldHVybiB7IG9rOiB0cnVlLCBhbHJlYWR5QWN0aXZlOiBmYWxzZSwgY2hhbmdlZDogY2hhbmdlZCB9OwogICAgfQogICAgZnVuY3Rpb24gbmF2QnV0dG9ucygpIHsKICAgICAgdmFyIG5hdiA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoIm1haW4gbmF2IikgfHwgZG9jdW1lbnQucXVlcnlTZWxlY3RvcigibmF2Iik7CiAgICAgIHJldHVybiBuYXYgPyBuYXYucXVlcnlTZWxlY3RvckFsbCgiYnV0dG9uIikgOiBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCJtYWluIGJ1dHRvbiIpOwogICAgfQogICAgZnVuY3Rpb24gZmluZFBhZ2VCdXR0b24obGFiZWwpIHsKICAgICAgdmFyIGJ0bnMgPSBuYXZCdXR0b25zKCk7CiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYnRucy5sZW5ndGg7IGkrKykgewogICAgICAgIGlmICh0eHQoYnRuc1tpXSkgPT09IGxhYmVsKSByZXR1cm4gYnRuc1tpXTsKICAgICAgfQogICAgICByZXR1cm4gbnVsbDsKICAgIH0KICAgIGZ1bmN0aW9uIGZpbmRTdGVwQnV0dG9uKGRpcmVjdGlvbikgewogICAgICB2YXIgYnRucyA9IG5hdkJ1dHRvbnMoKTsKICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBidG5zLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgdmFyIHQgPSAodHh0KGJ0bnNbaV0pICsgIiAiICsgKGJ0bnNbaV0uZ2V0QXR0cmlidXRlKCJhcmlhLWxhYmVsIikgfHwgIiIpKS50b0xvd2VyQ2FzZSgpOwogICAgICAgIGlmICh0LmluZGV4T2YoZGlyZWN0aW9uKSAhPT0gLTEpIHJldHVybiBidG5zW2ldOwogICAgICB9CiAgICAgIHJldHVybiBudWxsOwogICAgfQogICAgZnVuY3Rpb24gY3VycmVudFBhZ2UoKSB7CiAgICAgIHZhciBjID0gcmVhZENvdW50KCk7CiAgICAgIGlmICghYyB8fCAhYy5mcm9tKSByZXR1cm4gMTsKICAgICAgcmV0dXJuIE1hdGguZmxvb3IoKGMuZnJvbSAtIDEpIC8gUEFHRV9TSVpFKSArIDE7CiAgICB9CiAgICBhc3luYyBmdW5jdGlvbiBnb1RvUGFnZSh0YXJnZXQpIHsKICAgICAgdmFyIG5vdGUgPSBudWxsOwogICAgICBmb3IgKHZhciBndWFyZCA9IDA7IGd1YXJkIDwgMTU7IGd1YXJkKyspIHsKICAgICAgICB2Y
22947XIgY3VyID0gY3VycmVudFBhZ2UoKTsKICAgICAgICBpZiAoY3VyID09PSB0YXJnZXQpIHJldHVybiB7IGFwcGxpZWQ6IHRydWUsIG5vdGU6IG5vdGUsIGN1cnJlbnQ6IGN1ciB9OwogICAgICAgIHZhciBkaXJlY3QgPSBmaW5kUGFnZUJ1dHRvbihTdHJpbmcodGFyZ2V0KSk7CiAgICAgICAgdmFyIHN0ZXBwZXIgPSBkaXJlY3QgPyBudWxsIDogZmluZFN0ZXBCdXR0b24oY3VyIDwgdGFyZ2V0ID8gIm5leHQiIDogInByZXZpb3VzIik7CiAgICAgICAgdmFyIGJ0biA9IGRpcmVjdCB8fCBzdGVwcGVyOwogICAgICAgIGlmICghYnRuIHx8IGJ0bi5kaXNhYmxlZCkgewogICAgICAgICAgbm90ZSA9ICJjb3VsZCBub3QgcmVhY2ggcGFnZSAiICsgdGFyZ2V0ICsgIjsgdGhlIHBhZ2luYXRpb24gY29udHJvbCBzdG9wcGVkIGF0IHBhZ2UgIiArIGN1cjsKICAgICAgICAgIHJldHVybiB7IGFwcGxpZWQ6IGZhbHNlLCBub3RlOiBub3RlLCBjdXJyZW50OiBjdXIgfTsKICAgICAgICB9CiAgICAgICAgc2Nyb2xsVG8oYnRuKTsKICAgICAgICB2YXIgYmVmb3JlID0gc25hcHNob3QoKTsKICAgICAgICBidG4uY2xpY2soKTsKICAgICAgICBhd2FpdCBzZXR0bGUoYmVmb3JlLCA5MDAwKTsKICAgICAgfQogICAgICByZXR1cm4geyBhcHBsaWVkOiBjdXJyZW50UGFnZSgpID09PSB0YXJnZXQsIG5vdGU6IG5vdGUgfHwgInBhZ2luYXRpb24gZ3VhcmQgbGltaXQgcmVhY2hlZCIsIGN1cnJlbnQ6IGN1cnJlbnRQYWdlKCkgfTsKICAgIH0KCiAgICB0cnkgewogICAgICBpZiAoYXJncy50b3BpYyAmJiBUT1BJQ1MuaW5kZXhPZihhcmdzLnRvcGljKSA9PT0gLTEpIHsKICAgICAgICByZXR1cm4gb3V0KHsKICAgICAgICAgIHN0YXR1czogImVycm9yIiwgYWN0aW9uOiAidmFsaWRhdGUgdG9waWMgYXJndW1lbnQiLAogICAgICAgICAgcmVhc29uOiAidG9waWMgJyIgKyBhcmdzLnRvcGljICsgIicgaXMgbm90IGFuIGFjY2VwdGVkIGZhY2V0IHZhbHVlIiwKICAgICAgICAgIGF2YWlsYWJsZU9wdGlvbnM6IHsgdG9waWM6IFRPUElDUyB9LAogICAgICAgICAgbmV4dFN0ZXA6ICJjYWxsIGFnYWluIHdpdGggdG9waWMgc2V0IHRvIG9uZSBvZiBhdmFpbGFibGVPcHRpb25zLnRvcGljIiwKICAgICAgICAgIHVybDogbG9jYXRpb24uaHJlZiwgc3VtbWFyeTogImludmFsaWQgdG9waWM7IHNlZSBhdmFpbGFibGVPcHRpb25zIgogICAgICAgIH0pOwogICAgICB9CiAgICAgIGlmIChhcmdzLnByb2R1Y3QgJiYgUFJPRFVDVFMuaW5kZXhPZihhcmdzLnByb2R1Y3QpID09PSAtMSkgewogICAgICAgIHJldHVybiBvdXQoewogICAgICAgICAgc3RhdHVzOiAiZXJyb3IiLCBhY3Rpb246ICJ2YWxpZGF0ZSBwcm9kdWN0IGFyZ3VtZW50IiwKICAgICAgICAgIHJlYXNvbjogInByb2R1Y3QgJyIgKyBhcmdzLnByb2R1Y3QgKyAiJyBpcyBub3QgYW4gYWNjZXB0ZWQgZmFjZXQgdmFsdWUiLAogICAgICAgICAgYXZhaWxhYmxlT3B0aW9uczogeyBwcm9kdWN0OiBQUk9EVUNUUyB9LAogICAgICAgICAgbmV4dFN0ZXA6ICJjYWxsIGFnYWluIHdpdGggcHJvZHVjdCBzZXQgdG8gb25lIG9mIGF2YWlsYWJsZU9wdGlvbnMucHJvZHVjdCIsCiAgICAgICAgICB1cmw6IGxvY2F0aW9uLmhyZWYsIHN1bW1hcnk6ICJpbnZhbGlkIHByb2R1Y3Q7IHNlZSBhdmFpbGFibGVPcHRpb25zIgogICAgICAgIH0pOwogICAgICB9CiAgICAgIGlmIChhcmdzLnBhZ2UgIT0gbnVsbCAmJiAodHlwZW9mIGFyZ3MucGFnZSAhPT0gIm51bWJlciIgfHwgIWlzRmluaXRlKGFyZ3MucGFnZSkgfHwgYXJncy5wYWdlIDwgMSB8fCBNYXRoLmZsb29yKGFyZ3MucGFnZSkgIT09IGFyZ3MucGFnZSkpIHsKICAgICAgICByZXR1cm4gb3V0KHsKICAgICAgICAgIHN0YXR1czogImVycm9yIiwgYWN0aW9uOiAidmFsaWRhdGUgcGFnZSBhcmd1bWVudCIsCiAgICAgICAgICByZWFzb246ICJwYWdlIG11c3QgYmUgYSB3aG9sZSBudW1iZXIgb2YgMSBvciBncmVhdGVyICgxMiByZXN1bHRzIHBlciBwYWdlKSIsCiAgICAgICAgICBuZXh0U3RlcDogImNhbGwgYWdhaW4gd2l0aCBwYWdlIG9taXR0ZWQgb3Igc2V0IHRvIGFuIGludGVnZXIgPj0gMSIsCiAgICAgICAgICB1cmw6IGxvY2F0aW9uLmhyZWYsIHN1bW1hcnk6ICJpbnZhbGlkIHBhZ2UgYXJndW1lbnQiCiAgICAgICAgfSk7CiAgICAgIH0KCiAgICAgIHZhciBpbnB1dCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoImFzaWRlIGZvcm0gaW5wdXRbdHlwZT0nc2VhcmNoJ10iKSB8fAogICAgICAgIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoImZvcm0gaW5wdXRbdHlwZT0nc2VhcmNoJ10iKSB8fAogICAgICAgIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoImlucHV0W3R5cGU9J3NlYXJjaCddIik7CiAgICAgIGlmICghaW5wdXQpIHsKICAgICAgICByZXR1cm4gb3V0KHsKICAgICAgICAgIHN0YXR1czogImVycm9yIiwgYWN0aW9uOiAibG9jYXRlIHRoZSBzaXRlIHNlYXJjaCBib3giLAogICAgICAgICAgcmVhc29uOiAibm8gc2VhcmNoIGlucHV0IChhc2lkZSBmb3JtIGlucHV0W3R5cGU9J3NlYXJjaCddKSBleGlzdHMgb24gdGhpcyBwYWdlIiwKICAgICAgICAgIG5leHRTdGVwOiAibmF2aWdhdGUgdG8gaHR0cHM6Ly93d3cub3B0aW1pemVseS5jb20vc2VhcmNoLyBhbmQgY2FsbCB0aGlzIHRvb2wgYWdhaW4iLAogICAgICAgICAgdXJsOiBsb2NhdGlvbi5ocmVmLCBzdW1tYXJ5OiAic2VhcmNoIGJveCBub3QgZm91bmQgb24gdGhpcyBwYWdlIgogICAgICAgIH0pOwogICAgICB9CgogICAgICB2YXIgZm9ybSA9IGlucHV0LmNsb3Nlc3QoImZvcm0iKSB8fCBpbnB1dC5wYXJlbnRFbGVtZW50OwogICAgICBzY3JvbGxUbyhmb3JtIHx8IGlucHV0KTsKICAgICAgaGlnaGxpZ2h0KGZvcm0gfHwgaW5wdXQpOwogICAgICBhd2FpdCBzbGVlcCgzMDApOwoKICAgICAgdmFyIG5vdGVzID0gW107CiAgICAgIHZhciB3YW50Q2xlYXIgPSAodHlwZW9mIGFyZ3MuY2xlYXJGaWx0ZXJzID09PSAiYm9vbGVhbiIpID8gYXJncy5jbGVhckZpbHRlcnMgOiAhIShhcmdzLnRvcGljIHx8IGFyZ3MucHJvZHVjdCk7CiAgICAgIGlmICh3YW50Q2xlYXIgJiYgYW55RmlsdGVyQWN0aXZlKCkpIHsKICAgICAgICB2Y
22947XIgY2xlYXJCdG4gPSBmaW5kQ2xlYXJCdXR0b24oKTsKICAgICAgICBpZiAoY2xlYXJCdG4pIHsKICAgICAgICAgIHZhciBiZWZvcmVDbGVhciA9IHNuYXBzaG90KCk7CiAgICAgICAgICBjbGVhckJ0bi5jbGljaygpOwogICAgICAgICAgYXdhaXQgc2V0dGxlKGJlZm9yZUNsZWFyLCA4MDAwKTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgbm90ZXMucHVzaCgibm8gJ0NsZWFyJyBidXR0b24gd2FzIHZpc2libGUsIGV4aXN0aW5nIGZpbHRlcnMgd2VyZSBsZWZ0IGluIHBsYWNlIik7CiAgICAgICAgfQogICAgICB9CgogICAgICB2YXIgcXVlcnlBcHBsaWVkID0gbnVsbDsKICAgICAgaWYgKHR5cGVvZiBhcmdzLnF1ZXJ5ID09PSAic3RyaW5nIiAmJiBhcmdzLnF1ZXJ5Lmxlbmd0aCkgewogICAgICAgIHZhciBmaWxsZWQgPSBmYWxzZTsKICAgICAgICBpZiAodHlwZW9mIGZpbGxJbnB1dCA9PT0gImZ1bmN0aW9uIikgewogICAgICAgICAgdHJ5IHsKICAgICAgICAgICAgZmlsbElucHV0KCJhc2lkZSBmb3JtIGlucHV0W3R5cGU9J3NlYXJjaCddIiwgYXJncy5xdWVyeSk7CiAgICAgICAgICAgIGZpbGxlZCA9IGlucHV0LnZhbHVlID09PSBhcmdzLnF1ZXJ5OwogICAgICAgICAgfSBjYXRjaCAoZSkgeyBmaWxsZWQgPSBmYWxzZTsgfQogICAgICAgIH0KICAgICAgICBpZiAoIWZpbGxlZCkgewogICAgICAgICAgdHJ5IHsKICAgICAgICAgICAgdmFyIHNldHRlciA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3Iod2luZG93LkhUTUxJbnB1dEVsZW1lbnQucHJvdG90eXBlLCAidmFsdWUiKS5zZXQ7CiAgICAgICAgICAgIHNldHRlci5jYWxsKGlucHV0LCBhcmdzLnF1ZXJ5KTsKICAgICAgICAgIH0gY2F0Y2ggKGUyKSB7IGlucHV0LnZhbHVlID0gYXJncy5xdWVyeTsgfQogICAgICAgICAgaW5wdXQuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoImlucHV0IiwgeyBidWJibGVzOiB0cnVlIH0pKTsKICAgICAgICAgIGlucHV0LmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCJjaGFuZ2UiLCB7IGJ1YmJsZXM6IHRydWUgfSkpOwogICAgICAgIH0KICAgICAgICB0cnkgeyBpbnB1dC5mb2N1cygpOyB9IGNhdGNoIChlMykge30KICAgICAgICB2Y
22947XIgYmVmb3JlU2VhcmNoID0gc25hcHNob3QoKTsKICAgICAgICBbImtleWRvd24iLCAia2V5cHJlc3MiLCAia2V5dXAiXS5mb3JFYWNoKGZ1bmN0aW9uICh0eXBlKSB7CiAgICAgICAgICBpbnB1dC5kaXNwYXRjaEV2ZW50KG5ldyBLZXlib2FyZEV2ZW50KHR5cGUsIHsKICAgICAgICAgICAga2V5OiAiRW50ZXIiLCBjb2RlOiAiRW50ZXIiLCBrZXlDb2RlOiAxMywgd2hpY2g6IDEzLCBidWJibGVzOiB0cnVlLCBjYW5jZWxhYmxlOiB0cnVlCiAgICAgICAgICB9KSk7CiAgICAgICAgfSk7CiAgICAgICAgaWYgKGZvcm0gJiYgZm9ybS50YWdOYW1lID09PSAiRk9STSIpIHsKICAgICAgICAgIHRyeSB7IGZvcm0uZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoInN1Ym1pdCIsIHsgYnViYmxlczogdHJ1ZSwgY2FuY2VsYWJsZTogdHJ1ZSB9KSk7IH0gY2F0Y2ggKGU0KSB7fQogICAgICAgIH0KICAgICAgICBhd2FpdCBzZXR0bGUoYmVmb3JlU2VhcmNoLCAxMDAwMCk7CiAgICAgICAgcXVlcnlBcHBsaWVkID0gaW5wdXQudmFsdWU7CiAgICAgIH0KCiAgICAgIHZhciBhcHBsaWVkRmlsdGVycyA9IFtdOwogICAgICBpZiAoYXJncy50b3BpYykgewogICAgICAgIHZhciB0UmVzID0gYXdhaXQgYXBwbHlGYWNldCgiVG9waWNzIiwgInRvcGljIiwgYXJncy50b3BpYyk7CiAgICAgICAgaWYgKHRSZXMub2spIGFwcGxpZWRGaWx0ZXJzLnB1c2goeyBncm91cDogIlRvcGljIiwgdmFsdWU6IGFyZ3MudG9waWMsIGFscmVhZHlBY3RpdmU6ICEhdFJlcy5hbHJlYWR5QWN0aXZlIH0pOwogICAgICAgIGVsc2Ugbm90ZXMucHVzaCh0UmVzLnJlYXNvbik7CiAgICAgIH0KICAgICAgaWYgKGFyZ3MucHJvZHVjdCkgewogICAgICAgIHZhciBwUmVzID0gYXdhaXQgYXBwbHlGYWNldCgiUHJvZHVjdHMiLCAicHJvZHVjdCIsIGFyZ3MucHJvZHVjdCk7CiAgICAgICAgaWYgKHBSZXMub2spIGFwcGxpZWRGaWx0ZXJzLnB1c2goeyBncm91cDogIlByb2R1Y3QiLCB2YWx1ZTogYXJncy5wcm9kdWN0LCBhbHJlYWR5QWN0aXZlOiAhIXBSZXMuYWxyZWFkeUFjdGl2ZSB9KTsKICAgICAgICBlbHNlIG5vdGVzLnB1c2gocFJlcy5yZWFzb24pOwogICAgICB9CgogICAgICB2YXIgcGFn
22947ZVJlc3VsdCA9IHsgYXBwbGllZDogdHJ1ZSwgbm90ZTogbnVsbCwgY3VycmVudDogY3VycmVudFBhZ2UoKSB9OwogICAgICBpZiAoYXJncy5wYWdlICYmIGFyZ3MucGFnZSA+IDApIHsKICAgICAgICBwYWdlUmVzdWx0ID0gYXdhaXQgZ29Ub1BhZ2UoYXJncy5wYWdlKTsKICAgICAgICBpZiAocGFnZVJlc3VsdC5ub3RlKSBub3Rlcy5wdXNoKHBhZ2VSZXN1bHQubm90ZSk7CiAgICAgIH0KCiAgICAgIHZhciBvYnNlcnZlZCA9IHRydWU7CiAgICAgIHRyeSB7CiAgICAgICAgYXdhaXQgd2FpdEZvcihSRVNVTFRTX1NFTEVDVE9SLCAxMDAwMCk7CiAgICAgIH0gY2F0Y2ggKGU1KSB7CiAgICAgICAgb2JzZXJ2ZWQgPSBmYWxzZTsKICAgICAgfQogICAgICBhd2FpdCBzbGVlcCg0MDApOwoKICAgICAgdmFyIGNvdW50ID0gcmVhZENvdW50KCk7CiAgICAgIHZhciBjYXJkcyA9IHJlYWRDYXJkcygpOwogICAgICB2YXIgbWFpbiA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoIm1haW4iKSB8fCBkb2N1bWVudC5ib2R5OwogICAgICBpZiAoY2FyZHMubGVuZ3RoKSB7CiAgICAgICAgc2Nyb2xsVG8obWFpbik7CiAgICAgICAgaGlnaGxpZ2h0KG1haW4pOwogICAgICB9CgogICAgICB2YXIgdHJ1bmNhdGVkID0gY2FyZHMubGVuZ3RoID4gMTAgfHwgKGNvdW50ICYmIGNvdW50LnRvdGFsID4gY2FyZHMubGVuZ3RoKTsKICAgICAgdmFyIHJlc3VsdHMgPSBjYXJkcy5zbGljZSgwLCAxMCk7CiAgICAgIHZhciBhY3Rpb25QYXJ0cyA9IFtdOwogICAgICBpZiAocXVlcnlBcHBsaWVkICE9IG51bGwpIGFjdGlvblBhcnRzLnB1c2goInNlYXJjaGVkIGZvciAnIiArIHRydW5jKHF1ZXJ5QXBwbGllZCwgODApICsgIiciKTsKICAgICAgaWYgKGFwcGxpZWRGaWx0ZXJzLmxlbmd0aCkgewogICAgICAgIGFjdGlvblBhcnRzLnB1c2goImFwcGxpZWQgIiArIGFwcGxpZWRGaWx0ZXJzLm1hcChmdW5jdGlvbiAoZikgeyByZXR1cm4gZi5ncm91cCArICI9IiArIGYudmFsdWU7IH0pLmpvaW4oIiArICIpKTsKICAgICAgfQogICAgICBpZiAoYXJncy5wYWdlKSBhY3Rpb25QYXJ0cy5wdXNoKCJvcGVuZWQgcGFnZSAiICsgYXJncy5wYWdlKTsKICAgICAgaWYgKCFhY3Rpb25QYXJ0cy5sZW5ndGgpIGFjdGlvblBhcnRzLnB1c2goInJlYWQgdGhlIGN1cnJlbnQgc2VhcmNoIHJlc3VsdHMiKTsKICAgICAgdmFyIGFjdGlvbiA9IGFjdGlvblBhcnRzLmpvaW4oIiwgIikgKyAiIG9uIHRoZSBzaXRlIHNlYXJjaCBwYWdlIjsKCiAgICAgIGlmICghb2JzZXJ2ZWQgJiYgIWNhcmRzLmxlbmd0aCkgewogICAgICAgIHJldHVybiBvdXQoewogICAgICAgICAgc3RhdHVzOiAicGFydGlhbCIsIG9ic2VydmVkOiBmYWxzZSwgYWN0aW9uOiBhY3Rpb24sCiAgICAgICAgICBxdWVyeTogcXVlcnlBcHBsaWVkICE9IG51bGwgPyBxdWVyeUFwcGxpZWQgOiAoaW5wdXQudmFsdWUgfHwgbnVsbCksCiAgICAgICAgICB0b3BpYzogYXJncy50b3BpYyB8fCBudWxsLCBwcm9kdWN0OiBhcmdzLnByb2R1Y3QgfHwgbnVsbCwKICAgICAgICAgIHBhZ2U6IGFyZ3MucGFnZSB8fCBudWxsLCBhcHBsaWVkRmlsdGVyczogYXBwbGllZEZpbHRlcnMsCiAgICAgICAgICBzaG93aW5nOiBjb3VudCA/IGNvdW50LnNob3dpbmcgOiBudWxsLAogICAgICAgICAgcmVzdWx0Q291bnQ6IGNvdW50ID8gY291bnQudG90YWwgOiBudWxsLAogICAgICAgICAgcmVzdWx0czogW10sIHRydW5jYXRlZDogZmFsc2UsCiAgICAgICAgICBub3Rlczogbm90ZXMsCiAgICAgICAgICByZWFzb246ICJ0aGUgcmVzdWx0cyBncmlkIGRpZCBub3QgcmVuZGVyIGFueSBjYXJkcyBiZWZvcmUgdGhlIHdhaXQgdGltZWQgb3V0IiwKICAgICAgICAgIG5leHRTdGVwOiAicmV0cnkgdGhlIGNhbGwsIG9yIGxvb3NlbiB0aGUgcXVlcnkvZmlsdGVycyAtIHRoZSBjdXJyZW50IGNvbWJpbmF0aW9uIG1heSBoYXZlIG5vIG1hdGNoZXMiLAogICAgICAgICAgdXJsOiBsb2NhdGlvbi5ocmVmLAogICAgICAgICAgc3VtbWFyeTogImludGVyYWN0aW9uIHJhbiBidXQgbm8gcmVzdWx0IGNhcmRzIGNvdWxkIGJlIHJlYWQgZnJvbSB0aGUgcGFnZSIKICAgICAgICB9KTsKICAgICAgfQoKICAgICAgdmFyIG5vUmVzdWx0cyA9IGNhcmRzLmxlbmd0aCA9PT0gMCAmJiBjb3VudCAmJiBjb3VudC50b3RhbCA9PT0gMDsKICAgICAgdmFyIHN0YXR1cyA9IChjYXJkcy5sZW5ndGggfHwgbm9SZXN1bHRzKSA/IChub3Rlcy5sZW5ndGggPyAicGFydGlhbCIgOiAib2siKSA6ICJwYXJ0aWFsIjsKCiAgICAgIHJldHVybiBvdXQoewogICAgICAgIHN0YXR1czogc3RhdHVzLAogICAgICAgIG9ic2VydmVkOiB0cnVlLAogICAgICAgIGFjdGlvbjogYWN0aW9uLAogICAgICAgIHF1ZXJ5OiBxdWVyeUFwcGxpZWQgIT0gbnVsbCA/IHF1ZXJ5QXBwbGllZCA6IChpbnB1dC52YWx1ZSB8fCBudWxsKSwKICAgICAgICB0b3BpYzogYXJncy50b3BpYyB8fCBudWxsLAogICAgICAgIHByb2R1Y3Q6IGFyZ3MucHJvZHVjdCB8fCBudWxsLAogICAgICAgIHBhZ2U6IGFyZ3MucGFnZSB8fCBwYWdlUmVzdWx0LmN1cnJlbnQgfHwgMSwKICAgICAgICBhcHBsaWVkRmlsdGVyczogYXBwbGllZEZpbHRlcnMsCiAgICAgICAgc2hvd2luZzogY291bnQgPyBjb3VudC5zaG93aW5nIDogbnVsbCwKICAgICAgICByZXN1bHRDb3VudDogY291bnQgPyBjb3VudC50b3RhbCA6IGNhcmRzLmxlbmd0aCwKICAgICAgICByZXN1bHRzOiByZXN1bHRzLAogICAgICAgIHRydW5jYXRlZDogISF0cnVuY2F0ZWQsCiAgICAgICAgbm90ZXM6IG5vdGVzLAogICAgICAgIHVybDogbG9jYXRpb24uaHJlZiwKICAgICAgICBzdW1tYXJ5OiAoY291bnQgPyBjb3VudC50b3RhbCA6IGNhcmRzLmxlbmd0aCkgKyAiIG1hdGNoaW5nIHJlc291cmNlczsgcmV0dXJuaW5nICIgKyByZXN1bHRzLmxlbmd0aCArCiAgICAgICAgICAiIGNhcmQiICsgKHJlc3VsdHMubGVuZ3RoID09PSAxID8gIiIgOiAicyIpICsgIiByZWFkIGZyb20gdGhlIHBhZ2UiCiAgICAgIH0pOwogICAgfSBjYXRjaCAoZXJyKSB7CiAgICAgIHJldHVybiBvdXQoewogICAgICAgIHN0YXR1czogImVycm9yIiwKICAgICAgICBhY3Rpb246ICJzZWFyY2ggYW5kIGZpbHRlciB0aGUgT3B0aW1pemVseSByZXNvdXJjZSBsaWJyYXJ5IiwKICAgICAgICByZWFzb246IChlcnIgJiYgZXJyLm1lc3NhZ2UpID8gdHJ1bmMoZXJyLm1lc3NhZ2UsIDMwMCkgOiAidW5leHBlY3RlZCBmYWlsdXJlIHdoaWxlIGRyaXZpbmcgdGhlIHNlYXJjaCBwYWdlIiwKICAgICAgICBuZXh0U3RlcDogIm1ha2Ugc3VyZSBodHRwczovL3d3dy5vcHRpbWl6ZWx5LmNvbS9zZWFyY2gvIGlzIGZ1bGx5IGxvYWRlZCwgdGhlbiBjYWxsIHRoaXMgdG9vbCBhZ2FpbiIsCiAgICAgICAgdXJsOiBsb2NhdGlvbi5ocmVmLAogICAgICAgI
22947HN1bW1hcnk6ICJ0aGUgc2VhcmNoIGludGVyYWN0aW9uIGZhaWxlZCBiZWZvcmUgcmVzdWx0cyBjb3VsZCBiZSByZWFkIgogICAgICB9KTsKICAgIH0KICB9Cn0pOwoKICAvKiBSZWdpc3RlciB0aGUgYWN0aXZlIHJvdXRlJ3MgdG9vbHMsIHRoZW4gd2F0Y2ggZm9yIFNQQSByb3V0ZSBjaGFuZ2VzLiAqLwogIHRyeSB7IF9zeW5jUm91dGUoKTsgfSBjYXRjaCAoZSkgeyAvKiBuZXZlciBicmVhayByZWdpc3RyYXRpb24gKi8gfQogIHRyeSB7IF9pbnN0YWxsUm91dGVyKCk7IH0gY2F0Y2ggKGUpIHsgLyogbmV2ZXIgYnJlYWsgcmVnaXN0cmF0aW9uICovIH0KICAvKiBTaWduYWwgdGhhdCB0aGlzIHBhZ2UncyB0b29sIHNldCBpcyBub3cgcmVnaXN0ZXJlZC4gKi8KICBfZGlzcGF0Y2hUb29sY2hhbmdlKCk7Cn0pKCk7Cg==')));document.head.appendChild(s);})();
22948}, "id": "3845aeda-e788-4f4e-ae59-5b4fb9abf48d", "dependencies": []}]}, {"viewId": "5585749758705664", "changes": [{"type": "custom_code", "value": function($){(function(){var s=document.createElement('script');s.type='text/javascript';s.text=decodeURIComponent(escape(atob('LyogV2ViTUNQIHRvb2xzIGZvciBvcHRpbWl6ZWx5IC0gZ2VuZXJhdGVkIGJ5IHRoZSBPcGFsIFdlYk1DUCBTaXRlIEFuYWx5emVyICovCihmdW5jdGlvbiAoKSB7CiAgJ3VzZSBzdHJpY3QnOwogIHZhciBfbWNSYXcgPSAodHlwZW9mIGRvY3VtZW50ICE9PSAndW5kZWZpbmVkJyAmJiBkb2N1bWVudC5tb2RlbENvbnRleHQpIHx8CiAgICAgICAgICAgICAgICh0eXBlb2YgbmF2aWdhdG9yICE9PSAndW5kZWZpbmVkJyAmJiBuYXZpZ2F0b3IubW9kZWxDb250ZXh0KTsKICBpZiAoIV9tY1JhdyB8fCB0eXBlb2YgX21jUmF3LnJlZ2lzdGVyVG9vbCAhPT0gJ2Z1bmN0aW9uJykgewogICAgY29uc29sZS53YXJuKCdbV2ViTUNQXSBObyBtb2RlbENvbnRleHQgYXZhaWxhYmxlOyB0b29scyBub3QgcmVnaXN0ZXJlZC4nKTsKICAgIHJldHVybjsKICB9CgogIGZ1bmN0aW9uIHRyYWNrV2ViTUNQVG9vbChuYW1lKSB7CiAgICB0cnkgewogICAgICB3aW5kb3cub3B0aW1pemVseSA9IHdpbmRvdy5vcHRpbWl6ZWx5IHx8IFtdOwogICAgICB3aW5kb3cub3B0aW1pemVseS5wdXNoKHsKICAgICAgICB0eXBlOiAnZXZlbnQnLCBldmVudE5hbWU6ICdXZWJNQ1BUb29sJywKICAgICAgICBwcm9wZXJ0aWVzOiB7IFdlYk1DUFRvb2w6IG5hbWUgfHwgJ3Vua25vd24nIH0KICAgICAgfSk7CiAgICB9IGNhdGNoIChlKSB7IC8qIHRyYWNraW5nIG11c3QgbmV2ZXIgYnJlYWsgYSB0b29sICovIH0KICB9CgogIGZ1bmN0aW9uIF93cmFwSGFuZGxlcihmbiwgdG9vbE5hbWUpIHsKICAgIHJldHVybiBmdW5jdGlvbiAoKSB7IHRyYWNrV2ViTUNQVG9vbCh0b29sTmFtZSk7IHJldHVybiBmbi5hcHBseSh0aGlzLCBhcmd1bWVudHMpOyB9OwogIH0KCiAgZnVuY3Rpb24gX3BhdGhNYXRjaGVzKHBhdHRlcm4pIHsKICAgIGlmICghcGF0dGVybiB8fCBwYXR0ZXJuID09PSAnLyoqJykgeyByZXR1cm4gdHJ1ZTsgfQogICAgdmFyIHBhdGggPSBsb2NhdGlvbi5wYXRobmFtZS5yZXBsYWNlKC9cLyskLywgJycpIHx8ICcvJzsKICAgIHZhciBwYXQgID0gU3RyaW5nKHBhdHRlcm4pLnJlcGxhY2UoL1wvKyQvLCAnJykgfHwgJy8nOwogICAgaWYgKHBhdCA9PT0gJy8nKSB7IHJldHVybiBwYXRoID09PSAnLyc7IH0KICAgIHZhciByeCA9IHBhdC5zcGxpdCgnKionKS5tYXAoZnVuY3Rpb24gKGNodW5rKSB7CiAgICAgIHJldHVybiBjaHVuay5zcGxpdCgnKicpLm1hcChmdW5jdGlvbiAobGl0KSB7CiAgICAgICAgcmV0dXJuIGxpdC5yZXBsYWNlKC9bLiorP14ke30oKXxbXF1cXF0vZywgJ1xcJCYnKTsKICAgICAgfSkuam9pbignW14vXSonKTsKICAgIH0pLmpvaW4oJy4qJyk7CiAgICB0cnkgeyByZXR1cm4gbmV3IFJlZ0V4cCgnXicgKyByeCArICckJykudGVzdChwYXRoKTsgfSBjYXRjaCAoZSkgeyByZXR1cm4gdHJ1ZTsgfQogIH0KCiAgLyogUm91dGUtc2NvcGVkIHRvb2xzIGFyZSBSRUNPUkRFRCBoZXJlLCBub3QgcmVnaXN0ZXJlZCBlYWdlcmx5LCBzbyBhIHJlYWwgU1BBCiAgICAgKGNsaWVudC1zaWRlIHJvdXRlIGNoYW5nZXMgdmlhIGhpc3RvcnkucHVzaFN0YXRlL3JlcGxhY2VTdGF0ZSwgV0lUSE9VVCBhCiAgICAgZnVsbCBwYWdlIHJlbG9hZCkgY2FuIHJlZ2lzdGVyIGEgcm91dGUncyB0b29scyB0aGUgbW9tZW50IGl0cyBwYXRoIGJlY29tZXMKICAgICBhY3RpdmUuIFRoZSBtb2RlbENvbnRleHQgQVBJIGV4cG9zZXMgb25seSByZWdpc3RlclRvb2wgLS0gdGhlcmUgaXMgTk8KICAgICB1bnJlZ2lzdGVyIC0tIHNvIHJlZ2lzdHJhdGlvbiBpcyByZWdpc3Rlci1vbmNlLWFjY3VtdWxhdGU6IF9zeW5jUm91dGUgaXMKICAgICBpZGVtcG90ZW50IHZpYSBlYWNoIGVudHJ5J3MgYHJlZ2lzdGVyZWRgIGZsYWcsIGFuZCB0b29scyBmcm9tIGFuCiAgICAgYWxyZWFkeS12aXNpdGVkIHJvdXRlIHN0YXkgcmVnaXN0ZXJlZCBhZnRlciBhIHNvZnQgbmF2aWdhdGlvbiAoaGFybWxlc3M6CiAgICAgZXZlcnkgaGFuZGxlciBndWFyZHMgaXRzIERPTSB3b3JrIHdpdGggd2FpdEZvckVsZW1lbnQgKyB0aW1lb3V0KS4gKi8KICB2YXIgX3JvdXRlUmVnaXN0cnkgPSBbXTsKCiAgZnVuY3Rpb24gX3N5bmNSb3V0ZSgpIHsKICAgIHZhciBjaGFuZ2VkID0gZmFsc2U7CiAgICBmb3IgKHZhciBpID0gMDsgaSA8IF9yb3V0ZVJlZ2lzdHJ5Lmxlbmd0aDsgaSsrKSB7CiAgICAgIHZhciBlbnRyeSA9IF9yb3V0ZVJlZ2lzdHJ5W2ldOwogICAgICBpZiAoIWVudHJ5LnJlZ2lzdGVyZWQgJiYgX3BhdGhNYXRjaGVzKGVudHJ5LnBhdHRlcm4pKSB7CiAgICAgICAgdHJ5IHsKICAgICAgICAgIG1jLnJlZ2lzdGVyVG9vbChlbnRyeS5kZXNjcmlwdG9yKTsKICAgICAgICAgIGVudHJ5LnJlZ2lzdGVyZWQgPSB0cnVlOwogICAgICAgICAgY2hhbmdlZCA9IHRydWU7CiAgICAgICAgfSBjYXRjaCAoZSkgeyAvKiBvbmUgYmFkIHRvb2wgbXVzdCBuZXZlciBicmVhayB0aGUgcmVzdCAqLyB9CiAgICAgI
22948H0KICAgIH0KICAgIHJldHVybiBjaGFuZ2VkOwogIH0KCiAgLyogVGVsbCBhIGxpc3RlbmluZyBhZ2VudCB0aGUgcGFnZSdzIHRvb2wgc2V0IGNoYW5nZWQgc28gaXQgcmUtcmVhZHMgdGhlIG1vZGVsCiAgICAgY29udGV4dC4gRmlyZWQgYWZ0ZXIgdGhlIGluaXRpYWwgcmVnaXN0cmF0aW9uIEFORCBhZnRlciBldmVyeSBzb2Z0IHJvdXRlCiAgICAgY2hhbmdlLiBCZXN0LWVmZm9ydDsgbXVzdCBuZXZlciBicmVhayByZWdpc3RyYXRpb24uICovCiAgZnVuY3Rpb24gX2Rpc3BhdGNoVG9vbGNoYW5nZSgpIHsKICAgIHRyeSB7CiAgICAgIGlmICh0eXBlb2YgZG9jdW1lbnQgIT09ICd1bmRlZmluZWQnICYmIHR5cGVvZiBFdmVudCA9PT0gJ2Z1bmN0aW9uJykgewogICAgICAgIGRvY3VtZW50LmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCd0b29sY2hhbmdlJykpOwogICAgICB9CiAgICB9IGNhdGNoIChlKSB7IC8qIG5ldmVyIGJyZWFrIHJlZ2lzdHJhdGlvbiAqLyB9CiAgfQoKICAvKiBXaXJlIHRoZSBTUEEgcm91dGVyIGhvb2sgT05DRTogd3JhcCBoaXN0b3J5LnB1c2hTdGF0ZS9yZXBsYWNlU3RhdGUgYW5kCiAgICAgbGlzdGVuIGZvciBwb3BzdGF0ZS9oYXNoY2hhbmdlLCBzbyBhIGNsaWVudC1zaWRlIHJvdXRlIGNoYW5nZSByZS1ydW5zCiAgICAgX3N5bmNSb3V0ZSAocmVnaXN0ZXJpbmcgYW55IHRvb2xzIHdob3NlIHBhdHRlcm4gbm93IG1hdGNoZXMpIGFuZCByZS1zaWduYWxzCiAgICAgdG9vbGNoYW5nZS4gT24gYW4gTVBBIHRoaXMgc2ltcGx5IG5ldmVyIGZpcmVzIC0tIHRoZSBmdWxsIHJlbG9hZCByZS1ydW5zIHRoZQogICAgIHdob2xlIElJRkUgaW5zdGVhZC4gKi8KICB2YXIgX3JvdXRlckluc3RhbGxlZCA9IGZhbHNlOwogIGZ1bmN0aW9uIF9pbnN0YWxsUm91dGVyKCkgewogICAgaWYgKF9yb3V0ZXJJbnN0YWxsZWQgfHwgdHlwZW9mIHdpbmRvdyA9PT0gJ3VuZGVmaW5lZCcgfHwgIXdpbmRvdy5oaXN0b3J5KSB7IHJldHVybjsgfQogICAgX3JvdXRlckluc3RhbGxlZCA9IHRydWU7CiAgICBmdW5jdGlvbiBfb25Sb3V0ZUNoYW5nZSgpIHsKICAgICAgdHJ5IHsgX3N5bmNSb3V0ZSgpOyB9IGNhdGNoIChlKSB7IC8qIG5ldmVyIGJyZWFrICovIH0KICAgICAgX2Rpc3BhdGNoVG9vbGNoYW5nZSgpOwogICAgfQogICAgWydwdXNoU3RhdGUnLCAncmVwbGFjZVN0YXRlJ10uZm9yRWFjaChmdW5jdGlvbiAobSkgewogICAgICB2YXIgb3JpZyA9IHdpbmRvdy5oaXN0b3J5W21dOwogICAgICBpZiAodHlwZW9mIG9yaWcgIT09ICdmdW5jdGlvbicpIHsgcmV0dXJuOyB9CiAgICAgIHdpbmRvdy5oaXN0b3J5W21dID0gZnVuY3Rpb24gKCkgewogICAgICAgIHZhciByID0gb3JpZy5hcHBseSh0aGlzLCBhcmd1bWVudHMpOwogICAgICAgIHRyeSB7IF9vblJvdXRlQ2hhbmdlKCk7IH0gY2F0Y2ggKGUpIHsgLyogbmV2ZXIgYnJlYWsgbmF2aWdhdGlvbiAqLyB9CiAgICAgICAgcmV0dXJuIHI7CiAgICAgIH07CiAgICB9KTsKICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdwb3BzdGF0ZScsIF9vblJvdXRlQ2hhbmdlKTsKICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdoYXNoY2hhbmdlJywgX29uUm91dGVDaGFuZ2UpOwogIH0KCiAgdmFyIG1jID0gewogICAgcmVnaXN0ZXJUb29sOiBmdW5jdGlvbiAoZGVzY3JpcHRvcikgewogICAgICBpZiAoZGVzY3JpcHRvciAmJiB0eXBlb2YgZGVzY3JpcHRvciA9PT0gJ29iamVjdCcpIHsKICAgICAgICB2Y
22948XIgX25hbWUgPSBkZXNjcmlwdG9yLm5hbWU7CiAgICAgICAgT2JqZWN0LmtleXMoZGVzY3JpcHRvcikuZm9yRWFjaChmdW5jdGlvbiAoaykgewogICAgICAgICAgaWYgKHR5cGVvZiBkZXNjcmlwdG9yW2tdID09PSAnZnVuY3Rpb24nKSB7CiAgICAgICAgICAgIGRlc2NyaXB0b3Jba10gPSBfd3JhcEhhbmRsZXIoZGVzY3JpcHRvcltrXSwgX25hbWUpOwogICAgICAgICAgfQogICAgICAgIH0pOwogICAgICB9CiAgICAgIHJldHVybiBfbWNSYXcucmVnaXN0ZXJUb29sKGRlc2NyaXB0b3IpOwogICAgfSwKICAgIC8qIFJlY29yZCBhIHJvdXRlLXNjb3BlZCB0b29sLCB0aGVuIHJlZ2lzdGVyIGl0IGlmIGl0cyBwYXRoIGlzIGFscmVhZHkKICAgICAgIGFjdGl2ZS4gTGF0ZXIgc29mdC1uYXZpZ2F0aW9ucyBwaWNrIGl0IHVwIHZpYSBfc3luY1JvdXRlIChTUEEgc3VwcG9ydCkuICovCiAgICByZWdpc3RlclRvb2xJZjogZnVuY3Rpb24gKHBhdHRlcm4sIGRlc2NyaXB0b3IpIHsKICAgICAgX3JvdXRlUmVnaXN0cnkucHVzaCh7IHBhdHRlcm46IHBhdHRlcm4sIGRlc2NyaXB0b3I6IGRlc2NyaXB0b3IsIHJlZ2lzdGVyZWQ6IGZhbHNlIH0pOwogICAgICBfc3luY1JvdXRlKCk7CiAgICAgIHJldHVybiBudWxsOwogICAgfQogIH07CgogIGZ1bmN0aW9uIHdhaXRGb3JFbGVtZW50KHNlbGVjdG9yLCB0aW1lb3V0KSB7CiAgICB0aW1lb3V0ID0gdGltZW91dCB8fCA1MDAwOwogICAgcmV0dXJuIG5ldyBQcm9taXNlKGZ1bmN0aW9uIChyZXNvbHZlLCByZWplY3QpIHsKICAgICAgdmFyIGZvdW5kID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihzZWxlY3Rvcik7CiAgICAgIGlmIChmb3VuZCkgeyByZXR1cm4gcmVzb2x2ZShmb3VuZCk7IH0KICAgICAgdmFyIG9icyA9IG5ldyBNdXRhdGlvbk9ic2VydmVyKGZ1bmN0aW9uICgpIHsKICAgICAgICB2Y
22948XIgZWwgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKHNlbGVjdG9yKTsKICAgICAgICBpZiAoZWwpIHsgb2JzLmRpc2Nvbm5lY3QoKTsgY2xlYXJUaW1lb3V0KHRpbWVyKTsgcmVzb2x2ZShlbCk7IH0KICAgICAgfSk7CiAgICAgIG9icy5vYnNlcnZlKGRvY3VtZW50LmRvY3VtZW50RWxlbWVudCwgeyBjaGlsZExpc3Q6IHRydWUsIHN1YnRyZWU6IHRydWUgfSk7CiAgICAgIHZhciB0aW1lciA9IHNldFRpbWVvdXQoZnVuY3Rpb24gKCkgewogICAgICAgIG9icy5kaXNjb25uZWN0KCk7CiAgICAgICAgcmVqZWN0KG5ldyBFcnJvcignW1dlYk1DUF0gdGltZW91dCB3YWl0aW5nIGZvciAnICsgc2VsZWN0b3IpKTsKICAgICAgfSwgdGltZW91dCk7CiAgICB9KTsKICB9CgogIGZ1bmN0aW9uIHNldE5hdGl2ZVZhbHVlKGVsLCB2YWx1ZSkgewogICAgdmFyIGRlc2MgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKE9iamVjdC5nZXRQcm90b3R5cGVPZihlbCksICd2YWx1ZScpOwogICAgaWYgKGRlc2MgJiYgZGVzYy5zZXQpIHsgZGVzYy5zZXQuY2FsbChlbCwgdmFsdWUpOyB9IGVsc2UgeyBlbC52YWx1ZSA9IHZhbHVlOyB9CiAgfQoKICBhc3luYyBmdW5jdGlvbiBmaWxsSW5wdXQoc2VsZWN0b3IsIHZhbHVlKSB7CiAgICB2YXIgZWwgPSBhd2FpdCB3YWl0Rm9yRWxlbWVudChzZWxlY3Rvcik7CiAgICBlbC5mb2N1cygpOwogICAgc2V0TmF0aXZlVmFsdWUoZWwsIHZhbHVlKTsKICAgIGVsLmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCdpbnB1dCcsICB7IGJ1YmJsZXM6IHRydWUgfSkpOwogICAgZWwuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoJ2NoYW5nZScsIHsgYnViYmxlczogdHJ1ZSB9KSk7CiAgICByZXR1cm4gZWw7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBmaWxsU2VsZWN0KHNlbGVjdG9yLCB2YWx1ZSkgewogICAgdmFyIGVsID0gYXdhaXQgd2FpdEZvckVsZW1lbnQoc2VsZWN0b3IpOwogICAgZWwudmFsdWUgPSB2YWx1ZTsKICAgIGlmIChlbC5zZWxlY3RlZEluZGV4ID09PSAtMSkgewogICAgICB2YXIgb3B0ID0gQXJyYXkucHJvdG90eXBlLmZpbmQuY2FsbChlbC5vcHRpb25zLCBmdW5jdGlvbiAobykgewogICAgICAgIHJldHVybiBvLnRleHRDb250ZW50LnRyaW0oKS50b0xvd2VyQ2FzZSgpID09PSBTdHJpbmcodmFsdWUpLnRyaW0oKS50b0xvd2VyQ2FzZSgpOwogICAgICB9KTsKICAgICAgaWYgKG9wdCkgeyBlbC52YWx1ZSA9IG9wdC52YWx1ZTsgfQogICAgfQogICAgZWwuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoJ2NoYW5nZScsIHsgYnViYmxlczogdHJ1ZSB9KSk7CiAgICByZXR1cm4gZWw7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBmaWxsQ2hlY2tib3goc2VsZWN0b3IsIGNoZWNrZWQpIHsKICAgIHZhciBlbCA9IGF3YWl0IHdhaXRGb3JFbGVtZW50KHNlbGVjdG9yKTsKICAgIGlmIChlbC5jaGVja2VkICE9PSAhIWNoZWNrZWQpIHsgZWwuY2xpY2soKTsgfQogICAgZWwuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoJ2NoYW5nZScsIHsgYnViYmxlczogdHJ1ZSB9KSk7CiAgICByZXR1cm4gZWw7CiAgfQoKICAvKiBSZWFkIE9ORSBmaWVsZCBvZmYgT05FIGxlYWYgbm9kZS4gJ0BhdHRyJyByZWFkcyBhbiBhdHRyaWJ1dGUgb2YgdGhlIGl0ZW0KICAgICBpdHNlbGY7ICdzZWwgQGF0dHInIHJlYWRzIGFuIGF0dHJpYnV0ZSBvZiBhIGRlc2NlbmRhbnQuIE5ldmVyIHJldHVybnMgdGhlCiAgICAgY29uY2F0ZW5hdGVkIHRleHQgb2YgYSB3aG9sZSBjb250YWluZXIuICovCiAgZnVuY3Rpb24gX2ZpZWxkKHJvb3QsIHNwZWMpIHsKICAgIGlmICghc3BlYykgeyByZXR1cm4gbnVsbDsgfQogICAgc3BlYyA9IFN0cmluZyhzcGVjKS50cmltKCk7CiAgICB2YXIgYXQgPSBzcGVjLmluZGV4T2YoJ0AnKTsKICAgIGlmIChhdCA9PT0gMCkgeyByZXR1cm4gcm9vdC5nZXRBdHRyaWJ1dGUoc3BlYy5zbGljZSgxKSk7IH0KICAgIGlmIChhdCA+IDApIHsKICAgICAgdmFyIGVsMCA9IHJvb3QucXVlcnlTZWxlY3RvcihzcGVjLnNsaWNlKDAsIGF0KS50cmltKCkpOwogICAgICByZXR1cm4gZWwwID8gZWwwLmdldEF0dHJpYnV0ZShzcGVjLnNsaWNlKGF0ICsgMSkpIDogbnVsbDsKICAgIH0KICAgIHZhciBlbCA9IHJvb3QucXVlcnlTZWxlY3RvcihzcGVjKTsKICAgIGlmICghZWwpIHsgcmV0dXJuIG51bGw7IH0KICAgIHJldHVybiAoZWwudGV4dENvbnRlbnQgfHwgJycpLnJlcGxhY2UoL1xzKy9nLCAnICcpLnRyaW0oKTsKICB9CgogIGZ1bmN0aW9uIGV4dHJhY3RJdGVtcyhpdGVtU2VsZWN0b3IsIGZpZWxkTWFwLCBsaW1pdCkgewogICAgbGltaXQgPSBsaW1pdCB8fCAxMDsKICAgIHZhciBub2RlcyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoaXRlbVNlbGVjdG9yKSk7CiAgICByZXR1cm4gewogICAgICByZXN1bHRDb3VudDogbm9kZXMubGVuZ3RoLAogICAgICB0cnVuY2F0ZWQ6IG5vZGVzLmxlbmd0aCA+IGxpbWl0LAogICAgICByZXN1bHRzOiBub2Rlcy5zbGljZSgwLCBsaW1pdCkubWFwKGZ1bmN0aW9uIChuKSB7CiAgICAgICAgdmFyIG8gPSB7fTsKICAgICAgICBPYmplY3Qua2V5cyhmaWVsZE1hcCB8fCB7fSkuZm9yRWFjaChmdW5jdGlvbiAoaykgewogICAgICAgICAgdmFyIHYgPSBfZmllbGQobiwgZmllbGRNYXBba10pOwogICAgICAgICAgaWYgKHYgIT09IG51bGwgJiYgdiAhPT0gdW5kZWZpbmVkICYmIHYgIT09ICcnKSB7IG9ba10gPSBTdHJpbmcodikuc2xpY2UoMCwgMzAwKTsgfQogICAgICAgIH0pOwogICAgICAgIHJldHVybiBvOwogICAgICB9KQogICAgfTsKICB9CgogIC8qIFBhcnNlIHRoZSBpbnRlZ2VyIHRoZSBwYWdlIGl0c2VsZiBzdGF0ZXMsIGUuZy4gIjUgaXRlbXMiIC0+IDUuICovCiAgZnVuY3Rpb24gcmVhZFJlc3VsdENvdW50KHNlbGVjdG9yLCBmYWxsYmFjaykgewogICAgaWYgKCFzZWxlY3RvcikgeyByZXR1cm4gZmFsbGJhY2s7IH0KICAgIHZhciBlbCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3Ioc2VsZWN0b3IpOwogICAgaWYgKCFlbCkgeyByZXR1cm4gZmFsbGJhY2s7IH0KICAgIHZhciBtID0gKGVsLnRleHRDb250ZW50IHx8ICcnKS5yZXBsYWNlKC8sL2csICcnKS5tYXRjaCgvXGQrLyk7CiAgICByZXR1cm4gbSA/IHBhcnNlSW50KG1bMF0sIDEwKSA6IGZhbGxiYWNrOwogIH0KCiAgZnVuY3Rpb24gYnVpbGRVcmwodGVtcGxhdGUsIGFyZ3MpIHsKICAgIHJldHVybi
22948B0ZW1wbGF0ZS5yZXBsYWNlKC9ceyhcdyspXH0vZywgZnVuY3Rpb24gKF8sIGspIHsKICAgICAgcmV0dXJuIGFyZ3MgJiYgYXJnc1trXSAhPSBudWxsID8gZW5jb2RlVVJJQ29tcG9uZW50KGFyZ3Nba10pIDogJyc7CiAgICB9KS5yZXBsYWNlKC9bPyZdXHcrPSg/PSZ8JCkvZywgJycpLnJlcGxhY2UoL1w/Ji8sICc/JykucmVwbGFjZSgvWz8mXSQvLCAnJyk7CiAgfQoKICAvKiBXaG9sZS1zaXRlIGNhcGFiaWxpdHkgbWFwLCBkZXJpdmVkIG9uY2UgYXQgYXNzZW1ibGUgdGltZSBhbmQgZW1iZWRkZWQgYXMgYQogICAgIHN0YXRpYyBsaXRlcmFsIHNvIEVWRVJZIHBhZ2UgY2FycmllcyB0aGUgRlVMTCBtYXAgKG5vdCBqdXN0IHRoZSB0b29scyB0aGF0CiAgICAgcmVnaXN0ZXJlZCBvbiB0aGUgY3VycmVudCBwYXRoKS4gU2FtZSBieXRlcyBhcyB0aGUgZW1pdHRlZAogICAgIHdlYm1jcC1jYXBhYmlsaXRpZXMtPHByZWZpeD4uanNvbjogb25lIHNvdXJjZSBvZiB0cnV0aCwgdHdvIGRpc2NvdmVyeQogICAgIHN1cmZhY2VzLiAqLwogIHZhciBfV0VCTUNQX0NBUFMgPSB7InNpdGUiOiAiIiwgImdlbmVyYXRlZF9hdCI6ICIyMDI2LTA4LTIwVDIyOjE5OjU1LjQ0NjU0OFoiLCAidG9vbF9wcmVmaXgiOiAib3B0aW1pemVseSIsICJ0b29sX2NvdW50IjogNywgImNhcGFiaWxpdGllcyI6IFt7Im5hbWUiOiAib3B0aW1pemVseS1zdWJtaXQtb3B0aW1pemVseS1sZWFkLWZvcm0iLCAiZGVzY3JpcHRpb24iOiAiIiwgInVybFBhdHRlcm4iOiAiLyJ9LCB7Im5hbWUiOiAib3B0aW1pemVseS1zdWJtaXQtb3B0aW1pemVseS1sZWFkLWZvcm0tc3VibWl0IiwgImRlc2NyaXB0aW9uIjogIiIsICJ1cmxQYXR0ZXJuIjogIi8ifSwgeyJuYW1lIjogIm9wdGltaXplbHktc3VibWl0LWRlbW8tcmVxdWVzdCIsICJkZXNjcmlwdGlvbiI6ICIiLCAidXJsUGF0dGVybiI6ICIvZ2V0LXN0YXJ0ZWQvIn0sIHsibmFtZSI6ICJvcHRpbWl6ZWx5LXN1Ym1pdC1kZW1vLXJlcXVlc3Qtc3VibWl0IiwgImRlc2NyaXB0aW9uIjogIiIsICJ1cmxQYXR0ZXJuIjogIi9nZXQtc3RhcnRlZC8ifSwgeyJuYW1lIjogIm9wdGltaXplbHktc2VhcmNoLW9wdGltaXplbHktcmVzb3VyY2VzIiwgImRlc2NyaXB0aW9uIjogIiIsICJ1cmxQYXR0ZXJuIjogIi9zZWFyY2gvIn0sIHsibmFtZSI6ICJvcHRpbWl6ZWx5LXN1Ym1pdC1iZWNvbWUtYS1wYXJ0bmVyLWlucXVpcnkiLCAiZGVzY3JpcHRpb24iOiAiIiwgInVybFBhdHRlcm4iOiAiL3BhcnRuZXJzLyJ9LCB7Im5hbWUiOiAib3B0aW1pemVseS1zdWJtaXQtYmVjb21lLWEtcGFydG5lci1pbnF1aXJ5LXN1Ym1pdCIsICJkZXNjcmlwdGlvbiI6ICIiLCAidXJsUGF0dGVybiI6ICIvcGFydG5lcnMvIn1dLCAiYnlQYWdlIjogeyIvIjogWyJvcHRpbWl6ZWx5LXN1Ym1pdC1vcHRpbWl6ZWx5LWxlYWQtZm9ybSIsICJvcHRpbWl6ZWx5LXN1Ym1pdC1vcHRpbWl6ZWx5LWxlYWQtZm9ybS1zdWJtaXQiXSwgIi9nZXQtc3RhcnRlZC8iOiBbIm9wdGltaXplbHktc3VibWl0LWRlbW8tcmVxdWVzdCIsICJvcHRpbWl6ZWx5LXN1Ym1pdC1kZW1vLXJlcXVlc3Qtc3VibWl0Il0sICIvc2VhcmNoLyI6IFsib3B0aW1pemVseS1zZWFyY2gtb3B0aW1pemVseS1yZXNvdXJjZXMiXSwgIi9wYXJ0bmVycy8iOiBbIm9wdGltaXplbHktc3VibWl0LWJlY29tZS1hLXBhcnRuZXItaW5xdWlyeSIsICJvcHRpbWl6ZWx5LXN1Ym1pdC1iZWNvbWUtYS1wYXJ0bmVyLWlucXVpcnktc3VibWl0Il19fTsKCiAgLyogT25lIGdsb2JhbGx5LXJlZ2lzdGVyZWQgZGlzY292ZXJ5IHRvb2w6IGFuIGFnZW50IG9uIEFOWSBwYWdlIGNhbiBzZWUgdGhlCiAgICAgd2hvbGUgc2l0ZSdzIGNhcGFiaWxpdGllcyAoaW50ZW50IC0+IHBhZ2UgLT4gdG9vbHMpIGFuZCBqdW1wIHRvIHRoZSBwYWdlCiAgICAgdGhhdCBob3N0cyBhIGNhcGFiaWxpdHkuIFJlZ2lzdGVyZWQgd2l0aCB0aGUgYmFyZSByZWdpc3RlclRvb2wgKHBhdHRlcm4KICAgICAvKiopLCBzbyBpdCBzdXJ2aXZlcyBldmVyeSBmdWxsLXBhZ2UgbmF2aWdhdGlvbi4gUjkgdHJhY2tpbmcgaXMgYXBwbGllZCBieQogICAgIHRoZSByZWdpc3RlclRvb2wgd3JhcHBlciwgc28gZG8gTk9UIGNhbGwgdHJhY2tXZWJNQ1BUb29sIGhlcmUuICovCiAgbWMucmVnaXN0ZXJUb29sKHsKICAgIG5hbWU6ICdvcHRpbWl6ZWx5LWRpc2NvdmVyLWNhcGFiaWxpdGllcycsCiAgICBkZXNjcmlwdGlvbjogJ0xpc3QgZXZlcnkgV2ViTUNQIGNhcGFiaWxpdHkgYWNyb3NzIHRoaXMgd2hvbGUgc2l0ZTogdGhlIGludGVudCwgdGhlIHBhZ2UgVVJMIHBhdHRlcm4gdGhhdCBob3N0cyBpdCwgYW5kIHRoZSB0b29sIG5hbWVzIHRvIGNhbGwgdGhlcmUuIE9wdGlvbmFsbHkgcGFzcyBuYXZpZ2F0ZVRvIHRvIGp1bXAgdG8gdGhlIHBhZ2UgdGhhdCBob3N0cyBhIGNhcGFiaWxpdHkgKHRoZSBmdWxsIHBhZ2UgcmVsb2FkczsgY2FsbCB0aGUgdGFyZ2V0IHRvb2wgYWZ0ZXIgaXQgbG9hZHMpLiBVc2UgdGhpcyBmaXJzdCB3aGVuIHRoZSB0b29sIHlvdSBuZWVkIGlzIG5vdCByZWdpc3RlcmVkIG9uIHRoZSBjdXJyZW50IHBhZ2UuJywKICAgIGlucHV0U2NoZW1hOiB7CiAgICAgIHR5cGU6ICdvYmplY3QnLAogICAgICBwcm9wZXJ0aWVzOiB7CiAgICAgICAgbmF2aWdhdGVUbzogewogICAgICAgICAgdHlwZTogJ3N0cmluZycsCiAgICAgICAgICBkZXNjcmlwdGlvbjogJ09wdGlvbmFsIHNhbWUtb3JpZ2luIHBhdGggb3IgVVJMIHRvIG5hdmlnYXRlIHRvLCBlLmcuIGEgY2FwYWJpbGl0eSB1cmxQYXR0ZXJuIHdpdGggYW55ICogcmVwbGFjZWQgYnkgYSByZWFsIHZhbHVlLiBDcm9zcy1vcmlnaW4gZGVzdGluYXRpb25zIGFyZSByZWZ1c2VkLicKICAgICAgICB9CiAgICAgIH0KICAgIH0sCiAgICBleGVjdXRlOiBmdW5jdGlvbiAoYXJncykgewogICAgICB2YXIgcGF5bG9hZCA9IHsKICAgICAgICBzaXRlOiBfV0VCTUNQX0NBUFMuc2l0ZSB8fCBsb2NhdGlvbi5vcmlnaW4sCiAgICAgICAgY3VycmVudFBhdGg6IGxvY2F0aW9uLnBhdGhuYW1lLAogICAgICAgIHRvb2xfY291bnQ6IChfV0VCTUNQX0NBUFMuY2FwYWJpbGl0aWVzIHx8IFtdKS5sZW5ndGgsCiAgICAgICAgY2FwYWJpbGl0aWVzOiBfV0VCTUNQX0NBUFMuY2FwYWJpbGl0aWVzIHx8IFtdLAogICAgICAgIGJ5UGFnZTogX1dFQk1DUF9DQVBTLmJ5UGFnZSB8fCB7fQogICAgICB9OwogICAgICB2Y
22948XIgZGVzdCA9IChhcmdzICYmIGFyZ3MubmF2aWdhdGVUbykgPyBTdHJpbmcoYXJncy5uYXZpZ2F0ZVRvKS50cmltKCkgOiAnJzsKICAgICAgaWYgKGRlc3QpIHsKICAgICAgICB2YXIgdXJsID0gbnVsbDsKICAgICAgICB0cnkgeyB1cmwgPSBuZXcgVVJMKGRlc3QsIGxvY2F0aW9uLm9yaWdpbik7IH0KICAgICAgICBjYXRjaCAoZSkgeyBwYXlsb2FkLm5hdmlnYXRpb24gPSB7IHN0YXR1czogJ2ludmFsaWQnLCBuYXZpZ2F0ZVRvOiBkZXN0IH07IH0KICAgICAgICBpZiAodXJsKSB7CiAgICAgICAgICBpZiAodXJsLm9yaWdpbiAhPT0gbG9jYXRpb24ub3JpZ2luKSB7CiAgICAgICAgICAgIHBheWxvYWQubmF2aWdhdGlvbiA9IHsgc3RhdHVzOiAncmVmdXNlZCcsIHJlYXNvbjogJ2Nyb3NzLW9yaWdpbicsIG5hdmlnYXRlVG86IGRlc3QgfTsKICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIHBheWxvYWQubmF2aWdhdGlvbiA9IHsgc3RhdHVzOiAnbmF2aWdhdGVkJywgdG86IHVybC5wYXRobmFtZSArIHVybC5zZWFyY2ggfTsKICAgICAgICAgICAgdmFyIG91dCA9IHsgY29udGVudDogW3sgdHlwZTogJ3RleHQnLCB0ZXh0OiBKU09OLnN0cmluZ2lmeShwYXlsb2FkKSB9XSB9OwogICAgICAgICAgICBsb2NhdGlvbi5hc3NpZ24odXJsLmhyZWYpOwogICAgICAgICAgICByZXR1cm4gb3V0OwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfQogICAgICByZXR1cm4geyBjb250ZW50OiBbeyB0eXBlOiAndGV4dCcsIHRleHQ6IEpTT04uc3RyaW5naWZ5KHBheWxvYWQpIH1dIH07CiAgICB9CiAgfSk7CgogIC8qIC0tLS0gb3B0aW1pemVseS1zdWJtaXQtYmVjb21lLWEtcGFydG5lci1pbnF1aXJ5LCBvcHRpbWl6ZWx5LXN1Ym1pdC1iZWNvbWUtYS1wYXJ0bmVyLWlucXVpcnktc3VibWl0IC0tLS0gKi8KaWYgKCF3aW5kb3cuX193bWNwUmVnKSB7CiAgd2luZG93Ll9fd21jcFJlZyA9IChmdW5jdGlvbiAoKSB7CiAgICB2YXIgbGl2ZSA9IHt9OwogICAgZnVuY3Rpb24gZ2xvYlRvUmUoZ2xvYikgewogICAgICB2YXIgcmUgPSBnbG9iCiAgICAgICAgLnJlcGxhY2UoL1suK14ke30oKXxbXF1cXF0vZywgIlxcJCYiKQogICAgICAgIC5yZXBsYWNlKC9cL1wqXCovZywgIigvLiopPyIpCiAgICAgICAgLnJlcGxhY2UoL1wqL2csICJbXi9dKyIpOwogICAgICByZXR1cm4gbmV3IFJlZ0V4cCgiXiIgKyByZSArICIkIik7CiAgICB9CiAgICBmdW5jdGlvbiByZWNvbmNpbGUoKSB7CiAgICAgIHZhciBwYXRoID0gbG9jYXRpb24ucGF0aG5hbWU7CiAgICAgIE9iamVjdC5rZXlzKGxpdmUpLmZvckVhY2goZnVuY3Rpb24gKG5hbWUpIHsKICAgICAgICB2Y
22948XIgZSA9IGxpdmVbbmFtZV07CiAgICAgICAgdmFyIG1hdGNoID0gZS5yZS50ZXN0KHBhdGgpOwogICAgICAgIGlmIChtYXRjaCAmJiAhZS5vbikgeyBtYy5yZWdpc3RlclRvb2woZS5kZWYpOyBlLm9uID0gdHJ1ZTsgfQogICAgICAgIGVsc2UgaWYgKCFtYXRjaCAmJiBlLm9uICYmIG1jLnVucmVnaXN0ZXJUb29sKSB7IG1jLnVucmVnaXN0ZXJUb29sKG5hbWUpOyBlLm9uID0gZmFsc2U7IH0KICAgICAgfSk7CiAgICB9CiAgICBmdW5jdGlvbiByZWdpc3RlclRvb2xJZihnbG9iLCBkZWYpIHsKICAgICAgbGl2ZVtkZWYubmFtZV0gPSB7IHJlOiBnbG9iVG9SZShnbG9iKSwgZGVmOiBkZWYsIG9uOiBmYWxzZSB9OwogICAgICByZWNvbmNpbGUoKTsKICAgIH0KICAgIGlmICghd2luZG93Ll9fd21jcFJvdXRlKSB7CiAgICAgIHdpbmRvdy5fX3dtY3BSb3V0ZSA9IHRydWU7CiAgICAgIFsicHVzaFN0YXRlIiwgInJlcGxhY2VTdGF0ZSJdLmZvckVhY2goZnVuY3Rpb24gKG0pIHsKICAgICAgICB2YXIgb3JpZyA9IGhpc3RvcnlbbV07CiAgICAgICAgaGlzdG9yeVttXSA9IGZ1bmN0aW9uICgpIHsKICAgICAgICAgIHZhciByID0gb3JpZy5hcHBseSh0aGlzLCBhcmd1bWVudHMpOwogICAgICAgICAgd2luZG93LmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCJ3bWNwOnJvdXRlIikpOwogICAgICAgICAgcmV0dXJuIHI7CiAgICAgICAgfTsKICAgICAgfSk7CiAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCJwb3BzdGF0ZSIsIGZ1bmN0aW9uICgpIHsKICAgICAgICB3aW5kb3cuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoIndtY3A6cm91dGUiKSk7CiAgICAgIH0pOwogICAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcigid21jcDpyb3V0ZSIsIHJlY29uY2lsZSk7CiAgICB9CiAgICByZXR1cm4geyByZWdpc3RlclRvb2xJZjogcmVnaXN0ZXJUb29sSWYsIHJlY29uY2lsZTogcmVjb25jaWxlIH07CiAgfSkoKTsKfQoKaWYgKCF3aW5kb3cuX193bWNwT3B0aVBhcnRuZXIpIHsKICB3aW5kb3cuX193bWNwT3B0aVBhcnRuZXIgPSAoZnVuY3Rpb24gKCkgewogICAgdmFyIFRFWFRfTUFQID0gewogICAgICBmaXJzdE5hbWU6ICJmaXJzdE5hbWUiLAogICAgICBsYXN0TmFtZTogImxhc3ROYW1lIiwKICAgICAgY29tcGFueTogImNvbXBhbnkiLAogICAgICB0aXRsZTogInRpdGxlIiwKICAgICAgZW1haWw6ICJlbWFpbCIsCiAgICAgI
22948HdlYnNpdGU6ICJ3ZWJzaXRlIiwKICAgICAgcGFydG5lcmluZ0ludGVyZXN0OiAiZ2VuZXJhbDA1IiwKICAgICAgcHJvZHVjdEludGVyZXN0OiAiT3RoZXJfQWRkaXRpb25hbF9JbmZvcm1hdGlvbl9fYyIKICAgIH07CiAgICB2YXIgVEVYVEFSRUFTID0geyBnZW5lcmFsMDU6IHRydWUsIE90aGVyX0FkZGl0aW9uYWxfSW5mb3JtYXRpb25fX2M6IHRydWUgfTsKICAgIHZhciBTRUxFQ1RfTUFQID0gewogICAgICBudW1iZXJPZkVtcGxveWVlczogIk5vX29mX0VtcGxveWVlc19JbnRlcnZhbF9fY19hY2NvdW50IiwKICAgICAgb3B0aW1pemVseUV4cGVyaWVuY2U6ICJ0YXJnZXRBY2NvdW50UHJpb3JpdGl6YXRpb24iLAogICAgICBjbGllbnRJbnRlcmVzdGVkOiAiZ2VuZXJhbDA3IiwKICAgICAgZGVsaXZlcnlQcmFjdGljZTogImdlbmVyYWwwOCIKICAgIH07CiAgICB2YXIgU1RBVElDX09QVElPTlMgPSB7CiAgICAgIE5vX29mX0VtcGxveWVlc19JbnRlcnZhbF9fY19hY2NvdW50OiBbIlVuZGVyIDUwIiwgIjUwLTEwMCIsICIxMDAtMjUwIiwgIjI1MC01MDAiLCAiNTAwLTEwMDAiLCAiMTAwMC0yMDAwIiwgIjIwMDArIl0sCiAgICAgIHRhcmdldEFjY291bnRQcmlvcml0aXphdGlvbjogWyJZZXMiLCAiTm8iXSwKICAgICAgZ2VuZXJhbDA3OiBbIlllcyIsICJObyJdLAogICAgICBnZW5lcmFsMDg6IFsiWWVzIiwgIk5vIl0KICAgIH07CiAgICB2YXIgUkVRVUlSRUQgPSBbImZpcnN0TmFtZSIsICJsYXN0TmFtZSIsICJjb21wYW55IiwgInRpdGxlIiwgImVtYWlsIiwgIndlYnNpdGUiXTsKICAgIHZhciBBTExfUkVBRCA9IFsiZmlyc3ROYW1lIiwgImxhc3ROYW1lIiwgImNvbXBhbnkiLCAidGl0bGUiLCAiZW1haWwiLCAid2Vic2l0ZSIsCiAgICAgICJOb19vZl9FbXBsb3llZXNfSW50ZXJ2YWxfX2NfYWNjb3VudCIsICJjb3VudHJ5IiwgImdlbmVyYWwwNSIsCiAgICAgICJ0YXJnZXRBY2NvdW50UHJpb3JpdGl6YXRpb24iLCAiZ2VuZXJhbDA3IiwgIk90aGVyX0FkZGl0aW9uYWxfSW5mb3JtYXRpb25fX2MiLCAiZ2VuZXJhbDA4Il07CgogICAgZnVuY3Rpb24gc2xlZXAobXMpIHsgcmV0dXJuIG5ldyBQcm9taXNlKGZ1bmN0aW9uIChyKSB7IHNldFRpbWVvdXQociwgbXMpOyB9KTsgfQoKICAgIGFzeW5jIGZ1bmN0aW9uIHdhaXRGb3IoZm4sIHRpbWVvdXQsIGludGVydmFsKSB7CiAgICAgIHRpbWVvdXQgPSB0aW1lb3V0IHx8IDEwMDAwOwogICAgICBpbnRlcnZhbCA9IGludGVydmFsIHx8IDIwMDsKICAgICAgdmFyIHN0YXJ0ID0gRGF0ZS5ub3coKTsKICAgICAgd2hpbGUgKERhdGUubm93KCkgLSBzdGFydCA8IHRpbWVvdXQpIHsKICAgICAgICB0cnkgeyB2YXIgdiA9IGZuKCk7IGlmICh2KSByZXR1cm4gdjsgfSBjYXRjaCAoZSkge30KICAgICAgICBhd2FpdCBzbGVlcChpbnRlcnZhbCk7CiAgICAgIH0KICAgICAgcmV0dXJuIG51bGw7CiAgICB9CgogICAgZnVuY3Rpb24gdHh0KG5vZGUpIHsKICAgICAgaWYgKCFub2RlKSByZXR1cm4gIiI7CiAgICAgIHJldHVybiBTdHJpbmcobm9kZS50ZXh0Q29udGVudCB8fCAiIikucmVwbGFjZSgvXHUwMGEwL2csICIgIikucmVwbGFjZSgvXHMrL2csICIgIikudHJpbSgpOwogICAgfQoKICAgIGZ1bmN0aW9uIHRydW5jKHMsIG4pIHsKICAgICAgcyA9IChzID09PSBudWxsIHx8IHMgPT09IHVuZGVmaW5lZCkgPyAiIiA6IFN0cmluZyhzKS5yZXBsYWNlKC9ccysvZywgIiAiKS50cmltKCk7CiAgICAgIG4gPSBuIHx8IDMwMDsKICAgICAgcmV0dXJuIHMubGVuZ3RoID4gbiA/IHMuc2xpY2UoMCwgbikgKyAiLi4uIiA6IHM7CiAgICB9CgogICAgZnVuY3Rpb24gZ2V0Rm9ybSgpIHsKICAgICAgdmFyIGZvcm1zID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgiZm9ybS5vcHRpLWZvcm0iKTsKICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBmb3Jtcy5sZW5ndGg7IGkrKykgewogICAgICAgIGlmIChmb3Jtc1tpXS5xdWVyeVNlbGVjdG9yKCdpbnB1dFtuYW1lPSJmaXJzdE5hbWUiXScpKSByZXR1cm4gZm9ybXNbaV07CiAgICAgIH0KICAgICAgdmFyIGFsdCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJ2lucHV0W25hbWU9ImZpcnN0TmFtZSJdJyk7CiAgICAgIHJldHVybiBhbHQgPyBhbHQuY2xvc2VzdCgiZm9ybSIpIDogbnVsbDsKICAgIH0KCiAgICBmdW5jdGlvbiBuYXRpdmVTZXQoZWwsIHZhbHVlKSB7CiAgICAgIHZhciBwcm90byA9IHdpbmRvdy5IVE1MSW5wdXRFbGVtZW50LnByb3RvdHlwZTsKICAgICAgaWYgKGVsIGluc3RhbmNlb2Ygd2luZG93LkhUTUxUZXh0QXJlYUVsZW1lbnQpIHByb3RvID0gd2luZG93LkhUTUxUZXh0QXJlYUVsZW1lbnQucHJvdG90eXBlOwogICAgICBlbHNlIGlmIChlbCBpbnN0YW5jZW9mIHdpbmRvdy5IVE1MU2VsZWN0RWxlbWVudCkgcHJvdG8gPSB3aW5kb3cuSFRNTFNlbGVjdEVsZW1lbnQucHJvdG90eXBlOwogICAgICB2YXIgZGVzYyA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IocHJvdG8sICJ2YWx1ZSIpOwogICAgICBpZiAoZGVzYyAmJiBkZXNjLnNldCkgZGVzYy5zZXQuY2FsbChlbCwgdmFsdWUpOyBlbHNlIGVsLnZhbHVlID0gdmFsdWU7CiAgICAgIGVsLmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCJpbnB1dCIsIHsgYnViYmxlczogdHJ1ZSB9KSk7CiAgICAgIGVsLmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCJjaGFuZ2UiLCB7IGJ1YmJsZXM6IHRydWUgfSkpOwogICAgfQoKICAgIGZ1bmN0aW9uIHNldFRleHQoZm9ybSwgbmFtZSwgdmFsdWUpIHsKICAgICAgdmFyIGVsID0gZm9ybS5xdWVyeVNlbGVjdG9yKChURVhUQVJFQVNbbmFtZV0gPyAidGV4dGFyZWEiIDogImlucHV0IikgKyAnW25hbWU9IicgKyBuYW1lICsgJyJdJykKICAgICAgICB8fCBmb3JtLnF1ZXJ5U2VsZWN0b3IoJ1tuYW1lPSInICsgbmFtZSArICciXScpOwogICAgICBpZiAoIWVsKSByZXR1cm4geyBvazogZmFsc2UsIHZhbHVlOiAiIiB9OwogICAgICB0cnkgeyBlbC5mb2N1cygpOyB9IGNhdGNoIChlKSB7fQogICAgICBuYXRpdmVTZXQoZWwsIFN0cmluZyh2YWx1ZSkpOwogICAgICBlbC5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgiYmx1ciIsIHsgYnViYmxlczogdHJ1ZSB9KSk7CiAgICAgI
22948HJldHVybiB7IG9rOiBlbC52YWx1ZSA9PT0gU3RyaW5nKHZhbHVlKSwgdmFsdWU6IGVsLnZhbHVlIH07CiAgICB9CgogICAgZnVuY3Rpb24gbGl2ZU9wdGlvbnMoZm9ybSwgbmFtZSkgewogICAgICB2YXIgc2VsID0gZm9ybSA/IGZvcm0ucXVlcnlTZWxlY3Rvcignc2VsZWN0W25hbWU9IicgKyBuYW1lICsgJyJdJykgOiBudWxsOwogICAgICBpZiAoc2VsICYmIHNlbC5vcHRpb25zICYmIHNlbC5vcHRpb25zLmxlbmd0aCkgewogICAgICAgIHZhciBvdXQgPSBbXTsKICAgICAgICBBcnJheS5wcm90b3R5cGUuZm9yRWFjaC5jYWxsKHNlbC5vcHRpb25zLCBmdW5jdGlvbiAobykgeyBpZiAoby52YWx1ZSkgb3V0LnB1c2goby52YWx1ZSk7IH0pOwogICAgICAgIGlmIChvdXQubGVuZ3RoKSByZXR1cm4gb3V0OwogICAgICB9CiAgICAgIHJldHVybiBTVEFUSUNfT1BUSU9OU1tuYW1lXSB8fCBbXTsKICAgIH0KCiAgICBhc3luYyBmdW5jdGlvbiBzZXRTZWxlY3QoZm9ybSwgbmFtZSwgdmFsdWUpIHsKICAgICAgdmFyIHNlbCA9IGZvcm0ucXVlcnlTZWxlY3Rvcignc2VsZWN0W25hbWU9IicgKyBuYW1lICsgJyJdJyk7CiAgICAgIGlmICghc2VsKSByZXR1cm4geyBvazogZmFsc2UsIHZhbHVlOiAiIiwgcmVhc29uOiAiaGlkZGVuIHNlbGVjdCBbbmFtZT0iICsgbmFtZSArICJdIG5vdCBmb3VuZCIgfTsKICAgICAgbmF0aXZlU2V0KHNlbCwgU3RyaW5nKHZhbHVlKSk7CiAgICAgIGlmIChzZWwudmFsdWUgIT09IFN0cmluZyh2YWx1ZSkpIHsKICAgICAgICB2YXIgd3JhcCA9IHNlbC5jbG9zZXN0KCIub3B0aWZvcm1zc2VsZWN0aW9uZWxlbWVudCIpIHx8IHNlbC5wYXJlbnRFbGVtZW50OwogICAgICAgIHZhciBidG4gPSB3cmFwID8gd3JhcC5xdWVyeVNlbGVjdG9yKCdidXR0b25bcm9sZT0iY29tYm9ib3giXSwgW3JvbGU9ImNvbWJvYm94Il0nKSA6IG51bGw7CiAgICAgICAgaWYgKGJ0bikgewogICAgICAgICAgYnRuLmNsaWNrKCk7CiAgICAgICAgICB2Y
22948XIgb3B0ID0gYXdhaXQgd2FpdEZvcihmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgIHZhciBvcHRzID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnW3JvbGU9Im9wdGlvbiJdJyk7CiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgb3B0cy5sZW5ndGg7IGkrKykgewogICAgICAgICAgICAgIGlmICh0eHQob3B0c1tpXSkudG9Mb3dlckNhc2UoKSA9PT0gU3RyaW5nKHZhbHVlKS50b0xvd2VyQ2FzZSgpKSByZXR1cm4gb3B0c1tpXTsKICAgICAgICAgICAgfQogICAgICAgICAgICByZXR1cm4gbnVsbDsKICAgICAgICAgIH0sIDQwMDAsIDEyMCk7CiAgICAgICAgICBpZiAob3B0KSB7CiAgICAgICAgICAgIG9wdC5kaXNwYXRjaEV2ZW50KG5ldyBNb3VzZUV2ZW50KCJtb3VzZWRvd24iLCB7IGJ1YmJsZXM6IHRydWUgfSkpOwogICAgICAgICAgICBvcHQuY2xpY2soKTsKICAgICAgICAgICAgYXdhaXQgc2xlZXAoMjUwKTsKICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIGJ0bi5kaXNwYXRjaEV2ZW50KG5ldyBLZXlib2FyZEV2ZW50KCJrZXlkb3duIiwgeyBrZXk6ICJFc2NhcGUiLCBidWJibGVzOiB0cnVlIH0pKTsKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIH0KICAgICAgcmV0dXJuIHsgb2s6IHNlbC52YWx1ZSA9PT0gU3RyaW5nKHZhbHVlKSwgdmFsdWU6IHNlbC52YWx1ZSB9OwogICAgfQoKICAgIGFzeW5jIGZ1bmN0aW9uIHNldENvdW50cnkoZm9ybSwgdmFsdWUpIHsKICAgICAgdmFyIGhpZGRlbiA9IGZvcm0ucXVlcnlTZWxlY3RvcignaW5wdXRbdHlwZT0iaGlkZGVuIl1bbmFtZT0iY291bnRyeSJdJyk7CiAgICAgIHZhciBpbnB1dCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoIiNjb3VudHJ5LWlucHV0IikKICAgICAgICB8fCBmb3JtLnF1ZXJ5U2VsZWN0b3IoJ2lucHV0W3JvbGU9ImNvbWJvYm94Il1bYXJpYS1sYWJlbD0iQ291bnRyeSJdJyk7CiAgICAgIGlmICghaW5wdXQpIHsKICAgICAgICBpZiAoaGlkZGVuKSB7IG5hdGl2ZVNldChoaWRkZW4sIFN0cmluZyh2YWx1ZSkpOyByZXR1cm4geyBvazogISFoaWRkZW4udmFsdWUsIHZhbHVlOiBoaWRkZW4udmFsdWUgfTsgfQogICAgICAgIHJldHVybiB7IG9rOiBmYWxzZSwgdmFsdWU6ICIiLCByZWFzb246ICJjb3VudHJ5IGNvbnRyb2wgbm90IGZvdW5kIiB9OwogICAgICB9CiAgICAgIHRyeSB7IGlucHV0LmZvY3VzKCk7IH0gY2F0Y2ggKGUpIHt9CiAgICAgIG5hdGl2ZVNldChpbnB1dCwgIiIpOwogICAgICBhd2FpdCBzbGVlcCgxMjApOwogICAgICBuYXRpdmVTZXQoaW5wdXQsIFN0cmluZyh2YWx1ZSkpOwogICAgICBpbnB1dC5kaXNwYXRjaEV2ZW50KG5ldyBLZXlib2FyZEV2ZW50KCJrZXlkb3duIiwgeyBrZXk6ICJBcnJvd0Rvd24iLCBidWJibGVzOiB0cnVlIH0pKTsKICAgICAgdmFyIG9wdCA9IGF3YWl0IHdhaXRGb3IoZnVuY3Rpb24gKCkgewogICAgICAgIHZhciBtZW51ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiI2NvdW50cnktbWVudSIpOwogICAgICAgIHZhciBvcHRzID0gKG1lbnUgfHwgZG9jdW1lbnQpLnF1ZXJ5U2VsZWN0b3JBbGwoJ1tyb2xlPSJvcHRpb24iXScpOwogICAgICAgIHZhciBleGFjdCA9IG51bGwsIHBhcnRpYWwgPSBudWxsOwogICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgb3B0cy5sZW5ndGg7IGkrKykgewogICAgICAgICAgdmFyIHQgPSB0eHQob3B0c1tpXSkudG9Mb3dlckNhc2UoKTsKICAgICAgICAgIGlmICghdCkgY29udGludWU7CiAgICAgICAgICBpZiAodCA9PT0gU3RyaW5nKHZhbHVlKS50b0xvd2VyQ2FzZSgpKSBleGFjdCA9IG9wdHNbaV07CiAgICAgICAgICBlbHNlIGlmICghcGFydGlhbCAmJiB0LmluZGV4T2YoU3RyaW5nKHZhbHVlKS50b0xvd2VyQ2FzZSgpKSA9PT0gMCkgcGFydGlhbCA9IG9wdHNbaV07CiAgICAgICAgfQogICAgICAgIHJldHVybiBleGFjdCB8fCBwYXJ0aWFsOwogICAgICB9LCA2MDAwLCAxNTApOwogICAgICBpZiAob3B0KSB7CiAgICAgICAgb3B0LmRpc3BhdGNoRXZlbnQobmV3IE1vdXNlRXZlbnQoIm1vdXNlZG93biIsIHsgYnViYmxlczogdHJ1ZSB9KSk7CiAgICAgICAgb3B0LmNsaWNrKCk7CiAgICAgICAgYXdhaXQgc2xlZXAoMzAwKTsKICAgICAgfSBlbHNlIHsKICAgICAgICBpbnB1dC5kaXNwYXRjaEV2ZW50KG5ldyBLZXlib2FyZEV2ZW50KCJrZXlkb3duIiwgeyBrZXk6ICJFbnRlciIsIGJ1YmJsZXM6IHRydWUgfSkpOwogICAgICAgIGF3YWl0IHNsZWVwKDMwMCk7CiAgICAgIH0KICAgICAgaGlkZGVuID0gZm9ybS5xdWVyeVNlbGVjdG9yKCdpbnB1dFt0eXBlPSJoaWRkZW4iXVtuYW1lPSJjb3VudHJ5Il0nKTsKICAgICAgdmFyIHZhbCA9IGhpZGRlbiA/IGhpZGRlbi52YWx1ZSA6ICIiOwogICAgICBpZiAoIXZhbCAmJiBoaWRkZW4pIHsgbmF0aXZlU2V0KGhpZGRlbiwgU3RyaW5nKHZhbHVlKSk7IHZhbCA9IGhpZGRlbi52YWx1ZTsgfQogICAgICByZXR1cm4geyBvazogISF2YWwsIHZhbHVlOiB2YWwsIHR5cGVkOiBpbnB1dC52YWx1ZSwgbWF0Y2hlZDogb3B0ID8gdHh0KG9wdCkgOiAiIiB9OwogICAgfQoKICAgIGZ1bmN0aW9uIHJlYWRGaWVsZHMoZm9ybSkgewogICAgICB2YXIgb3V0ID0ge307CiAgICAgIEFMTF9SRUFELmZvckVhY2goZnVuY3Rpb24gKG5hbWUpIHsKICAgICAgICB2Y
22948XIgZWwgPSBmb3JtLnF1ZXJ5U2VsZWN0b3IoJ3NlbGVjdFtuYW1lPSInICsgbmFtZSArICciXScpCiAgICAgICAgICB8fCBmb3JtLnF1ZXJ5U2VsZWN0b3IoJ3RleHRhcmVhW25hbWU9IicgKyBuYW1lICsgJyJdJykKICAgICAgICAgIHx8IGZvcm0ucXVlcnlTZWxlY3RvcignaW5wdXRbbmFtZT0iJyArIG5hbWUgKyAnIl0nKTsKICAgICAgICBvdXRbbmFtZV0gPSBlbCA/IHRydW5jKGVsLnZhbHVlLCAzMDApIDogbnVsbDsKICAgICAgfSk7CiAgICAgIHJldHVybiBvdXQ7CiAgICB9CgogICAgZnVuY3Rpb24gY29sbGVjdFZhbGlkYXRpb25FcnJvcnMoZm9ybSkgewogICAgICB2YXIgbXNncyA9IFtdOwogICAgICBmdW5jdGlvbiBwdXNoKHMpIHsKICAgICAgICBzID0gdHJ1bmMocywgMzAwKTsKICAgICAgICBpZiAoIXMgfHwgcyA9PT0gIi0iIHx8IG1zZ3MuaW5kZXhPZihzKSAhPT0gLTEpIHJldHVybjsKICAgICAgICBpZiAocy5sZW5ndGggPiAzMDApIHMgPSBzLnNsaWNlKDAsIDMwMCk7CiAgICAgICAgbXNncy5wdXNoKHMpOwogICAgICB9CiAgICAgIGlmICghZm9ybSB8fCAhZG9jdW1lbnQuY29udGFpbnMoZm9ybSkpIHJldHVybiBtc2dzOwogICAgICB2YXIgaW52YWxpZHMgPSBmb3JtLnF1ZXJ5U2VsZWN0b3JBbGwoJ1thcmlhLWludmFsaWQ9InRydWUiXScpOwogICAgICBBcnJheS5wcm90b3R5cGUuZm9yRWFjaC5jYWxsKGludmFsaWRzLCBmdW5jdGlvbiAoZWwpIHsKICAgICAgICB2YXIgaWRzID0gU3RyaW5nKGVsLmdldEF0dHJpYnV0ZSgiYXJpYS1kZXNjcmliZWRieSIpIHx8ICIiKS5zcGxpdCgvXHMrLyk7CiAgICAgICAgaWRzLmZvckVhY2goZnVuY3Rpb24gKGlkKSB7CiAgICAgICAgICBpZiAoIWlkKSByZXR1cm47CiAgICAgICAgICB2Y
22948XIgbiA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGlkKTsKICAgICAgICAgIGlmIChuICYmICFuLmNsYXNzTGlzdC5jb250YWlucygiaGlkZGVuIikpIHB1c2godHh0KG4pKTsKICAgICAgICB9KTsKICAgICAgICB2YXIgd3JhcCA9IGVsLmNsb3Nlc3QoIltjbGFzcyo9J29wdGlmb3JtcyddIik7CiAgICAgICAgaWYgKHdyYXApIHsKICAgICAgICAgIHZhciBzcHMgPSB3cmFwLnF1ZXJ5U2VsZWN0b3JBbGwoInNwYW4iKTsKICAgICAgICAgIEFycmF5LnByb3RvdHlwZS5mb3JFYWNoLmNhbGwoc3BzLCBmdW5jdGlvbiAoc3ApIHsKICAgICAgICAgICAgaWYgKCFzcC5jbGFzc0xpc3QuY29udGFpbnMoImhpZGRlbiIpICYmICFzcC5xdWVyeVNlbGVjdG9yKCIqIikpIHB1c2godHh0KHNwKSk7CiAgICAgICAgICB9KTsKICAgICAgICB9CiAgICAgIH0pOwogICAgICB2YXIgaGVscGVycyA9IGZvcm0ucXVlcnlTZWxlY3RvckFsbCgiW2NsYXNzKj0nb3B0aWZvcm1zJ10gPiBzcGFuIik7CiAgICAgIEFycmF5LnByb3RvdHlwZS5mb3JFYWNoLmNhbGwoaGVscGVycywgZnVuY3Rpb24gKHNwKSB7CiAgICAgICAgaWYgKHNwLmNsYXNzTGlzdC5jb250YWlucygiaGlkZGVuIikpIHJldHVybjsKICAgICAgICBpZiAoc3AucXVlcnlTZWxlY3RvcigiKiIpKSByZXR1cm47CiAgICAgICAgdmFyIHQgPSB0eHQoc3ApOwogICAgICAgIGlmICh0ICYmIHQubGVuZ3RoIDwgMzAwICYmIC9bYS16XS9pLnRlc3QodCkpIHB1c2godCk7CiAgICAgIH0pOwogICAgICB2YXIgYWxlcnRzID0gZm9ybS5xdWVyeVNlbGVjdG9yQWxsKCdbcm9sZT0iYWxlcnQiXScpOwogICAgICBBcnJheS5wcm90b3R5cGUuZm9yRWFjaC5jYWxsKGFsZXJ0cywgZnVuY3Rpb24gKGEpIHsgcHVzaCh0eHQoYSkpOyB9KTsKICAgICAgcmV0dXJuIG1zZ3Muc2xpY2UoMCwgMTApOwogICAgfQoKICAgIGZ1bmN0aW9uIGZpbmRDb25maXJtYXRpb24oKSB7CiAgICAgIHZhciByb290ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiI2JlY29tZS1hLXBhcnRuZXIiKSB8fCBkb2N1bWVudC5ib2R5OwogICAgICB2YXIgbm9kZXMgPSByb290LnF1ZXJ5U2VsZWN0b3JBbGwoImgxLGgyLGgzLGg0LHAsW2NsYXNzKj0ndGhhbmsnXSxbY2xhc3MqPSdzdWNjZXNzJ10iKTsKICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBub2Rlcy5sZW5ndGg7IGkrKykgewogICAgICAgIHZhciB0ID0gdHh0KG5vZGVzW2ldKTsKICAgICAgICBpZiAoIXQgfHwgdC5sZW5ndGggPiAzMDApIGNvbnRpbnVlOwogICAgICAgIGlmICgvdGhhbmsgeW91fHRoYW5rc3x3ZSgnfFx1MjAxOSk/bGwgYmUgaW4gdG91Y2h8cmVjZWl2ZWQgeW91cnxzdWJtaXNzaW9uIHJlY2VpdmVkfHN1Y2Nlc3MvaS50ZXN0KHQpKSByZXR1cm4gdDsKICAgICAgfQogICAgICByZXR1cm4gIiI7CiAgICB9CgogICAgZnVuY3Rpb24gaGlnaGxpZ2h0KGVsKSB7CiAgICAgIGlmICghZWwpIHJldHVybjsKICAgICAgdHJ5IHsKICAgICAgICBlbC5zY3JvbGxJbnRvVmlldyh7IGJlaGF2aW9yOiAic21vb3RoIiwgYmxvY2s6ICJjZW50ZXIiIH0pOwogICAgICAgIHZhciBwcmV2ID0gZWwuc3R5bGUub3V0bGluZTsKICAgICAgICBlbC5zdHlsZS5vdXRsaW5lID0gIjNweCBzb2xpZCAjMDAzN2ZmIjsKICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uICgpIHsgdHJ5IHsgZWwuc3R5bGUub3V0bGluZSA9IHByZXY7IH0gY2F0Y2ggKGUpIHt9IH0sIDgwMDApOwogICAgICB9IGNhdGNoIChlKSB7fQogICAgfQoKICAgIHJldHVybiB7CiAgICAgIFRFWFRfTUFQOiBURVhUX01BUCwgU0VMRUNUX01BUDogU0VMRUNUX01BUCwgUkVRVUlSRUQ6IFJFUVVJUkVELAogICAgICBzbGVlcDogc2xlZXAsIHdhaXRGb3I6IHdhaXRGb3IsIHR4dDogdHh0LCB0cnVuYzogdHJ1bmMsIGdldEZvcm06IGdldEZvcm0sCiAgICAgIHNldFRleHQ6IHNldFRleHQsIHNldFNlbGVjdDogc2V0U2VsZWN0LCBzZXRDb3VudHJ5OiBzZXRDb3VudHJ5LAogICAgICBsaXZlT3B0aW9uczogbGl2ZU9wdGlvbnMsIHJlYWRGaWVsZHM6IHJlYWRGaWVsZHMsCiAgICAgIGNvbGxlY3RWYWxpZGF0aW9uRXJyb3JzOiBjb2xsZWN0VmFsaWRhdGlvbkVycm9ycywgZmluZENvbmZpcm1hdGlvbjogZmluZENvbmZpcm1hdGlvbiwKICAgICAgaGlnaGxpZ2h0OiBoaWdobGlnaHQKICAgIH07CiAgfSkoKTsKfQoKd2luZG93Ll9fd21jcFJlZy5yZWdpc3RlclRvb2xJZigiL3BhcnRuZXJzLyoqIiwgewogIG5hbWU6ICJvcHRpbWl6ZWx5LXN1Ym1pdC1iZWNvbWUtYS1wYXJ0bmVyLWlucXVpcnkiLAogIGRlc2NyaXB0aW9uOiAiRmlsbHMgdGhlICdCZWNvbWUgYSBzb2x1dGlvbiBwYXJ0bmVyJyBpbnF1aXJ5IGZvcm0gaW4gdGhlICNiZWNvbWUtYS1wYXJ0bmVyIHNlY3Rpb24gb2YgdGhlIE9wdGltaXplbHkgcGFydG5lcnMgcGFnZSAoaHR0cHM6Ly93d3cub3B0aW1pemVseS5jb20vcGFydG5lcnMvKSBmb3IgYW4gYWdlbmN5LCBjb25zdWx0YW5jeSBvciBzeXN0ZW1zIGludGVncmF0b3IgdGhhdCB3YW50cyB0byBqb2luIHRoZSBPcHRpbWl6ZWx5IHNvbHV0aW9uLXBhcnRuZXIgZWNvc3lzdGVtLiBJdCBzY3JvbGxzIHRvIHRoZSBmb3JtLCBoaWdobGlnaHRzIGl0LCB0eXBlcyB0aGUgY29udGFjdCBhbmQgY29tcGFueSBkZXRhaWxzLCBzZXRzIHRoZSBSZWFjdC9SYWRpeCBkcm9wZG93bnMgdmlhIHRoZWlyIGF1dGhvcml0YXRpdmUgaGlkZGVuIDxzZWxlY3Q+IGVsZW1lbnRzIGFuZCBwaWNrcyB0aGUgQ291bnRyeSBmcm9tIGl0cyBhdXRvY29tcGxldGUsIHRoZW4gcmVhZHMgZXZlcnkgdmFsdWUgYmFjayBvdXQgb2YgdGhlIERPTS4gUmV0dXJucyB7c3RhdHVzLCBhY3Rpb24sIGZpZWxkcyAoZWFjaCBmb3JtIGZpZWxkIG5hbWUgbWFwcGVkIHRvIHRoZSB2YWx1ZSBhY3R1YWxseSBzaXR0aW5nIGluIHRoZSBpbnB1dCksIG1pc3NpbmcgKHJlcXVpcmVkIGZpZWxkcyBzdGlsbCBlbXB0eSksIHJlYWR5LCBzdWJtaXRUb29sTmFtZSwgdXJsLCBzdW1tYXJ5fSBzbyB0aGUgY2FsbGVyIGNhbiB2ZXJpZnkgd2hhdCB3YXMgZW50ZXJlZCBiZWZvcmUgc3VibWl0dGluZy4gRG9lcyBOT1Qgc3VibWl0IC0gY2FsbCBvcHRpbWl6ZWx5LXN1Ym1pdC1iZWNvbWUtYS1wYXJ0bmVyLWlucXVpcnktc3VibWl0IGZvciB0aGF0LiBOb3QgZm9yIHRoZSB0ZWNobm9sb2d5L0lTViBwYXJ0bmVyIGZvcm0gYXQgL3BhcnRuZXJzL2JlY29tZS1hLXBhcnRuZXIvdGVjaC8gYW5kIG5vdCBmb3IgdGhlIHBhcnRuZXIgZGlyZWN0b3J5IHNlYXJjaC4iLAogIGlucHV0U2NoZW
229481hOiB7CiAgICB0eXBlOiAib2JqZWN0IiwKICAgIHByb3BlcnRpZXM6IHsKICAgICAgZmlyc3ROYW1lOiB7IHR5cGU6ICJzdHJpbmciLCBkZXNjcmlwdGlvbjogIkZpcnN0IG5hbWUgKHJlcXVpcmVkIGJ5IHRoZSBmb3JtJ3MgSlMgdmFsaWRhdGlvbikuIiB9LAogICAgICBsYXN0TmFtZTogeyB0eXBlOiAic3RyaW5nIiwgZGVzY3JpcHRpb246ICJMYXN0IG5hbWUgKHJlcXVpcmVkKS4iIH0sCiAgICAgIGNvbXBhbnk6IHsgdHlwZTogInN0cmluZyIsIGRlc2NyaXB0aW9uOiAiWW91ciBjb21wYW55IC8gYWdlbmN5IG5hbWUgKHJlcXVpcmVkKS4iIH0sCiAgICAgIHRpdGxlOiB7IHR5cGU6ICJzdHJpbmciLCBkZXNjcmlwdGlvbjogIkpvYiB0aXRsZSAocmVxdWlyZWQpLiIgfSwKICAgICAgZW1haWw6IHsgdHlwZTogInN0cmluZyIsIGRlc2NyaXB0aW9uOiAiV29yayBlbWFpbCAtIG11c3QgYmUgYSBidXNpbmVzcyBlbWFpbCBhZGRyZXNzIChyZXF1aXJlZCkuIiB9LAogICAgICB3ZWJzaXRlOiB7IHR5cGU6ICJzdHJpbmciLCBkZXNjcmlwdGlvbjogIkNvbXBhbnkgd2Vic2l0ZSBVUkwgKHJlcXVpcmVkKS4iIH0sCiAgICAgIGNvdW50cnk6IHsgdHlwZTogInN0cmluZyIsIGRlc2NyaXB0aW9uOiAiQ291bnRyeSBuYW1lIGFzIGl0IGFwcGVhcnMgaW4gdGhlIGF1dG9jb21wbGV0ZSBsaXN0LCBlLmcuICdVbml0ZWQgU3RhdGVzJywgJ1VuaXRlZCBLaW5nZG9tJywgJ0dlcm1hbnknLiIgfSwKICAgICAgbnVtYmVyT2ZFbXBsb3llZXM6IHsKICAgICAgICB0eXBlOiAic3RyaW5nIiwKICAgICAgICBkZXNjcmlwdGlvbjogIk51bWJlciBvZiBjb21wYW55IGVtcGxveWVlcy4iLAogICAgICAgIGVudW06IFsiVW5kZXIgNTAiLCAiNTAtMTAwIiwgIjEwMC0yNTAiLCAiMjUwLTUwMCIsICI1MDAtMTAwMCIsICIxMDAwLTIwMDAiLCAiMjAwMCsiXQogICAgICB9LAogICAgICBvcHRpbWl6ZWx5RXhwZXJpZW5jZTogewogICAgICAgIHR5cGU6ICJzdHJpbmciLAogICAgICAgIGRlc2NyaXB0aW9uOiAiRG8geW91IGhhdmUgZXhwZXJpZW5jZSB3b3JraW5nIHdpdGggT3B0aW1pemVseSBwcm9kdWN0cz8iLAogICAgICAgIGVudW06IFsiWWVzIiwgIk5vIl0KICAgICAgfSwKICAgICAgY2xpZW50SW50ZXJlc3RlZDogewogICAgICAgIHR5cGU6ICJzdHJpbmciLAogICAgICAgIGRlc2NyaXB0aW9uOiAiRG8geW91IGhhdmUgYSBjdXJyZW50IGNsaWVudCBpbnRlcmVzdGVkIGluIG9uZSBvZiBvdXIgcHJvZHVjdHM/IiwKICAgICAgICBlbnVtOiBbIlllcyIsICJObyJdCiAgICAgIH0sCiAgICAgIGRlbGl2ZXJ5UHJhY3RpY2U6IHsKICAgICAgICB0eXBlOiAic3RyaW5nIiwKICAgICAgICBkZXNjcmlwdGlvbjogIkFyZSB5b3UgaW50ZXJlc3RlZCBpbiBidWlsZGluZyBhbiBPcHRpbWl6ZWx5IGRlbGl2ZXJ5IHByYWN0aWNlIGFzIHBhcnQgb2YgeW91ciBhZ2VuY3kgc2VydmljZXMgb2ZmZXJpbmc/IiwKICAgICAgICBlbnVtOiBbIlllcyIsICJObyJdCiAgICAgIH0sCiAgICAgIHBhcnRuZXJpbmdJbnRlcmVzdDogeyB0eXBlOiAic3RyaW5nIiwgZGVzY3JpcHRpb246ICJGcmVlIHRleHQ6IHdoYXQgaW50ZXJlc3RzIHlvdSBhYm91dCBwYXJ0bmVyaW5nIHdpdGggT3B0aW1pemVseT8iIH0sCiAgICAgIHByb2R1Y3RJbnRlcmVzdDogeyB0eXBlOiAic3RyaW5nIiwgZGVzY3JpcHRpb246ICJGcmVlIHRleHQ6IHdoaWNoIE9wdGltaXplbHkgcHJvZHVjdHMgYXJlIHlvdSBpbnRlcmVzdGVkIGluPyIgfQogICAgfQogIH0sCiAgYXN5bmMgZXhlY3V0ZShhcmdzKSB7CiAgICB2YXIgVSA9IHdpbmRvdy5fX3dtY3BPcHRpUGFydG5lcjsKICAgIGFyZ3MgPSBhcmdzIHx8IHt9OwogICAgdHJ5IHsKICAgICAgdHJ5IHsgYXdhaXQgd2FpdEZvckVsZW1lbnQoJ2Zvcm0ub3B0aS1mb3JtIGlucHV0W25hbWU9ImZpcnN0TmFtZSJdJyk7IH0gY2F0Y2ggKGUpIHt9CiAgICAgIHZhciBmb3JtID0gVS5nZXRGb3JtKCk7CiAgICAgIGlmICghZm9ybSkgewogICAgICAgIHJldHVybiB7IGNvbnRlbnQ6IFt7IHR5cGU6ICJ0ZXh0IiwgdGV4dDogSlNPTi5zdHJpbmdpZnkoewogICAgICAgICAgc3RhdHVzOiAiZXJyb3IiLAogICAgICAgICAgYWN0aW9uOiAibG9jYXRlIHRoZSBiZWNvbWUtYS1wYXJ0bmVyIHNvbHV0aW9uIHBhcnRuZXIgZm9ybSIsCiAgICAgICAgICByZWFzb246ICJubyBmb3JtLm9wdGktZm9ybSBjb250YWluaW5nIGlucHV0W25hbWU9XCJmaXJzdE5hbWVcIl0gd2FzIGZvdW5kIG9uIHRoaXMgcGFnZSIsCiAgICAgICAgICBuZXh0U3RlcDogIm5hdmlnYXRlIHRvIGh0dHBzOi8vd3d3Lm9wdGltaXplbHkuY29tL3BhcnRuZXJzLyNiZWNvbWUtYS1wYXJ0bmVyIGFuZCBjYWxsIHRoaXMgdG9vbCBhZ2FpbiIsCiAgICAgICAgICB1cmw6IGxvY2F0aW9uLmhyZWYsCiAgICAgICAgICBzdW1tYXJ5OiAic29sdXRpb24gcGFydG5lciBmb3JtIG5vdCBwcmVzZW50IG9uIHRoaXMgcGFnZSIKICAgICAgICB9KSB9XSB9OwogICAgICB9CgogICAgICB2YXIgZW51bUNoZWNrcyA9IFsKICAgICAgICB7IGFyZzogIm51bWJlck9mRW1wbG95ZWVzIiwgZmllbGQ6ICJOb19vZl9FbXBsb3llZXNfSW50ZXJ2YWxfX2NfYWNjb3VudCIgfSwKICAgICAgICB7IGFyZzogIm9wdGltaXplbHlFeHBlcmllbmNlIiwgZmllbGQ6ICJ0YXJnZXRBY2NvdW50UHJpb3JpdGl6YXRpb24iIH0sCiAgICAgICAgeyBhcmc6ICJjbGllbnRJbnRlcmVzdGVkIiwgZmllbGQ6ICJnZW5lcmFsMDciIH0sCiAgICAgICAgeyBhcmc6ICJkZWxpdmVyeVByYWN0aWNlIiwgZmllbGQ6ICJnZW5lcmFsMDgiIH0KICAgICAgXTsKICAgICAgZm9yICh2YXIgYyA9IDA7IGMgPCBlbnVtQ2hlY2tzLmxlbmd0aDsgYysrKSB7CiAgICAgICAgdmFyIGNoayA9IGVudW1DaGVja3NbY107CiAgICAgICAgdmFyIHZhbCA9IGFyZ3NbY2hrLmFyZ107CiAgICAgICAgaWYgKHZhbCA9PT0gdW5kZWZpbmVkIHx8IHZhbCA9PT0gbnVsbCB8fCB2YWwgPT09ICIiKSBjb250aW51ZTsKICAgICAgICB2Y
22948XIgYWxsb3dlZCA9IFUubGl2ZU9wdGlvbnMoZm9ybSwgY2hrLmZpZWxkKTsKICAgICAgICBpZiAoYWxsb3dlZC5sZW5ndGggJiYgYWxsb3dlZC5pbmRleE9mKFN0cmluZyh2YWwpKSA9PT0gLTEpIHsKICAgICAgICAgIHZhciBhdmFpbCA9IHt9OwogICAgICAgICAgYXZhaWxbY2hrLmFyZ10gPSBhbGxvd2VkOwogICAgICAgICAgcmV0dXJuIHsgY29udGVudDogW3sgdHlwZTogInRleHQiLCB0ZXh0OiBKU09OLnN0cmluZ2lmeSh7CiAgICAgICAgICAgIHN0YXR1czogImVycm9yIiwKICAgICAgICAgICAgYWN0aW9uOiAidmFsaWRhdGUgIiArIGNoay5hcmcgKyAiIGFyZ3VtZW50IiwKICAgICAgICAgICAgcmVhc29uOiBjaGsuYXJnICsgIiAnIiArIFUudHJ1bmModmFsLCAxMDApICsgIicgaXMgbm90IGFuIGFjY2VwdGVkIHZhbHVlIiwKICAgICAgICAgICAgYXZhaWxhYmxlT3B0aW9uczogYXZhaWwsCiAgICAgICAgICAgIG5leHRTdGVwOiAiY2FsbCBhZ2FpbiB3aXRoICIgKyBjaGsuYXJnICsgIiBzZXQgdG8gb25lIG9mIGF2YWlsYWJsZU9wdGlvbnMuIiArIGNoay5hcmcsCiAgICAgICAgICAgIHVybDogbG9jYXRpb24uaHJlZiwKICAgICAgICAgICAgc3VtbWFyeTogImludmFsaWQgIiArIGNoay5hcmcgKyAiOyBzZWUgYXZhaWxhYmxlT3B0aW9ucyIKICAgICAgICAgIH0pIH1dIH07CiAgICAgICAgfQogICAgICB9CgogICAgICBVLmhpZ2hsaWdodChmb3JtKTsKICAgICAgYXdhaXQgVS5zbGVlcCgzMDApOwoKICAgICAgdmFyIG5vdGVzID0gW107CiAgICAgIE9iamVjdC5rZXlzKFUuVEVYVF9NQVApLmZvckVhY2goZnVuY3Rpb24gKGFyZ05hbWUpIHsKICAgICAgICB2Y
22948XIgdiA9IGFyZ3NbYXJnTmFtZV07CiAgICAgICAgaWYgKHYgPT09IHVuZGVmaW5lZCB8fCB2ID09PSBudWxsIHx8IHYgPT09ICIiKSByZXR1cm47CiAgICAgICAgdmFyIHIgPSBVLnNldFRleHQoZm9ybSwgVS5URVhUX01BUFthcmdOYW1lXSwgdik7CiAgICAgICAgaWYgKCFyLm9rKSBub3Rlcy5wdXNoKCJjb3VsZCBub3Qgc2V0ICIgKyBVLlRFWFRfTUFQW2FyZ05hbWVdKTsKICAgICAgfSk7CgogICAgICB2YXIgc2VsQXJncyA9IE9iamVjdC5rZXlzKFUuU0VMRUNUX01BUCk7CiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgc2VsQXJncy5sZW5ndGg7IGkrKykgewogICAgICAgIHZhciBhID0gc2VsQXJnc1tpXTsKICAgICAgICBpZiAoYXJnc1thXSA9PT0gdW5kZWZpbmVkIHx8IGFyZ3NbYV0gPT09IG51bGwgfHwgYXJnc1thXSA9PT0gIiIpIGNvbnRpbnVlOwogICAgICAgIHZhciBzciA9IGF3YWl0IFUuc2V0U2VsZWN0KGZvcm0sIFUuU0VMRUNUX01BUFthXSwgYXJnc1thXSk7CiAgICAgICAgaWYgKCFzci5vaykgbm90ZXMucHVzaCgiY291bGQgbm90IHNldCAiICsgVS5TRUxFQ1RfTUFQW2FdICsgKHNyLnJlYXNvbiA/ICIgKCIgKyBzci5yZWFzb24gKyAiKSIgOiAiIikpOwogICAgICB9CgogICAgICBpZiAoYXJncy5jb3VudHJ5KSB7CiAgICAgICAgdmFyIGNyID0gYXdhaXQgVS5zZXRDb3VudHJ5KGZvcm0sIGFyZ3MuY291bnRyeSk7CiAgICAgICAgaWYgKCFjci5vaykgbm90ZXMucHVzaCgiY291bGQgbm90IHJlc29sdmUgY291bnRyeSAnIiArIFUudHJ1bmMoYXJncy5jb3VudHJ5LCA2MCkgKyAiJyBpbiB0aGUgYXV0b2NvbXBsZXRlIGxpc3QiKTsKICAgICAgfQoKICAgICAgYXdhaXQgVS5zbGVlcCg0MDApOwogICAgICBmb3JtID0gVS5nZXRGb3JtKCkgfHwgZm9ybTsKICAgICAgdmFyIGZpZWxkcyA9IFUucmVhZEZpZWxkcyhmb3JtKTsKICAgICAgdmFyIG1pc3NpbmcgPSBbXTsKICAgICAgVS5SRVFVSVJFRC5mb3JFYWNoKGZ1bmN0aW9uIChuYW1lKSB7CiAgICAgICAgaWYgKCFmaWVsZHNbbmFtZV0pIG1pc3NpbmcucHVzaChuYW1lKTsKICAgICAgfSk7CgogICAgICByZXR1cm4geyBjb250ZW50OiBbeyB0eXBlOiAidGV4dCIsIHRleHQ6IEpTT04uc3RyaW5naWZ5KHsKICAgICAgICBzdGF0dXM6IG1pc3NpbmcubGVuZ3RoIHx8IG5vdGVzLmxlbmd0aCA/ICJwYXJ0aWFsIiA6ICJvayIsCiAgICAgICAgYWN0aW9uOiAiZmlsbGVkIHRoZSBiZWNvbWUtYS1zb2x1dGlvbi1wYXJ0bmVyIGlucXVpcnkgZm9ybSBhbmQgcmVhZCB0aGUgdmFsdWVzIGJhY2sgZnJvbSB0aGUgRE9NIiwKICAgICAgICBmaWVsZHM6IGZpZWxkcywKICAgICAgICBtaXNzaW5nOiBtaXNzaW5nLAogICAgICAgIG5vdGVzOiBub3Rlcy5zbGljZSgwLCAxMCksCiAgICAgICAgcmVhZHk6IG1pc3NpbmcubGVuZ3RoID09PSAwLAogICAgICAgIHN1Ym1pdFRvb2xOYW1lOiAib3B0aW1pemVseS1zdWJtaXQtYmVjb21lLWEtcGFydG5lci1pbnF1aXJ5LXN1Ym1pdCIsCiAgICAgICAgdXJsOiBsb2NhdGlvbi5ocmVmLAogICAgICAgIHN1bW1hcnk6IG1pc3NpbmcubGVuZ3RoID09PSAwCiAgICAgICAgICA/ICJGb3JtIGZpbGxlZDsgYWxsIHJlcXVpcmVkIGZpZWxkcyBwb3B1bGF0ZWQgLSByZWFkeSB0byBzdWJtaXQuIgogICAgICAgICAgOiAiRm9ybSBmaWxsZWQgYnV0IHN0aWxsIG1pc3Npbmc6ICIgKyBtaXNzaW5nLmpvaW4oIiwgIikKICAgICAgfSkgfV0gfTsKICAgIH0gY2F0Y2ggKGVycikgewogICAgICByZXR1cm4geyBjb250ZW50OiBbeyB0eXBlOiAidGV4dCIsIHRleHQ6IEpTT04uc3RyaW5naWZ5KHsKICAgICAgICBzdGF0dXM6ICJlcnJvciIsCiAgICAgICAgYWN0aW9uOiAiZmlsbCB0aGUgYmVjb21lLWEtc29sdXRpb24tcGFydG5lciBpbnF1aXJ5IGZvcm0iLAogICAgICAgIHJlYXNvbjogU3RyaW5nKGVyciAmJiBlcnIubWVzc2FnZSA/IGVyci5tZXNzYWdlIDogZXJyKS5zbGljZSgwLCAzMDApLAogICAgICAgIG5leHRTdGVwOiAicmVsb2FkIGh0dHBzOi8vd3d3Lm9wdGltaXplbHkuY29tL3BhcnRuZXJzLyNiZWNvbWUtYS1wYXJ0bmVyIGFuZCB0cnkgYWdhaW4iLAogICAgICAgIHVybDogbG9jYXRpb24uaHJlZiwKICAgICAgICBzdW1tYXJ5OiAidW5leHBlY3RlZCBlcnJvciB3aGlsZSBmaWxsaW5nIHRoZSBwYXJ0bmVyIGlucXVpcnkgZm9ybSIKICAgICAgfSkgfV0gfTsKICAgIH0KICB9Cn0pOwoKd2luZG93Ll9fd21jcFJlZy5yZWdpc3RlclRvb2xJZigiL3BhcnRuZXJzLyoqIiwgewogIG5hbWU6ICJvcHRpbWl6ZWx5LXN1Ym1pdC1iZWNvbWUtYS1wYXJ0bmVyLWlucXVpcnktc3VibWl0IiwKICBkZXNjcmlwdGlvbjogIlN1Ym1pdHMgdGhlIGFscmVhZHktZmlsbGVkICdCZWNvbWUgYSBzb2x1dGlvbiBwYXJ0bmVyJyBpbnF1aXJ5IG9uIGh0dHBzOi8vd3d3Lm9wdGltaXplbHkuY29tL3BhcnRuZXJzLyNiZWNvbWUtYS1wYXJ0bmVyIGJ5IGNsaWNraW5nIHRoZSB2aXNpYmxlICdCZWNvbWUgYSBzb2x1dGlvbiBwYXJ0bmVyJyBidXR0b24sIHRoZW4gd2FpdHMgYW5kIG9ic2VydmVzIHRoZSBwYWdlIG91dGNvbWUgKHRoZSBmb3JtJ3MgSlMgdmFsaWRhdGlvbiBtZXNzYWdlcywgdGhlIGZvcm0gYmVpbmcgcmVtb3ZlZC9yZXBsYWNlZCwgYSB0aGFuay15b3UgcGFuZWwsIG9yIGEgcmVkaXJlY3QgdG8gdGhlIHRoYW5rLXlvdSBwYWdlKS4gUmV0dXJucyB7c3RhdHVzLCBhY3Rpb24sIHZhbGlkYXRpb25FcnJvcnMgKG1lc3NhZ2VzIHJlYWQgZnJvbSB0aGUgRE9NKSwgY29uZmlybWF0aW9uIChzdWNjZXNzIHRleHQgaWYgcHJlc2VudCksIG9ic2VydmVkLCBmaWVsZHMgKHZhbHVlcyB0aGF0IHdlcmUgc3VibWl0dGVkKSwgdXJsLCBzdW1tYXJ5fS4gc3RhdHVzIGlzICdlcnJvcicgd2hlbiB2YWxpZGF0aW9uIGVycm9ycyBhcmUgcHJlc2VudC4gUnVuIG9wdGltaXplbHktc3VibWl0LWJlY29tZS1hLXBhcnRuZXItaW5xdWlyeSBmaXJzdCB0byBwb3B1bGF0ZSB0aGUgZmllbGRzLiIsCiAgaW5wdXRTY2hlbWE6IHsgdHlwZTogIm9iamVjdCIsIHByb3BlcnRpZXM6IHt9IH0sCiAgYXN5bmMgZXhlY3V0ZSgpIHsKICAgIHZhciBVID0gd2luZG93Ll9fd21jcE9wdGlQYXJ0bmVyOwogICAgdHJ5IHsKICAgICAgdHJ5IHsgYXdhaXQgd2FpdEZvckVsZW1lbnQoJ2Zvcm0ub3B0aS1mb3JtIGlucHV0W25hbWU9ImZpcnN0TmFtZSJdJyk7IH0gY2F0Y2ggKGUpIHt9CiAgICAgI
22948HZhciBmb3JtID0gVS5nZXRGb3JtKCk7CiAgICAgIGlmICghZm9ybSkgewogICAgICAgIHJldHVybiB7IGNvbnRlbnQ6IFt7IHR5cGU6ICJ0ZXh0IiwgdGV4dDogSlNPTi5zdHJpbmdpZnkoewogICAgICAgICAgc3RhdHVzOiAiZXJyb3IiLAogICAgICAgICAgYWN0aW9uOiAibG9jYXRlIHRoZSBiZWNvbWUtYS1wYXJ0bmVyIHNvbHV0aW9uIHBhcnRuZXIgZm9ybSBiZWZvcmUgc3VibWl0dGluZyIsCiAgICAgICAgICByZWFzb246ICJubyBmb3JtLm9wdGktZm9ybSBjb250YWluaW5nIGlucHV0W25hbWU9XCJmaXJzdE5hbWVcIl0gd2FzIGZvdW5kIG9uIHRoaXMgcGFnZSIsCiAgICAgICAgICBuZXh0U3RlcDogIm5hdmlnYXRlIHRvIGh0dHBzOi8vd3d3Lm9wdGltaXplbHkuY29tL3BhcnRuZXJzLyNiZWNvbWUtYS1wYXJ0bmVyLCBmaWxsIHRoZSBmb3JtIHdpdGggb3B0aW1pemVseS1zdWJtaXQtYmVjb21lLWEtcGFydG5lci1pbnF1aXJ5LCB0aGVuIGNhbGwgdGhpcyB0b29sIiwKICAgICAgICAgIHVybDogbG9jYXRpb24uaHJlZiwKICAgICAgICAgIHN1bW1hcnk6ICJzb2x1dGlvbiBwYXJ0bmVyIGZvcm0gbm90IHByZXNlbnQgb24gdGhpcyBwYWdlIgogICAgICAgIH0pIH1dIH07CiAgICAgIH0KCiAgICAgIHZhciBmaWVsZHMgPSBVLnJlYWRGaWVsZHMoZm9ybSk7CiAgICAgIHZhciBtaXNzaW5nID0gW107CiAgICAgIFUuUkVRVUlSRUQuZm9yRWFjaChmdW5jdGlvbiAobikgeyBpZiAoIWZpZWxkc1tuXSkgbWlzc2luZy5wdXNoKG4pOyB9KTsKCiAgICAgIHZhciBidG4gPSBmb3JtLnF1ZXJ5U2VsZWN0b3IoJy5vcHRpZm9ybXNzdWJtaXRlbGVtZW50IGJ1dHRvblt0eXBlPSJzdWJtaXQiXScpCiAgICAgICAgfHwgZm9ybS5xdWVyeVNlbGVjdG9yKCdidXR0b25bdHlwZT0ic3VibWl0Il0nKTsKICAgICAgaWYgKCFidG4pIHsKICAgICAgICByZXR1cm4geyBjb250ZW50OiBbeyB0eXBlOiAidGV4dCIsIHRleHQ6IEpTT04uc3RyaW5naWZ5KHsKICAgICAgICAgIHN0YXR1czogImVycm9yIiwKICAgICAgICAgIGFjdGlvbjogImxvY2F0ZSB0aGUgJ0JlY29tZSBhIHNvbHV0aW9uIHBhcnRuZXInIHN1Ym1pdCBidXR0b24iLAogICAgICAgICAgcmVhc29uOiAibm8gYnV0dG9uW3R5cGU9XCJzdWJtaXRcIl0gZm91bmQgaW5zaWRlIHRoZSBwYXJ0bmVyIGlucXVpcnkgZm9ybSIsCiAgICAgICAgICBmaWVsZHM6IGZpZWxkcywKICAgICAgICAgIG5leHRTdGVwOiAic2Nyb2xsIHRvIHRoZSAjYmVjb21lLWEtcGFydG5lciBzZWN0aW9uIGFuZCByZS1ydW4gdGhlIGZpbGwgdG9vbCwgdGhlbiByZXRyeSIsCiAgICAgICAgICB1cmw6IGxvY2F0aW9uLmhyZWYsCiAgICAgICAgICBzdW1tYXJ5OiAic3VibWl0IGJ1dHRvbiBub3QgZm91bmQiCiAgICAgICAgfSkgfV0gfTsKICAgICAgfQoKICAgICAgVS5oaWdobGlnaHQoYnRuKTsKICAgICAgdmFyIHVybEJlZm9yZSA9IGxvY2F0aW9uLmhyZWY7CiAgICAgIGJ0bi5jbGljaygpOwoKICAgICAgdHJ5IHsgYXdhaXQgd2FpdEZvckVsZW1lbnQoImZvcm0ub3B0aS1mb3JtIik7IH0gY2F0Y2ggKGUpIHt9CgogICAgICB2YXIgb3V0Y29tZSA9IGF3YWl0IFUud2FpdEZvcihmdW5jdGlvbiAoKSB7CiAgICAgICAgdmFyIGVycnMgPSBVLmNvbGxlY3RWYWxpZGF0aW9uRXJyb3JzKGZvcm0pOwogICAgICAgIGlmIChlcnJzLmxlbmd0aCkgcmV0dXJuIHsga2luZDogImVycm9ycyIsIGVycm9yczogZXJycyB9OwogICAgICAgIGlmICghZG9jdW1lbnQuY29udGFpbnMoZm9ybSkpIHJldHVybiB7IGtpbmQ6ICJnb25lIiB9OwogICAgICAgIGlmIChsb2NhdGlvbi5ocmVmICE9PSB1cmxCZWZvcmUpIHJldHVybiB7IGtpbmQ6ICJuYXZpZ2F0ZWQiIH07CiAgICAgICAgdmFyIGNvbmYgPSBVLmZpbmRDb25maXJtYXRpb24oKTsKICAgICAgICBpZiAoY29uZikgcmV0dXJuIHsga2luZDogImNvbmZpcm1hdGlvbiIsIHRleHQ6IGNvbmYgfTsKICAgICAgICByZXR1cm4gbnVsbDsKICAgICAgfSwgMTUwMDAsIDMwMCk7CgogICAgICBpZiAoIW91dGNvbWUpIHsKICAgICAgICByZXR1cm4geyBjb250ZW50OiBbeyB0eXBlOiAidGV4dCIsIHRleHQ6IEpTT04uc3RyaW5naWZ5KHsKICAgICAgICAgIHN0YXR1czogInBhcnRpYWwiLAogICAgICAgICAgYWN0aW9uOiAiY2xpY2tlZCB0aGUgJ0JlY29tZSBhIHNvbHV0aW9uIHBhcnRuZXInIHN1Ym1pdCBidXR0b24iLAogICAgICAgICAgb2JzZXJ2ZWQ6IGZhbHNlLAogICAgICAgICAgdmFsaWRhdGlvbkVycm9yczogW10sCiAgICAgICAgICBjb25maXJtYXRpb246ICIiLAogICAgICAgICAgc3VibWl0QnV0dG9uRGlzYWJsZWQ6ICEhYnRuLmRpc2FibGVkLAogICAgICAgICAgZmllbGRzOiBmaWVsZHMsCiAgICAgICAgICBtaXNzaW5nOiBtaXNzaW5nLAogICAgICAgICAgdXJsOiBsb2NhdGlvbi5ocmVmLAogICAgICAgICAgc3VtbWFyeTogIlN1Ym1pdCBjbGlja2VkIGJ1dCBubyBjb25maXJtYXRpb24sIHJlZGlyZWN0IG9yIHZhbGlkYXRpb24gbWVzc2FnZSBhcHBlYXJlZCB3aXRoaW4gMTVzLiIKICAgICAgICB9KSB9XSB9OwogICAgICB9CgogICAgICBpZiAob3V0Y29tZS5raW5kID09PSAiZXJyb3JzIikgewogICAgICAgIHJldHVybiB7IGNvbnRlbnQ6IFt7IHR5cGU6ICJ0ZXh0IiwgdGV4dDogSlNPTi5zdHJpbmdpZnkoewogICAgICAgICAgc3RhdHVzOiAiZXJyb3IiLAogICAgICAgICAgYWN0aW9uOiAiY2xpY2tlZCBzdWJtaXQ7IHRoZSBmb3JtIHJlcG9ydGVkIHZhbGlkYXRpb24gZXJyb3JzIiwKICAgICAgICAgIG9ic2VydmVkOiB0cnVlLAogICAgICAgICAgdmFsaWRhdGlvbkVycm9yczogb3V0Y29tZS5lcnJvcnMsCiAgICAgICAgICBjb25maXJtYXRpb246ICIiLAogICAgICAgICAgZmllbGRzOiBmaWVsZHMsCiAgICAgICAgICBtaXNzaW5nOiBtaXNzaW5nLAogICAgICAgICAgcmVhc29uOiAidGhlIGZvcm0ncyBjbGllbnQtc2lkZSB2YWxpZGF0aW9uIHJlamVjdGVkICIgKyBvdXRjb21lLmVycm9ycy5sZW5ndGggKyAiIGZpZWxkKHMpIiwKICAgICAgICAgIG5leHRTdGVwOiAiZml4IHRoZSBsaXN0ZWQgZmllbGRzIHdpdGggb3B0aW1pemVseS1zdWJtaXQtYmVjb21lLWEtcGFydG5lci1pbnF1aXJ5IChub3RlOiBlbWFpbCBtdXN0IGJlIGEgYnVzaW5lc3MgYWRkcmVzcykgYW5kIHN1Ym1pdCBhZ2FpbiIsCiAgICAgICAgICB1cmw6IGxvY2F0aW9uLmhy
22948ZWYsCiAgICAgICAgICBzdW1tYXJ5OiAiU3VibWlzc2lvbiBibG9ja2VkOiAiICsgVS50cnVuYyhvdXRjb21lLmVycm9ycy5qb2luKCIgfCAiKSwgMzAwKQogICAgICAgIH0pIH1dIH07CiAgICAgIH0KCiAgICAgIHZhciBjb25maXJtYXRpb24gPSBvdXRjb21lLmtpbmQgPT09ICJjb25maXJtYXRpb24iID8gb3V0Y29tZS50ZXh0IDogVS5maW5kQ29uZmlybWF0aW9uKCk7CiAgICAgIHJldHVybiB7IGNvbnRlbnQ6IFt7IHR5cGU6ICJ0ZXh0IiwgdGV4dDogSlNPTi5zdHJpbmdpZnkoewogICAgICAgIHN0YXR1czogIm9rIiwKICAgICAgICBhY3Rpb246ICJzdWJtaXR0ZWQgdGhlIGJlY29tZS1hLXNvbHV0aW9uLXBhcnRuZXIgaW5xdWlyeSBhbmQgb2JzZXJ2ZWQgdGhlIG91dGNvbWUiLAogICAgICAgIG9ic2VydmVkOiB0cnVlLAogICAgICAgIHZhbGlkYXRpb25FcnJvcnM6IFtdLAogICAgICAgIGNvbmZpcm1hdGlvbjogVS50cnVuYyhjb25maXJtYXRpb24gfHwgKG91dGNvbWUua2luZCA9PT0gImdvbmUiCiAgICAgICAgICA/ICJGb3JtIHJlbW92ZWQgZnJvbSB0aGUgcGFnZSAoc3VjY2VzcyBzaWduYWwgZm9yIHRoaXMgTWFya2V0byBmb3JtKS4iCiAgICAgICAgICA6ICJSZWRpcmVjdGVkIHRvIHRoZSB0aGFuay15b3UgcGFnZS4iKSwgMzAwKSwKICAgICAgICBvdXRjb21lU2lnbmFsOiBvdXRjb21lLmtpbmQsCiAgICAgICAgZmllbGRzOiBmaWVsZHMsCiAgICAgICAgdXJsOiBsb2NhdGlvbi5ocmVmLAogICAgICAgIHN1bW1hcnk6ICJQYXJ0bmVyIGlucXVpcnkgc3VibWl0dGVkOyAiICsgKG91dGNvbWUua2luZCA9PT0gIm5hdmlnYXRlZCIKICAgICAgICAgID8gInRoZSBwYWdlIG5hdmlnYXRlZCB0byB0aGUgdGhhbmsteW91IGRlc3RpbmF0aW9uLiIKICAgICAgICAgIDogKGNvbmZpcm1hdGlvbiA/ICJjb25maXJtYXRpb24gdGV4dCBzaG93biBvbiB0aGUgcGFnZS4iIDogInRoZSBmb3JtIHdhcyByZW1vdmVkLCB3aGljaCBpcyB0aGlzIGZvcm0ncyBzdWNjZXNzIHNpZ25hbC4iKSkKICAgICAgfSkgfV0gfTsKICAgIH0gY2F0Y2ggKGVycikgewogICAgICByZXR1cm4geyBjb250ZW50OiBbeyB0eXBlOiAidGV4dCIsIHRleHQ6IEpTT04uc3RyaW5naWZ5KHsKICAgICAgICBzdGF0dXM6ICJlcnJvciIsCiAgICAgICAgYWN0aW9uOiAic3VibWl0IHRoZSBiZWNvbWUtYS1zb2x1dGlvbi1wYXJ0bmVyIGlucXVpcnkiLAogICAgICAgIHJlYXNvbjogU3RyaW5nKGVyciAmJiBlcnIubWVzc2FnZSA/IGVyci5tZXNzYWdlIDogZXJyKS5zbGljZSgwLCAzMDApLAogICAgICAgIHZhbGlkYXRpb25FcnJvcnM6IFtdLAogICAgICAgIGNvbmZpcm1hdGlvbjogIiIsCiAgICAgICAgbmV4dFN0ZXA6ICJyZWxvYWQgaHR0cHM6Ly93d3cub3B0aW1pemVseS5jb20vcGFydG5lcnMvI2JlY29tZS1hLXBhcnRuZXIsIHJlLWZpbGwgdGhlIGZvcm0gYW5kIHRyeSBhZ2FpbiIsCiAgICAgICAgdXJsOiBsb2NhdGlvbi5ocmVmLAogICAgICAgIHN1bW1hcnk6ICJ1bmV4cGVjdGVkIGVycm9yIHdoaWxlIHN1Ym1pdHRpbmcgdGhlIHBhcnRuZXIgaW5xdWlyeSBmb3JtIgogICAgICB9KSB9XSB9OwogICAgfQogIH0KfSk7CgogIC8qIFJlZ2lzdGVyIHRoZSBhY3RpdmUgcm91dGUncyB0b29scywgdGhlbiB3YXRjaCBmb3IgU1BBIHJvdXRlIGNoYW5nZXMuICovCiAgdHJ5IHsgX3N5bmNSb3V0ZSgpOyB9IGNhdGNoIChlKSB7IC8qIG5ldmVyIGJyZWFrIHJlZ2lzdHJhdGlvbiAqLyB9CiAgdHJ5IHsgX2luc3RhbGxSb3V0ZXIoKTsgfSBjYXRjaCAoZSkgeyAvKiBuZXZlciBicmVhayByZWdpc3RyYXRpb24gKi8gfQogIC8qIFNpZ25hbCB0aGF0IHRoaXMgcGFnZSdzIHRvb2wgc2V0IGlzIG5vdyByZWdpc3RlcmVkLiAqLwogIF9kaXNwYXRjaFRvb2xjaGFuZ2UoKTsKfSkoKTsK')));document.head.appendChild(s);})();
22949}, "id": "54f11909-2e84-4d7c-88a1-b0f76dc3f9c6", "dependencies": []}]}]}], "weightDistributions": [{"entityId": "6314365995974656", "endOfRange": 10000}], "name": "Optimizely WebMCP Tools", "bucketingStrategy": null, "experimentMetadata": {"allocationPolicy": "manual", "layerId": "4598093977485312"}, "deployed": false}], "policy": "single_experiment", "viewIds": ["4506880884604928", "4697099063787520", "5047722560978944", "5349484731826176", "5484585880911872", "5585749758705664", "5808189738844160", "5860379681619968", "5876630495690752", "6092126671339520", "6327368875245568", "6394057822306304"], "weightDistributions": null, "decisionMetadata": {"experimentPriorities": [[{"entityId": "5309577053339648", "endOfRange": null}]], "experimentWeights": null, "offerConsistency": null}, "concluded": false}, {"changes": null, "id": "4964319343935488", "name": "App In-product Promo", "commitId": "6505557968814080", "groupId": null, "holdback": 0, "activation": {}, "integrationSettings": {"google_analytics_4": {}, "5196871765852160": {}}, "integrationStringVersion": 2, "experiments": [{"audienceIds": null, "audienceName": "Everyone else", "changes": null, "id": "5263981024116736", "integrationSettings": null, "variations": [{"id": "5468665240354816", "name": "Original", "actions": [{"viewId": "5559117419380736", "changes": [{"id": "0B33CAFA-8C30-453B-A83A-9E8B33B954E1", "type": "widget", "dependencies": [], "name": "In-product promo", "widget_id": "6735230963286016", "config": {"link": "https://www.optimizely.com/field-notes/opticon", "title": "Gathering the bolde
22949st marketers, digital leaders, and experience-makers for the biggest day out in digital, Camp Opticon is open.  ", "image": "//cdn.optimizely.com/img/26942930964/254804722b64481386d369b6c0f79311.svg"}}, {"css": {}, "dependencies": [], "attributes": {}, "type": "attribute", "id": "929C187F-1B3D-49BE-8C28-CB4D2B8BA75D", "selector": ".marketing-banner"}]}]}], "weightDistributions": null, "name": "App In-product Promo", "bucketingStrategy": null, "experimentMetadata": {"allocationPolicy": "manual", "layerId": "4964319343935488"}, "deployed": false}], "policy": "single_experiment", "viewIds": ["5559117419380736"], "weightDistributions": null, "decisionMetadata": null, "concluded": false}, {"changes": null, "id": "5036880318693376", "name": "VT webinar", "commitId": "6656974933196800", "groupId": null, "holdback": 0, "activation": {}, "integrationSettings": {"google_analytics_4": {}, "5196871765852160": {}}, "integrationStringVersion": 2, "experiments": [{"audienceIds": ["and", "5266314971840512"], "audienceName": "VT webinar, form submitted", "changes": null, "id": "6498053962596352", "integrationSettings": null, "variations": [{"id": "5904564291895296", "name": "Variation #1", "actions": [{"viewId": "5125093494358016", "changes": [{"css": {}, "dependencies": [], "attributes": {"html": "Get started"}, "type": "attribute", "id": "8E0D7626-37F4-4243-879B-C4FCCF7B016A", "selector": "body > main > div:nth-of-type(1) > div > div > div > div > a"}]}]}], "weightDistributions": null, "name": "VT webinar, form submitted", "bucketingStrategy": null, "experimentMetadata": {"allocationPolicy": "manual", "layerId": "5036880318693376"}, "deployed": false}], "policy": "equal_priority", "viewIds": ["5125093494358016"], "weightDistributions": null, "decisionMetadata": {"experimentPriorities": [[{"entityId": "6498053962596352", "endOfRange": null}]], "experimentWeights": null, "offerConsistency": null}, "concluded": false}, {"changes": null, "id": "5093950334173184", "name": "[260811 Felix] demo pages: hide nav, darker bg", "commitId": "6310841170001920", "groupId": null, "holdback": 0, "activation": {}, "integrationSettings": {"google_analytics_4": {}, "5196871765852160": {}}, "integrationStringVersion": 2, "experiments": [{"audienceIds": null, "audienceName": "Everyone else", "changes": null, "id": "5594383515385856", "integrationSettings": null, "variations": [{"id": "4572332293881856", "name": "Original", "actions": []}, {"id": "5394411482251264", "name": "Variation #1", "actions": [{"viewId": "5778351393079296", "changes": [{"css": {}, "dependencies": [], "attributes": {"remove": true}, "type": "attribute", "id": "A113A11C-0225-4872-971D-7010E31E260C", "selector": "body > div > nav > div > div > div:nth-of-type(2) > div"}, {"css": {}, "dependencies": [], "attributes": {"style": "filter: brightness(70%);"}, "type": "attribute", "id": "DC875867-63D2-453E-82DF-572676E98E3A", "selector": "body > main > div > div > img"}]}]}], "weightDistributions": [{"entityId": "4572332293881856", "endOfRange": 5000}, {"entityId": "5394411482251264", "endOfRange": 10000}], "name": "[260811 Felix] demo pages: hide nav, darker bg", "bucketingStrategy": null, "experimentMetadata": {"allocationPolicy": "manual", "layerId": "5093950334173184"}, "deployed": false}], "policy": "single_experiment", "viewIds": ["5778351393079296"], "weightDistributions": null, "decisionMetadata": null, "concluded": false}, {"changes": null, "id": "5567469018939392", "name": "Multi Step", "commitId": "5176683249008640", "groupId": null, "holdback": 0, "activation": {}, "integrationSettings": {"google_analytics_4": {}, "5196871765852160": {}}, "integrationStringVersion": 2, "experiments": [{"audienceIds": null, "audienceName": "Everyone else", "changes": null, "id": "6264923724972032", "integrationSettings": null, "variations": [{"id": "4736053964177408", "name": "Original", "actions": []}, {"id": "5139023818129408", "name": "Variation #1", "actions": [{"viewId": "5658597101666304", "changes": [{"id": "B09CA7DF-A7D9-47D2-B259-724B1D442D9E", "type": "redirect", "dependencies": [], "dest": "https://www.optimizely.com/get-started-ms", "preserveParameters": true, "allowAdditionalRedirect": false}]}]}], "weightDistributions": [{"entityId": "4736053964177408", "endOfRange": 5000}, {"entityId": "5139023818129408", "endOfRange": 10000}], "name": "Multi Step", "bucketingStrategy": null, "experimentMetadata": {"allocationPolicy": "manual", "layerId": "5567469018939392"}, "deployed": false}], "policy": "single_experiment", "viewIds": ["5658597101666304"], "weightDistributions": null, "decisionMetadata": null, "concluded": false}, {"changes": null, "id": "6024894505287680", "name": "[260806 Felix] Opticon personalized for Nathalie", "commitId": "6027222467739648", "groupId": null, "holdback": 0, "activation": {}, "integrationSettings": {"google_analytics_4": {}, "5196871765852160": {}}, "integrationStringVersion": 2, "experiments": [{"audienceIds": ["and", "5842697026535424"], "audienceName": "Opticon ABM New York", "changes": null, "id": "4521664004751360", "integrationSettings": null, "variations": [{"id": "4838918181355520", "name": "Variation #1", "actions": [{"viewId": "4636714610720768", "changes": [{"css": {}, "dependencies": [], "attributes": {"html": "<div class=\"relative flex w-full flex-col bg-secondary-ltblue pt-20 pb-27 px-7 -mt-6 -mb-6 justify-start rounded-t-default lg:rou
22949nded-t-lg lg:pt-32 lg:pb-42 lg:-mb-10 md:-mt-6 lg:-mt-10 lg:px-16\" style=\"--separator-color:var(--color-blue-300);--timeline-bar-color:#0D3A29;--timeline-dot-color:#B8D4D0;--timeline-dot-active-color:#ABFF44\"><div class=\"container mx-auto flex grow flex-col justify-start\"><div class=\"grid-cols-1 md:grid-cols-2 gap-x-4 lg:gap-x-24 gap-y-4 grid mt-0 mb-0 bg-transparent px-0 py-0\"><div class=\"flex flex-1 flex-col flex-nowrap justify-start gap-4\"><div class=\"pt-0 pr-0 pb-0 pl-0\"><h1 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-8xl md:text-10xl lg:text-12xl 2xl:text-12xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] 2xl:leading-[0.9] text-left mb-8 z-1 text-(--color-secondary-darkfir)\"><span class=\"block\"><span class=\"relative z-10\">Join us at</span></span><span class=\"block\"><span class=\"inline-block origin-bottom-left\"><!--$--><span class=\"relative inline-block\"><span class=\"inline-block whitespace-pre-wrap relative pointer-events-none select-none\" aria-hidden=\"true\" style=\"color:var(--color-extrusion-5);z-index:10;-webkit-text-stroke:0.025em var(--color-fir-darkfir);paint-order:stroke fill;transform:none\">Camp Opticon</span><span class=\"inline-block whitespace-pre-wrap absolute inset-0 pointer-events-none select-none\" aria-hidden=\"true\" style=\"color: var(--color-extrusion-4); z-index: 11; -webkit-text-stroke: 0.025em var(--color-fir-darkfir); paint-order: stroke; transform: translateX(5.58502px) translateY(-5.58502px);\">Camp Opticon</span><span class=\"inline-block whitespace-pre-wrap absolute inset-0 pointer-events-none select-none\" aria-hidden=\"true\" style=\"color: var(--color-extrusion-3); z-index: 12; -webkit-text-stroke: 0.025em var(--color-fir-darkfir); paint-order: stroke; transform: translateX(11.17px) translateY(-11.17px);\">Camp Opticon</span><span class=\"inline-block whitespace-pre-wrap absolute inset-0 pointer-events-none select-none\" aria-hidden=\"true\" style=\"color: var(--color-extrusion-2); z-index: 13; -webkit-text-stroke: 0.025em var(--color-fir-darkfir); paint-order: stroke; transform: translateX(16.7551px) translateY(-16.7551px);\">Camp Opticon</span><span class=\"inline-block whitespace-pre-wrap absolute inset-0\" aria-hidden=\"false\" style=\"color: var(--color-extrusion-1); z-index: 14; -webkit-text-stroke: 0.025em var(--color-fir-darkfir); paint-order: stroke; transform: translateX(22.3401px) translateY(-22.3401px);\">Camp Opticon</span></span><!--/$--></span></span></h1></div><div class=\"element text-content-element px-0 pt-0 pr-0 pb-4 lg:pb-6 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none\"><p>For centuries, people have gathered at the campfire to swap stories, learn, and see things differently. This year, Opticon brings that spirit to the city. Camp Opticon gathers the boldest marketers, digital leaders, and experience-makers for the biggest day out in digital.<br>&nbsp;<br>Face to face in New York. Expect a grounded space, the sharpest thinking in agentic AI, and a ride home with fire in your belly.</p>\n<hr>\n<h3>
22949Your journey starts here</h3>\n<p>Join us at Camp Opticon New York and claim your free fire stove.</p>\n<p><a title=\"Register now\" href=\"https://events.optimizely.com/Opticon26NY\">Register now</a></p></div></div></div><div class=\"flex flex-1 flex-col flex-nowrap justify-start gap-4 self-end\"><div class=\"image-text-container-block @container/itc w-full h-full\"><a href=\"https://events.optimizely.com/Opticon26NY\" title=\"Claim your spot\" data-epi-edit=\"Link\" class=\"flex flex-col rounded-2xl p-2 md:rounded-3xl md:p-3 h-full @[800px]/itc:flex-row @[800px]/itc:gap-4 @[800px]/itc:p-4 bg-(--color-tertiary-ltpink) group focus-visible:ring-2 focus-visible:ring-(--color-primary-goodtogo) focus-visible:ring-offset-2 focus-visible:outline-none\" style=\"--prose-type-color:var(--color-secondary-darkfir)\"><div class=\"relative overflow-hidden rounded-xl md:rounded-2xl aspect-16/9 @[800px]/itc:aspect-video @[800px]/itc:flex-1 @[800px]/itc:order-2\"><img loading=\"lazy\" decoding=\"async\" src=\"https://images1.cmp.optimizely.com/Zz00ZTc4MGFlYTk0YzYxMWYxOWYwYTJlODU1NjAxODEyOA==\" alt=\"\" class=\"absolute inset-0 size-full object-cover transition-transform duration-300 group-hover:scale-105 group-active:scale-100\"></div><div class=\"flex flex-1 flex-col gap-5 px-3 py-6 md:gap-6 md:px-3 md:py-8 @[800px]/itc:justify-center @[800px]/itc:gap-6 @[800px]/itc:py-10 @[800px]/itc:order-1 @[800px]/itc:pl-4 @[800px]/itc:w-[432px] @[800px]/itc:flex-none @[800px]/itc:pr-22\"><div class=\"flex flex-col gap-4 @[800px]/itc:gap-6\"><h3 class=\"font-headline font-(--font-weight-headline-extrabold) text-headline-xs md:text-headline-m @[800px]/itc:text-headline-xl text-(--color-secondary-darkfir)\">Come to Camp. Get a stove.</h3></div><div data-epi-edit=\"Description\" class=\"prose font-body text-body-xs max-w-none leading-[1.3] font-(--font-weight-body-normal) md:text-2xl/tight text-(--color-secondary-darkfir)\"><p>You didn't get the standard invite - you got this one. Show up at Camp Opticon New York and we'll fuel what's next with a free fire stove, sent straight to your door after the event.</p>\n<p><br>No forms to chase. No hoops. Check in at the Javits on September 1, and it's yours.</p></div><div class=\"mt-auto flex @[800px]/itc:pt-4\"><span class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-secondary-darkfir bg-tertiary-midfir text-tertiary-2 hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-secondary-darkfir h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px] lg:h-14 lg:rounded-[20px] lg:px-6 lg:text-body-med lg:tracking-[0.4px] group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] group-focus-visible:translate-x-0.5 group-focus-visible:-translate-y-0.5 group-focus-visible:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] group-active:translate-x-0 group-active:translate-y-0 group-active:shadow-none group-active:bg-secondary-darkfir\">Claim your spot</span></div></div></a></div><div class=\"element text-content-element px-0 pt-0 pr-4 lg:pr-6 pb-0 pl-4 lg:pl-6\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none prose-p-size-14\"><p>**US shipping only. One stove per registered guest. Ships after the event to confirmed attendees</p></div></div></div></div></div></div>"}, "type": "attribute", "id": "C9C298D8-B50E-417C-8491-77A6F263AC66", "selector": "body > main > div:nth-of-type(1) > div:nth-of-type(2)"}, {"css": {}, "dependencies": [], "attributes": {"remove": true}, "type": "attribute", "id": "50A15852-18A1-4AF8-9481-E90F9ED4B420", "selector": "body > main > div:nth-of-type(1) > div:nth-of-type(1) > div > div > div > div:nth-of-type(3)"}, {"css": {}, "dependencies": [], "attributes": {"remove": true}, "type": "attribute", "id": "0AAD74F9-93E8-4394-B5D7-74E2A8713346", "selector": "body > main > div:nth-of-type(1) > div:nth-of-type(3) > div:nth-of-type(1)"}, {"css": {}, "dependencies": [], "attributes": {"remove": true}, "type": "attribute", "id": "369A3ADF-EB88-4C7C-B37E-56F097BB5B51", "selector": "body > main > div > div:nth-of-type(5) > div > div > div:nth-of-type(1) > div > [data-epi-edit='Link']"}, {"css": {}, "dependencies": [], "attributes": {"remove": true}, "type": "attribute", "id": "B75A02D0-097C-438A-93D4-99B4E65F6B44", "selector": "body > main > div > div:nth-of-type(5) > div > div > div:nth-of-type(3) > div > [data-epi-edit='Link']"}, {"css": {}, "dependencies": [], "attributes": {"html": "<div class=\"flex flex-1 flex-col flex-nowrap justify-start gap-4\"><div class=\"image-text-container-block @container/itc w-full h-full\"><a href=\"https://events.optimizely.com/Opticon26NY\" title=\"Opticon 26 New York\" data-epi-edit=\"Link\" class=\"flex flex-col rounded-2xl p-2 md:rounded-3xl md:p-3 h-full @[800px]/itc:flex-row @[800px]/itc:gap-4 @[800px]/itc:p-4 bg-(--color-tertiary-ltpink) group focus-visible:ring-2 focus-visible:ring-(--color-primary-goodtogo) focus-visible:ring-offset-2 focus-visible:outline-none\" style=\"--prose-type-color: var(--color-secondary-darkfir);\"><div class=\"flex flex-1 flex-col gap-5 px-3 py-6 md:gap-6 md:px-3 md:py-8 @[800px]/itc:justify-center @[800px]/itc:gap-6 @[800px]/itc:py-10 @[800px]/itc:order-1 @[800px]/itc:pl-4 @[800px]/itc:pr-4\"><div class=\"flex flex-col gap-4 @[800px]/itc:gap-6\"><div class=\"flex items-center gap-2 text-(--color-secondary-darkfir)\"><p class=\"font-body text-body-xxs md:text-body-s/tight leading-[1.35] font-(--font-weight-body-medium)\">01 September</p></div><h3 class=\"font-headline font-(--font-weight-headline-extrabold) text-headline-xs md:text-headline-m @[800px]/itc:text-headline-xl text-(--color-secondary-darkfir)\">In-person in New York</h3></div><div data-epi-edit=\"Description\" class=\"prose font-body text-body-xs max-w-none leading-[1.3] font-(--font-weight-body-normal) md:text-2xl/tight text-(--color-secondary-darkfir)\"><p>Includes Online access. Meet your fellow marketing creators in New York at the Javits Center.</p></div><div class=\"mt-auto flex @[800px]/itc:pt-4\"><span class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-secondary-darkfir bg-tertiary-mi
22949dfir text-tertiary-2 hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-secondary-darkfir h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px] lg:h-14 lg:rounded-[20px] lg:px-6 lg:text-body-med lg:tracking-[0.4px] group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] group-focus-visible:translate-x-0.5 group-focus-visible:-translate-y-0.5 group-focus-visible:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] group-active:translate-x-0 group-active:translate-y-0 group-active:shadow-none group-active:bg-secondary-darkfir\">Pack your bag</span></div></div></a></div></div>"}, "type": "attribute", "id": "756D11CA-60F0-40F0-AEE9-E8887DCB8354", "selector": "body > main > div > div:nth-of-type(5) > div > div > div:nth-of-type(2) > div > [data-epi-edit='Link']"}]}]}], "weightDistributions": null, "name": "NY", "bucketingStrategy": null, "experimentMetadata": {"allocationPolicy": "manual", "layerId": "6024894505287680"}, "deployed": false}, {"audienceIds": ["and", "4970900963983360"], "audienceName": "Opticon ABM London", "changes": null, "id": "6568025288081408", "integrationSettings": null, "variations": [{"id": "5202386615009280", "name": "Variation #1", "actions": [{"viewId": "4636714610720768", "changes": [{"css": {}, "dependencies": [], "attributes": {"html": "<div class=\"relative flex w-full flex-col bg-secondary-ltblue pt-20 pb-27 px-7 -mt-6 -mb-6 justify-start rounded-t-default lg:rounded-t-lg lg:pt-32 lg:pb-42 lg:-mb-10 md:-mt-6 lg:-mt-10 lg:px-16\" style=\"--separator-color:var(--color-blue-300);--timeline-bar-color:#0D3A29;--timeline-dot-color:#B8D4D0;--timeline-dot-active-color:#ABFF44\"><div class=\"container mx-auto flex grow flex-col justify-start\"><div class=\"grid-cols-1 md:grid-cols-2 gap-x-4 lg:gap-x-24 gap-y-4 grid mt-0 mb-0 bg-transparent px-0 py-0\"><div class=\"flex flex-1 flex-col flex-nowrap justify-start gap-4\"><div class=\"pt-0 pr-0 pb-0 pl-0\"><h1 class=\"w-full font-extrabold tracking-[-0.01em] font-nudge text-8xl md:text-10xl lg:text-12xl 2xl:text-12xl leading-[0.9] md:leading-[0.9] lg:leading-[0.9] 2xl:leading-[0.9] text-left mb-8 z-1 text-(--color-secondary-darkfir)\"><span class=\"block\"><span class=\"relative z-10\">Join us at</span></span><span class=\"block\"><span class=\"inline-block origin-bottom-left\"><!--$--><span class=\"relative inline-block\"><span class=\"inline-block whitespace-pre-wrap relative pointer-events-none select-none\" aria-hidden=\"true\" style=\"color:var(--color-extrusion-5);z-index:10;-webkit-text-stroke:0.025em var(--color-fir-darkfir);paint-order:stroke fill;transform:none\">Camp Opticon</span><span class=\"inline-block whitespace-pre-wrap absolute inset-0 pointer-events-none select-none\" aria-hidden=\"true\" style=\"color: var(--color-extrusion-4); z-index: 11; -webkit-text-stroke: 0.025em var(--color-fir-darkfir); paint-order: stroke; transform: translateX(5.58502px) translateY(-5.58502px);\">Camp Opticon</span><span class=\"inline-block whitespace-pre-wrap absolute inset-0 pointer-events-none select-none\" aria-hidden=\"true\" style=\"color: var(--color-extrusion-3); z-index: 12; -webkit-text-stroke: 0.025em var(--color-fir-darkfir); paint-order: stroke; transform: translateX(11.17px) translateY(-11.17px);\">Camp Opticon</span><span class=\"inline-block whitespace-pre-wrap absolute inset-0 pointer-events-none select-none\" aria-hidden=\"true\" style=\"color: var(--color-extrusion-2); z-index: 13; -webkit-text-stroke: 0.025em var(--color-fir-darkfir); paint-order: stroke; transform: translateX(16.7551px) translateY(-16.7551px);\">Camp Opticon</span><span class=\"inline-block whitespace-pre-wrap absolute inset-0\" aria-hidden=\"false\" style=\"color: var(--color-extrusion-1); z-index: 14; -webkit-text-stroke: 0.025em var(--color-fir-darkfir); paint-order: stroke; transform: translateX(22.3401px) translateY(-22.3401px);\">Camp Opticon</span></span><!--/$--></span></span></h1></div><div class=\"element text-content-element px-0 pt-0 pr-0 pb-4 lg:pb-6 pl-0\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none\"><p>For centuries, people have gathered at the campfire to swap stories, learn, and see things differently. This year, Opticon brings that spirit to the city. Camp Opticon gathers the bolde
22949st marketers, digital leaders, and experience-makers for the biggest day out in digital.<br>&nbsp;<br>Face to face in London. Expect a grounded space, the sharpest thinking in agentic AI, and ideas that travel well.</p>\n<hr>\n<h3>Your journey starts here</h3>\n<p>Join us at Camp Opticon London and claim your free picnic hamper.</p>\n<p><a title=\"Register now\" href=\"https://events.optimizely.com/Opticon26London\">Register now</a></p></div></div></div><div class=\"flex flex-1 flex-col flex-nowrap justify-start gap-4 self-end\"><div class=\"image-text-container-block @container/itc w-full h-full\"><a href=\"https://events.optimizely.com/Opticon26London\" title=\"Claim your spot\" data-epi-edit=\"Link\" class=\"flex flex-col rounded-2xl p-2 md:rounded-3xl md:p-3 h-full @[800px]/itc:flex-row @[800px]/itc:gap-4 @[800px]/itc:p-4 bg-(--color-tertiary-ltpink) group focus-visible:ring-2 focus-visible:ring-(--color-primary-goodtogo) focus-visible:ring-offset-2 focus-visible:outline-none\" style=\"--prose-type-color:var(--color-secondary-darkfir)\"><div class=\"relative overflow-hidden rounded-xl md:rounded-2xl aspect-16/9 @[800px]/itc:aspect-video @[800px]/itc:flex-1 @[800px]/itc:order-2\"><img loading=\"lazy\" decoding=\"async\" src=\"https://images2.cmp.optimizely.com/Zz04ZTE3MDk3YWE1MmYxMWYxYjkwZDBlODVkOTJmNWQyOQ==\" alt=\"\" class=\"absolute inset-0 size-full object-cover transition-transform duration-300 group-hover:scale-105 group-active:scale-100\"></div><div class=\"flex flex-1 flex-col gap-5 px-3 py-6 md:gap-6 md:px-3 md:py-8 @[800px]/itc:justify-center @[800px]/itc:gap-6 @[800px]/itc:py-10 @[800px]/itc:order-1 @[800px]/itc:pl-4 @[800px]/itc:w-[432px] @[800px]/itc:flex-none @[800px]/itc:pr-22\"><div class=\"flex flex-col gap-4 @[800px]/itc:gap-6\"><h3 class=\"font-headline font-(--font-weight-headline-extrabold) text-headline-xs md:text-headline-m @[800px]/itc:text-headline-xl text-(--color-secondary-darkfir)\">Come to Camp. Get a hamper.</h3></div><div data-epi-edit=\"Description\" class=\"prose font-body text-body-xs max-w-none leading-[1.3] font-(--font-weight-body-normal) md:text-2xl/tight text-(--color-secondary-darkfir)\"><p>You didn't get the standard invite - you got this one. Show up at Camp Opticon London and take home a complimentary picnic hamper, delivered straight to your door after the event.</p>\n<p><br>No forms to chase. No hoops. Check in at the Barbican Centre, and it's yours.</p></div><div class=\"mt-auto flex @[800px]/itc:pt-4\"><span class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-secondary-darkfir bg-tertiary-midfir text-tertiary-2 hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-secondary-darkfir h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px] lg:h-14 lg:rounded-[20px] lg:px-6 lg:text-body-med lg:tracking-[0.4px] group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] group-focus-visible:translate-x-0.5 group-focus-visible:-translate-y-0.5 group-focus-visible:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] group-active:translate-x-0 group-active:translate-y-0 group-active:shadow-none group-active:bg-secondary-darkfir\">Claim your spot</span></div></div></a></div><div class=\"element text-content-element px-0 pt-0 pr-4 lg:pr-6 pb-0 pl-4 lg:pl-6\"><div data-epi-edit=\"MainBody\" class=\"prose prose-rte max-w-none prose-p-size-14\"><p>**UK shipping only. One picnic hamper per registered guest. Ships after the event to confirmed attendees</p></div></div></div></div></div></div>"}, "type": "attribute", "id": "AE90B411-FF7A-403B-9A61-534888E1E9FB", "selector": "body > main > div:nth-of-type(1) > div:nth-of-type(2)"}, {"css": {}, "dependencies": [], "attributes": {"remove": true}, "type": "attribute", "id": "12B061FE-47E7-4557-9AD9-F245783B9948", "selector": "body > main > div:nth-of-type(1) > div:nth-of-type(1) > div > div > div > div:nth-of-type(3)"}, {"css": {}, "dependencies": [], "attributes": {"remove": true}, "type": "attribute", "id": "50298521-E095-47B6-B87E-FCFFBF0B7A97", "selector": "body > main > div:nth-of-type(1) > div:nth-of-type(3) > div:nth-of-type(1)"}, {"css": {}, "dependencies": [], "attributes": {"remove": true}, "type": "attribute", "id": "0AA8D415-02D7-4AEC-8100-514314108B53", "selector": "body > main > div > div:nth-of-type(5) > div > div > div:nth-of-type(1) > div > [data-epi-edit='Link']"}, {"css": {}, "dependencies": [], "attributes": {"remove": true}, "type": "attribute", "id": "DD090A44-C7D1-499E-930F-2B3DF0370721", "selector": "body > main > div > div:nth-of-type(5) > div > div > div:nth-of-type(3) > div > [data-epi-edit='Link']"}, {"css": {}, "dependencies": [], "attributes": {"html": "<div class=\"flex flex-1 flex-col flex-nowrap justify-start gap-4\"><div class=\"image-text-container-block @container/itc w-full h-full\"><a href=\"https://events.optimizely.com/event/Opticon26London/register\" title=\"Opticon 26 New York\" data-epi-edit=\"Link\" class=\"flex flex-col rounded-2xl p-2 md:rounded-3xl md:p-3 h-full @[800px]/itc:flex-row @[800px]/itc:gap-4 @[800px]/itc:p-4 bg-(--color-tertiary-ltpink) group focus-visible:ring-2 focus-visible:ring-(--color-primary-goodtogo) focus-visible:ring-offset-2 focus-visible:outline-none\" style=\"--prose-type-color: var(--color-secondary-darkfir);\"><div class=\"flex flex-1 flex-col gap-5 px-3 py-6 md:gap-6 md:px-3 md:py-8 @[800px]/itc:justify-center @[800px]/itc:gap-6 @[800px]/itc:py-10 @[800px]/itc:order-1 @[800px]/itc:pl-4 @[800px]/itc:pr-4\"><div class=\"flex flex-col gap-4 @[800px]/itc:gap-6\"><div class=\"flex items-center gap-2 text-(--color-secondary-darkfir)\"><p class=\"font-body text-body-xxs md:text-body-s/tight leading-[1.35] font-(--font-weight-body-medium)\">13 October</p></div><h3 class=\"font-headline font-(--font-weight-headline-extrabold) text-headline-xs md:text-headline-m @[800px]/itc:text-headline-xl text-(--color-secondary-darkfir)\">In-person in London</h3></div><div data-epi-edit=\"Description\" class=\"prose font-body text-body-xs max-w-none leading-[1.3] font-(--font-weight-body-normal) md:text-2xl/tight text-(--color-secondary-darkfir)\"><p>Includes Online access. Meet your fellow marketing creators in London at the Barbican.</p></div><div class=\"mt-auto flex @[800px]/itc:pt-4\"><span class=\"inline-flex items-center justify-center gap-3 font-button font-(--font-weight-button) text-center transition-all cursor-pointer border-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary-ltblue disabled:pointer-events-none disabled:opacity-50 border border-secondary-darkfir bg-tertiary-mi
22949dfir text-tertiary-2 hover:translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] active:translate-x-0 active:translate-y-0 active:shadow-none active:bg-secondary-darkfir h-10 rounded-(--radius-cta-sml) px-4 text-(length:--text-body-xs) leading-[1.3] tracking-[0.32px] lg:h-14 lg:rounded-[20px] lg:px-6 lg:text-body-med lg:tracking-[0.4px] group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] group-focus-visible:translate-x-0.5 group-focus-visible:-translate-y-0.5 group-focus-visible:shadow-[-2px_2px_0_0_var(--color-secondary-darkfir)] group-active:translate-x-0 group-active:translate-y-0 group-active:shadow-none group-active:bg-secondary-darkfir\">Pack your bag</span></div></div></a></div></div>"}, "type": "attribute", "id": "77654204-0468-4BFF-AD6C-64369D285E5E", "selector": "body > main > div > div:nth-of-type(5) > div > div > div:nth-of-type(2) > div > [data-epi-edit='Link']"}]}]}], "weightDistributions": null, "name": "London", "bucketingStrategy": null, "experimentMetadata": {"allocationPolicy": "manual", "layerId": "6024894505287680"}, "deployed": false}], "policy": "equal_priority", "viewIds": ["4636714610720768"], "weightDistributions": null, "decisionMetadata": {"experimentPriorities": [[{"entityId": "4521664004751360", "endOfRange": null}], [{"entityId": "6568025288081408", "endOfRange": null}]], "experimentWeights": null, "offerConsistency": null}, "concluded": false}], "groups": [], "audiences": [{"id": "4691728056188928", "name": "[Opticon 26 sponsor] Valtech", "conditions": ["and", ["or", ["or", {"match": "exact", "name": "sponsor", "type": "query", "value": "valtech"}]]]}, {"id": "4949734828277760", "name": "[Opticon 26 sponsor] MSQ DX", "conditions": ["and", ["or", ["or", {"match": "exact", "name": "sponsor", "type": "query", "value": "msq-dx"}]]]}, {"id": "4970900963983360", "name": "Opticon ABM London", "conditions": ["and", ["or", ["or", {"match": "exact", "name": "utm_location", "type": "query", "value": "ABM-London"}]], ["or", ["or", {"match": "exact", "name": "tab", "type": "query", "value": "london"}]]]}, {"id": "5070167707746304", "name": "[Opticon 26 sponsor] Columbus", "conditions": ["and", ["or", ["or", {"match": "exact", "name": "sponsor", "type": "query", "value": "columbus"}]]]}, {"id": "5119077402804224", "name": "[Opticon 26 sponsor] Netcel", "conditions": ["and", ["or", ["or", {"match": "exact", "name": "sponsor", "type": "query", "value": "netcel"}]]]}, {"id": "5266314971840512", "name": "VT webinar, form submitted", "conditions": ["and", ["or", ["or", {"match": null, "name": "custom", "type": "vendor.odp", "value": "copy_content__beyond_form_submitted"}]]]}, {"id": "5465773739606016", "name": "[Opticon 26 sponsor] Niteco", "conditions": ["and", ["or", ["or", {"match": "exact", "name": "sponsor", "type": "query", "value": "niteco"}]]]}, {"id": "5842697026535424", "name": "Opticon ABM New York", "conditions": ["and", ["or", ["or", {"match": "exact", "name": "utm_location", "type": "query", "value": "ABM-NY"}]]]}, {"id": "6080144903766016", "name": "[Opticon 26 sponsor] DEPT", "conditions": ["and", ["or", ["or", {"match": "exact", "name": "sponsor", "type": "query", "value": "dept"}]]]}, {"id": "6717352492924928", "name": "[Opticon 26 sponsor] UNRVLD", "conditions": ["and", ["or", ["or", {"match": "exact", "name": "sponsor", "type": "query", "value": "unrvld"}]]]}], "listTargetingKeys": [], "visitorAttributes": [], "visitorIdLocator": null, "dcpServiceId": "30322590051", "dcpKeyfieldLocators": [{"dcp_datasource_id": "30260470401", "is_optimizely": false, "type": "cookie", "name": "iv"}, {"dcp_datasource_id": "5389221694275584", "is_optimizely": true, "type": "uid", "name": ""}], "integrationSettings": [{"id": "google_analytics_4", "global_is_use_gtm": "on"}, {"id": "odp", "dataFetchMethod": "internal_list", "public_key": "8yoUTdBkNwpVOLCXeZpKdw", "zaius_host": "zaius-us-endpoint", "is_passthrough": "off", "user_id": "vuid", "confidence_level": "low", "identifier_type": "cookie", "identifier_name": ""}], "views": [{"id": "27173780584", "category": "other", "apiName": "26942930964_all_glossary", "name": "All glossary", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "optimization-glossary"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "27175750646", "category": "other", "apiName": "26942930964_products", "name": "[page] - All Product Pages", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "https://www.optimizely.com/products/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": [{"category": "other", "apiName": "product", "locator": ".has-logo > .t-overline", "locatorType": "css_selector", "valueType": "string"}, {"category": "other", "apiName": "slug", "locator": function($){var tagValue = location.pathname.endsWith('/') ? location.pathname : location.pathname + "/";
22950return tagValue;
22951}, "locatorType": "javascript", "valueType": "string"}]}, {"id": "27183970331", "category": "other", "apiName": "26942930964_home", "name": "Home", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/"}, {"match": "simple", "type": "url", "value": "https://www.optimizely.com/no/"}, {"match": "simple", "type": "url", "value": "https://www.optimizely.com/sv/"}, {"match": "simple", "type": "url", "value": "https://www.optimizely.com/de/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "27192720379", "category": "other", "apiName": "26942930964_insights_no_overview", "name": "Insights (no overview)", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "/insights/"}], ["not", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/insights/"}]], ["not", ["or", {"match": "regex", "type": "url", "value": "optimizely.com/(de|sv|no)/"}]]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": [{"category": "other", "apiName": "resource_name", "locator": "[data-epi-type='content'] > main > div > div > div > div > div > div > h1", "locatorType": "css_selector", "valueType": "string"}]}, {"id": "27357450212", "category": "other", "apiName": "26942930964_all_optimizelycom_pages", "name": "All optimizely.com pages", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "https://www.optimizely.com/"}, {"match": "substring", "type": "url", "value": "optimizely.com"}], ["not", ["or", {"match": "substring", "type": "url", "value": "/sv"}, {"match": "substring", "type": "url", "value": "/de"}, {"match": "substring", "type": "url", "value": "/no"}]]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": [{"category": "other", "apiName": "href", "locator": function($){var tagValue = window.location.href; 
22952return tagValue;
22953}, "locatorType": "javascript", "valueType": "string"}]}, {"id": "29200870840", "category": "other", "apiName": "26942930964_partners", "name": "[page] - All Partners Pages", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "optimizely.com/partners/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "29233670854", "category": "other", "apiName": "26942930964_get_started", "name": "Get Started", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "optimizely.com/get-started/"}], ["or", {"type": "custom_code", "value": function jsCondition() {
22954  window.localStorage.setItem('_optiLayer', "{viewed: 1}");
22955  return true;
22956}
22957}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": [{"category": "other", "apiName": "get_started_company", "locator": "input[autocomplete=\"organization\"]", "locatorType": "css_selector", "valueType": "string"}, {"category": "other", "apiName": "get_started_job_title", "locator": "input[autocomplete=\"organization-title\"]", "locatorType": "css_selector", "valueType": "string"}, {"category": "other", "apiName": "get_started_country", "locator": "button[aria-label=\"
22957Select a country\"] > span.value", "locatorType": "css_selector", "valueType": "string"}]}, {"id": "29339530553", "category": "other", "apiName": "26942930964_page__opticon_2024_landing_page", "name": "Opticon main page", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "http://www.optimizely.com/field-notes/opticon"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "30090641230", "category": "other", "apiName": "26942930964_visited_developers", "name": "Visited developers", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": ".com/developers/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "30215930051", "category": "other", "apiName": "26942930964_visited_partners", "name": "Visited partners", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": ".com/partners/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4506880884604928", "category": "other", "apiName": "26942930964_optimizely_search_1_1", "name": "Optimizely Search (1)", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/search/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4565787736801280", "category": "other", "apiName": "26942930964_optimizely_search", "name": "Optimizely Search", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/search"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4567723307433984", "category": "landing_page", "apiName": "26942930964_vt_solutions__virtual_teammates_hero_dualcta", "name": "VT Solutions \u2014 Virtual Teammates (Hero Dual-CTA)", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "https://www.optimizely.com/solutions/virtual-teammates"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": [], "activationType": "dom_changed"}, {"id": "4589145262129152", "category": "other", "apiName": "26942930964_cart_page", "name": "Cart Page", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "/cart"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4595250977570816", "category": "other", "apiName": "26942930964_hyp_g_checkout_trustabove_checkout__trust_signal_row", "name": "[hyp_g_checkout_trustabove] Checkout \u2014 Trust Signal Row Test", "staticConditions": ["and", ["or", {"match": "exact", "type": "url", "value": "https://www.optimizely.com/checkout"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4600687600861184", "category": "other", "apiName": "26942930964_optimizely_get_started_1", "name": "Optimizely Get Started", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/get-started/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4625423122825216", "category": "other", "apiName": "26942930964_get_started__optimizelycom", "name": "Get Started \u2014 optimizely.com", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/get-started"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4633723734327296", "category": "other", "apiName": "26942930964_optimizely_search_1", "name": "Optimizely Search", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/search/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4636714610720768", "category": "other", "apiName": "26942930964_url_targeting_for_ty_page_custom", "name": "URL Targeting for [260806 Felix] Opticon personalized for Nathalie", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/field-notes/opticon"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4641660028583936", "category": "other", "apiName": "26942930964_optimizely_connectors", "name": "Optimizely Connectors", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/connectors"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4643364325294080", "category": "other", "apiName": "26942930964_hyp_g_pdp_stickycta_product_detail__sticky_cta_test", "name": "[hyp_g_pdp_stickycta] Product Detail \u2014 Sticky CTA Test", "staticConditions": null, "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4656171783553024", "category": "other", "apiName": "26942930964_hyp_g_cmsnosalescallsignal__cms_product_page", "name": "hyp_g_cms-no-sales-call-signal \u2014 CMS Product Page", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/products/content-management"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4658337671806976", "category": "other", "apiName": "26942930964_hyp_g_checkout_nextc
22957opy_checkout__expectationsetting", "name": "[hyp_g_checkout_nextcopy] Checkout \u2014 Expectation-Setting Copy Test", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/checkout"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4697099063787520", "category": "other", "apiName": "26942930964_optimizely_developers", "name": "Optimizely Developers", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/developers/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4705426418171904", "category": "other", "apiName": "26942930964_hyp_g_cmspeerreviewrating__cms_product_page", "name": "hyp_g_cms-peer-review-rating \u2014 CMS Product Page", "staticConditions": ["and", ["or", {"match": "exact", "type": "url", "value": "https://www.optimizely.com/products/content-management"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4718501372362752", "category": "other", "apiName": "26942930964_hyp_g_getstartedinlinetestimonial_get_started__inlin", "name": "[hyp_g_get-started-inline-testimonial] Get Started \u2014 Inline Testimonial Test", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/get-started"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4731880799469568", "category": "other", "apiName": "26942930964_webmcp__contact", "name": "WebMCP - Contact", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/contact"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4766570646339584", "category": "other", "apiName": "26942930964_hyp_g_getstartedsocialmomentum_get_started__social_m", "name": "[hyp_g_get-started-social-momentum] Get Started \u2014 Social Momentum Counter Test", "staticConditions": ["and", ["or", {"match": "exact", "type": "url", "value": "https://www.optimizely.com/get-started/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4770851638214656", "category": "other", "apiName": "26942930964_optimizely__plans", "name": "Optimizely - Plans", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/plans"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4772069043339264", "category": "other", "apiName": "26942930964_opticon_2026__sponsors", "name": "Opticon 2026 / sponsors", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "https://www.optimizely.com/field-notes/opticon"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4816416744931328", "category": "home", "apiName": "26942930964_homepage__industry__channel_personalization", "name": "Homepage \u2014 Industry & Channel Personalization", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": [], "activationType": "dom_changed"}, {"id": "4836559688564736", "category": "other", "apiName": "26942930964_optimizely_search_3", "name": "Optimizely Search", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/search"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4920638236786688", "category": "other", "apiName": "26942930964_hyp_g_getstartedsecondaryoffer_get_started__secondar", "name": "[hyp_g_get-started-secondary-offer] Get Started \u2014 Secondary Offer Test", "staticConditions": ["and", ["or", {"match": "exact", "type": "url", "value": "https://www.optimizely.com/get-started"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4958657622835200", "category": "other", "apiName": "26942930964_optimizely_partners_1_3", "name": "Optimizely Partners (3)", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/partners/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4962582283419648", "category": "other", "apiName": "26942930964_hyp_g_getstartedtrustbadges_get_started__trust_badge", "name": "[hyp_g_get-started-trust-badges] Get Started \u2014 Trust Badge Test", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/get-started"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4984039738703872", "category": "other", "apiName": "26942930964_optimizely_plans", "name": "Optimizely /plans", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/plans"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4990851825270784", "category": "other", "apiName": "26942930964_opticon_nyc_2026__registration_page", "name": "Opticon NYC 2026 \u2014 Registration Page", "staticConditions": ["and", ["or", {"match": "exact", "type": "url", "value": "https://www.optimizely.com/event/Opticon26NY/register"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4991108231462912", "category": "other", "apiName": "26942930964_hyp_g_cmsintegrationecosystemstrip__cms_product_page", "name": "hyp_g_cms-integration-ecosystem-strip \u2014 CMS Product Page", "staticConditions": ["and", ["or", {"match": "exact", "type": "url", "value": "https://www.optimizely.com/products/content-management"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4993845534916608", "category": "other", "apiName": "26942930964_hyp_g_cmstrialdetailsexpandable__cms_product_page", "name": "hyp_g_cms-trial-details-expandable \u2014 CMS Product Page", "staticConditions": ["and", ["or", {"match": "exact", "type": "url", "value": "https://www.optimizely.com/products/content-management"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "4998718980620288", "category": "other", "apiName": "26942930964_solutions_template__dualcta_hero_test", "name": "Solutions Template \u2014 Dual-CTA Hero Test", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "https://www.optimizely.com/solutions/"}], ["not", ["or", {"match": "substring", "type": "url", "value": "/en/solutions/"}, {"match": "substring", "type": "url", "value": "/de/solutions/"}, {"match": "substring", "type": "url", "value": "/no/solutions/"}]]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5028342057926656", "category": "other", "apiName": "26942930964_homepage_homepage", "name": "Homepage (/homepage)", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/homepage"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5044421878611968", "category": "other", "apiName": "26942930964_optimizely_partners_1_2", "name": "Optimizely Partners (2)", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/partners/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5046018734030848", "category": "other", "apiName": "26942930964_optimizely_get_started_1_2", "name": "Optimizely Get Started (2)", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/get-started/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5047722560978944", "category": "other", "apiName": "26942930964_optimizely_beta", "name": "Optimizely Beta", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/beta"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5077509820121088", "category": "other", "apiName": "26942930964_hyp_g_planscompetitorcomparisonstrip_plans_page__com", "name": "[hyp_g_plans-competitor-comparison-strip] Plans Page \u2014 Competitor Comparison Strip Test", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/plans"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5125093494358016", "category": "other", "apiName": "26942930964_url_targeting_for_vt_web
22957inar", "name": "URL Targeting for VT webinar", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5181097988849664", "category": "other", "apiName": "26942930964_optimizely__get_started", "name": "Optimizely - Get Started", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/get-started"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5190630953975808", "category": "other", "apiName": "26942930964_optimizely__products_experimentation", "name": "Optimizely - Products Experimentation", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/products/experimentation/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5203698459082752", "category": "other", "apiName": "26942930964_hyp_g_cart_trust_cart_page__trust_signal_test", "name": "[hyp_g_cart_trust] Cart Page \u2014 Trust Signal Test", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/cart"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5208363313659904", "category": "other", "apiName": "26942930964_developer_hub__technical_evaluation_path", "name": "Developer Hub \u2014 Technical Evaluation Path", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/developers"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5210186946707456", "category": "other", "apiName": "26942930964_hyp_g_plans_plans_page__cta_copy_reframe", "name": "[hyp_g_plans] Plans Page \u2014 CTA Copy Reframe", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/plans"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5241920998342656", "category": "other", "apiName": "26942930964_opti_id_login", "name": "Opti ID login", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://login.optimizely.com/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5251583099797504", "category": "other", "apiName": "26942930964_optimizely_get_started_2", "name": "Optimizely Get Started", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/get-started"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5267515297759232", "category": "other", "apiName": "26942930964_page__homepage_homepage", "name": "[page] - Homepage (/homepage)", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/homepage"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5273455673737216", "category": "other", "apiName": "26942930964_url_targeting_for_httpsoptimizelycomplans", "name": "URL Targeting for https://optimizely.com/plans/", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "https://optimizely.com/plans/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5296507249754112", "category": "other", "apiName": "26942930964_hyp_g_cart_cta_trust_cart_page__cta_trust_test", "name": "[hyp_g_cart_cta_trust] Cart Page \u2014 CTA Trust Test", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/cart"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5317312895516672", "category": "other", "apiName": "26942930964_see_pages", "name": "All Insights", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "https://www.optimizely.com/insights/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5349484731826176", "category": "other", "apiName": "26942930964_optimizely_product_updates", "name": "Optimizely Product Updates", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/product-updates"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5358897102323712", "category": "other", "apiName": "26942930964_hyp_g_cmsherosubheadline__cms_product_page", "name": "hyp_g_cms-hero-subheadline \u2014 CMS Product Page", "staticConditions": ["and", ["or", {"match": "exact", "type": "url", "value": "https://www.optimizely.com/products/content-management"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5371995813052416", "category": "other", "apiName": "26942930964_hyp_g_plandynamicfeatureannouncement_plans_page__dyn", "name": "[hyp_g_plan-dynamic-feature-announcement] Plans Page \u2014 Dynamic Feature Announcement Test", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/plans"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5480995221143552", "category": "other", "apiName": "26942930964_optimizely_search_3_1", "name": "Optimizely Search (4)", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/search"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5481298905530368", "category": "other", "apiName": "26942930964_hp001_get_started__phone_field_removal", "name": "[HP-001] Get started - Phone field removal", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/get-started/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5484585880911872", "category": "other", "apiName": "26942930964_optimizely_agents", "name": "Optimizely Agents", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/agents/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5488953728434176", "category": "other", "apiName": "26942930964_hyp_g_getstartedanalystroianchor_get_started__analys", "name": "[hyp_g_get-started-analyst-roi-anchor] Get Started \u2014 Analyst ROI Anchor Test", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/get-started"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5516257556955136", "category": "other", "apiName": "26942930964_hello_world__localized_banner_campaign", "name": "Hello World \u2014 Localized Banner Campaign", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "https://www.optimizely.com"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5545565977575424", "category": "other", "apiName": "26942930964_hyp_g_getstartedgroupdemorsvp_get_started__group_dem", "name": "[hyp_g_get-started-group-demo-rsvp] Get Started \u2014 Group Demo RSVP Test", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/get-started"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5559117419380736", "category": "other", "apiName": "26942930964_url_targeting_for_inproduct_promo_test", "name": "URL Targeting for App In-product Promo", "staticConditions": ["or", ["or", {"match": "substring", "type": "url", "value": "home.optimizely.com"}], ["or", {"match": "substring", "type": "url", "value": "dashboard"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5575817781051392", "category": "other", "apiName": "26942930964_hyp_g_cmsctabuttoncontrast__cms_product_page", "name": "hyp_g_cms-cta-button-contrast \u2014 CMS Product Page", "staticConditions": ["and", ["or", {"match": "exact", "type": "url", "value": "https://www.optimizely.com/products/content-management"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5585749758705664", "category": "other", "apiName": "26942930964_optimizely_partners_1_1", "name": "Optimizely Partners (1)", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/partners/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5606242054307840", "category": "other", "apiName": "26942930964_get_started__engetstarted", "name": "Get Started \u2014 /en/get
22957-started", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/en/get-started"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5623920877895680", "category": "other", "apiName": "26942930964_products__content_management_cms_pers", "name": "Products / Content Management (CMS Pers)", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "/products/content-management"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": [], "activationType": "dom_changed"}, {"id": "5628526743781376", "category": "other", "apiName": "26942930964_ai_product_page", "name": "AI Product Page", "staticConditions": ["and", ["or", {"match": "exact", "type": "url", "value": "https://www.optimizely.com/products/ai"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5658597101666304", "category": "other", "apiName": "26942930964_url_targeting_for_multi_step", "name": "URL Targeting for Multi Step", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/get-started"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5685356291424256", "category": "other", "apiName": "26942930964_hyp_g_getstartedlogobrandupgrade_get_started__logo_b", "name": "[hyp_g_get-started-logo-brand-upgrade] Get Started \u2014 Logo Brand-Tier Upgrade Test", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/get-started"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5690336155009024", "category": "other", "apiName": "26942930964_field_notes__content_hub_articles_excl_opticon", "name": "Field Notes \u2014 Content Hub Articles (excl. Opticon)", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "/field-notes/"}], ["not", ["or", {"match": "substring", "type": "url", "value": "/field-notes/opticon"}]]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": [], "activationType": "dom_changed"}, {"id": "5693645846740992", "category": "other", "apiName": "26942930964_hyp_g_globalnavctaprominence_global_navigation__site", "name": "[hyp_g_global-nav-cta-prominence] Global Navigation \u2014 Site-Wide CTA Prominence Test", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "optimizely.com"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5713096998649856", "category": "other", "apiName": "26942930964_hyp_g_planexitintent_plans_page__exitintent_overlay", "name": "[hyp_g_plan-exit-intent] Plans Page \u2014 Exit-Intent Overlay", "staticConditions": ["and", ["or", {"match": "exact", "type": "url", "value": "https://www.optimizely.com/plan"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5743238609371136", "category": "other", "apiName": "26942930964_optimizely__homepage", "name": "Optimizely - Homepage", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5743545112330240", "category": "other", "apiName": "26942930964_hyp_g_cmsnolockinguarantee__cms_product_page", "name": "hyp_g_cms-no-lockin-guarantee \u2014 CMS Product Page", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/products/content-management"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5778351393079296", "category": "other", "apiName": "26942930964_url_targeting_for_260811_felix_hide_nav_on_demo_page", "name": "URL Targeting for [260811 Felix] demo pages: hide nav, darker bg", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "https://www.optimizely.com/demos/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5808189738844160", "category": "other", "apiName": "26942930964_optimizely_get_started_1_1", "name": "Optimizely Get Started (1)", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/get-started/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5822984156348416", "category": "other", "apiName": "26942930964_hyp_g_pdp_cta_product_detail__cta_replacement_test", "name": "[hyp_g_pdp_cta] Product Detail \u2014 CTA Replacement Test", "staticConditions": ["and", ["or", {"match": "exact", "type": "url", "value": "https://www.optimizely.com/product-detail"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5830541738508288", "category": "other", "apiName": "26942930964_hyp_g_cart_stickycta_cart_page__sticky_cta_test", "name": "[hyp_g_cart_stickycta] Cart Page \u2014 Sticky CTA Test", "staticConditions": ["and", ["or", {"match": "exact", "type": "url", "value": "https://www.optimizely.com/cart"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5854690997436416", "category": "other", "apiName": "26942930964_optimizely__partners", "name": "Optimizely - Partners", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/partners"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5860379681619968", "category": "other", "apiName": "26942930964_optimizely_plans_2", "name": "Optimizely Plans", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/plans"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5876630495690752", "category": "other", "apiName": "26942930964_optimizely_field_notes", "name": "Optimizely Field Notes", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/field-notes"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5901063490895872", "category": "other", "apiName": "26942930964_hyp_g_homepagedemocapacityurgency_homepage__demo_cap", "name": "[hyp_g_homepage-demo-capacity-urgency] Homepage \u2014 Demo Capacity Urgency Line", "staticConditions": ["and", ["or", {"match": "exact", "type": "url", "value": "https://www.optimizely.com/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5931393610416128", "category": "other", "apiName": "26942930964_optimizely_homepage_1", "name": "Optimizely Homepage", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5936695053385728", "category": "other", "apiName": "26942930964_jett_test", "name": "Jett test", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.coolblue.nl/laptops"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "5955175918075904", "category": "other", "apiName": "26942930964_pdp__product_detail_1", "name": "PDP \u2014 Product Detail", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "https://www.optimizely.com/product-detail"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6009331848577024", "category": "other", "apiName": "26942930964_hyp_g_get_started__microcopy_test", "name": "[hyp_g] Get Started \u2014 Microcopy Test", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/get-started/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6030781921886208", "category": "other", "apiName": "26942930964_products_exp", "name": "Product Page: Agentic Experimentation", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/products/experimentation"}, {"match": "simple", "type": "url", "value": "https://www.optimizely.com/products/experimentation/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6034155266375680", "category": "other", "apiName": "26942930964_hyp_g_solutionsintegrationtrust_solutions_page__inte", "n
22957ame": "[hyp_g_solutions-integration-trust] Solutions Page \u2014 Integration Trust Test", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/solutions"}, {"match": "simple", "type": "url", "value": "https://www.optimizely.com/solutions/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6046744570494976", "category": "other", "apiName": "26942930964_optimizely__connectors", "name": "Optimizely - Connectors", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/connectors"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6081648075210752", "category": "other", "apiName": "26942930964_hyp_g_getstartedvideotestimonial_get_started__video_", "name": "[hyp_g_get-started-video-testimonial] Get Started \u2014 Video Testimonial", "staticConditions": ["and", ["or", {"match": "exact", "type": "url", "value": "https://www.optimizely.com/get-started"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6085853536059392", "category": "other", "apiName": "26942930964_optimizely_get_started", "name": "Optimizely Get Started", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/get-started"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6092126671339520", "category": "other", "apiName": "26942930964_optimizely_homepage", "name": "Optimizely Homepage", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6095288220254208", "category": "other", "apiName": "26942930964_hyp_g_cart_pricingclarity_cart_page__pricing_clarity", "name": "[hyp_g_cart_pricingclarity] Cart Page \u2014 Pricing Clarity Test", "staticConditions": ["and", ["or", {"match": "exact", "type": "url", "value": "https://www.optimizely.com/cart"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6097783864688640", "category": "other", "apiName": "26942930964_cart_page_1", "name": "Cart Page", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "https://www.optimizely.com/cart"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6189946682212352", "category": "other", "apiName": "26942930964_hyp_g_getstartedvaluepropbesideform_get_started__val", "name": "[hyp_g_get-started-value-prop-beside-form] Get Started \u2014 Value Prop Beside Form Test", "staticConditions": ["and", ["or", {"match": "exact", "type": "url", "value": "https://www.optimizely.com/get-started"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6222229552234496", "category": "other", "apiName": "26942930964_hyp_g_getstartedsubmitctacopy_get_started__submit_ct", "name": "[hyp_g_get-started-submit-cta-copy] Get Started \u2014 Submit CTA Copy Test", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/get-started/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6229100342280192", "category": "other", "apiName": "26942930964_experimentation_product_page", "name": "Experimentation Product Page", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/products/experimentation"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6242082635907072", "category": "other", "apiName": "26942930964_pdp__product_detail", "name": "PDP - Product Detail", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "/product-detail"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6253264113500160", "category": "other", "apiName": "26942930964_solutions", "name": "Solutions", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/solutions/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6254637597065216", "category": "other", "apiName": "26942930964_optimizely_partners", "name": "Optimizely Partners", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/partners"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6299765707636736", "category": "other", "apiName": "26942930964_hyp_g_planoutcomeledtiercopy_plans_page__outcomeled_", "name": "[hyp_g_plan-outcome-led-tier-copy] Plans Page \u2014 Outcome-Led Tier Copy Test", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/plans"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6299967168446464", "category": "other", "apiName": "26942930964_hyp_g_cmsheroeyebrowproof__cms_product_page", "name": "hyp_g_cms-hero-eyebrow-proof \u2014 CMS Product Page", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/products/content-management"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6302132251394048", "category": "other", "apiName": "26942930964_optimizely_search_1_2", "name": "Optimizely Search (2)", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/search/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6310629105991680", "category": "other", "apiName": "26942930964_optimizely_get_started_1_3", "name": "Optimizely Get Started (3)", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/get-started/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6327368875245568", "category": "other", "apiName": "26942930964_optimizely_connectors_2", "name": "Optimizely Connectors", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/connectors"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6366032506388480", "category": "other", "apiName": "26942930964_optimizely_search_1_3", "name": "Optimizely Search (3)", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/search/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6367079874428928", "category": "other", "apiName": "26942930964_optimizely_connectors_1", "name": "Optimizely Connectors (1)", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/connectors"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6394057822306304", "category": "other", "apiName": "26942930964_optimizely_products", "name": "Optimizely Products", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/products/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6404568412585984", "category": "other", "apiName": "26942930964_hyp_g_cart_cta_trust_cart_page__cta_trust_signal_tes", "name": "[hyp_g_cart_cta_trust] Cart Page \u2014 CTA Trust Signal Test", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "https://www.optimizely.com/cart"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6420012529876992", "category": "other", "apiName": "26942930964_ty_pages_no_demo", "name": "TY pages (no demo)", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "/thank-you"}], ["not", ["or", {"match": "substring", "type": "url", "value": "demo"}]]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6446811110440960", "category": "other", "apiName": "26942930964_hyp_g_getstartedheroheadline_get_started__hero_headl", "name": "[hyp_g_get-started-hero-headline] Get Started \u2014 Hero Headline Test", "staticConditions": ["and", ["or", {"match": "exact", "type": "url", "value": "https://www.optimizely.com/get-started/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6448710677168128", "category": "other", "apiName": "26942930964_url_targeting_for_httpsoptimizelycom", "name": "URL Targeting for https://optimizely.com", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "https://optimizely.com"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6488491620564992", "category": "other", "apiName": "26942930964_insights__content_hub_articles_no_overview_no_locale", "name": "Insights \u2014 Content Hub Articles (no overview, no locale)", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "/insights/"}], ["not", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/insights/"}]], ["not", ["or", {"match": "regex", "type": "url", "value": "optimizely.com/(de|sv|no)/"}]]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": [], "activationType": "dom_changed"}, {"id": "6492755499679744", "category": "other", "apiName": "26942930964_hyp_g_planaipositioningeyebrow_plans_page__ai_positi", "name": "[hyp_g_plan-ai-positioning-eyebrow] Plans Page \u2014 AI Positioning Eyebrow Test", "staticConditions": ["and", ["or", {"match": "exact", "type": "url", "value": "https://www.optimizely.com/plans"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6506827104387072", "category": "other", "apiName": "26942930964_optimizely_plans_1", "name": "Optimizely Plans", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/plans"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6512105132654592", "category": "other", "apiName": "26942930964_hyp_g_cmsroicalculatorcta__cms_product_page", "name": "hyp_g_cms-roi-calculator-cta \u2014 CMS Product Page", "staticConditions": ["and", ["or", {"match": "exact", "type": "url", "value": "https://www.optimizely.com/products/content-management"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6527124381630464", "category": "other", "apiName": "26942930964_webmcp__get_started", "name": "WebMCP - Get Started", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/get-started"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6542525498916864", "category": "other", "apiName": "26942930964_demo_ty_page", "name": "TY page (demo only)", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "demo/thank-you"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6554463794626560", "category": "other", "apiName": "26942930964_hyp_g_cart_cta_trust_visual_optimization", "name": "hyp_g_cart_cta_trust Visual Optimization", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "https://www.optimizely.com/cart"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6567315645399040", "category": "other", "apiName": "26942930964_url_targeting_for_partner_personalization_example", "name": "URL Targeting for Opticon 26 personalized sponsor pages", "staticConditions": ["and", ["or", {"match": "substring", "type": "url", "value": "https://www.optimizely.com/field-notes/opticon"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6576448524255232", "category": "other", "apiName": "26942930964_optimizely_get_started_3", "name": "Optimizely Get Started", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/get-started"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6589869005209600", "category": "other", "apiName": "26942930964_hyp_g_cmsstickyctabar__cms_product_page", "name": "hyp_g_cms-sticky-cta-bar \u2014 CMS Product Page", "staticConditions": null, "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6613830711640064", "category": "pricing", "apiName": "26942930964_hyp_g_plananalystroianchor_plans_page__analyst_roi_a", "name": "[hyp_g_plan-analyst-roi-anchor] Plans Page \u2014 Analyst ROI Anchor Test", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/plans"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6650875525988352", "category": "other", "apiName": "26942930964_optimizely_search_2", "name": "Optimizely Search", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/search"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6672652100698112", "category": "other", "apiName": "26942930964_plansremovephonefield_plans_page__www_regex", "name": "[plans-remove-phone-field] Plans Page \u2014 www Regex", "staticConditions": ["and", ["or", {"match": "regex", "type": "url", "value": "^https://(www\\.)?optimizely\\.com/plans/?(\\?.*)?$"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6678143065391104", "category": "other", "apiName": "26942930964_hyp_g_cmspricingmicrocopy__cms_product_page", "name": "hyp_g_cms-pricing-microcopy \u2014 CMS Product Page", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/products/content-management"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}, {"id": "6706671983001600", "category": "other", "apiName": "26942930964_optimizely_partners_1", "name": "Optimizely Partners", "staticConditions": ["and", ["or", {"match": "simple", "type": "url", "value": "https://www.optimizely.com/partners/"}]], "deactivationEnabled": false, "undoOnDeactivation": false, "tags": []}], "events": [{"id": "27159860471", "viewId": null, "name": "Form success", "category": "other", "apiName": "form_success", "eventType": "custom", "eventFilter": null}, {"id": "27181980720", "viewId": "27183970331", "name": "Nav CTA", "category": "other", "apiName": "26942930964_nav_cta", "eventType": "click", "eventFilter": {"filterType": "target_selector", "selector": ".site-header-cta-nav__list .button__content"}}, {"id": "27704600377", "viewId": null, "name": "Scroll event", "category": "other", "apiName": "scroll75", "eventType": "custom", "eventFilter": null}, {"id": "29344590088", "viewId": null, "name": "scroll_percent", "category": "other", "apiName": "scroll_percent", "eventType": "custom", "eventFilter": null}
22957, {"id": "29351230641", "viewId": "29339530553", "name": "[click] - Opticon Landing / Register", "category": "other", "apiName": "26942930964_click__opticon_landing__register", "eventType": "click", "eventFilter": {"filterType": "target_selector", "selector": "[data-epi-type='content'] > main > div > div > div > section > div > a[data-cursor-wrap='true'] > span:nth-of-type(1) > span:nth-of-type(1)"}}, {"id": "29361360724", "viewId": "29339530553", "name": "[click] - Opticon Landing / Grab your Ticket", "category": "other", "apiName": "26942930964_click__opticon_landing__grab_your_ticket", "eventType": "click", "eventFilter": {"filterType": "target_selector", "selector": "[data-epi-type='content'] > main > div > div > div > div:nth-of-type(3) > section > div > div > div > a[data-cursor-wrap='true'] > span:nth-of-type(1)"}}, {"id": "29378370146", "viewId": "29339530553", "name": "[click] - Opticon Landing / Claim your spot", "category": "other", "apiName": "26942930964_click__opticon_landing__claim_your_spot", "eventType": "click", "eventFilter": {"filterType": "target_selector", "selector": "[data-epi-type='content'] > main > div > div > div > div:nth-of-type(7) > section > div > div > div > div > a[data-cursor-wrap='true'] > span:nth-of-type(1) > span:nth-of-type(1)"}}, {"id": "4507834686111744", "viewId": "5901063490895872", "name": "[hyp_g_homepage-demo-capacity-urgency] Secondary CTA \u2014 Start a Free Trial Click", "category": "convert", "apiName": "hyp_g_homepage_demo_capacity_urgency_trial_cta_click", "eventType": "click", "eventFilter": {"filterType": "target_selector", "selector": ".secondary-cta-start-trial"}}, {"id": "4555931625717760", "viewId": "4567723307433984", "name": "VT Hero Secondary CTA Click \u2014 or see the teammates", "category": "other", "apiName": "vt_hero_secondary_cta_click", "eventType": "click", "eventFilter": {"filterType": "target_selector", "selector": "a.vt-hero-secondary-cta"}}, {"id": "4709736830992384", "viewId": "29233670854", "name": "Next Button CTA Clicks", "category": "other", "apiName": "26942930964_next_button_cta_clicks", "eventType": "click", "eventFilter": {"filterType": "target_selector", "selector": "[data-f-type='navigationbar'] > button:nth-of-type(2)"}}, {"id": "4757251154509824", "viewId": "6448710677168128", "name": "Homepage Hero CTA Clicks \u2014 Action Label Test", "category": "other", "apiName": "hp_hero_cta_click_action_label", "eventType": "click", "eventFilter": {"filterType": "target_selector", "selector": "main > div:nth-of-type(1) a[href=\"/get-started/\"]"}}, {"id": "4769944812912640", "viewId": null, "name": "Book Demo", "category": "convert", "apiName": "book_demo", "eventType": "custom", "eventFilter": null}, {"id": "4887103987777536", "viewId": "27183970331", "name": "Homepage Below-Fold CTA Click", "category": "other", "apiName": "homepage_below_fold_cta_click", "eventType": "click", "eventFilter": {"filterType": "target_selector", "selector": ".below-fold-proof-strip .cta-button"}}, {"id": "4913844588380160", "viewId": null, "name": "Chat Open", "category": "other", "apiName": "chat-open", "eventType": "custom", "eventFilter": null}, {"id": "4920548227022848", "viewId": null, "name": "Form Start", "category": "convert", "apiName": "form_start", "eventType": "custom", "eventFilter": null}, {"id": "4975486277779456", "viewId": "4772069043339264", "name": "[click] Option LP / Save your seat", "category": "other", "apiName": "26942930964_save_your_seat", "eventType": "click", "eventFilter": {"filterType": "target_selector", "selector": "div[data-state='active'] > div > div:nth-of-type(4) > div > div > div > div > div > [data-epi-edit='Link']"}}, {"id": "5307977983328256", "viewId": null, "name": "WebMCPTool", "category": "other", "apiName": "WebMCPTool", "eventType": "custom", "eventFilter": null}, {"id": "5338814825103360", "viewId": "4998718980620288", "name": "Solutions Hero Secondary CTA Click \u2014 See It In Action", "category": "other", "apiName": "solutions_hero_secondary_cta_click", "eventType": "click", "eventFilter": {"filterType": "target_selector", "selector": "a.vt-secondary-cta"}}, {"id": "5347797933752320", "viewId": null, "name": "Bounce Rate", "category": "other", "apiName": "bounce_rate", "eventType": "custom", "eventFilter": null}, {"id": "5659281861640192", "viewId": "5559117419380736", "name": "in-product banner clicks", "category": "other", "apiName": "26942930964_inproduct_banner_clicks", "eventType": "click", "eventFilter": {"filterType": "target_selector", "selector": ".marketing-banner"}}, {"id": "5699361290584064", "viewId": "4998718980620288", "name": "Solutions Hero CTA Click \u2014 Dual-CTA Test", "category": "convert", "apiName": "solutions_hero_cta_click_dual_cta", "eventType": "click", "eventFilter": {"filterType": "target_selector", "selector": "body > main > div:nth-of-type(1) > div > div > div > div > a[href=\"/get-started/\"]"}}, {"id": "5774229482766336", "viewId": "4567723307433984", "name": "VT Hero Primary CTA Click \u2014 Get a live demo", "category": "convert", "apiName": "vt_hero_primary_cta_click", "eventType": "click", "eventFilter": {"filterType": "target_selector", "selector": ".lg\\:gap-x-24 div.inline-flex.w-fit > a[href=\"/get-started/\"]"}}, {"id": "5820226854453248", "viewId": null, "name": "Form Abandonment Rate", "category": "other", "apiName": "form_abandonment_rate", "eventType": "custom", "eventFilter": null}, {"id": "5832567352459264", "viewId": null, "name": "Product Overview Download Click", "category": "convert", "apiName": "product_overview_download_click", "eventType": "custom", "eventFilter": null}, {"id": "5909866429022208", "viewId": "5623920877895680", "name": "Features Button Clicks", "category": "other", "apiName": "features_button_click", "eventType": "click", "eventFilter": {"filterType": "target_selector", "selector": "a[href*='features'], .features-button, [data-nav='features']"}}, {"id": "5910747887173632", "viewId": null, "name": "Visit Plans", "category": "other", "apiName": "visit_plans", "eventType": "custom", "eventFilter": null}, {"id": "5914498148597760", "viewId": null, "name": "Quiz Start", "category": "other", "apiName": "quiz-start", "eventType": "custom", "eventFilter": null}, {"id": "5996977576476672", "viewId": null, "name": "Time on Page", "category": "other", "apiName": "time_on_page", "eventType": "custom", "eventFilter": null}, {"id": "6058296019255296", "viewId": "5743545112330240", "name": "Data Portability Link Click", "category": "other", "apiName": "data_portability_link_click", "eventType": "click", "eventFilter": {"filterType": "target_selector", "selector": "a.data-portability-link"}}, {"id": "6098908609249280", "viewId": null, "name": "checkout initiated", "category": "other", "apiName": "checkout_initiated", "eventType": "custom", "eventFilter": null}, {"id": "6189126360236032", "viewId": "6030781921886208", "name": "[click] [product: exp] Get Started", "category": "other", "apiName": "26942930964_click_product_exp_get_started", "eventType": "click", "eventFilter": {"filterType": "target_selector", "selector": "a[data-epi-edit='Link'].bg-primary-lfgreen"}}, {"id": "6237902762409984", "viewId": "5317312895516672", "name": "Promotion click", "category": "other", "apiName": "26942930964_promotion_click", "eventType": "click", "eventFilter": {"filterType": "target_selector", "selector": ".promotion-placeholder"}}, {"id": "6247834855407616", "viewId": null, "name": "Accordion Interaction Rate", "category": "other", "apiName": "accordion_
22957interaction_rate", "eventType": "custom", "eventFilter": null}, {"id": "6263924205551616", "viewId": null, "name": "Scroll Depth \u2014 Hero Fold", "category": "other", "apiName": "scroll_depth_hero", "eventType": "custom", "eventFilter": null}, {"id": "6333773258686464", "viewId": "5623920877895680", "name": "Jump In CTA Clicks", "category": "convert", "apiName": "jump_in_cta_click", "eventType": "click", "eventFilter": {"filterType": "target_selector", "selector": "a[href*='demo'], .jump-in-cta, [data-cta='jump-in']"}}, {"id": "6390341920620544", "viewId": null, "name": "Get Started Form Submit", "category": "sign_up", "apiName": "get_started", "eventType": "custom", "eventFilter": null}, {"id": "6403820282970112", "viewId": null, "name": "Quiz Complete", "category": "convert", "apiName": "quiz-complete", "eventType": "custom", "eventFilter": null}, {"id": "6436920941608960", "viewId": "5317312895516672", "name": "Nav", "category": "other", "apiName": "26942930964_nav", "eventType": "click", "eventFilter": {"filterType": "target_selector", "selector": "nav"}}, {"id": "6526897805328384", "viewId": null, "name": "Opticon NYC 2026 Registration Form Submission", "category": "convert", "apiName": "opticon_registration_complete", "eventType": "custom", "eventFilter": null}, {"id": "6654330642628608", "viewId": null, "name": "pdp cta click", "category": "other", "apiName": "pdp_cta_click", "eventType": "custom", "eventFilter": null}, {"id": "6754983201210368", "viewId": null, "name": "Plans Page View", "category": "other", "apiName": "plans_page_view", "eventType": "custom", "eventFilter": null}], "dimensions": [{"id": "4801758346608640", "name": "OptiID products", "apiName": "optiid", "segmentId": null}, {"id": "5457414089080832", "name": "Is Logged In", "apiName": "is_logged_in", "segmentId": null}], "projectJS": function(){window.optimizely = window.optimizely || [];
22958(function() {
22959  if(location.hostname == "home.optimizely.com"){
22960function getProductNames(){
22961  	var productnames = [];
22962		var products = document.getElementsByClassName('ProductCardWithIcons_product-card__product-name__YQ3H3');
22963		for (var i = 0; i < products.length; i++){
22964  			productnames.push(products[i].innerText);
22965		}
22966		window.optimizely = window.optimizely || [];
22967		window.optimizely.push({
22968 		 "type": "user",
22969 		 "attributes": {
22970			"optiid": productnames.join()
22971		  }
22972});
22973}
22974setTimeout(getProductNames, 1000);
22975}
22976})();
22977}, "plugins": [function(PluginManager) {
22978  var Hogan=function(t){function r(e){if(n[e])return n[e].exports;var i=n[e]={exports:{},id:e,loaded:!1};return t[e].call(i.exports,i,i.exports,r),i.loaded=!0,i.exports}var n={};return r.m=t,r.c=n,r.p="",r(0)}([function(t,r){function n(t){this.r=t,this.buf=""}function e(t,r){var n;if(r&&"object"==typeof r)if(void 0!==r[t])n=r[t];return n}function i(t){return String(null===t||void 0===t?"":t)}function o(t){return t=i(t),p.test(t)?t.replace(u,"&amp;").replace(f,"&lt;").replace(c,"&gt;").replace(l,"&#39;").replace(a,"&quot;"):t}t.exports=n,n.prototype={r:function(t,r,n){return""},v:o,t:i,render:function(t,r,n){return this.ri([t],r||{},n)},ri:function(t,r,n){return this.r(t,r,n)},rs:function(t,r,n){var e=t[t.length-1];if(!s(e))return void n(t,r,this);for(var i=0;i<e.length;i++)t.push(e[i]),n(t,r,this),t.pop()},s:function(t,r,n,e,i,o,u){var f;if(s(t)&&0===t.length)return!1;if(f=!!t,!e&&f&&r)r.push("object"==typeof t?t:r[r.length-1]);return f},d:function(t,r,n,i){var o,u=t.split("."),f=this.f(u[0],r,n,i),c=null;if("."===t&&s(r[r.length-2]))f=r[r.length-1];else for(var l=1;l<u.length;l++)if(o=e(u[l],f),void 0!==o)c=f,f=o;else f="";if(i&&!f)return!1;if(!i&&"function"==typeof f)r.push(c),f=this.mv(f,r,n),r.pop();return f},f:function(t,r,n,i){for(var o=!1,u=null,f=!1,c=r.length-1;c>=0;c--)if(u=r[c],o=e(t,u),void 0!==o){f=!0;break}if(!f)return i?!1:"";if(!i&&"function"==typeof o)o=this.mv(o,r,n);return o},b:function(t){this.buf+=t},fl:function(){var t=this.buf;return this.buf="",t},mv:function(t,r,n){var e=r[r.length-1],o=t.call(e);if("function"==typeof o)return this.ct(i(o.call(e)),e,n);else return o}};var u=/&/g,f=/</g,c=/>/g,l=/'/g,a=/"/g,p=/[&<>"']/,s=Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)}}]);
22979  PluginManager.registerAnalyticsTracker('5196871765852160', {
22980      preRedirectPolicy: 'PERSIST_BEFORE_REDIRECT',
22981      postRedirectPolicy: 'TRACK_IMMEDIATELY',
22982      nonRedirectPolicy: 'TRACK_IMMEDIATELY',
22983      trackLayerDecision: function(decisionData) {
22984        var extension = decisionData.integrationSettings && decisionData.integrationSettings['5196871765852160'];
22985        if (!extension) {
22986          return;
22987        }
22988        var campaign = {
22989          id: decisionData.layerId,
22990          name: decisionData.layerName,
22991          policy: decisionData.layerPolicy,
22992          integrationStringVersion: decisionData.integrationStringVersion,
22993        };
22994        var campaignId = campaign.id;
22995        var experimentId = decisionData.experimentId;
22996        var variationId = decisionData.variationId;
22997        var isHoldback = decisionData.isLayerHoldback;
22998        extension.$fieldDefaults = [];
22999        (function(widg) {
23000  var i = 0;
23001  var field;
23002  for (; i < widg.$fieldDefaults.length; ++i) {
23003    field = widg.$fieldDefaults[i];
23004    if (!widg.hasOwnProperty(field.name)) {
23005      widg[field.name] = field.default_value;
23006    }
23007  }
23008  })(extension);
23009        var state = window['optimizely'].get('state');
23010var campaignObject = state.getDecisionObject({'campaignId':campaignId});
23011
23012if(campaignObject !== null){
23013 var utils = window["optimizely"].get("utils");
23014 utils.waitUntil(function() {
23015 	return typeof(clarity) === 'function';
23016 }).then(function() {
23017   clarity("set", "Optimizely", campaignObject.experiment +' - ' + campaignObject.variation);
23018 });
23019}
23020
23021      },
23022      serializeSettings: JSON.stringify,
23023      deserializeSettings: JSON.parse,
23024    });
23025    
23026
23027PluginManager.registerWidget({
23028      widgetId: '6735230963286016',
23029      showFn: function(event) {
23030        var $ = window.optimizely.get('jquery');
23031        var widget = event.data.config;
23032        var _template = new Hogan(function(c,p,i) {var t=this;t.b(i=i||"");t.b("<div id=\"optimizely-extension-");t.b(t.v(t.d("extension.$instance",c,p,0)));t.b("\" class=\"in-product-promo\">");t.b("\n" + i);t.b("  <a href=\"");t.b(t.v(t.d("extension.link",c,p,0)));t.b("\" id=\"marketing-banner-");t.b(t.v(t.d("extension.$instance",c,p,0)));t.b("\" title=\"");t.b(t.v(t.d("extension.title",c,p,0)));t.b("\" class=\"marketing-banner\">");t.b("\n" + i);t.b("			<img alt=\"");t.b(t.v(t.d("extension.title",c,p,0)));t.b("\" src=\"");t.b(t.v(t.d("extension.image",c,p,0)));t.b("\"/>
23032");t.b("\n" + i);t.b("		</a>");t.b("\n" + i);t.b("</div>");t.b("\n");t.b("\n");return t.fl(); })
23033        widget.$id = "6735230963286016";
23034        widget.$instance = event.data.id;
23035        widget.$render = _template.render.bind(_template)
23036        widget.$fieldDefaults = [{"name":"link","default_value":"#url-as-string"},{"name":"title","default_value":""},{"name":"image","default_value":"https://placehold.co/600x400/EEE/31343C"}];
23037        (function(widg) {
23038  var i = 0;
23039  var field;
23040  for (; i < widg.$fieldDefaults.length; ++i) {
23041    field = widg.$fieldDefaults[i];
23042    if (!widg.hasOwnProperty(field.name)) {
23043      widg[field.name] = field.default_value;
23044    }
23045  }
23046  })(widget);
23047        widget.$html = _template.render({ widget: widget, extension: widget })
23048        var extension = widget;
23049        widget._styleTag = document.createElement('style');
23050widget._styleTag.id = 'widget-css-6735230963286016';
23051widget._styleTag.innerHTML = '.marketing-banner {	display: block;}.marketing-banner img {	width: 100%;	display: block;	border-radius: 4px;}';
23052document.getElementsByTagName('head')[0].appendChild(widget._styleTag);
23053        /*
23054 * Editor now supports ES6 compliant code. Note that adding ES6
23055 * specific code to an experiment will break for users running
23056 * ES5-only browsers as code entered is not transpiled down to ES5.
23057 */
23058
23059  const utils = window.optimizely.get('utils');
23060
23061  utils.waitForElement('#marketing-space')
23062  .then(function(elem) {
23063    // Prepend the extension html to the body
23064    elem.insertAdjacentHTML('afterbegin', extension.$html);
23065    
23066  });
23067
23068      },
23069      hideFn: function(event) {
23070        var $ = window.optimizely.get('jquery');
23071        var widget = event.data.config;
23072        widget.$id = "6735230963286016";
23073        widget.$instance = event.data.id;
23074        widget.$fieldDefaults = [{"name":"link","default_value":"#url-as-string"},{"name":"title","default_value":""},{"name":"image","default_value":"https://placehold.co/600x400/EEE/31343C"}];
23075        (function(widg) {
23076  var i = 0;
23077  var field;
23078  for (; i < widg.$fieldDefaults.length; ++i) {
23079    field = widg.$fieldDefaults[i];
23080    if (!widg.hasOwnProperty(field.name)) {
23081      widg[field.name] = field.default_value;
23082    }
23083  }
23084  })(widget);
23085        var extension = widget;
23086        widget._styleTag = document.getElementById('widget-css-6735230963286016');
23087if (widget._styleTag) widget._styleTag.parentNode.removeChild(widget._styleTag);
23088        /*
23089 * Editor now supports ES6 compliant code. Note that adding ES6
23090 * specific code to an experiment will break for users running
23091 * ES5-only browsers as code entered is not transpiled down to ES5.
23092 */
23093 
23094  const extensionElement = document.getElementById('optimizely-extension-' + extension.$instance);
23095  if (extensionElement) {
23096    extensionElement.parentElement.removeChild(extensionElement);
23097  }
23098
23099      },
23100    });
23101}
23102], "interestGroups": [], "tagGroups": []},p=__webpack_require__(376),g="initializeOptimizelyPreview";if(p.initGlobalStore(h),l.populateDirectiveData(),c.clientHasAlreadyInitialized())a.warn("Main / Disabling because Optimizely has already initialized on this page load. Are there multiple snippets on the page?");else if(c.shouldBailForDesktopApp())a.log("Main / Disabling because of desktop app.");else if(c.conflictInObservingChanges())a.log("Main / Disabling: Observe Changes Indefinitely is on, but browser does not support it.");else{if(c.shouldLoadInnie())f.registerFunction("getProjectId",(function(){return h.projectId})),f.registerFunction("getAccountId",(function(){return h.accountId})),d.addScriptAsync("https://app.optimizely.com/js/innie.js"),a.log("Main / Disabling in favor of the editor client.");else if(c.shouldLoadEmbeddedEditor())a.log("Main / Loading embedded editor"),v.loadEmbeddedEditor("https://app.optimizely.com/dist/visual-editor/main.js");else if(c.shouldLoadPreview()){(c.isSlave()?window.optimizely:window.optimizely=window.optimizely||[]).push({type:"load",data:h}),a.log("Main / Disabling in favor of the preview client."),__webpack_require__(843).setupPreviewGlobal(),__webpack_require__(843).pushToPreviewGlobal({type:"pushPreviewData",name:"liveCommitData",data:h}),c.isSlave()||(f.registerFunction("getProjectId",(function(){return h.projectId})),d.addScriptSync("https://cdn.optimizely.com/js/preview2/26942930964.js"))}else if(c.shouldBootstrapDataForPreview()){f.registerFunction(g,(function(t){e(),f.unregisterFunction(g)}));var m=c.isSlave()?PROJECT_ID_FOR_SLAVE_PREVIEW:f.getFunction("getProjectId")();_=c.getProjectToken(),w=m,E=c.getPreviewLayerIds(),s="/dist/preview_data.js?token=__TOKEN__&preview_layer_ids=__PREVIEW_LAYER_IDS__".replace("__TOKEN__",_).replace("__PROJECT_ID__",w).replace("__PREVIEW_LAYER_IDS__",E.join(",")).replace("__GET_ONLY_PREVIEW_LAYERS__",!0);var b=c.getForceVariationIds();b&&b.length>0&&(s+="&optimizely_x="+b.join(",")),d.addScriptSync(s),__webpack_require__(843).setupPreviewGlobal(),d.addScriptAsync("/dist/js/preview_ui.js")}else c.shouldBootstrapDataForEditor()?(f.registerFunction(g,(function(t){e(),f.unregisterFunction(g)})),d.addScriptAsync(window.optimizely_editor_data_endpoint)):c.shouldInitialize()&&(e(),!(y=window.optiIQ)||!y.factory&&Array.isArray(y)?(t(),c.observe(t)):a.log("Main / Not loading optiIQ: already present on the page."));var y,_,w,E;i.timeEnd("block");var I=i.now();i.setMark("optimizelyFinished",I,I)}}try{main()}catch(t){try{__webpack_require__(8813).handleError(t)}catch(t){console.log(t)}}})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.