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 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 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 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 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 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 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 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> <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> <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,"&").replace(f,"<").replace(c,">").replace(l,"'").replace(a,"""):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.