4898 lines
113 KiB
JavaScript
4898 lines
113 KiB
JavaScript
|
/*! DSFR v1.11.2 | SPDX-License-Identifier: MIT | License-Filename: LICENSE.md | restricted use (see terms and conditions) */
|
|||
|
|
|||
|
const config = {
|
|||
|
prefix: 'fr',
|
|||
|
namespace: 'dsfr',
|
|||
|
organisation: '@gouvfr',
|
|||
|
version: '1.11.2'
|
|||
|
};
|
|||
|
|
|||
|
const api = window[config.namespace];
|
|||
|
|
|||
|
const patch = {
|
|||
|
namespace: 'a4e35ba2a938ba9d007689dbf3f46acbb9807869'
|
|||
|
};
|
|||
|
|
|||
|
const Collection = {
|
|||
|
MANUAL: 'manual',
|
|||
|
LOAD: 'load',
|
|||
|
FULL: 'full',
|
|||
|
HASH: 'hash'
|
|||
|
};
|
|||
|
|
|||
|
const key = '_EA_';
|
|||
|
const DISABLED = `${key}disabled`;
|
|||
|
const TOGGLE = `${key}toggle`;
|
|||
|
|
|||
|
class Opt {
|
|||
|
constructor () {
|
|||
|
this._configure();
|
|||
|
}
|
|||
|
|
|||
|
_configure () {
|
|||
|
const scope = this;
|
|||
|
window[DISABLED] = () => scope.isDisabled;
|
|||
|
window[TOGGLE] = this.toggle.bind(this);
|
|||
|
}
|
|||
|
|
|||
|
get isDisabled () {
|
|||
|
return localStorage.getItem(key);
|
|||
|
}
|
|||
|
|
|||
|
toggle () {
|
|||
|
if (this.isDisabled) this.enable();
|
|||
|
else this.disable();
|
|||
|
}
|
|||
|
|
|||
|
enable () {
|
|||
|
if (localStorage.getItem(key)) {
|
|||
|
localStorage.removeItem(key);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
disable () {
|
|||
|
localStorage.setItem(key, '1');
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const opt = new Opt();
|
|||
|
|
|||
|
const PUSH = 'EA_push';
|
|||
|
|
|||
|
class Init {
|
|||
|
constructor (domain) {
|
|||
|
this._domain = domain;
|
|||
|
this._isLoaded = false;
|
|||
|
this._promise = new Promise((resolve, reject) => {
|
|||
|
this._resolve = resolve;
|
|||
|
this._reject = reject;
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
get id () {
|
|||
|
return this._id;
|
|||
|
}
|
|||
|
|
|||
|
get store () {
|
|||
|
return this._store;
|
|||
|
}
|
|||
|
|
|||
|
configure () {
|
|||
|
this.init();
|
|||
|
return this._promise;
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
let bit = 5381;
|
|||
|
for (let i = this._domain.length - 1; i > 0; i--) bit = (bit * 33) ^ this._domain.charCodeAt(i);
|
|||
|
bit >>>= 0;
|
|||
|
this._id = `_EA_${bit}`;
|
|||
|
|
|||
|
this._store = [];
|
|||
|
this._store.eah = this._domain;
|
|||
|
window[this._id] = this._store;
|
|||
|
|
|||
|
if (!window[PUSH]) window[PUSH] = (...args) => this.store.push(args);
|
|||
|
|
|||
|
if (opt.isDisabled) {
|
|||
|
api.inspector.warn('User opted out, eulerian is disabled');
|
|||
|
this._reject('User opted out, eulerian is disabled');
|
|||
|
} else this.load();
|
|||
|
}
|
|||
|
|
|||
|
load () {
|
|||
|
const stamp = new Date() / 1E7 | 0;
|
|||
|
const offset = stamp % 26;
|
|||
|
const key = String.fromCharCode(97 + offset, 122 - offset, 65 + offset) + (stamp % 1E3);
|
|||
|
this._script = document.createElement('script');
|
|||
|
this._script.ea = this.id;
|
|||
|
this._script.async = true;
|
|||
|
this._script.addEventListener('load', this.loaded.bind(this));
|
|||
|
this._script.addEventListener('error', this.error.bind(this));
|
|||
|
this._script.src = `//${this._domain}/${key}.js?2`;
|
|||
|
const node = document.getElementsByTagName('script')[0];
|
|||
|
node.parentNode.insertBefore(this._script, node);
|
|||
|
}
|
|||
|
|
|||
|
error () {
|
|||
|
api.inspector.error('unable to load Eulerian script file. the domain declared in your configuration must match the domain provided by the Eulerian interface (tag creation)');
|
|||
|
this._reject('eulerian script loading error');
|
|||
|
}
|
|||
|
|
|||
|
loaded () {
|
|||
|
if (this._isLoaded) return;
|
|||
|
this._isLoaded = true;
|
|||
|
this._resolve();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
(function(e, a) {
|
|||
|
var i = e.length,
|
|||
|
y = 5381,
|
|||
|
k = 'script',
|
|||
|
s = window,
|
|||
|
v = document,
|
|||
|
o = v.createElement(k);
|
|||
|
for (; i;) {
|
|||
|
i -= 1;
|
|||
|
y = (y * 33) ^ e.charCodeAt(i)
|
|||
|
}
|
|||
|
y = '_EA_' + (y >>>= 0);
|
|||
|
(function(e, a, s, y) {
|
|||
|
s[a] = s[a] || function() {
|
|||
|
(s[y] = s[y] || []).push(arguments);
|
|||
|
s[y].eah = e;
|
|||
|
};
|
|||
|
}(e, a, s, y));
|
|||
|
i = new Date / 1E7 | 0;
|
|||
|
o.ea = y;
|
|||
|
y = i % 26;
|
|||
|
o.async = 1;
|
|||
|
o.src = '//' + e + '/' + String.fromCharCode(97 + y, 122 - y, 65 + y) + (i % 1E3) + '.js?2';
|
|||
|
s = v.getElementsByTagName(k)[0];
|
|||
|
s.parentNode.insertBefore(o, s);
|
|||
|
})
|
|||
|
('mon.domainedetracking.com', 'EA_push');
|
|||
|
*/
|
|||
|
|
|||
|
/*
|
|||
|
(function(e, a) {
|
|||
|
var i = e.length,
|
|||
|
y = 5381,
|
|||
|
k = 'script',
|
|||
|
z = '_EA_',
|
|||
|
zd = z + 'disabled',
|
|||
|
s = window,
|
|||
|
v = document,
|
|||
|
o = v.createElement(k),
|
|||
|
l = s.localStorage;
|
|||
|
for (; i;) {
|
|||
|
i -= 1;
|
|||
|
y = (y * 33) ^ e.charCodeAt(i)
|
|||
|
}
|
|||
|
y = z + (y >>>= 0);
|
|||
|
(function(e, a, s, y, z, zd, l) {
|
|||
|
s[a] = s[a] || function() {
|
|||
|
(s[y] = s[y] || []).push(arguments);
|
|||
|
s[y].eah = e;
|
|||
|
};
|
|||
|
s[zd] = function() {
|
|||
|
return l.getItem(z);
|
|||
|
};
|
|||
|
s[z + 'toggle'] = function() {
|
|||
|
(s[zd]()) ? l.removeItem(z): l.setItem(z, 1);
|
|||
|
}
|
|||
|
}(e, a, s, y, z, zd, l));
|
|||
|
if (!s[zd]()) {
|
|||
|
i = new Date / 1E7 | 0;
|
|||
|
o.ea = y;
|
|||
|
y = i % 26;
|
|||
|
o.async = 1;
|
|||
|
o.src = '//' + e + '/' + String.fromCharCode(97 + y, 122 - y, 65 + y) + (i % 1E3) + '.js?2';
|
|||
|
s = v.getElementsByTagName(k)[0];
|
|||
|
s.parentNode.insertBefore(o, s);
|
|||
|
}
|
|||
|
})('mon.domainedetracking.com', 'EA_push');
|
|||
|
*/
|
|||
|
|
|||
|
const State = {
|
|||
|
UNKNOWN: -1,
|
|||
|
CONFIGURING: 0,
|
|||
|
CONFIGURED: 1,
|
|||
|
INITIATED: 2,
|
|||
|
READY: 3
|
|||
|
};
|
|||
|
|
|||
|
class TarteAuCitronIntegration {
|
|||
|
constructor (config) {
|
|||
|
this._config = config;
|
|||
|
this._state = State.UNKNOWN;
|
|||
|
this._promise = new Promise((resolve, reject) => {
|
|||
|
this._resolve = resolve;
|
|||
|
this._reject = reject;
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
configure () {
|
|||
|
if (this._state >= State.CONFIGURED) return this._promise;
|
|||
|
if (this._state === State.UNKNOWN) {
|
|||
|
api.inspector.info('analytics configures tarteaucitron');
|
|||
|
this._state = State.CONFIGURING;
|
|||
|
}
|
|||
|
|
|||
|
const tarteaucitron = window.tarteaucitron;
|
|||
|
if (!tarteaucitron || !tarteaucitron.services) {
|
|||
|
window.requestAnimationFrame(this.configure.bind(this));
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
this._state = State.CONFIGURED;
|
|||
|
const init = this.init.bind(this);
|
|||
|
|
|||
|
const data = {
|
|||
|
key: 'eulerian',
|
|||
|
type: 'analytic',
|
|||
|
name: 'Eulerian Analytics',
|
|||
|
needConsent: true,
|
|||
|
cookies: ['etuix'],
|
|||
|
uri: 'https://eulerian.com/vie-privee',
|
|||
|
js: init,
|
|||
|
fallback: () => { tarteaucitron.services.eulerian.js(); }
|
|||
|
};
|
|||
|
|
|||
|
tarteaucitron.services.eulerian = data;
|
|||
|
if (!tarteaucitron.job) tarteaucitron.job = [];
|
|||
|
tarteaucitron.job.push('eulerian');
|
|||
|
|
|||
|
return this._promise;
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
if (this._state >= State.INITIATED) return;
|
|||
|
this._state = State.INITIATED;
|
|||
|
window.__eaGenericCmpApi = this.integrate.bind(this);
|
|||
|
const update = this.update.bind(this);
|
|||
|
window.addEventListener('tac.close_alert', update);
|
|||
|
window.addEventListener('tac.close_panel', update);
|
|||
|
}
|
|||
|
|
|||
|
integrate (cmpApi) {
|
|||
|
if (this._state >= State.READY) return;
|
|||
|
this._state = State.READY;
|
|||
|
this._cmpApi = cmpApi;
|
|||
|
|
|||
|
api.inspector.info('analytics has integrated tarteaucitron');
|
|||
|
|
|||
|
this._resolve();
|
|||
|
this.update();
|
|||
|
}
|
|||
|
|
|||
|
update () {
|
|||
|
if (this._state < State.READY) return;
|
|||
|
this._cmpApi('tac', window.tarteaucitron, 1);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class ConsentManagerPlatform {
|
|||
|
constructor (config) {
|
|||
|
this._config = config;
|
|||
|
|
|||
|
if (config) {
|
|||
|
switch (config.id) {
|
|||
|
case 'tarteaucitron':
|
|||
|
this.integrateTarteAuCitron();
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
integrateTarteAuCitron () {
|
|||
|
this._tac = new TarteAuCitronIntegration(this._config);
|
|||
|
return this._tac.configure();
|
|||
|
}
|
|||
|
|
|||
|
optin () {
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const push = (type, layer) => {
|
|||
|
if (typeof window.EA_push !== 'function') {
|
|||
|
api.inspector.warn('Analytics datalayer not sent, Eulerian API isn\'t yet avalaible');
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
api.inspector.info('analytics', type, layer);
|
|||
|
|
|||
|
window.EA_push(type, layer);
|
|||
|
};
|
|||
|
|
|||
|
const PushType = {
|
|||
|
COLLECTOR: 'collector',
|
|||
|
ACTION: 'action',
|
|||
|
ACTION_PARAMETER: 'actionparam'
|
|||
|
};
|
|||
|
|
|||
|
class Renderer {
|
|||
|
constructor () {
|
|||
|
this._renderables = [];
|
|||
|
this._rendering = this.render.bind(this);
|
|||
|
requestAnimationFrame(this._rendering);
|
|||
|
}
|
|||
|
|
|||
|
add (renderable) {
|
|||
|
const index = this._renderables.indexOf(renderable);
|
|||
|
if (index === -1) this._renderables.push(renderable);
|
|||
|
}
|
|||
|
|
|||
|
remove (renderable) {
|
|||
|
const index = this._renderables.indexOf(renderable);
|
|||
|
if (index > -1) this._renderables.splice(index, 1);
|
|||
|
}
|
|||
|
|
|||
|
render () {
|
|||
|
this._renderables.forEach(renderable => renderable.render());
|
|||
|
requestAnimationFrame(this._rendering);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const renderer = new Renderer();
|
|||
|
|
|||
|
const SLICE = 80;
|
|||
|
|
|||
|
class Queue {
|
|||
|
constructor () {
|
|||
|
this._startingActions = [];
|
|||
|
this._endingActions = [];
|
|||
|
this._handlingVisibilityChange = this._handleVisibilityChange.bind(this);
|
|||
|
this._handlingEnd = this._handleEnd.bind(this);
|
|||
|
this._isStarted = false;
|
|||
|
this._isListening = false;
|
|||
|
this.reset();
|
|||
|
}
|
|||
|
|
|||
|
setCollector (collector) {
|
|||
|
this._collector = collector;
|
|||
|
}
|
|||
|
|
|||
|
reset (ending = false) {
|
|||
|
this._type = PushType.ACTION;
|
|||
|
if (!ending) this._startingActions.length = 0;
|
|||
|
this._endingActions.length = 0;
|
|||
|
this._count = 0;
|
|||
|
this._delay = -1;
|
|||
|
this._isRequested = false;
|
|||
|
this._unlisten();
|
|||
|
}
|
|||
|
|
|||
|
start () {
|
|||
|
if (this._isStarted) return;
|
|||
|
this._isStarted = true;
|
|||
|
renderer.add(this);
|
|||
|
}
|
|||
|
|
|||
|
collect () {
|
|||
|
this._type = PushType.COLLECTOR;
|
|||
|
this._request();
|
|||
|
}
|
|||
|
|
|||
|
appendStartingAction (action, data) {
|
|||
|
if (!this._collector.isActionEnabled && !action.isForced) return;
|
|||
|
if (!action || this._startingActions.some(queued => queued.test(action))) {
|
|||
|
api.inspector.log('appendStartingAction exists or null', action);
|
|||
|
return;
|
|||
|
}
|
|||
|
const queued = new QueuedAction(action, data);
|
|||
|
this._startingActions.push(queued);
|
|||
|
this._request();
|
|||
|
}
|
|||
|
|
|||
|
appendEndingAction (action, data) {
|
|||
|
if (!this._collector.isActionEnabled && !action.isForced) return;
|
|||
|
if (!action || this._endingActions.some(queued => queued.test(action))) {
|
|||
|
api.inspector.log('appendEndingAction exists or null', action);
|
|||
|
return;
|
|||
|
}
|
|||
|
const queued = new QueuedAction(action, data);
|
|||
|
this._endingActions.push(queued);
|
|||
|
this._request();
|
|||
|
}
|
|||
|
|
|||
|
_request () {
|
|||
|
this._listen();
|
|||
|
this._isRequested = true;
|
|||
|
this._delay = 4;
|
|||
|
}
|
|||
|
|
|||
|
_listen () {
|
|||
|
if (this._isListening) return;
|
|||
|
this._isListening = true;
|
|||
|
document.addEventListener('visibilitychange', this._handlingVisibilityChange);
|
|||
|
document.addEventListener('unload', this._handlingEnd);
|
|||
|
document.addEventListener('beforeunload', this._handlingEnd);
|
|||
|
document.addEventListener('pagehide', this._handlingEnd);
|
|||
|
}
|
|||
|
|
|||
|
_unlisten () {
|
|||
|
if (!this._isListening) return;
|
|||
|
this._isListening = false;
|
|||
|
document.removeEventListener('visibilitychange', this._handlingVisibilityChange);
|
|||
|
document.removeEventListener('unload', this._handlingEnd);
|
|||
|
document.removeEventListener('beforeunload', this._handlingEnd);
|
|||
|
document.removeEventListener('pagehide', this._handlingEnd);
|
|||
|
}
|
|||
|
|
|||
|
_handleVisibilityChange (e) {
|
|||
|
if (document.visibilityState === 'hidden') this.send();
|
|||
|
}
|
|||
|
|
|||
|
_handleEnd () {
|
|||
|
this.send();
|
|||
|
}
|
|||
|
|
|||
|
render () {
|
|||
|
if (this._delay <= -1) return;
|
|||
|
this._delay--;
|
|||
|
this._count++;
|
|||
|
switch (true) {
|
|||
|
case this._count > 20:
|
|||
|
case this._delay === 0:
|
|||
|
this.send();
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
send (ending = false) {
|
|||
|
if (!this._isRequested) return;
|
|||
|
const actionLayers = [];
|
|||
|
if (!ending) actionLayers.push(...this._startingActions.map(queued => queued.start()).filter(layer => layer.length > 0));
|
|||
|
actionLayers.push(...this._endingActions.map(queued => queued.end()).filter(layer => layer.length > 0));
|
|||
|
|
|||
|
const length = ((actionLayers.length / SLICE) + 1) | 0;
|
|||
|
const slices = [];
|
|||
|
for (let i = 0; i < length; i++) {
|
|||
|
const slice = actionLayers.slice(i * SLICE, (i + 1) * SLICE);
|
|||
|
slices.push(slice.flat());
|
|||
|
}
|
|||
|
|
|||
|
if (this._type === PushType.COLLECTOR && this._collector.isCollecting) {
|
|||
|
const layer = this._collector.layer;
|
|||
|
if (slices.length > 0) {
|
|||
|
const slice = slices.splice(0, 1)[0];
|
|||
|
if (slice.length > 0) layer.push.apply(layer, slice);
|
|||
|
}
|
|||
|
layer.flat();
|
|||
|
if (layer.length > 0) push(PushType.COLLECTOR, layer);
|
|||
|
}
|
|||
|
|
|||
|
if (slices.length > 0) {
|
|||
|
for (let i = 0; i < slices.length; i++) {
|
|||
|
const slice = slices[i];
|
|||
|
if (slice.length > 0) push(PushType.ACTION, slice);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
this.reset(ending);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class QueuedAction {
|
|||
|
constructor (action, data) {
|
|||
|
this._action = action;
|
|||
|
this._data = data;
|
|||
|
}
|
|||
|
|
|||
|
test (action) {
|
|||
|
return this._action === action;
|
|||
|
}
|
|||
|
|
|||
|
start () {
|
|||
|
return this._action.start(this._data);
|
|||
|
}
|
|||
|
|
|||
|
end () {
|
|||
|
return this._action.end(this._data);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const queue = new Queue();
|
|||
|
|
|||
|
class Debug {
|
|||
|
get debugger () {
|
|||
|
return window._oEa;
|
|||
|
}
|
|||
|
|
|||
|
get isActive () {
|
|||
|
if (!this.debugger) return false;
|
|||
|
return this.debugger._dbg === '1';
|
|||
|
}
|
|||
|
|
|||
|
set isActive (value) {
|
|||
|
if (!this.debugger || this.isActive === value) return;
|
|||
|
this.debugger.debug(value ? 1 : 0);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const debug = new Debug();
|
|||
|
|
|||
|
const Status = {
|
|||
|
CONNECTED: {
|
|||
|
id: 'connected',
|
|||
|
value: 'connecté',
|
|||
|
isConnected: true,
|
|||
|
isDefault: true
|
|||
|
},
|
|||
|
ANONYMOUS: {
|
|||
|
id: 'anonymous',
|
|||
|
value: 'anonyme',
|
|||
|
isConnected: false,
|
|||
|
isDefault: true
|
|||
|
},
|
|||
|
GUEST: {
|
|||
|
id: 'guest',
|
|||
|
value: 'invité',
|
|||
|
isConnected: false
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
const Type$2 = {
|
|||
|
INDIVIDUAL: {
|
|||
|
id: 'individual',
|
|||
|
value: 'part'
|
|||
|
},
|
|||
|
PROFESSIONNAL: {
|
|||
|
id: 'professionnal',
|
|||
|
value: 'pro'
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
/* '["\'<>*$&~`|\\\\?^~]'; */
|
|||
|
var RESTRICTED = {
|
|||
|
'0x0022': '"',
|
|||
|
'0x0024': '$',
|
|||
|
'0x0026': '&',
|
|||
|
'0x0027': ''',
|
|||
|
'0x002a': '*',
|
|||
|
'0x002c': ',',
|
|||
|
'0x003c': '<',
|
|||
|
'0x003e': '>',
|
|||
|
'0x003f': '?',
|
|||
|
'0x005c': '\',
|
|||
|
'0x005e': '^',
|
|||
|
'0x0060': '`',
|
|||
|
'0x007c': '|',
|
|||
|
'0x007e': '~'
|
|||
|
};
|
|||
|
|
|||
|
// import TABLE from './unicode-table';
|
|||
|
|
|||
|
const charCodeHex = (char) => {
|
|||
|
const code = char.charCodeAt(0).toString(16);
|
|||
|
return '0x0000'.slice(0, -code.length) + code;
|
|||
|
};
|
|||
|
|
|||
|
const normalize = (text) => {
|
|||
|
if (!text) return text;
|
|||
|
// text = [...text].map(char => TABLE[charCodeHex(char)] || char).join('');
|
|||
|
text = [...text].map(char => RESTRICTED[charCodeHex(char)] || char).join('');
|
|||
|
text = text.replace(/\s+/g, ' ').replace(/\s/g, '_');
|
|||
|
text = text.toLowerCase();
|
|||
|
return text;
|
|||
|
};
|
|||
|
|
|||
|
const validateString = (value, name, allowNull = true) => {
|
|||
|
switch (true) {
|
|||
|
case typeof value === 'number':
|
|||
|
return `${value}`;
|
|||
|
|
|||
|
case typeof value === 'string':
|
|||
|
return value;
|
|||
|
|
|||
|
case value === undefined && allowNull:
|
|||
|
case value === null && allowNull:
|
|||
|
return '';
|
|||
|
}
|
|||
|
|
|||
|
api.inspector.warn(`unexpected value '${value}' set at analytics.${name}. Expecting a String`);
|
|||
|
return null;
|
|||
|
};
|
|||
|
|
|||
|
const validateNumber = (value, name, allowNull = true) => {
|
|||
|
switch (true) {
|
|||
|
case !isNaN(value):
|
|||
|
return value;
|
|||
|
|
|||
|
case typeof value === 'string' && !isNaN(Number(value)):
|
|||
|
return Number(value);
|
|||
|
|
|||
|
case value === undefined && allowNull:
|
|||
|
case value === null && allowNull:
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
api.inspector.warn(`unexpected value '${value}' set at analytics.${name}. Expecting a Number`);
|
|||
|
return null;
|
|||
|
};
|
|||
|
|
|||
|
const validateBoolean = (value, name) => {
|
|||
|
switch (true) {
|
|||
|
case typeof value === 'boolean':
|
|||
|
return value;
|
|||
|
|
|||
|
case typeof value === 'string' && value.toLowerCase() === 'true':
|
|||
|
case value === '1':
|
|||
|
case value === 1:
|
|||
|
return true;
|
|||
|
|
|||
|
case typeof value === 'string' && value.toLowerCase() === 'false':
|
|||
|
case value === '0':
|
|||
|
case value === 0:
|
|||
|
return false;
|
|||
|
|
|||
|
case value === undefined:
|
|||
|
case value === null:
|
|||
|
return value;
|
|||
|
}
|
|||
|
|
|||
|
api.inspector.warn(`unexpected value '${value}' set at analytics.${name}. Expecting a Boolean`);
|
|||
|
return null;
|
|||
|
};
|
|||
|
|
|||
|
const validateLang = (value, name, allowNull = true) => {
|
|||
|
switch (true) {
|
|||
|
case typeof value === 'string' && /^[A-Za-z]{2}$|^[A-Za-z]{2}[-_]/.test(value):
|
|||
|
return value.split(/[-_]/)[0].toLowerCase();
|
|||
|
|
|||
|
case value === undefined && allowNull:
|
|||
|
case value === null && allowNull:
|
|||
|
return '';
|
|||
|
}
|
|||
|
|
|||
|
api.inspector.warn(`unexpected value '${value}' set at analytics.${name}. Expecting language as a String following ISO 639-1 format`);
|
|||
|
return null;
|
|||
|
};
|
|||
|
|
|||
|
const validateGeography = (value, name, allowNull = true) => {
|
|||
|
switch (true) {
|
|||
|
case typeof value === 'string':
|
|||
|
if (!/^FR-[A-Z0-9]{2,3}$/.test(value)) api.inspector.warn(`value '${value}' set at analytics.${name} with wrong format. Geographic location should be a String following ISO 3166-2:FR format`);
|
|||
|
return value;
|
|||
|
|
|||
|
case value === undefined && allowNull:
|
|||
|
case value === null && allowNull:
|
|||
|
return '';
|
|||
|
}
|
|||
|
|
|||
|
api.inspector.warn(`unexpected value '${value}' set at analytics.${name}. Expecting geographic location as a String following ISO 3166-2:FR format`);
|
|||
|
return null;
|
|||
|
};
|
|||
|
|
|||
|
const normaliseISODate = (date) => date.toISOString().split('T')[0];
|
|||
|
|
|||
|
const validateDate = (value, name, allowNull = true) => {
|
|||
|
switch (true) {
|
|||
|
case value instanceof Date:
|
|||
|
return normaliseISODate(value);
|
|||
|
|
|||
|
case typeof value === 'string': {
|
|||
|
const date = new Date(value);
|
|||
|
if (date.toString() !== 'Invalid Date') return normaliseISODate(date);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case value === undefined && allowNull:
|
|||
|
case value === null && allowNull:
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
api.inspector.warn(`unexpected value '${value}' set at analytics.${name}. Expecting a Date`);
|
|||
|
return null;
|
|||
|
};
|
|||
|
|
|||
|
class User {
|
|||
|
constructor (config) {
|
|||
|
this._config = config || {};
|
|||
|
}
|
|||
|
|
|||
|
reset (clear = false) {
|
|||
|
this._isConnected = false;
|
|||
|
this.status = Status.ANONYMOUS;
|
|||
|
if (!clear && this._config.connect) this.connect(this._config.connect.uid, this._config.connect.email, this._config.connect.isNew);
|
|||
|
else {
|
|||
|
this._uid = undefined;
|
|||
|
this._email = undefined;
|
|||
|
this._isNew = false;
|
|||
|
}
|
|||
|
this.profile = clear ? undefined : this._config.profile;
|
|||
|
this.language = clear ? undefined : this._config.language;
|
|||
|
this.type = clear ? undefined : this._config.type;
|
|||
|
}
|
|||
|
|
|||
|
connect (uid, email, isNew = false) {
|
|||
|
this._uid = validateString(uid, 'user.uid');
|
|||
|
if (/^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]{2,}@[a-zA-Z0-9-]{2,}\.[a-zA-Z]{2,}$/.test(email)) api.inspector.warn('Please check analytics.user.email is properly encrypted ');
|
|||
|
this._email = validateString(email, 'user.email');
|
|||
|
this._isNew = validateBoolean(isNew);
|
|||
|
this._isConnected = true;
|
|||
|
this.status = Status.CONNECTED;
|
|||
|
}
|
|||
|
|
|||
|
get uid () {
|
|||
|
return this._uid;
|
|||
|
}
|
|||
|
|
|||
|
get email () {
|
|||
|
return this._email;
|
|||
|
}
|
|||
|
|
|||
|
get isNew () {
|
|||
|
return this._isNew;
|
|||
|
}
|
|||
|
|
|||
|
set status (id) {
|
|||
|
const stati = Object.values(Status).filter(status => status.isConnected === this._isConnected);
|
|||
|
this._status = stati.filter(status => status.id === id || status.value === id)[0] || stati.filter(status => status.isDefault)[0];
|
|||
|
}
|
|||
|
|
|||
|
get status () {
|
|||
|
return this._status.id;
|
|||
|
}
|
|||
|
|
|||
|
set profile (value) {
|
|||
|
const valid = validateString(value, 'user.profile');
|
|||
|
if (valid !== null) this._profile = valid;
|
|||
|
}
|
|||
|
|
|||
|
get profile () {
|
|||
|
return this._profile.id;
|
|||
|
}
|
|||
|
|
|||
|
set language (value) {
|
|||
|
const valid = validateLang(value, 'user.language');
|
|||
|
if (valid !== null) this._language = valid;
|
|||
|
}
|
|||
|
|
|||
|
get language () {
|
|||
|
return this._language || navigator.language;
|
|||
|
}
|
|||
|
|
|||
|
set type (id) {
|
|||
|
this._type = Object.values(Type$2).filter(type => type.id === id || type.value === id)[0];
|
|||
|
}
|
|||
|
|
|||
|
get type () {
|
|||
|
return this._type.id;
|
|||
|
}
|
|||
|
|
|||
|
get layer () {
|
|||
|
const layer = [];
|
|||
|
if (this.uid) layer.push('uid', normalize(this.uid));
|
|||
|
if (this.email) layer.push('email', normalize(this.email));
|
|||
|
if (this.isNew) layer.push('newcustomer', '1');
|
|||
|
if (this.language) layer.push('user_language', this.language);
|
|||
|
layer.push('user_login_status', this._status.value);
|
|||
|
if (this._profile) layer.push('profile', this._profile);
|
|||
|
if (this._type) layer.push('user_type', this._type.value);
|
|||
|
return layer;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
User.Status = Status;
|
|||
|
User.Type = Type$2;
|
|||
|
|
|||
|
const Environment = {
|
|||
|
DEVELOPMENT: {
|
|||
|
id: 'development',
|
|||
|
value: 'dev'
|
|||
|
},
|
|||
|
STAGE: {
|
|||
|
id: 'stage',
|
|||
|
value: 'stage'
|
|||
|
},
|
|||
|
PRODUCTION: {
|
|||
|
id: 'production',
|
|||
|
value: 'prod'
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
class Site {
|
|||
|
constructor (config) {
|
|||
|
this._config = config || {};
|
|||
|
}
|
|||
|
|
|||
|
reset (clear = false) {
|
|||
|
this.environment = clear ? Environment.DEVELOPMENT.id : this._config.environment;
|
|||
|
this.entity = clear ? undefined : this._config.entity;
|
|||
|
this.language = clear ? undefined : this._config.language;
|
|||
|
this.target = clear ? undefined : this._config.target;
|
|||
|
this.type = clear ? undefined : this._config.type;
|
|||
|
this.region = clear ? undefined : this._config.region;
|
|||
|
this.department = clear ? undefined : this._config.department;
|
|||
|
this.version = clear ? undefined : this._config.version;
|
|||
|
this._api = api.version;
|
|||
|
}
|
|||
|
|
|||
|
set environment (value) {
|
|||
|
switch (value) {
|
|||
|
case Environment.PRODUCTION.id:
|
|||
|
case Environment.PRODUCTION.value:
|
|||
|
this._environment = Environment.PRODUCTION;
|
|||
|
break;
|
|||
|
|
|||
|
case Environment.STAGE.id:
|
|||
|
case Environment.STAGE.value:
|
|||
|
this._environment = Environment.STAGE;
|
|||
|
break;
|
|||
|
|
|||
|
case Environment.DEVELOPMENT.id:
|
|||
|
case Environment.DEVELOPMENT.value:
|
|||
|
this._environment = Environment.DEVELOPMENT;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
this._environment = Environment.DEVELOPMENT;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
get environment () {
|
|||
|
return this._environment ? this._environment.id : Environment.DEVELOPMENT.id;
|
|||
|
}
|
|||
|
|
|||
|
set entity (value) {
|
|||
|
const valid = validateString(value, 'site.entity');
|
|||
|
if (valid !== null) this._entity = valid;
|
|||
|
}
|
|||
|
|
|||
|
get entity () {
|
|||
|
return this._entity;
|
|||
|
}
|
|||
|
|
|||
|
set language (value) {
|
|||
|
const valid = validateLang(value, 'site.language');
|
|||
|
if (valid !== null) this._language = valid;
|
|||
|
}
|
|||
|
|
|||
|
get language () {
|
|||
|
return this._language || document.documentElement.lang;
|
|||
|
}
|
|||
|
|
|||
|
set target (value) {
|
|||
|
const valid = validateString(value, 'site.target');
|
|||
|
if (valid !== null) this._target = valid;
|
|||
|
}
|
|||
|
|
|||
|
get target () {
|
|||
|
return this._target;
|
|||
|
}
|
|||
|
|
|||
|
set type (value) {
|
|||
|
const valid = validateString(value, 'site.type');
|
|||
|
if (valid !== null) this._type = valid;
|
|||
|
}
|
|||
|
|
|||
|
get type () {
|
|||
|
return this._type;
|
|||
|
}
|
|||
|
|
|||
|
set region (value) {
|
|||
|
const valid = validateGeography(value, 'site.region');
|
|||
|
if (valid !== null) this._region = valid;
|
|||
|
}
|
|||
|
|
|||
|
get region () {
|
|||
|
return this._region;
|
|||
|
}
|
|||
|
|
|||
|
set department (value) {
|
|||
|
const valid = validateGeography(value, 'site.department');
|
|||
|
if (valid !== null) this._department = valid;
|
|||
|
}
|
|||
|
|
|||
|
get department () {
|
|||
|
return this._department;
|
|||
|
}
|
|||
|
|
|||
|
set version (value) {
|
|||
|
const valid = validateString(value, 'site.version');
|
|||
|
if (valid !== null) this._version = valid;
|
|||
|
}
|
|||
|
|
|||
|
get version () {
|
|||
|
return this._version;
|
|||
|
}
|
|||
|
|
|||
|
get api () {
|
|||
|
return this._api;
|
|||
|
}
|
|||
|
|
|||
|
get layer () {
|
|||
|
const layer = [];
|
|||
|
layer.push('site_environment', this._environment.value);
|
|||
|
if (this.entity) layer.push('site_entity', normalize(this.entity));
|
|||
|
else api.inspector.warn('entity is required in analytics.site');
|
|||
|
if (this.language) layer.push('site_language', this.language);
|
|||
|
if (this.target) layer.push('site_target', normalize(this.target));
|
|||
|
if (this.type) layer.push('site_type', normalize(this.type));
|
|||
|
if (this.region) layer.push('site_region', this.region);
|
|||
|
if (this.department) layer.push('site_department', this.department);
|
|||
|
if (this.version) layer.push('site_version', this.version);
|
|||
|
if (this.api) layer.push('api_version', this.api);
|
|||
|
return layer;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Site.Environment = Environment;
|
|||
|
|
|||
|
const Inventory = {
|
|||
|
accordion: api.internals.ns.selector('accordion'),
|
|||
|
alert: api.internals.ns.selector('alert'),
|
|||
|
badge: api.internals.ns.selector('badge'),
|
|||
|
breadcrumb: api.internals.ns.selector('breadcrumb'),
|
|||
|
button: api.internals.ns.selector('btn'),
|
|||
|
callout: api.internals.ns.selector('callout'),
|
|||
|
card: api.internals.ns.selector('card'),
|
|||
|
checkbox: api.internals.ns.selector('checkbox-group'),
|
|||
|
connect: api.internals.ns.selector('connect'),
|
|||
|
consent: api.internals.ns.selector('consent-banner'),
|
|||
|
content: api.internals.ns.selector('content-media'),
|
|||
|
download: api.internals.ns.selector('download'),
|
|||
|
follow: api.internals.ns.selector('follow'),
|
|||
|
footer: api.internals.ns.selector('footer'),
|
|||
|
header: api.internals.ns.selector('header'),
|
|||
|
highlight: api.internals.ns.selector('highlight'),
|
|||
|
input: api.internals.ns.selector('input-group'),
|
|||
|
link: api.internals.ns.selector('link'),
|
|||
|
modal: api.internals.ns.selector('modal'),
|
|||
|
navigation: api.internals.ns.selector('nav'),
|
|||
|
notice: api.internals.ns.selector('notice'),
|
|||
|
pagination: api.internals.ns.selector('pagination'),
|
|||
|
quote: api.internals.ns.selector('quote'),
|
|||
|
radio: api.internals.ns.selector('radio-group'),
|
|||
|
search: api.internals.ns.selector('search-bar'),
|
|||
|
select: api.internals.ns.selector('select'),
|
|||
|
share: api.internals.ns.selector('share'),
|
|||
|
sidemenu: api.internals.ns.selector('sidemenu'),
|
|||
|
stepper: api.internals.ns.selector('stepper'),
|
|||
|
summary: api.internals.ns.selector('summary'),
|
|||
|
tab: api.internals.ns.selector('tabs'),
|
|||
|
table: api.internals.ns.selector('table'),
|
|||
|
tag: api.internals.ns.selector('tag'),
|
|||
|
tile: api.internals.ns.selector('tile'),
|
|||
|
toggle: api.internals.ns.selector('toggle'),
|
|||
|
tooltip: api.internals.ns.selector('tooltip'),
|
|||
|
transcription: api.internals.ns.selector('transcription'),
|
|||
|
translate: api.internals.ns.selector('translate'),
|
|||
|
upload: api.internals.ns.selector('upload-group')
|
|||
|
};
|
|||
|
|
|||
|
const CollectionState = {
|
|||
|
COLLECTABLE: 'collectable',
|
|||
|
COLLECTING: 'collecting',
|
|||
|
COLLECTED: 'collected'
|
|||
|
};
|
|||
|
|
|||
|
class Page {
|
|||
|
constructor (config) {
|
|||
|
this._config = config || {};
|
|||
|
this._state = CollectionState.COLLECTABLE;
|
|||
|
}
|
|||
|
|
|||
|
reset (clear = false) {
|
|||
|
this.path = clear ? '' : this._config.path;
|
|||
|
this.referrer = clear ? '' : this._config.referrer;
|
|||
|
this.title = clear ? '' : this._config.title;
|
|||
|
this.name = clear ? '' : this._config.name;
|
|||
|
this.id = clear ? '' : this._config.id;
|
|||
|
this.author = clear ? '' : this._config.author;
|
|||
|
this.date = clear ? '' : this._config.date;
|
|||
|
this._labels = clear || !this._config.labels ? ['', '', '', '', ''] : this._config.labels;
|
|||
|
this._labels.length = 5;
|
|||
|
this._tags = clear || !this._config.tags ? [] : this._config.tags;
|
|||
|
this._categories = clear || !this._config.categories ? ['', '', ''] : this._config.categories;
|
|||
|
this.isError = !clear && this._config.isError;
|
|||
|
this.template = clear ? '' : this._config.template;
|
|||
|
this.group = clear ? '' : this._config.group;
|
|||
|
this.segment = clear ? '' : this._config.segment;
|
|||
|
this.subtemplate = clear ? '' : this._config.subtemplate;
|
|||
|
this.theme = clear ? '' : this._config.theme;
|
|||
|
this.subtheme = clear ? '' : this._config.subtheme;
|
|||
|
this.related = clear ? '' : this._config.related;
|
|||
|
this.depth = clear || isNaN(this._config.depth) ? 0 : this._config.depth;
|
|||
|
this.current = clear || isNaN(this._config.current) ? -1 : this._config.current;
|
|||
|
this.total = clear || isNaN(this._config.total) ? -1 : this._config.total;
|
|||
|
this._filters = clear || !this._config.filters ? [] : this._config.filters;
|
|||
|
}
|
|||
|
|
|||
|
collecting () {
|
|||
|
if (this._state !== CollectionState.COLLECTABLE) {
|
|||
|
api.inspector.warn(`current path '${this.path}' was already collected`);
|
|||
|
return false;
|
|||
|
}
|
|||
|
this._state = CollectionState.COLLECTING;
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
get isCollecting () {
|
|||
|
return this._state === CollectionState.COLLECTING;
|
|||
|
}
|
|||
|
|
|||
|
set path (value) {
|
|||
|
const valid = validateString(value, 'page.path');
|
|||
|
if (valid !== null) {
|
|||
|
this._path = valid;
|
|||
|
this._state = CollectionState.COLLECTABLE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
get path () {
|
|||
|
return this._path || `${document.location.pathname}${document.location.search}`;
|
|||
|
}
|
|||
|
|
|||
|
set referrer (value) {
|
|||
|
const valid = validateString(value, 'page.referrer');
|
|||
|
if (valid !== null) this._referrer = valid;
|
|||
|
}
|
|||
|
|
|||
|
get referrer () {
|
|||
|
return this._referrer;
|
|||
|
}
|
|||
|
|
|||
|
set title (value) {
|
|||
|
const valid = validateString(value, 'page.title');
|
|||
|
if (valid !== null) this._title = valid;
|
|||
|
}
|
|||
|
|
|||
|
get title () {
|
|||
|
return this._title || document.title;
|
|||
|
}
|
|||
|
|
|||
|
set id (value) {
|
|||
|
const valid = validateString(value, 'page.id');
|
|||
|
if (valid !== null) this._id = valid;
|
|||
|
}
|
|||
|
|
|||
|
get id () {
|
|||
|
return this._id;
|
|||
|
}
|
|||
|
|
|||
|
set author (value) {
|
|||
|
const valid = validateString(value, 'page.author');
|
|||
|
if (valid !== null) this._author = valid;
|
|||
|
}
|
|||
|
|
|||
|
get author () {
|
|||
|
return this._author;
|
|||
|
}
|
|||
|
|
|||
|
set date (value) {
|
|||
|
const valid = validateDate(value, 'page.date');
|
|||
|
if (valid !== null) this._date = valid;
|
|||
|
}
|
|||
|
|
|||
|
get date () {
|
|||
|
return this._date;
|
|||
|
}
|
|||
|
|
|||
|
get tags () {
|
|||
|
return this._tags;
|
|||
|
}
|
|||
|
|
|||
|
set name (value) {
|
|||
|
const valid = validateString(value, 'page.name');
|
|||
|
if (valid !== null) this._name = valid;
|
|||
|
}
|
|||
|
|
|||
|
get name () {
|
|||
|
return this._name || this.title;
|
|||
|
}
|
|||
|
|
|||
|
get labels () {
|
|||
|
return this._labels;
|
|||
|
}
|
|||
|
|
|||
|
get categories () {
|
|||
|
return this._categories;
|
|||
|
}
|
|||
|
|
|||
|
set isError (value) {
|
|||
|
const valid = validateBoolean(value, 'page.isError');
|
|||
|
if (valid !== null) this._isError = valid;
|
|||
|
}
|
|||
|
|
|||
|
get isError () {
|
|||
|
return this._isError;
|
|||
|
}
|
|||
|
|
|||
|
set template (value) {
|
|||
|
const valid = validateString(value, 'page.template');
|
|||
|
if (valid !== null) this._template = valid;
|
|||
|
}
|
|||
|
|
|||
|
get template () {
|
|||
|
return this._template || 'autres';
|
|||
|
}
|
|||
|
|
|||
|
set segment (value) {
|
|||
|
const valid = validateString(value, 'page.segment');
|
|||
|
if (valid !== null) this._segment = valid;
|
|||
|
}
|
|||
|
|
|||
|
get segment () {
|
|||
|
return this._segment || this.template;
|
|||
|
}
|
|||
|
|
|||
|
set group (value) {
|
|||
|
const valid = validateString(value, 'page.group');
|
|||
|
if (valid !== null) this._group = valid;
|
|||
|
}
|
|||
|
|
|||
|
get group () {
|
|||
|
return this._group || this.template;
|
|||
|
}
|
|||
|
|
|||
|
set subtemplate (value) {
|
|||
|
const valid = validateString(value, 'page.subtemplate');
|
|||
|
if (valid !== null) this._subtemplate = valid;
|
|||
|
}
|
|||
|
|
|||
|
get subtemplate () {
|
|||
|
return this._subtemplate;
|
|||
|
}
|
|||
|
|
|||
|
set theme (value) {
|
|||
|
const valid = validateString(value, 'page.theme');
|
|||
|
if (valid !== null) this._theme = valid;
|
|||
|
}
|
|||
|
|
|||
|
get theme () {
|
|||
|
return this._theme;
|
|||
|
}
|
|||
|
|
|||
|
set subtheme (value) {
|
|||
|
const valid = validateString(value, 'page.subtheme');
|
|||
|
if (valid !== null) this._subtheme = valid;
|
|||
|
}
|
|||
|
|
|||
|
get subtheme () {
|
|||
|
return this._subtheme;
|
|||
|
}
|
|||
|
|
|||
|
set related (value) {
|
|||
|
const valid = validateString(value, 'page.related');
|
|||
|
if (valid !== null) this._related = valid;
|
|||
|
}
|
|||
|
|
|||
|
get related () {
|
|||
|
return this._related;
|
|||
|
}
|
|||
|
|
|||
|
set depth (value) {
|
|||
|
const valid = validateNumber(value, 'page.depth');
|
|||
|
if (valid !== null) this._depth = valid;
|
|||
|
}
|
|||
|
|
|||
|
get depth () {
|
|||
|
return this._depth;
|
|||
|
}
|
|||
|
|
|||
|
set current (value) {
|
|||
|
const valid = validateNumber(value, 'page.current');
|
|||
|
if (valid !== null) this._current = valid;
|
|||
|
}
|
|||
|
|
|||
|
get current () {
|
|||
|
return this._current;
|
|||
|
}
|
|||
|
|
|||
|
set total (value) {
|
|||
|
const valid = validateNumber(value, 'page.total');
|
|||
|
if (valid !== null) this._total = valid;
|
|||
|
}
|
|||
|
|
|||
|
get total () {
|
|||
|
return this._total;
|
|||
|
}
|
|||
|
|
|||
|
get filters () {
|
|||
|
return this._filters;
|
|||
|
}
|
|||
|
|
|||
|
get layer () {
|
|||
|
this._state = CollectionState.COLLECTED;
|
|||
|
const layer = [];
|
|||
|
if (this.path) layer.push('path', normalize(this.path));
|
|||
|
if (this.referrer) layer.push('referrer', normalize(this.referrer));
|
|||
|
if (this.title) layer.push('page_title', normalize(this.title));
|
|||
|
if (this.name) layer.push('page_name', normalize(this.name));
|
|||
|
if (this.id) layer.push('page_id', normalize(this.id));
|
|||
|
if (this.author) layer.push('page_author', normalize(this.author));
|
|||
|
if (this.date) layer.push('page_date', normalize(this.date));
|
|||
|
|
|||
|
const components = Object.keys(Inventory).map(id => document.querySelector(Inventory[id]) !== null ? id : null).filter(id => id !== null).join(',');
|
|||
|
if (components) layer.push('page_components', components);
|
|||
|
|
|||
|
const labels = this._labels.slice(0, 5);
|
|||
|
labels.length = 5;
|
|||
|
if (labels.some(label => label)) layer.push('pagelabel', labels.map(label => typeof label === 'string' ? normalize(label) : '').join(','));
|
|||
|
|
|||
|
const tags = this._tags;
|
|||
|
if (tags.some(tag => tag)) layer.push('pagetag', tags.map(tag => typeof tag === 'string' ? normalize(tag) : '').join(','));
|
|||
|
|
|||
|
this._categories.forEach((category, index) => {
|
|||
|
if (category) layer.push(`page_category${index + 1}`, category);
|
|||
|
});
|
|||
|
|
|||
|
if (this._isError) layer.push('error', '1');
|
|||
|
|
|||
|
layer.push('page_template', normalize(this.template));
|
|||
|
layer.push('pagegroup', normalize(this.group));
|
|||
|
layer.push('site-segment', normalize(this.segment));
|
|||
|
|
|||
|
if (this.subtemplate) layer.push('page_subtemplate', normalize(this.subtemplate));
|
|||
|
if (this.theme) layer.push('page_theme', normalize(this.theme));
|
|||
|
if (this.subtheme) layer.push('page_subtheme', normalize(this.subtheme));
|
|||
|
if (this.related) layer.push('page_related', normalize(this.related));
|
|||
|
if (!isNaN(this.depth)) layer.push('page_depth', this.depth);
|
|||
|
|
|||
|
if (!isNaN(this.current) && this.current > -1) {
|
|||
|
let pagination = `${this.current}`;
|
|||
|
if (!isNaN(this.total) && this.total > -1) pagination += `/${this.total}`;
|
|||
|
layer.push('page_pagination', pagination);
|
|||
|
}
|
|||
|
|
|||
|
if (this.filters.length && this.filters.some(label => label)) {
|
|||
|
const filters = this.filters.map(filter => typeof filter === 'string' ? normalize(filter) : '');
|
|||
|
layer.push('page_filters', filters.join(','));
|
|||
|
}
|
|||
|
return layer;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const Method = {
|
|||
|
STANDARD: {
|
|||
|
id: 'standard',
|
|||
|
value: 'standard',
|
|||
|
isDefault: true
|
|||
|
},
|
|||
|
AUTOCOMPLETE: {
|
|||
|
id: 'autocomplete',
|
|||
|
value: 'autocompletion'
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
class Search {
|
|||
|
constructor (config) {
|
|||
|
this._config = config || {};
|
|||
|
}
|
|||
|
|
|||
|
reset (clear = false) {
|
|||
|
this.engine = clear ? undefined : this._config.engine;
|
|||
|
this.results = clear || isNaN(this._config.results) ? -1 : this._config.results;
|
|||
|
this.terms = clear ? undefined : this._config.terms;
|
|||
|
this.category = clear ? undefined : this._config.category;
|
|||
|
this.theme = clear ? undefined : this._config.theme;
|
|||
|
this.type = clear ? undefined : this._config.type;
|
|||
|
this.method = clear ? undefined : this._config.method;
|
|||
|
}
|
|||
|
|
|||
|
set engine (value) {
|
|||
|
const valid = validateString(value, 'search.engine');
|
|||
|
if (valid !== null) this._engine = valid;
|
|||
|
}
|
|||
|
|
|||
|
get engine () {
|
|||
|
return this._engine;
|
|||
|
}
|
|||
|
|
|||
|
set results (value) {
|
|||
|
const valid = validateNumber(value, 'search.results');
|
|||
|
if (valid !== null) this._results = valid;
|
|||
|
}
|
|||
|
|
|||
|
get results () {
|
|||
|
return this._results;
|
|||
|
}
|
|||
|
|
|||
|
set terms (value) {
|
|||
|
const valid = validateString(value, 'search.terms');
|
|||
|
if (valid !== null) this._terms = valid;
|
|||
|
}
|
|||
|
|
|||
|
get terms () {
|
|||
|
return this._terms;
|
|||
|
}
|
|||
|
|
|||
|
set category (value) {
|
|||
|
const valid = validateString(value, 'search.category');
|
|||
|
if (valid !== null) this._category = valid;
|
|||
|
}
|
|||
|
|
|||
|
get category () {
|
|||
|
return this._category;
|
|||
|
}
|
|||
|
|
|||
|
set theme (value) {
|
|||
|
const valid = validateString(value, 'search.theme');
|
|||
|
if (valid !== null) this._theme = valid;
|
|||
|
}
|
|||
|
|
|||
|
get theme () {
|
|||
|
return this._theme;
|
|||
|
}
|
|||
|
|
|||
|
set type (value) {
|
|||
|
const valid = validateString(value, 'search.type');
|
|||
|
if (valid !== null) this._type = valid;
|
|||
|
this._type = value;
|
|||
|
}
|
|||
|
|
|||
|
get type () {
|
|||
|
return this._type;
|
|||
|
}
|
|||
|
|
|||
|
set method (id) {
|
|||
|
const methods = Object.values(Method);
|
|||
|
this._method = methods.filter(method => method.id === id || method.value === id)[0] || methods.filter(method => method.isDefault)[0];
|
|||
|
}
|
|||
|
|
|||
|
get method () {
|
|||
|
return this._method;
|
|||
|
}
|
|||
|
|
|||
|
get layer () {
|
|||
|
const layer = [];
|
|||
|
if (this.engine) layer.push('isearchengine', normalize(this.engine));
|
|||
|
if (this.results > -1) layer.push('isearchresults', this.results);
|
|||
|
if (this.terms) layer.push('isearchkey', 'search_terms', 'isearchdata', normalize(this.terms));
|
|||
|
if (this.category) layer.push('isearchkey', 'search_category', 'isearchdata', normalize(this.category));
|
|||
|
if (this.theme) layer.push('isearchkey', 'search_theme', 'isearchdata', normalize(this.theme));
|
|||
|
if (this.type) layer.push('isearchkey', 'search_type', 'isearchdata', normalize(this.type));
|
|||
|
if (this._method && layer.length) layer.push('isearchkey', 'search_method', 'isearchdata', this._method.value);
|
|||
|
return layer;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Search.Method = Method;
|
|||
|
|
|||
|
class Funnel {
|
|||
|
constructor (config) {
|
|||
|
this._config = config || {};
|
|||
|
}
|
|||
|
|
|||
|
reset (clear = false) {
|
|||
|
this.id = clear ? undefined : this._config.id;
|
|||
|
this.type = clear ? undefined : this._config.type;
|
|||
|
this.name = clear ? undefined : this._config.name;
|
|||
|
this.step = clear ? undefined : this._config.step;
|
|||
|
this.current = clear || isNaN(this._config.current) ? -1 : this._config.current;
|
|||
|
this.total = clear || isNaN(this._config.total) ? -1 : this._config.total;
|
|||
|
this.objective = clear ? undefined : this._config.objective;
|
|||
|
this.error = clear ? undefined : this._config.error;
|
|||
|
}
|
|||
|
|
|||
|
set id (value) {
|
|||
|
const valid = validateString(value, 'funnel.id');
|
|||
|
if (valid !== null) this._id = valid;
|
|||
|
}
|
|||
|
|
|||
|
get id () {
|
|||
|
return this._id;
|
|||
|
}
|
|||
|
|
|||
|
set type (value) {
|
|||
|
const valid = validateString(value, 'funnel.type');
|
|||
|
if (valid !== null) this._type = valid;
|
|||
|
}
|
|||
|
|
|||
|
get type () {
|
|||
|
return this._type;
|
|||
|
}
|
|||
|
|
|||
|
set name (value) {
|
|||
|
const valid = validateString(value, 'funnel.name');
|
|||
|
if (valid !== null) this._name = valid;
|
|||
|
}
|
|||
|
|
|||
|
get name () {
|
|||
|
return this._name;
|
|||
|
}
|
|||
|
|
|||
|
set step (value) {
|
|||
|
const valid = validateString(value, 'funnel.step');
|
|||
|
if (valid !== null) this._step = valid;
|
|||
|
}
|
|||
|
|
|||
|
get step () {
|
|||
|
return this._step;
|
|||
|
}
|
|||
|
|
|||
|
set current (value) {
|
|||
|
const valid = validateNumber(value, 'funnel.current');
|
|||
|
if (valid !== null) this._current = valid;
|
|||
|
}
|
|||
|
|
|||
|
get current () {
|
|||
|
return this._current;
|
|||
|
}
|
|||
|
|
|||
|
set total (value) {
|
|||
|
const valid = validateNumber(value, 'funnel.total');
|
|||
|
if (valid !== null) this._total = valid;
|
|||
|
}
|
|||
|
|
|||
|
get total () {
|
|||
|
return this._total;
|
|||
|
}
|
|||
|
|
|||
|
set objective (value) {
|
|||
|
const valid = validateString(value, 'funnel.objective');
|
|||
|
if (valid !== null) this._objective = valid;
|
|||
|
this._objective = value;
|
|||
|
}
|
|||
|
|
|||
|
get objective () {
|
|||
|
return this._objective;
|
|||
|
}
|
|||
|
|
|||
|
set error (value) {
|
|||
|
const valid = validateString(value, 'funnel.error');
|
|||
|
if (valid !== null) this._error = valid;
|
|||
|
this._error = value;
|
|||
|
}
|
|||
|
|
|||
|
get error () {
|
|||
|
return this._error;
|
|||
|
}
|
|||
|
|
|||
|
get layer () {
|
|||
|
const layer = [];
|
|||
|
if (this.id) layer.push('funnel_id', normalize(this.id));
|
|||
|
if (this.type) layer.push('funnel_type', normalize(this.type));
|
|||
|
if (this.name) layer.push('funnel_name', normalize(this.name));
|
|||
|
if (this.step) layer.push('funnel_step_name', normalize(this.step));
|
|||
|
if (!isNaN(this.current) && this.current > -1) layer.push('funnel_step_number', this.current);
|
|||
|
if (!isNaN(this.total) && this.total > -1) layer.push('funnel_step_max', this.total);
|
|||
|
if (this.objective) layer.push('funnel_objective', normalize(this.objective));
|
|||
|
if (this.error) layer.push('funnel_error', normalize(this.error));
|
|||
|
return layer;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const ActionMode = {
|
|||
|
IN: 'in',
|
|||
|
OUT: 'out',
|
|||
|
NONE: 'none'
|
|||
|
};
|
|||
|
|
|||
|
const ActionStatus = {
|
|||
|
UNSTARTED: {
|
|||
|
id: 'unstarted',
|
|||
|
value: -1
|
|||
|
},
|
|||
|
STARTED: {
|
|||
|
id: 'started',
|
|||
|
value: 1
|
|||
|
},
|
|||
|
SINGULAR: {
|
|||
|
id: 'singular',
|
|||
|
value: 2
|
|||
|
},
|
|||
|
ENDED: {
|
|||
|
id: 'ended',
|
|||
|
value: 3
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
const getParametersLayer = (data) => {
|
|||
|
return Object.entries(data).map(([key, value]) => ['actionpname', normalize(key), 'actionpvalue', normalize(value)]).flat();
|
|||
|
};
|
|||
|
|
|||
|
class Action {
|
|||
|
constructor (name) {
|
|||
|
this._isMuted = false;
|
|||
|
this._isForced = false;
|
|||
|
this._name = name;
|
|||
|
this._status = ActionStatus.UNSTARTED;
|
|||
|
this._labels = [];
|
|||
|
this._parameters = {};
|
|||
|
this._sentData = [];
|
|||
|
}
|
|||
|
|
|||
|
get isMuted () {
|
|||
|
return this._isMuted;
|
|||
|
}
|
|||
|
|
|||
|
set isMuted (value) {
|
|||
|
this._isMuted = value;
|
|||
|
}
|
|||
|
|
|||
|
get isForced () {
|
|||
|
return this._isForced;
|
|||
|
}
|
|||
|
|
|||
|
set isForced (value) {
|
|||
|
this._isForced = value;
|
|||
|
}
|
|||
|
|
|||
|
get isSingular () {
|
|||
|
return this._status === ActionStatus.SINGULAR;
|
|||
|
}
|
|||
|
|
|||
|
get status () {
|
|||
|
return this._status;
|
|||
|
}
|
|||
|
|
|||
|
get name () {
|
|||
|
return this._name;
|
|||
|
}
|
|||
|
|
|||
|
get labels () {
|
|||
|
return this._labels;
|
|||
|
}
|
|||
|
|
|||
|
get reference () {
|
|||
|
return this._reference;
|
|||
|
}
|
|||
|
|
|||
|
get parameters () {
|
|||
|
return this._parameters;
|
|||
|
}
|
|||
|
|
|||
|
get mode () {
|
|||
|
return this._mode;
|
|||
|
}
|
|||
|
|
|||
|
singularize () {
|
|||
|
this._status = ActionStatus.SINGULAR;
|
|||
|
}
|
|||
|
|
|||
|
rewind () {
|
|||
|
this._sentData = [];
|
|||
|
this._status = ActionStatus.UNSTARTED;
|
|||
|
}
|
|||
|
|
|||
|
addParameter (key, value) {
|
|||
|
this._parameters[key] = value;
|
|||
|
}
|
|||
|
|
|||
|
removeParameter (key) {
|
|||
|
delete this._parameters[key];
|
|||
|
}
|
|||
|
|
|||
|
set reference (value) {
|
|||
|
const valid = validateString(value, `action ${this._name}`);
|
|||
|
if (valid !== null) this._reference = valid;
|
|||
|
}
|
|||
|
|
|||
|
get _base () {
|
|||
|
return ['actionname', this._name];
|
|||
|
}
|
|||
|
|
|||
|
_getLayer (data = {}) {
|
|||
|
if (this._isMuted) return [];
|
|||
|
|
|||
|
if (this._mode !== ActionMode.IN) this._sentData.push(JSON.stringify(data));
|
|||
|
|
|||
|
const layer = this._base;
|
|||
|
switch (this._mode) {
|
|||
|
case ActionMode.IN:
|
|||
|
case ActionMode.OUT:
|
|||
|
layer.push('actionmode', this._mode);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
const labels = this._labels.slice(0, 5);
|
|||
|
labels.length = 5;
|
|||
|
if (labels.some(label => label)) layer.push('actionlabel', labels.map(label => typeof label === 'string' ? normalize(label) : '').join(','));
|
|||
|
|
|||
|
if (this._reference) layer.push('actionref', this._reference);
|
|||
|
|
|||
|
layer.push.apply(layer, getParametersLayer(Object.assign(this._parameters, data || {})));
|
|||
|
return layer;
|
|||
|
}
|
|||
|
|
|||
|
start (data) {
|
|||
|
switch (this._status) {
|
|||
|
case ActionStatus.UNSTARTED:
|
|||
|
this._mode = ActionMode.IN;
|
|||
|
this._status = ActionStatus.STARTED;
|
|||
|
break;
|
|||
|
|
|||
|
case ActionStatus.SINGULAR:
|
|||
|
this._mode = ActionMode.NONE;
|
|||
|
this._status = ActionStatus.ENDED;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
api.inspector.error(`unexpected start on action ${this._name} with status ${this._status.id}`);
|
|||
|
return [];
|
|||
|
}
|
|||
|
return this._getLayer(data);
|
|||
|
}
|
|||
|
|
|||
|
end (data) {
|
|||
|
switch (this._status) {
|
|||
|
case ActionStatus.STARTED:
|
|||
|
this._mode = ActionMode.OUT;
|
|||
|
this._status = ActionStatus.ENDED;
|
|||
|
break;
|
|||
|
|
|||
|
case ActionStatus.UNSTARTED:
|
|||
|
this._mode = ActionMode.NONE;
|
|||
|
this._status = ActionStatus.ENDED;
|
|||
|
break;
|
|||
|
|
|||
|
case ActionStatus.SINGULAR:
|
|||
|
this._mode = ActionMode.NONE;
|
|||
|
this._status = ActionStatus.ENDED;
|
|||
|
break;
|
|||
|
|
|||
|
case ActionStatus.ENDED:
|
|||
|
if (this._sentData.includes(JSON.stringify(data))) return [];
|
|||
|
this._mode = ActionMode.NONE;
|
|||
|
this._status = ActionStatus.ENDED;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
return [];
|
|||
|
}
|
|||
|
return this._getLayer(data);
|
|||
|
}
|
|||
|
|
|||
|
resume (data) {
|
|||
|
if (this._isMuted) return [];
|
|||
|
if (this._status.value >= ActionStatus.ENDED.value) {
|
|||
|
api.inspector.error(`unexpected resuming on action ${this._name} with status ${this._status.id}`);
|
|||
|
return [];
|
|||
|
}
|
|||
|
const layer = this._base;
|
|||
|
if (data) layer.push.apply(layer, getParametersLayer(data));
|
|||
|
return layer;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class Actions {
|
|||
|
constructor () {
|
|||
|
this._actions = [];
|
|||
|
}
|
|||
|
|
|||
|
rewind () {
|
|||
|
this._actions.forEach(action => action.rewind());
|
|||
|
}
|
|||
|
|
|||
|
getAction (name) {
|
|||
|
let action = this._actions.filter(action => action.name === name)[0];
|
|||
|
if (!action) {
|
|||
|
action = new Action(name);
|
|||
|
this._actions.push(action);
|
|||
|
}
|
|||
|
return action;
|
|||
|
}
|
|||
|
|
|||
|
hasAction (name) {
|
|||
|
return this._actions.some(action => action.name === name);
|
|||
|
}
|
|||
|
|
|||
|
remove (action) {
|
|||
|
const index = this._actions.indexOf(action);
|
|||
|
if (index === -1) return false;
|
|||
|
this._actions.splice(index, 1);
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Actions.ActionMode = ActionMode;
|
|||
|
|
|||
|
const actions = new Actions();
|
|||
|
Actions.instance = actions;
|
|||
|
|
|||
|
class Location {
|
|||
|
constructor (onRouteChange, isListeningHash = false) {
|
|||
|
this._onRouteChange = onRouteChange;
|
|||
|
this._isListeningHash = isListeningHash;
|
|||
|
this._update();
|
|||
|
renderer.add(this);
|
|||
|
}
|
|||
|
|
|||
|
_update () {
|
|||
|
this._pathname = document.location.pathname;
|
|||
|
this._search = document.location.search;
|
|||
|
this._hash = document.location.hash;
|
|||
|
this._path = `${this._pathname}${this._search}`;
|
|||
|
if (this._isListeningHash) this._path += this._hash;
|
|||
|
this._hasTitle = this._title === document.title;
|
|||
|
this._title = document.title;
|
|||
|
}
|
|||
|
|
|||
|
render () {
|
|||
|
if (this._pathname !== document.location.pathname || this._search !== document.location.search) this.change();
|
|||
|
if (this._isListeningHash && this._hash !== document.location.hash) this.change();
|
|||
|
}
|
|||
|
|
|||
|
change () {
|
|||
|
this._referrer = this._path;
|
|||
|
this._update();
|
|||
|
this._onRouteChange();
|
|||
|
}
|
|||
|
|
|||
|
get path () {
|
|||
|
return this._path;
|
|||
|
}
|
|||
|
|
|||
|
get hasTitle () {
|
|||
|
return this._hasTitle;
|
|||
|
}
|
|||
|
|
|||
|
get title () {
|
|||
|
return this._title;
|
|||
|
}
|
|||
|
|
|||
|
get referrer () {
|
|||
|
return this._referrer;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const CollectorEvent = {
|
|||
|
COLLECT: api.internals.ns.event('collect')
|
|||
|
};
|
|||
|
|
|||
|
const ActioneeEmission = {
|
|||
|
REWIND: api.internals.ns.emission('analytics', 'rewind')
|
|||
|
};
|
|||
|
|
|||
|
class Collector {
|
|||
|
constructor (config) {
|
|||
|
switch (config.collection) {
|
|||
|
case Collection.MANUAL:
|
|||
|
case Collection.LOAD:
|
|||
|
case Collection.FULL:
|
|||
|
case Collection.HASH:
|
|||
|
this._collection = config.collection;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
/* deprecated start */
|
|||
|
if (config.mode) {
|
|||
|
switch (config.mode) {
|
|||
|
case 'manual':
|
|||
|
this._collection = config.collection;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
/* deprecated end */
|
|||
|
|
|||
|
switch (true) {
|
|||
|
/* deprecated */
|
|||
|
case config.mode === 'manual':
|
|||
|
this._collection = Collection.MANUAL;
|
|||
|
break;
|
|||
|
|
|||
|
case api.mode === api.Modes.ANGULAR:
|
|||
|
case api.mode === api.Modes.REACT:
|
|||
|
case api.mode === api.Modes.VUE:
|
|||
|
this._collection = Collection.FULL;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
this._collection = Collection.LOAD;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
this._isActionEnabled = config.isActionEnabled === 'false' || config.isActionEnabled;
|
|||
|
|
|||
|
this._user = new User(config.user);
|
|||
|
this._site = new Site(config.site);
|
|||
|
this._page = new Page(config.page);
|
|||
|
this._search = new Search(config.search);
|
|||
|
this._funnel = new Funnel(config.funnel);
|
|||
|
|
|||
|
this._delay = -1;
|
|||
|
queue.setCollector(this);
|
|||
|
}
|
|||
|
|
|||
|
get page () {
|
|||
|
return this._page;
|
|||
|
}
|
|||
|
|
|||
|
get user () {
|
|||
|
return this._user;
|
|||
|
}
|
|||
|
|
|||
|
get site () {
|
|||
|
return this._site;
|
|||
|
}
|
|||
|
|
|||
|
get search () {
|
|||
|
return this._search;
|
|||
|
}
|
|||
|
|
|||
|
get funnel () {
|
|||
|
return this._funnel;
|
|||
|
}
|
|||
|
|
|||
|
start () {
|
|||
|
const handleRouteChange = this._handleRouteChange.bind(this);
|
|||
|
switch (this._collection) {
|
|||
|
case Collection.LOAD:
|
|||
|
this.collect();
|
|||
|
break;
|
|||
|
|
|||
|
case Collection.FULL:
|
|||
|
this.collect();
|
|||
|
this._location = new Location(handleRouteChange);
|
|||
|
break;
|
|||
|
|
|||
|
case Collection.HASH:
|
|||
|
this.collect();
|
|||
|
this._location = new Location(handleRouteChange, true);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
_handleRouteChange () {
|
|||
|
queue.send(true);
|
|||
|
this._delay = 6;
|
|||
|
renderer.add(this);
|
|||
|
}
|
|||
|
|
|||
|
render () {
|
|||
|
this._delay--;
|
|||
|
if (this._delay < 0) {
|
|||
|
renderer.remove(this);
|
|||
|
this._routeChanged();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
_routeChanged () {
|
|||
|
actions.rewind();
|
|||
|
this._page.referrer = this._location.referrer;
|
|||
|
if (this._location.hasTitle) this._page.title = this._location.title;
|
|||
|
this._page.path = this._location.path;
|
|||
|
const event = new CustomEvent(CollectorEvent.COLLECT);
|
|||
|
document.documentElement.dispatchEvent(event);
|
|||
|
this.collect();
|
|||
|
if (api.internals && api.internals.stage && api.internals.stage.root) api.internals.stage.root.descend(ActioneeEmission.REWIND);
|
|||
|
}
|
|||
|
|
|||
|
reset (clear = false) {
|
|||
|
this._user.reset(clear);
|
|||
|
this._site.reset(clear);
|
|||
|
this._page.reset(clear);
|
|||
|
this._search.reset(clear);
|
|||
|
this._funnel.reset(clear);
|
|||
|
}
|
|||
|
|
|||
|
collect () {
|
|||
|
if (!this.page.collecting()) return;
|
|||
|
queue.collect();
|
|||
|
}
|
|||
|
|
|||
|
get collection () {
|
|||
|
return this._collection;
|
|||
|
}
|
|||
|
|
|||
|
get isCollecting () {
|
|||
|
return this._page.isCollecting;
|
|||
|
}
|
|||
|
|
|||
|
get isActionEnabled () {
|
|||
|
return this._isActionEnabled;
|
|||
|
}
|
|||
|
|
|||
|
set isActionEnabled (value) {
|
|||
|
this._isActionEnabled = value;
|
|||
|
}
|
|||
|
|
|||
|
get layer () {
|
|||
|
return [
|
|||
|
...this._user.layer,
|
|||
|
...this._site.layer,
|
|||
|
...this._page.layer,
|
|||
|
...this._search.layer,
|
|||
|
...this._funnel.layer
|
|||
|
];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class Analytics {
|
|||
|
constructor () {
|
|||
|
this._isReady = false;
|
|||
|
this._readiness = new Promise((resolve, reject) => {
|
|||
|
if (this._isReady) resolve();
|
|||
|
else {
|
|||
|
this._resolve = resolve;
|
|||
|
this._reject = reject;
|
|||
|
}
|
|||
|
});
|
|||
|
this._configure();
|
|||
|
}
|
|||
|
|
|||
|
_configure () {
|
|||
|
switch (true) {
|
|||
|
case window[patch.namespace] !== undefined:
|
|||
|
this._config = window[patch.namespace].configuration.analytics;
|
|||
|
window[patch.namespace].promise.then(this._build.bind(this), () => {});
|
|||
|
break;
|
|||
|
|
|||
|
case api.internals !== undefined && api.internals.configuration !== undefined && api.internals.configuration.analytics !== undefined && api.internals.configuration.analytics.domain !== undefined:
|
|||
|
this._config = api.internals.configuration.analytics;
|
|||
|
this._build();
|
|||
|
break;
|
|||
|
|
|||
|
case api.analytics !== undefined && api.analytics.domain !== undefined:
|
|||
|
this._config = api.analytics;
|
|||
|
this._build();
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
api.inspector.warn('analytics configuration is incorrect or missing (required : domain)');
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
_build () {
|
|||
|
this._init = new Init(this._config.domain);
|
|||
|
this._init.configure().then(this._start.bind(this), (reason) => this._reject(reason));
|
|||
|
}
|
|||
|
|
|||
|
get isReady () {
|
|||
|
return this._isReady;
|
|||
|
}
|
|||
|
|
|||
|
get readiness () {
|
|||
|
return this._readiness;
|
|||
|
}
|
|||
|
|
|||
|
_start () {
|
|||
|
if (this._isReady) return;
|
|||
|
|
|||
|
this._cmp = new ConsentManagerPlatform(this._config.cmp);
|
|||
|
this._collector = new Collector(this._config);
|
|||
|
this._collector.reset();
|
|||
|
|
|||
|
this._isReady = true;
|
|||
|
this._resolve();
|
|||
|
|
|||
|
queue.start();
|
|||
|
this._collector.start();
|
|||
|
}
|
|||
|
|
|||
|
get page () {
|
|||
|
return this._collector.page;
|
|||
|
}
|
|||
|
|
|||
|
get user () {
|
|||
|
return this._collector.user;
|
|||
|
}
|
|||
|
|
|||
|
get site () {
|
|||
|
return this._collector._site;
|
|||
|
}
|
|||
|
|
|||
|
get search () {
|
|||
|
return this._collector.search;
|
|||
|
}
|
|||
|
|
|||
|
get funnel () {
|
|||
|
return this._collector.funnel;
|
|||
|
}
|
|||
|
|
|||
|
get cmp () {
|
|||
|
return this._cmp;
|
|||
|
}
|
|||
|
|
|||
|
get opt () {
|
|||
|
return opt;
|
|||
|
}
|
|||
|
|
|||
|
get collection () {
|
|||
|
return this._collector.collection;
|
|||
|
}
|
|||
|
|
|||
|
get isActionEnabled () {
|
|||
|
return this._collector.isActionEnabled;
|
|||
|
}
|
|||
|
|
|||
|
set isActionEnabled (value) {
|
|||
|
this._collector.isActionEnabled = value;
|
|||
|
}
|
|||
|
|
|||
|
get isDebugging () {
|
|||
|
return debug.isActive;
|
|||
|
}
|
|||
|
|
|||
|
set isDebugging (value) {
|
|||
|
debug.isActive = value;
|
|||
|
}
|
|||
|
|
|||
|
push (type, layer) {
|
|||
|
push(type, layer);
|
|||
|
}
|
|||
|
|
|||
|
reset (clear = false) {
|
|||
|
this._collector.reset();
|
|||
|
}
|
|||
|
|
|||
|
collect () {
|
|||
|
this._collector.collect();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const analytics = new Analytics();
|
|||
|
|
|||
|
analytics.Collection = Collection;
|
|||
|
analytics.PushType = PushType;
|
|||
|
|
|||
|
/**
|
|||
|
* Copy properties from multiple sources including accessors.
|
|||
|
* source : https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#copier_des_accesseurs
|
|||
|
*
|
|||
|
* @param {object} [target] - Target object to copy into
|
|||
|
* @param {...objects} [sources] - Multiple objects
|
|||
|
* @return {object} A new object
|
|||
|
*
|
|||
|
* @example
|
|||
|
*
|
|||
|
* const obj1 = {
|
|||
|
* key: 'value'
|
|||
|
* };
|
|||
|
* const obj2 = {
|
|||
|
* get function01 () {
|
|||
|
* return a-value;
|
|||
|
* }
|
|||
|
* set function01 () {
|
|||
|
* return a-value;
|
|||
|
* }
|
|||
|
* };
|
|||
|
* completeAssign(obj1, obj2)
|
|||
|
*/
|
|||
|
const completeAssign = (target, ...sources) => {
|
|||
|
sources.forEach(source => {
|
|||
|
const descriptors = Object.keys(source).reduce((descriptors, key) => {
|
|||
|
descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
|
|||
|
return descriptors;
|
|||
|
}, {});
|
|||
|
|
|||
|
Object.getOwnPropertySymbols(source).forEach(sym => {
|
|||
|
const descriptor = Object.getOwnPropertyDescriptor(source, sym);
|
|||
|
if (descriptor.enumerable) {
|
|||
|
descriptors[sym] = descriptor;
|
|||
|
}
|
|||
|
});
|
|||
|
Object.defineProperties(target, descriptors);
|
|||
|
});
|
|||
|
return target;
|
|||
|
};
|
|||
|
|
|||
|
api.analytics = completeAssign(analytics, {});
|
|||
|
|
|||
|
const Type$1 = {
|
|||
|
// impression
|
|||
|
IMPRESSION: {
|
|||
|
id: 'impression', // element appeared in the page
|
|||
|
isSingular: true,
|
|||
|
isBeginning: true,
|
|||
|
attributed: false,
|
|||
|
type: 'impression'
|
|||
|
},
|
|||
|
// interaction
|
|||
|
CLICK: {
|
|||
|
id: 'click', // generic click interaction
|
|||
|
isBeginning: true,
|
|||
|
attributed: true,
|
|||
|
type: 'interaction',
|
|||
|
event: 'click',
|
|||
|
method: 'eventListener'
|
|||
|
},
|
|||
|
INTERNAL: {
|
|||
|
id: 'internal', // anchor click redirecting on an internal url
|
|||
|
isBeginning: true,
|
|||
|
attributed: true,
|
|||
|
type: 'interaction',
|
|||
|
event: 'click',
|
|||
|
method: 'eventListener'
|
|||
|
},
|
|||
|
EXTERNAL: {
|
|||
|
id: 'external', // anchor click redirecting on an external url
|
|||
|
isBeginning: true,
|
|||
|
attributed: true,
|
|||
|
type: 'interaction',
|
|||
|
event: 'click',
|
|||
|
method: 'eventListener'
|
|||
|
},
|
|||
|
DOWNLOAD: {
|
|||
|
id: 'download', // anchor click downloading a file
|
|||
|
isBeginning: true,
|
|||
|
attributed: true,
|
|||
|
type: 'interaction',
|
|||
|
event: 'click',
|
|||
|
method: 'eventListener'
|
|||
|
},
|
|||
|
BUTTON: {
|
|||
|
id: 'button', // button click
|
|||
|
isBeginning: true,
|
|||
|
attributed: true,
|
|||
|
type: 'interaction',
|
|||
|
event: 'click',
|
|||
|
method: 'eventListener'
|
|||
|
},
|
|||
|
DOUBLE_CLICK: {
|
|||
|
id: 'dblclick', // double click
|
|||
|
isBeginning: true,
|
|||
|
attributed: true,
|
|||
|
type: 'interaction',
|
|||
|
event: 'dblclick',
|
|||
|
method: 'eventListener'
|
|||
|
},
|
|||
|
// event
|
|||
|
OPEN: {
|
|||
|
id: 'open', // open event
|
|||
|
isSingular: true,
|
|||
|
attributed: false,
|
|||
|
type: 'event',
|
|||
|
method: 'eventListener'
|
|||
|
},
|
|||
|
COMPLETE: {
|
|||
|
id: 'complete', // complete event
|
|||
|
isSingular: true,
|
|||
|
attributed: false,
|
|||
|
type: 'event',
|
|||
|
method: 'eventListener'
|
|||
|
},
|
|||
|
FOCUS: {
|
|||
|
id: 'focus', // focus event
|
|||
|
isSingular: true,
|
|||
|
attributed: false,
|
|||
|
type: 'event',
|
|||
|
method: 'eventListener'
|
|||
|
},
|
|||
|
ERROR: {
|
|||
|
id: 'error', // error event
|
|||
|
isSingular: true,
|
|||
|
attributed: false,
|
|||
|
type: 'event'
|
|||
|
},
|
|||
|
ADD: {
|
|||
|
id: 'add', // add event
|
|||
|
isSingular: true,
|
|||
|
attributed: false,
|
|||
|
type: 'event'
|
|||
|
},
|
|||
|
REMOVE: {
|
|||
|
id: 'remove', // remove event
|
|||
|
isSingular: true,
|
|||
|
attributed: false,
|
|||
|
type: 'event'
|
|||
|
},
|
|||
|
DISPLAY: {
|
|||
|
id: 'display', // display event
|
|||
|
isSingular: true,
|
|||
|
attributed: false,
|
|||
|
type: 'event'
|
|||
|
},
|
|||
|
CHANGE: {
|
|||
|
id: 'change', // input event change
|
|||
|
isSingular: true,
|
|||
|
attributed: true,
|
|||
|
type: 'event',
|
|||
|
event: 'change',
|
|||
|
method: 'change'
|
|||
|
},
|
|||
|
PROGRESS: {
|
|||
|
id: 'progress', // video retention event with percent of the part reached
|
|||
|
isBeginning: true,
|
|||
|
attributed: true,
|
|||
|
type: 'event'
|
|||
|
},
|
|||
|
// component interaction
|
|||
|
SHARE: {
|
|||
|
id: 'share', // component share click (share)
|
|||
|
isBeginning: true,
|
|||
|
attributed: false,
|
|||
|
type: 'interaction'
|
|||
|
},
|
|||
|
PRESS: {
|
|||
|
id: 'press', // component press click (pressable tag)
|
|||
|
isBeginning: true,
|
|||
|
attributed: false,
|
|||
|
type: 'interaction'
|
|||
|
},
|
|||
|
RELEASE: {
|
|||
|
id: 'release', // component release click (pressable tag)
|
|||
|
isBeginning: true,
|
|||
|
attributed: false,
|
|||
|
type: 'interaction'
|
|||
|
},
|
|||
|
DISMISS: {
|
|||
|
id: 'dismiss', // component dismiss click (dismissible tag)
|
|||
|
isBeginning: true,
|
|||
|
attributed: false,
|
|||
|
type: 'interaction'
|
|||
|
},
|
|||
|
UPLOAD: {
|
|||
|
id: 'upload', // component upload click (upload)
|
|||
|
isBeginning: true,
|
|||
|
attributed: false,
|
|||
|
type: 'interaction'
|
|||
|
},
|
|||
|
CHECK: {
|
|||
|
id: 'check', // component check click (checkbox, radio, toggle)
|
|||
|
isBeginning: true,
|
|||
|
attributed: false,
|
|||
|
type: 'interaction'
|
|||
|
},
|
|||
|
UNCHECK: {
|
|||
|
id: 'uncheck', // component uncheck click (checkbox, radio, toggle)
|
|||
|
isBeginning: true,
|
|||
|
attributed: false,
|
|||
|
type: 'interaction'
|
|||
|
},
|
|||
|
SELECT: {
|
|||
|
id: 'select', // component select change (select)
|
|||
|
isBeginning: true,
|
|||
|
attributed: false,
|
|||
|
type: 'interaction'
|
|||
|
},
|
|||
|
SUBSCRIBE: {
|
|||
|
id: 'subscribe', // component subscribe click (follow)
|
|||
|
isBeginning: true,
|
|||
|
attributed: false,
|
|||
|
type: 'interaction'
|
|||
|
},
|
|||
|
// component event
|
|||
|
DISCLOSE: {
|
|||
|
id: 'disclose', // component disclose event (accordion, modal, tab)
|
|||
|
isBeginning: true,
|
|||
|
attributed: false,
|
|||
|
type: 'event'
|
|||
|
},
|
|||
|
SEARCH: {
|
|||
|
id: 'search', // component disclose event (accordion, modal, tab)
|
|||
|
isBeginning: true,
|
|||
|
attributed: false,
|
|||
|
type: 'event'
|
|||
|
},
|
|||
|
SHOW: {
|
|||
|
id: 'show', // component show event (tooltip)
|
|||
|
isSingular: true,
|
|||
|
attributed: false,
|
|||
|
type: 'event'
|
|||
|
},
|
|||
|
HIDE: {
|
|||
|
id: 'hide', // component hide event (tooltip)
|
|||
|
isSingular: true,
|
|||
|
attributed: false,
|
|||
|
type: 'event'
|
|||
|
},
|
|||
|
// video
|
|||
|
AUTOPLAY: {
|
|||
|
id: 'autoplay', // video autoplay event
|
|||
|
isBeginning: true,
|
|||
|
attributed: false,
|
|||
|
type: 'event'
|
|||
|
},
|
|||
|
PLAY: {
|
|||
|
id: 'play', // video play click
|
|||
|
isBeginning: true,
|
|||
|
attributed: false,
|
|||
|
type: 'interaction'
|
|||
|
},
|
|||
|
PAUSE: {
|
|||
|
id: 'pause', // video pause click
|
|||
|
isBeginning: true,
|
|||
|
attributed: false,
|
|||
|
type: 'interaction'
|
|||
|
},
|
|||
|
END: {
|
|||
|
id: 'end', // video end event
|
|||
|
isBeginning: true,
|
|||
|
attributed: false,
|
|||
|
type: 'event'
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
const Type = {
|
|||
|
UNDEFINED: 'undefined',
|
|||
|
HEADING: 'heading',
|
|||
|
COMPONENT: 'component',
|
|||
|
CONTENT: 'content'
|
|||
|
};
|
|||
|
|
|||
|
const NODE_POSITION = Node.DOCUMENT_POSITION_PRECEDING | Node.DOCUMENT_POSITION_CONTAINED_BY;
|
|||
|
|
|||
|
class Heading {
|
|||
|
constructor (heading) {
|
|||
|
this._label = heading.textContent.trim();
|
|||
|
this._level = Number(heading.tagName.charAt(1));
|
|||
|
}
|
|||
|
|
|||
|
get level () {
|
|||
|
return this._level;
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
return this._label;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class Member {
|
|||
|
constructor (node, target, level) {
|
|||
|
this._type = Type.UNDEFINED;
|
|||
|
this._node = node;
|
|||
|
this._target = target;
|
|||
|
this._level = level;
|
|||
|
this._label = '';
|
|||
|
this._component = '';
|
|||
|
this._isValid = true;
|
|||
|
this.analyse();
|
|||
|
}
|
|||
|
|
|||
|
_parseHeadings () {
|
|||
|
const selector = Array.from({ length: this._level }, (v, i) => `h${i + 1}`).join(',');
|
|||
|
this._headings = Array.from(this._node.querySelectorAll(selector)).filter(heading => heading === this._node || heading.parentNode === this._node || (heading.parentNode != null && heading.parentNode.parentNode === this._node)).filter(heading => (this._target.compareDocumentPosition(heading) & NODE_POSITION) > 0).map(heading => new Heading(heading)).reverse();
|
|||
|
}
|
|||
|
|
|||
|
_getComponent () {
|
|||
|
if (typeof api !== 'function') return false;
|
|||
|
const element = api(this._node);
|
|||
|
if (!element) return false;
|
|||
|
const instance = Object.values(element).filter(actionee => actionee.isActionee).sort((a, b) => b.priority - a.priority)[0];
|
|||
|
if (!instance) return false;
|
|||
|
|
|||
|
this._type = Type.COMPONENT;
|
|||
|
this._isValid = instance.validate(this._target);
|
|||
|
const selector = Array.from({ length: 6 }, (v, i) => `h${i + 1}`).join(',');
|
|||
|
const top = Array.from(this._node.querySelectorAll(selector)).map(heading => new Heading(heading)).sort((a, b) => a.level - b.level)[0];
|
|||
|
if (top && top.level <= this._level) this._level = top.level - 1;
|
|||
|
|
|||
|
const hx = this._node.closest(selector);
|
|||
|
if (hx) {
|
|||
|
const heading = new Heading(hx);
|
|||
|
if (heading.level <= this._level) this._level = heading.level - 1;
|
|||
|
}
|
|||
|
|
|||
|
if (!isNaN(instance.level) && instance.level < this._level) this._level = instance.level;
|
|||
|
this._label = instance.label;
|
|||
|
this._component = instance.component;
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
_getHeading () {
|
|||
|
if (!this._headings.length) return false;
|
|||
|
const labels = [];
|
|||
|
this._headings.forEach(heading => {
|
|||
|
if (heading.level <= this._level) {
|
|||
|
if (heading.level > 1) labels.unshift(heading.label);
|
|||
|
this._level = heading.level - 1;
|
|||
|
}
|
|||
|
});
|
|||
|
if (!labels.length) return false;
|
|||
|
this._type = Type.HEADING;
|
|||
|
this._label = labels.join(' › ');
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
analyse () {
|
|||
|
this._parseHeadings();
|
|||
|
if (this._getComponent()) return;
|
|||
|
if (this._getHeading()) return;
|
|||
|
if (this._node !== this._target) return;
|
|||
|
|
|||
|
const label = this._node.textContent.trim();
|
|||
|
if (!label) return;
|
|||
|
this._type = Type.CONTENT;
|
|||
|
this._label = label;
|
|||
|
}
|
|||
|
|
|||
|
get type () {
|
|||
|
return this._type;
|
|||
|
}
|
|||
|
|
|||
|
get level () {
|
|||
|
return this._level;
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
return this._label;
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return this._component;
|
|||
|
}
|
|||
|
|
|||
|
get node () {
|
|||
|
return this._node;
|
|||
|
}
|
|||
|
|
|||
|
get target () {
|
|||
|
return this._target;
|
|||
|
}
|
|||
|
|
|||
|
get isValid () {
|
|||
|
return this._isValid;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class Hierarchy {
|
|||
|
constructor (node) {
|
|||
|
this._node = node;
|
|||
|
this._process();
|
|||
|
}
|
|||
|
|
|||
|
_process () {
|
|||
|
// console.log('_______________ start ____________________');
|
|||
|
const member = new Member(this._node, this._node, 6);
|
|||
|
// console.log('- FIRST MEMBER', member);
|
|||
|
this._level = member.level;
|
|||
|
this._members = [member];
|
|||
|
|
|||
|
let node = this._node.parentNode;
|
|||
|
|
|||
|
while (document.documentElement.contains(node) && node !== document.documentElement && this._level > 0) {
|
|||
|
// console.log('MEMBERS ARRAY', this._members);
|
|||
|
// console.log('NODE ANALYSIS', node);
|
|||
|
const member = new Member(node, this._node, this._level);
|
|||
|
// console.log('NEW MEMBER', member);
|
|||
|
switch (true) {
|
|||
|
case member.type === Type.UNDEFINED:
|
|||
|
// console.log('****UNDEFINED');
|
|||
|
break;
|
|||
|
|
|||
|
case !member.isValid:
|
|||
|
// console.log('****INVALID');
|
|||
|
break;
|
|||
|
|
|||
|
case member.label === this._members[0].label && member.type === Type.HEADING && this._members[0].type === Type.COMPONENT:
|
|||
|
// console.log('***** SAME');
|
|||
|
// do nothing
|
|||
|
break;
|
|||
|
|
|||
|
case member.label === this._members[0].label && member.type === Type.COMPONENT && this._members[0].type === Type.HEADING:
|
|||
|
// console.log('***** SAME INVERT');
|
|||
|
this._members.splice(0, 1, member);
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
this._members.unshift(member);
|
|||
|
if (member.level < this._level) this._level = member.level;
|
|||
|
}
|
|||
|
|
|||
|
node = node.parentNode;
|
|||
|
}
|
|||
|
|
|||
|
this._label = normalize(this._members[this._members.length - 1].label);
|
|||
|
this._title = normalize(this._members.filter(member => member.label).map(member => member.label).join(' › '));
|
|||
|
const components = this._members.filter(member => member.component).map(member => member.component);
|
|||
|
this._component = normalize(components.join(' › '));
|
|||
|
this._localComponent = components[components.length - 1];
|
|||
|
this._globalComponent = components[0];
|
|||
|
|
|||
|
// console.log('========= end ===========');
|
|||
|
}
|
|||
|
|
|||
|
get localComponent () {
|
|||
|
return this._localComponent;
|
|||
|
}
|
|||
|
|
|||
|
get globalComponent () {
|
|||
|
return this._globalComponent;
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
return this._label;
|
|||
|
}
|
|||
|
|
|||
|
get title () {
|
|||
|
return this._title;
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return this._component;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class ActionElement {
|
|||
|
constructor (node, type, id, category = '', title = null, parameters = {}, isRating = false, isForced = false) {
|
|||
|
this._node = node;
|
|||
|
this._type = type;
|
|||
|
this._id = id || this._node.id;
|
|||
|
this._isMuted = false;
|
|||
|
this._title = title;
|
|||
|
this._category = category;
|
|||
|
this._parameters = parameters;
|
|||
|
this._isRating = isRating;
|
|||
|
this._isForced = isForced;
|
|||
|
this._hasBegun = false;
|
|||
|
|
|||
|
// this._init();
|
|||
|
requestAnimationFrame(this._init.bind(this));
|
|||
|
}
|
|||
|
|
|||
|
_init () {
|
|||
|
this._hierarchy = new Hierarchy(this._node);
|
|||
|
|
|||
|
let id = '';
|
|||
|
let type = '';
|
|||
|
if (this._id) id = `_[${this._id}]`;
|
|||
|
else api.inspector.warn(`Analytics API requires an id to be set on tracked element. Missing on ${this._node.outerHTML}`);
|
|||
|
if (this._type) type = `(${this._type.id})_`;
|
|||
|
this._name = `${type}${this._title || this._hierarchy.title}${id}`;
|
|||
|
|
|||
|
this._action = actions.getAction(this._name, this._type.status);
|
|||
|
if (this._type.isSingular) this._action.singularize();
|
|||
|
Object.keys(this._parameters).forEach(key => this._action.addParameter(key, this._parameters[key]));
|
|||
|
this._action.isMuted = this._isMuted;
|
|||
|
this._action.isForced = this._isForced;
|
|||
|
|
|||
|
this._action.labels[0] = this._type.id;
|
|||
|
this._action.labels[1] = this._hierarchy.globalComponent;
|
|||
|
this._action.labels[2] = this._hierarchy.localComponent;
|
|||
|
this._action.labels[4] = this._category;
|
|||
|
|
|||
|
if (this._hierarchy.label) this._action.addParameter('component_label', this._hierarchy.label);
|
|||
|
if (this._hierarchy.title) this._action.addParameter('heading_hierarchy', this._hierarchy.title);
|
|||
|
if (this._hierarchy.component) this._action.addParameter('component_hierarchy', this._hierarchy.component);
|
|||
|
|
|||
|
this.begin();
|
|||
|
}
|
|||
|
|
|||
|
get isMuted () {
|
|||
|
return this._action ? this._action.isMuted : this._isMuted;
|
|||
|
}
|
|||
|
|
|||
|
set isMuted (value) {
|
|||
|
this._isMuted = value;
|
|||
|
if (this._action) this._action.isMuted = value;
|
|||
|
}
|
|||
|
|
|||
|
get action () {
|
|||
|
return this._action;
|
|||
|
}
|
|||
|
|
|||
|
rewind () {
|
|||
|
this._hasBegun = false;
|
|||
|
this.begin();
|
|||
|
}
|
|||
|
|
|||
|
begin (data = {}) {
|
|||
|
if (this._hasBegun) return;
|
|||
|
this._hasBegun = true;
|
|||
|
if (this._type.isBeginning && (this._type.isSingular || this._isRating)) queue.appendStartingAction(this._action, data);
|
|||
|
}
|
|||
|
|
|||
|
act (data = {}) {
|
|||
|
if (this._isMuted) return;
|
|||
|
if (!this._action) {
|
|||
|
requestAnimationFrame(() => this.act(data));
|
|||
|
return;
|
|||
|
}
|
|||
|
queue.appendEndingAction(this._action, data);
|
|||
|
}
|
|||
|
|
|||
|
dispose () {
|
|||
|
actions.remove(this._action);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const ActionAttributes = {
|
|||
|
RATING: api.internals.ns.attr('analytics-rating'),
|
|||
|
ACTION: api.internals.ns.attr('analytics-action')
|
|||
|
};
|
|||
|
|
|||
|
class Actionee extends api.core.Instance {
|
|||
|
constructor (priority = -1, category = '', title = null, isForced = false) {
|
|||
|
super();
|
|||
|
this._type = null;
|
|||
|
this._priority = priority;
|
|||
|
this._category = category;
|
|||
|
this._title = title;
|
|||
|
this._parameters = {};
|
|||
|
this._data = {};
|
|||
|
this._isMuted = false;
|
|||
|
this._isForced = isForced;
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'Actionee';
|
|||
|
}
|
|||
|
|
|||
|
get proxy () {
|
|||
|
const scope = this;
|
|||
|
|
|||
|
const proxy = {
|
|||
|
validate: (target, members) => scope.validate(target, members)
|
|||
|
};
|
|||
|
|
|||
|
const proxyAccessors = {
|
|||
|
get isActionee () {
|
|||
|
return true;
|
|||
|
},
|
|||
|
get label () {
|
|||
|
return scope.label;
|
|||
|
},
|
|||
|
get priority () {
|
|||
|
return scope.priority;
|
|||
|
},
|
|||
|
get level () {
|
|||
|
return scope.level;
|
|||
|
},
|
|||
|
get node () {
|
|||
|
return scope.node; // TODO: remove in v2
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
return api.internals.property.completeAssign(super.proxy, proxy, proxyAccessors);
|
|||
|
}
|
|||
|
|
|||
|
get data () {
|
|||
|
return this._data;
|
|||
|
}
|
|||
|
|
|||
|
_config (element, registration) {
|
|||
|
super._config(element, registration);
|
|||
|
|
|||
|
if (this._type === null) {
|
|||
|
this._sort(element);
|
|||
|
this._isMuted = true;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
this._actionElement = new ActionElement(this.node, this._type, this.id, this._category, this.getAttribute(ActionAttributes.ACTION) || this._title, this._parameters, this.hasAttribute(ActionAttributes.RATING), this.hasAttribute(ActionAttributes.ACTION) || this._isForced);
|
|||
|
if (this._isMuted) this._actionElement.isMuted = true;
|
|||
|
|
|||
|
this.addDescent(ActioneeEmission.REWIND, this.rewind.bind(this));
|
|||
|
|
|||
|
this._sort(element);
|
|||
|
}
|
|||
|
|
|||
|
_sort (element) {
|
|||
|
const actionees = element.instances.filter(instance => instance.isActionee).sort((a, b) => b.priority - a.priority);
|
|||
|
if (actionees.length <= 1) return;
|
|||
|
actionees.forEach((actionee, index) => { actionee.isMuted = index > 0; });
|
|||
|
}
|
|||
|
|
|||
|
get isMuted () {
|
|||
|
return this._actionElement ? this._actionElement.isMuted : this._isMuted;
|
|||
|
}
|
|||
|
|
|||
|
set isMuted (value) {
|
|||
|
this._isMuted = value;
|
|||
|
if (this._actionElement) this._actionElement.isMuted = value;
|
|||
|
}
|
|||
|
|
|||
|
get priority () {
|
|||
|
return this._priority;
|
|||
|
}
|
|||
|
|
|||
|
setPriority (value) {
|
|||
|
this._priority = value;
|
|||
|
}
|
|||
|
|
|||
|
get isInteractive () {
|
|||
|
return this.node.tagName === 'A' || this.node.tagName === 'BUTTON';
|
|||
|
}
|
|||
|
|
|||
|
detectInteractionType (node) {
|
|||
|
if (!node) node = this.node;
|
|||
|
const tag = node.tagName;
|
|||
|
const href = node.getAttribute('href');
|
|||
|
const isDownload = node.hasAttribute('download');
|
|||
|
const hostname = node.hostname;
|
|||
|
|
|||
|
switch (true) {
|
|||
|
case tag !== 'A':
|
|||
|
this._type = Type$1.CLICK;
|
|||
|
break;
|
|||
|
|
|||
|
case isDownload:
|
|||
|
this._type = Type$1.DOWNLOAD;
|
|||
|
this.value = href;
|
|||
|
break;
|
|||
|
|
|||
|
case hostname === location.hostname :
|
|||
|
this._type = Type$1.INTERNAL;
|
|||
|
this.value = href;
|
|||
|
break;
|
|||
|
|
|||
|
case hostname.length > 0 :
|
|||
|
this._type = Type$1.EXTERNAL;
|
|||
|
this.value = href;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
this._type = Type$1.CLICK;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
setClickType () {
|
|||
|
this._type = Type$1.CLICK;
|
|||
|
}
|
|||
|
|
|||
|
listenActionClick (target) {
|
|||
|
if (target) {
|
|||
|
this._clickTarget = target;
|
|||
|
this._clickTarget.addEventListener('click', this._handlingClick, { capture: true });
|
|||
|
} else this.listenClick({ capture: true });
|
|||
|
}
|
|||
|
|
|||
|
handleClick () {
|
|||
|
this.act();
|
|||
|
}
|
|||
|
|
|||
|
setImpressionType () {
|
|||
|
this._type = Type$1.IMPRESSION;
|
|||
|
}
|
|||
|
|
|||
|
rewind () {
|
|||
|
if (this._actionElement) this._actionElement.rewind();
|
|||
|
}
|
|||
|
|
|||
|
act (data = {}) {
|
|||
|
if (this._actionElement !== undefined) {
|
|||
|
this._data.component_value = this.value;
|
|||
|
this._actionElement.act(Object.assign(this._data, data));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
getFirstText (node) {
|
|||
|
if (!node) node = this.node;
|
|||
|
if (node.childNodes && node.childNodes.length > 0) {
|
|||
|
for (let i = 0; i < node.childNodes.length; i++) {
|
|||
|
if (node.childNodes[i].nodeType === Node.TEXT_NODE) {
|
|||
|
const text = node.childNodes[i].textContent.trim();
|
|||
|
if (text) {
|
|||
|
return this.cropText(text);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
for (let i = 0; i < node.childNodes.length; i++) {
|
|||
|
const text = this.getFirstText(node.childNodes[i]);
|
|||
|
if (text) {
|
|||
|
return this.cropText(text);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return '';
|
|||
|
}
|
|||
|
|
|||
|
cropText (text, length = 50) {
|
|||
|
return text.length > 50 ? `${text.substring(0, 50).trim()}[...]` : text;
|
|||
|
}
|
|||
|
|
|||
|
getInteractionLabel () {
|
|||
|
const title = this.getAttribute('title');
|
|||
|
if (title) return this.cropText(title);
|
|||
|
|
|||
|
const text = this.getFirstText();
|
|||
|
if (text) return text;
|
|||
|
|
|||
|
const img = this.node.querySelector('img');
|
|||
|
if (img) {
|
|||
|
const alt = img.getAttribute('alt');
|
|||
|
if (alt) return this.cropText(alt);
|
|||
|
}
|
|||
|
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
getHeadingLabel (length = 6) {
|
|||
|
const selector = Array.from({ length: length }, (v, i) => `h${i + 1}`).join(',');
|
|||
|
const headings = Array.from(this.querySelectorAll(selector)).filter(heading => (this.node.compareDocumentPosition(heading) & Node.DOCUMENT_POSITION_CONTAINED_BY) > 0);
|
|||
|
if (headings.length) {
|
|||
|
for (const heading of headings) {
|
|||
|
const text = this.getFirstText(heading);
|
|||
|
if (text) return text;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
detectLevel (node) {
|
|||
|
if (!node) node = this.node;
|
|||
|
const selector = Array.from({ length: 6 }, (v, i) => `h${i + 1}`).join(',');
|
|||
|
const levels = Array.from(node.querySelectorAll(selector)).map(heading => Number(heading.tagName.charAt(1)));
|
|||
|
if (levels.length) this._level = Math.min.apply(null, levels) - 1;
|
|||
|
}
|
|||
|
|
|||
|
validate (target) {
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
get actionElement () {
|
|||
|
return this._actionElement;
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
get value () {
|
|||
|
return this._value || this.label;
|
|||
|
}
|
|||
|
|
|||
|
set value (value) {
|
|||
|
this._value = value;
|
|||
|
}
|
|||
|
|
|||
|
get isActionee () {
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
get level () {
|
|||
|
return this._level;
|
|||
|
}
|
|||
|
|
|||
|
get type () {
|
|||
|
return this._type;
|
|||
|
}
|
|||
|
|
|||
|
dispose () {
|
|||
|
if (this._clickTarget) {
|
|||
|
this._clickTarget.removeEventListener('click', this._handlingClick);
|
|||
|
}
|
|||
|
super.dispose();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class AttributeActionee extends Actionee {
|
|||
|
constructor () {
|
|||
|
super(100, '', null, true);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'AttributeActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this._attribute = this.registration.selector.replace(/[[\]]/g, '');
|
|||
|
const id = this._attribute.split('-').pop();
|
|||
|
this._type = Object.values(Type$1).filter(type => type.id === id)[0];
|
|||
|
this._title = this.getAttribute(this._attribute);
|
|||
|
if (this._type === Type$1.CLICK) this.detectInteractionType();
|
|||
|
|
|||
|
switch (this._type.method) {
|
|||
|
case 'eventListener':
|
|||
|
this.listen(this._type.event, this.handleEvent.bind(this));
|
|||
|
break;
|
|||
|
|
|||
|
case 'change':
|
|||
|
this.listen(this._type.event, this.handleChange.bind(this));
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
handleEvent (e) {
|
|||
|
this._actionElement.act();
|
|||
|
}
|
|||
|
|
|||
|
handleChange (e) {
|
|||
|
this._actionElement.act({ change_value: e.target.value });
|
|||
|
}
|
|||
|
|
|||
|
dispose () {
|
|||
|
this._actionElement.dispose();
|
|||
|
super.dispose();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateAttributes = () => {
|
|||
|
Object.values(Type$1)
|
|||
|
.filter(type => type.attributed)
|
|||
|
.forEach(type => api.internals.register(api.internals.ns.attr.selector(`analytics-${type.id}`), AttributeActionee));
|
|||
|
};
|
|||
|
|
|||
|
const ButtonEmission = {
|
|||
|
CLICK: api.internals.ns.emission('button', 'click')
|
|||
|
};
|
|||
|
|
|||
|
class ComponentActionee extends Actionee {
|
|||
|
constructor (priority = -1) {
|
|||
|
super(priority, 'dsfr_component');
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'ComponentActionee';
|
|||
|
}
|
|||
|
|
|||
|
get proxy () {
|
|||
|
const scope = this;
|
|||
|
|
|||
|
const proxyAccessors = {
|
|||
|
get component () {
|
|||
|
return scope.component;
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
return api.internals.property.completeAssign(super.proxy, proxyAccessors);
|
|||
|
}
|
|||
|
|
|||
|
setDiscloseType () {
|
|||
|
this._type = Type$1.DISCLOSE;
|
|||
|
}
|
|||
|
|
|||
|
listenDisclose () {
|
|||
|
this.listen(api.core.DisclosureEvent.DISCLOSE, this._handleDisclose.bind(this), { capture: true });
|
|||
|
}
|
|||
|
|
|||
|
_handleDisclose () {
|
|||
|
this.act();
|
|||
|
}
|
|||
|
|
|||
|
setChangeType () {
|
|||
|
this._type = Type$1.CHANGE;
|
|||
|
}
|
|||
|
|
|||
|
listenChange () {
|
|||
|
this.listen('change', this._handleChange.bind(this), { capture: true });
|
|||
|
}
|
|||
|
|
|||
|
_handleChange (e) {
|
|||
|
if (e.target && e.target.value) {
|
|||
|
this.setChangeValue(e);
|
|||
|
this.act();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
setChangeValue (e) {
|
|||
|
this.value = e.target.value;
|
|||
|
}
|
|||
|
|
|||
|
listenInputValidation (node, type = Type$1.CLICK, isSendingInputValue = false) {
|
|||
|
if (!node) node = this.node;
|
|||
|
this._type = type;
|
|||
|
this._isSendingInputValue = isSendingInputValue;
|
|||
|
this.addAscent(ButtonEmission.CLICK, this._actValidatedInput.bind(this));
|
|||
|
const button = this.element.getDescendantInstances('ButtonActionee', null, true)[0];
|
|||
|
if (button) button.isMuted = true;
|
|||
|
this._validatedInput = node.querySelector('input');
|
|||
|
this._handlingInputValidation = this._handleInputValidation.bind(this);
|
|||
|
if (this._validatedInput) this._validatedInput.addEventListener('keydown', this._handlingInputValidation);
|
|||
|
}
|
|||
|
|
|||
|
_handleInputValidation (e) {
|
|||
|
if (e.keyCode === 13) this._actValidatedInput();
|
|||
|
}
|
|||
|
|
|||
|
_actValidatedInput () {
|
|||
|
if (this._isActingValidatedInput) return;
|
|||
|
this._isActingValidatedInput = true;
|
|||
|
if (this._isSendingInputValue) this.value = this._validatedInput.value.trim();
|
|||
|
this.act();
|
|||
|
this.request(this._actedValidatedInput.bind(this));
|
|||
|
}
|
|||
|
|
|||
|
_actedValidatedInput () {
|
|||
|
this._isActingValidatedInput = false;
|
|||
|
}
|
|||
|
|
|||
|
setCheckType () {
|
|||
|
this._type = Type$1.CHECK;
|
|||
|
}
|
|||
|
|
|||
|
detectCheckableType () {
|
|||
|
const isChecked = this.node.checked;
|
|||
|
this._type = isChecked ? Type$1.UNCHECK : Type$1.CHECK;
|
|||
|
}
|
|||
|
|
|||
|
listenCheckable () {
|
|||
|
this.listen('change', this._handleCheckable.bind(this), { capture: true });
|
|||
|
}
|
|||
|
|
|||
|
_handleCheckable (e) {
|
|||
|
if (e.target && e.target.value !== 'on') {
|
|||
|
this.value = e.target.value;
|
|||
|
}
|
|||
|
|
|||
|
switch (true) {
|
|||
|
case this._type === Type$1.CHECK && e.target.checked:
|
|||
|
case this._type === Type$1.UNCHECK && !e.target.checked:
|
|||
|
this.act();
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
detectPressableType () {
|
|||
|
const isPressable = this.node.hasAttribute('aria-pressed');
|
|||
|
if (isPressable) {
|
|||
|
const isPressed = this.node.getAttribute('aria-pressed') === 'true';
|
|||
|
this._type = isPressed ? Type$1.RELEASE : Type$1.PRESS;
|
|||
|
}
|
|||
|
return isPressable;
|
|||
|
}
|
|||
|
|
|||
|
listenPressable () {
|
|||
|
this.listen('click', this._handlePressable.bind(this), { capture: true });
|
|||
|
}
|
|||
|
|
|||
|
_handlePressable (e) {
|
|||
|
switch (true) {
|
|||
|
case this._type === Type$1.PRESS && e.target.getAttribute('aria-pressed') === 'false':
|
|||
|
case this._type === Type$1.RELEASE && e.target.getAttribute('aria-pressed') === 'true':
|
|||
|
this.act();
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
setDismissType () {
|
|||
|
this._type = Type$1.DISMISS;
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
dispose () {
|
|||
|
if (this._validatedInput) {
|
|||
|
this._validatedInput.removeEventListener('keydown', this._handlingInputValidation);
|
|||
|
}
|
|||
|
|
|||
|
super.dispose();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const AccordionSelector = {
|
|||
|
ACCORDION: api.internals.ns.selector('accordion'),
|
|||
|
TITLE: api.internals.ns.selector('accordion__title')
|
|||
|
};
|
|||
|
|
|||
|
const ID$x = 'accordion';
|
|||
|
|
|||
|
class AccordionButtonActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(2);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'AccordionButtonActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.isMuted = true;
|
|||
|
}
|
|||
|
|
|||
|
get button () {
|
|||
|
return this.element.getInstance('CollapseButton');
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const firstText = this.getFirstText();
|
|||
|
if (firstText) return firstText;
|
|||
|
|
|||
|
return 'bouton d\'accordéon';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$x;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class AccordionActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(2);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'AccordionActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.setDiscloseType();
|
|||
|
this.wrapper = this.node.closest(AccordionSelector.ACCORDION);
|
|||
|
this.detectLevel(this.wrapper);
|
|||
|
this.register(`[aria-controls="${this.id}"]`, AccordionButtonActionee);
|
|||
|
this.listenDisclose();
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
if (this.wrapper) {
|
|||
|
const title = this.wrapper.querySelector(AccordionSelector.TITLE);
|
|||
|
if (title) {
|
|||
|
const text = this.getFirstText(title);
|
|||
|
if (text) return text;
|
|||
|
}
|
|||
|
}
|
|||
|
const instance = this.element.getInstance('Collapse');
|
|||
|
if (instance) {
|
|||
|
const button = instance.buttons.filter(button => button.isPrimary)[0];
|
|||
|
if (button) {
|
|||
|
const text = this.getFirstText(button);
|
|||
|
if (text) return text;
|
|||
|
}
|
|||
|
}
|
|||
|
return 'accordéon';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$x;
|
|||
|
}
|
|||
|
|
|||
|
dispose () {
|
|||
|
super.dispose();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateAccordion = () => {
|
|||
|
if (api.accordion) {
|
|||
|
api.internals.register(api.accordion.AccordionSelector.COLLAPSE, AccordionActionee);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
const AlertSelector = {
|
|||
|
ALERT: api.internals.ns.selector('alert'),
|
|||
|
TITLE: api.internals.ns.selector('alert__title')
|
|||
|
};
|
|||
|
|
|||
|
const ID$w = 'alert';
|
|||
|
|
|||
|
class AlertActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'AlertActionee';
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const alertTitle = this.node.querySelector(AlertSelector.TITLE);
|
|||
|
if (alertTitle) {
|
|||
|
const text = this.getFirstText(alertTitle);
|
|||
|
if (text) return text;
|
|||
|
}
|
|||
|
return 'alerte';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$w;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateAlert = () => {
|
|||
|
api.internals.register(AlertSelector.ALERT, AlertActionee);
|
|||
|
};
|
|||
|
|
|||
|
const BreadcrumbSelector = {
|
|||
|
LINK: `${api.internals.ns.selector('breadcrumb__link')}:not([aria-current])`,
|
|||
|
COLLAPSE: `${api.internals.ns.selector('breadcrumb')} ${api.internals.ns.selector('collapse')}`
|
|||
|
};
|
|||
|
|
|||
|
const ID$v = 'breadcrumb';
|
|||
|
|
|||
|
class BreadcrumbActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(2);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'BreadcrumbActionee';
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
return 'fil d\'ariane';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$v;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class BreadcrumbLinkActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(2);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'BreadcrumbLinkActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.detectInteractionType();
|
|||
|
this.listenActionClick();
|
|||
|
}
|
|||
|
|
|||
|
handleClick () {
|
|||
|
this.act();
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const firstText = this.getFirstText();
|
|||
|
if (firstText) return firstText;
|
|||
|
return 'lien fil d\'ariane';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateBreadcrumb = () => {
|
|||
|
if (api.breadcrumb) {
|
|||
|
api.internals.register(BreadcrumbSelector.COLLAPSE, BreadcrumbActionee);
|
|||
|
api.internals.register(BreadcrumbSelector.LINK, BreadcrumbLinkActionee);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
const ButtonSelector = {
|
|||
|
BUTTON: `${api.internals.ns.selector('btn')}:not(${api.internals.ns.selector('btn--close')})`
|
|||
|
};
|
|||
|
|
|||
|
const ID$u = 'button';
|
|||
|
|
|||
|
class ButtonActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
this._data = {};
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'ButtonActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.detectInteractionType();
|
|||
|
this.listenActionClick();
|
|||
|
}
|
|||
|
|
|||
|
handleClick () {
|
|||
|
this.ascend(ButtonEmission.CLICK);
|
|||
|
this.act();
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
if (this.node.tagName === 'input') {
|
|||
|
switch (this.node.type) {
|
|||
|
case 'button':
|
|||
|
case 'submit':
|
|||
|
if (this.hasAttribute('value')) return this.getAttribute('value');
|
|||
|
}
|
|||
|
}
|
|||
|
const firstText = this.getFirstText();
|
|||
|
if (firstText) return firstText;
|
|||
|
return 'bouton';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$u;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateButton = () => {
|
|||
|
api.internals.register(ButtonSelector.BUTTON, ButtonActionee);
|
|||
|
};
|
|||
|
|
|||
|
const CalloutSelector = {
|
|||
|
CALLOUT: api.internals.ns.selector('callout'),
|
|||
|
TITLE: api.internals.ns.selector('callout__title')
|
|||
|
};
|
|||
|
|
|||
|
const ID$t = 'callout';
|
|||
|
|
|||
|
class CalloutActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'CalloutActionee';
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const calloutTitle = this.node.querySelector(CalloutSelector.TITLE);
|
|||
|
if (calloutTitle) {
|
|||
|
const text = this.getFirstText(calloutTitle);
|
|||
|
if (text) return text;
|
|||
|
}
|
|||
|
|
|||
|
return 'mise en avant';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$t;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateCallout = () => {
|
|||
|
api.internals.register(CalloutSelector.CALLOUT, CalloutActionee);
|
|||
|
};
|
|||
|
|
|||
|
const CardSelector = {
|
|||
|
CARD: api.internals.ns.selector('card'),
|
|||
|
LINK: `${api.internals.ns.selector('card__title')} a, ${api.internals.ns.selector('card__title')} button`,
|
|||
|
TITLE: api.internals.ns.selector('card__title')
|
|||
|
};
|
|||
|
|
|||
|
const ID$s = 'card';
|
|||
|
|
|||
|
class CardActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'CardActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
const link = this.node.querySelector(CardSelector.LINK);
|
|||
|
if (link) {
|
|||
|
this.link = link;
|
|||
|
this.detectInteractionType(link);
|
|||
|
this.listenActionClick(link);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const cardTitle = this.node.querySelector(CardSelector.TITLE);
|
|||
|
if (cardTitle) {
|
|||
|
const text = this.getFirstText(cardTitle);
|
|||
|
if (text) return text;
|
|||
|
}
|
|||
|
|
|||
|
const heading = this.getHeadingLabel();
|
|||
|
if (heading) return heading;
|
|||
|
|
|||
|
return 'carte';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$s;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateCard = () => {
|
|||
|
api.internals.register(CardSelector.CARD, CardActionee);
|
|||
|
};
|
|||
|
|
|||
|
const CheckboxSelector = {
|
|||
|
INPUT: api.internals.ns.selector('checkbox-group [type="checkbox"]')
|
|||
|
};
|
|||
|
|
|||
|
const ID$r = 'checkbox';
|
|||
|
|
|||
|
class CheckboxActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
this._data = {};
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'CheckboxActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.detectCheckableType();
|
|||
|
this.listenCheckable();
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const label = this.node.parentNode.querySelector(api.internals.ns.selector('label'));
|
|||
|
if (label) {
|
|||
|
const text = this.getFirstText(label);
|
|||
|
if (text) return text;
|
|||
|
}
|
|||
|
return 'case à cocher';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$r;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateCheckbox = () => {
|
|||
|
api.internals.register(CheckboxSelector.INPUT, CheckboxActionee);
|
|||
|
};
|
|||
|
|
|||
|
const ConnectSelector = {
|
|||
|
CONNECT: api.internals.ns.selector('connect'),
|
|||
|
LINK: api.internals.ns.selector('connect + * a, connect + a')
|
|||
|
};
|
|||
|
|
|||
|
const ID$q = 'connect';
|
|||
|
|
|||
|
class ConnectActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'ConnectActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.detectInteractionType();
|
|||
|
this.listenActionClick();
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
if (this.node.classList.contains('fr-connect--plus')) return 'FranceConnect+';
|
|||
|
return 'FranceConnect';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$q;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class ConnectLinkActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(2);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'ConnectLinkActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.detectInteractionType();
|
|||
|
this.listenActionClick();
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
return this.getFirstText() || 'qu\'est-ce que FranceConnect ?';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$q;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateConnect = () => {
|
|||
|
api.internals.register(ConnectSelector.CONNECT, ConnectActionee);
|
|||
|
api.internals.register(ConnectSelector.LINK, ConnectLinkActionee);
|
|||
|
};
|
|||
|
|
|||
|
const ConsentSelector = {
|
|||
|
BANNER: api.internals.ns.selector('consent-banner')
|
|||
|
};
|
|||
|
|
|||
|
const ID$p = 'consent';
|
|||
|
|
|||
|
class ConsentActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'ConsentActionee';
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
return 'gestionnaire de consentement';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$p;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateConsent = () => {
|
|||
|
api.internals.register(ConsentSelector.BANNER, ConsentActionee);
|
|||
|
};
|
|||
|
|
|||
|
const DownloadSelector = {
|
|||
|
LINK: api.internals.ns.selector('download__link')
|
|||
|
};
|
|||
|
|
|||
|
const ID$o = 'download';
|
|||
|
|
|||
|
class DownloadActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'DownloadActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.detectInteractionType();
|
|||
|
this.listenActionClick();
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const text = this.getFirstText();
|
|||
|
if (text) return text;
|
|||
|
return 'téléchargement';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$o;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateDownload = () => {
|
|||
|
api.internals.register(DownloadSelector.LINK, DownloadActionee);
|
|||
|
};
|
|||
|
|
|||
|
const FollowSelector = {
|
|||
|
FOLLOW: api.internals.ns.selector('follow'),
|
|||
|
NEWSLETTER_INPUT_GROUP: api.internals.ns.selector('follow__newsletter') + ' ' + api.internals.ns.selector('input-group')
|
|||
|
};
|
|||
|
|
|||
|
const ID$n = 'follow';
|
|||
|
|
|||
|
class FollowActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(2);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'FollowActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this._inputGroup = this.querySelector(FollowSelector.NEWSLETTER_INPUT_GROUP);
|
|||
|
if (this._inputGroup) {
|
|||
|
this.listenInputValidation(this._inputGroup, Type$1.SUBSCRIBE);
|
|||
|
const input = this.element.getDescendantInstances('InputActionee', null, true)[0];
|
|||
|
if (input) input.isMuted = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
return 'lettre d\'information et réseaux sociaux';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$n;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateFollow = () => {
|
|||
|
api.internals.register(FollowSelector.FOLLOW, FollowActionee);
|
|||
|
};
|
|||
|
|
|||
|
const FooterSelector = {
|
|||
|
FOOTER: api.internals.ns.selector('footer'),
|
|||
|
FOOTER_LINKS: `${api.internals.ns.selector('footer')} a[href], ${api.internals.ns.selector('footer')} button`
|
|||
|
};
|
|||
|
|
|||
|
const ID$m = 'footer';
|
|||
|
|
|||
|
class FooterActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'FooterActionee';
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
return 'pied de page';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$m;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class FooterLinkActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(2);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'FooterLinkActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.detectInteractionType();
|
|||
|
this.listenActionClick();
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const label = this.getInteractionLabel();
|
|||
|
if (label) return label;
|
|||
|
|
|||
|
return 'lien pied de page';
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateFooter = () => {
|
|||
|
api.internals.register(FooterSelector.FOOTER, FooterActionee);
|
|||
|
api.internals.register(FooterSelector.FOOTER_LINKS, FooterLinkActionee);
|
|||
|
};
|
|||
|
|
|||
|
const ID$l = 'header';
|
|||
|
|
|||
|
class HeaderActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'HeaderActionee';
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
return 'en-tête';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$l;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class HeaderModalButtonActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(4);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'HeaderModalButtonActionee';
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class HeaderModalActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(0);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'HeaderModalActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
if (this.isBreakpoint(api.core.Breakpoints.LG)) {
|
|||
|
this.setPriority(4);
|
|||
|
this.register(`[aria-controls="${this.id}"]`, HeaderModalButtonActionee);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const HeaderSelector = {
|
|||
|
TOOLS_BUTTON: `${api.internals.ns.selector('header__tools-links')} ${api.internals.ns.selector('btns-group')} ${api.internals.ns.selector('btn')}`,
|
|||
|
MENU_BUTTON: `${api.internals.ns.selector('header__menu-links')} ${api.internals.ns.selector('btns-group')} ${api.internals.ns.selector('btn')}`
|
|||
|
};
|
|||
|
|
|||
|
class HeaderToolsButtonActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(4);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'HeaderToolsButtonActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
if (this.isBreakpoint(api.core.Breakpoints.LG)) this._priority = -1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class HeaderMenuButtonActionee extends ComponentActionee {
|
|||
|
static get instanceClassName () {
|
|||
|
return 'HeaderMenuButtonActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
if (this.isBreakpoint(api.core.Breakpoints.LG)) this.setPriority(4);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateHeader = () => {
|
|||
|
if (api.header) {
|
|||
|
api.internals.register(api.header.HeaderSelector.HEADER, HeaderActionee);
|
|||
|
api.internals.register(api.header.HeaderSelector.MODALS, HeaderModalActionee);
|
|||
|
api.internals.register(HeaderSelector.TOOLS_BUTTON, HeaderToolsButtonActionee);
|
|||
|
api.internals.register(HeaderSelector.MENU_BUTTON, HeaderMenuButtonActionee);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
const HighlightSelector = {
|
|||
|
HIGHLIGHT: api.internals.ns.selector('highlight')
|
|||
|
};
|
|||
|
|
|||
|
const ID$k = 'highlight';
|
|||
|
|
|||
|
class HighlightActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'HighlightActionee';
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
return 'mise en exergue';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$k;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateHighlight = () => {
|
|||
|
api.internals.register(HighlightSelector.HIGHLIGHT, HighlightActionee);
|
|||
|
};
|
|||
|
|
|||
|
const LinkSelector = {
|
|||
|
LINK: api.internals.ns.selector('link')
|
|||
|
};
|
|||
|
|
|||
|
const ID$j = 'link';
|
|||
|
|
|||
|
class LinkActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'LinkActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.detectInteractionType();
|
|||
|
this.listenActionClick();
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const firstText = this.getFirstText();
|
|||
|
if (firstText) return firstText;
|
|||
|
|
|||
|
return 'lien';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$j;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateLink = () => {
|
|||
|
api.internals.register(LinkSelector.LINK, LinkActionee);
|
|||
|
};
|
|||
|
|
|||
|
const InputSelector = {
|
|||
|
INPUT: api.internals.ns.selector('input-group')
|
|||
|
};
|
|||
|
|
|||
|
const ID$i = 'input';
|
|||
|
|
|||
|
class InputActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'InputActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this._input = this.querySelector(api.internals.ns.selector('input'));
|
|||
|
this._label = this.querySelector(api.internals.ns.selector('label'));
|
|||
|
this._inputWrap = this.querySelector(api.internals.ns.selector('input-wrap'));
|
|||
|
|
|||
|
if (this._inputWrap) this.listenInputValidation(this._inputWrap);
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
if (this._label) {
|
|||
|
const text = this.getFirstText(this._label);
|
|||
|
if (text) return text;
|
|||
|
}
|
|||
|
|
|||
|
return 'champ de saisie';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$i;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateInput = () => {
|
|||
|
api.internals.register(InputSelector.INPUT, InputActionee);
|
|||
|
};
|
|||
|
|
|||
|
const ModalSelector = {
|
|||
|
TITLE: api.internals.ns.selector('modal__title')
|
|||
|
};
|
|||
|
|
|||
|
const ID$h = 'modal';
|
|||
|
|
|||
|
class ModalActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(2);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'ModalActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.setDiscloseType();
|
|||
|
this.detectLevel();
|
|||
|
this.listenDisclose();
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const title = this.node.querySelector(ModalSelector.TITLE);
|
|||
|
|
|||
|
if (title) {
|
|||
|
const text = this.getFirstText(title);
|
|||
|
if (text) return text;
|
|||
|
}
|
|||
|
|
|||
|
const heading = this.getHeadingLabel(2);
|
|||
|
if (heading) return heading;
|
|||
|
|
|||
|
const instance = this.element.getInstance('Modal');
|
|||
|
if (instance) {
|
|||
|
const button = instance.buttons.filter(button => button.isPrimary)[0];
|
|||
|
if (button) {
|
|||
|
const text = this.getFirstText(button.node);
|
|||
|
if (text) return text;
|
|||
|
}
|
|||
|
}
|
|||
|
return 'modale';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$h;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateModal = () => {
|
|||
|
if (api.modal) {
|
|||
|
api.internals.register(api.modal.ModalSelector.MODAL, ModalActionee);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
class NavigationActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'NavigationActionee';
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
return 'navigation';
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const NavigationSelector = {
|
|||
|
LINK: api.internals.ns.selector('nav__link'),
|
|||
|
BUTTON: api.internals.ns.selector('nav__btn')
|
|||
|
};
|
|||
|
|
|||
|
const ID$g = 'navigation';
|
|||
|
|
|||
|
class NavigationLinkActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(2);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'NavigationLinkActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.detectInteractionType();
|
|||
|
this.listenActionClick();
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const firstText = this.getFirstText();
|
|||
|
if (firstText) return firstText;
|
|||
|
|
|||
|
return 'lien de navigation';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$g;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class NavigationSectionActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(2);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'NavigationSectionActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this._wrapper = this.node.closest(api.navigation.NavigationSelector.ITEM);
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
if (this._wrapper) {
|
|||
|
const button = this._wrapper.querySelector(NavigationSelector.BUTTON);
|
|||
|
if (button) {
|
|||
|
const text = this.getFirstText(button);
|
|||
|
if (text) return text;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const instance = this.element.getInstance('Collapse');
|
|||
|
if (instance) {
|
|||
|
const button = instance.buttons.filter(button => button.isPrimary)[0];
|
|||
|
if (button) {
|
|||
|
const text = this.getFirstText(button);
|
|||
|
if (text) return text;
|
|||
|
}
|
|||
|
}
|
|||
|
return 'section de navigation';
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateNavigation = () => {
|
|||
|
if (api.navigation) {
|
|||
|
api.internals.register(api.navigation.NavigationSelector.NAVIGATION, NavigationActionee);
|
|||
|
api.internals.register(NavigationSelector.LINK, NavigationLinkActionee);
|
|||
|
api.internals.register(api.navigation.NavigationSelector.COLLAPSE, NavigationSectionActionee);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
const PaginationSelector = {
|
|||
|
PAGINATION: api.internals.ns.selector('pagination'),
|
|||
|
LINK: api.internals.ns.selector('pagination__link'),
|
|||
|
NEXT_LINK: api.internals.ns.selector('pagination__link--next'),
|
|||
|
LAST_LINK: api.internals.ns.selector('pagination__link--last'),
|
|||
|
ANALYTICS_TOTAL: api.internals.ns.attr('analytics-page-total'),
|
|||
|
CURRENT: '[aria-current="page"]'
|
|||
|
};
|
|||
|
|
|||
|
const ID$f = 'pagination';
|
|||
|
|
|||
|
class PaginationActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'PaginationActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.setPagination();
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
return 'pagination';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$f;
|
|||
|
}
|
|||
|
|
|||
|
setPagination () {
|
|||
|
const currentLink = this.node.querySelector(PaginationSelector.CURRENT);
|
|||
|
if (!currentLink) return;
|
|||
|
const currentLabel = this.getFirstText(currentLink);
|
|||
|
if (!currentLabel) return;
|
|||
|
const current = this.getInt(currentLabel);
|
|||
|
if (isNaN(current)) return;
|
|||
|
api.analytics.page.current = current;
|
|||
|
|
|||
|
const total = this.getTotalPage();
|
|||
|
if (isNaN(total)) return;
|
|||
|
api.analytics.page.total = total;
|
|||
|
}
|
|||
|
|
|||
|
getTotalPage () {
|
|||
|
const attr = parseInt(this.node.getAttribute(PaginationSelector.ANALYTICS_TOTAL));
|
|||
|
if (!isNaN(attr)) return attr;
|
|||
|
const links = this.node.querySelectorAll(`${PaginationSelector.LINK}:not(${PaginationSelector.NEXT_LINK}):not(${PaginationSelector.LAST_LINK})`);
|
|||
|
if (!links) return null;
|
|||
|
const totalLabel = this.getFirstText(links[links.length - 1]);
|
|||
|
if (!totalLabel) return null;
|
|||
|
return this.getInt(totalLabel);
|
|||
|
}
|
|||
|
|
|||
|
getInt (val) {
|
|||
|
const ints = val.match(/\d+/);
|
|||
|
if (!ints || ints.length === 0) return null;
|
|||
|
return parseInt(ints[0]);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class PaginationLinkActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'PaginationLinkActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.detectInteractionType();
|
|||
|
this.listenActionClick();
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const firstText = this.getFirstText();
|
|||
|
if (firstText) return firstText;
|
|||
|
return 'lien pagination';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integratePagination = () => {
|
|||
|
api.internals.register(PaginationSelector.PAGINATION, PaginationActionee);
|
|||
|
api.internals.register(PaginationSelector.LINK, PaginationLinkActionee);
|
|||
|
};
|
|||
|
|
|||
|
const QuoteSelector = {
|
|||
|
QUOTE: api.internals.ns.selector('quote')
|
|||
|
};
|
|||
|
|
|||
|
const ID$e = 'quote';
|
|||
|
|
|||
|
class QuoteActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'QuoteActionee';
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const blockquote = this.node.querySelector('blockquote');
|
|||
|
if (blockquote) {
|
|||
|
const firstText = this.getFirstText(blockquote);
|
|||
|
if (firstText) {
|
|||
|
return firstText;
|
|||
|
}
|
|||
|
}
|
|||
|
return 'citation';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$e;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateQuote = () => {
|
|||
|
api.internals.register(QuoteSelector.QUOTE, QuoteActionee);
|
|||
|
};
|
|||
|
|
|||
|
const RadioSelector = {
|
|||
|
INPUT: api.internals.ns.selector('radio-group [type="radio"]')
|
|||
|
};
|
|||
|
|
|||
|
const FormSelector = {
|
|||
|
LABEL: api.internals.ns.selector('label'),
|
|||
|
FIELDSET: api.internals.ns.selector('fieldset'),
|
|||
|
LEGEND: api.internals.ns.selector('fieldset__legend')
|
|||
|
};
|
|||
|
|
|||
|
const ID$d = 'radio';
|
|||
|
|
|||
|
class RadioActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
this._data = {};
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'RadioActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.setCheckType();
|
|||
|
this.listenCheckable();
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const parts = [];
|
|||
|
const fieldset = this.node.closest(FormSelector.FIELDSET);
|
|||
|
if (fieldset) {
|
|||
|
const legend = fieldset.querySelector(FormSelector.LEGEND);
|
|||
|
if (legend) {
|
|||
|
const firstTextLegend = this.getFirstText(legend);
|
|||
|
if (firstTextLegend) parts.push(firstTextLegend);
|
|||
|
}
|
|||
|
}
|
|||
|
const label = this.node.parentNode.querySelector(api.internals.ns.selector('label'));
|
|||
|
if (label) {
|
|||
|
const firstTextLabel = this.getFirstText(label);
|
|||
|
if (firstTextLabel) parts.push(firstTextLabel);
|
|||
|
}
|
|||
|
return parts.join(' › ');
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$d;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateRadio = () => {
|
|||
|
api.internals.register(RadioSelector.INPUT, RadioActionee);
|
|||
|
};
|
|||
|
|
|||
|
const SearchSelector = {
|
|||
|
SEARCH_BAR: api.internals.ns.selector('search-bar')
|
|||
|
};
|
|||
|
|
|||
|
const ID$c = 'search';
|
|||
|
|
|||
|
class SearchActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(2);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'SearchActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.listenInputValidation(this.node, Type$1.SEARCH, true);
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
return 'barre de recherche';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$c;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateSearch = () => {
|
|||
|
api.internals.register(SearchSelector.SEARCH_BAR, SearchActionee);
|
|||
|
};
|
|||
|
|
|||
|
const SelectSelector = {
|
|||
|
SELECT: api.internals.ns.selector('select')
|
|||
|
};
|
|||
|
|
|||
|
const ID$b = 'select';
|
|||
|
|
|||
|
class SelectActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
this._data = {};
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'SelectActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.setChangeType();
|
|||
|
this.listenChange();
|
|||
|
}
|
|||
|
|
|||
|
setChangeValue (e) {
|
|||
|
if (!e.target || !e.target.selectedOptions) return;
|
|||
|
const value = Array.from(e.target.selectedOptions).map(option => option.text).join(' - ');
|
|||
|
if (value) this.value = value;
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const label = this.node.parentNode.querySelector(api.internals.ns.selector('label'));
|
|||
|
if (label) {
|
|||
|
const firstText = this.getFirstText(label);
|
|||
|
if (firstText) return firstText;
|
|||
|
}
|
|||
|
|
|||
|
return 'liste déroulante';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$b;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateSelect = () => {
|
|||
|
api.internals.register(SelectSelector.SELECT, SelectActionee);
|
|||
|
};
|
|||
|
|
|||
|
const ShareSelector = {
|
|||
|
SHARE: api.internals.ns.selector('share'),
|
|||
|
TITLE: api.internals.ns.selector('share__title')
|
|||
|
};
|
|||
|
|
|||
|
const ID$a = 'share';
|
|||
|
|
|||
|
class ShareActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'ShareActionee';
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const title = this.querySelector(ShareSelector.TITLE);
|
|||
|
if (title) {
|
|||
|
const firstText = this.getFirstText(title);
|
|||
|
if (firstText) return firstText;
|
|||
|
}
|
|||
|
return 'boutons de partage';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$a;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateShare = () => {
|
|||
|
api.internals.register(ShareSelector.SHARE, ShareActionee);
|
|||
|
};
|
|||
|
|
|||
|
const SidemenuSelector = {
|
|||
|
SIDEMENU: api.internals.ns.selector('sidemenu'),
|
|||
|
ITEM: api.internals.ns.selector('sidemenu__item'),
|
|||
|
LINK: api.internals.ns.selector('sidemenu__link'),
|
|||
|
BUTTON: api.internals.ns.selector('sidemenu__btn'),
|
|||
|
TITLE: api.internals.ns.selector('sidemenu__title')
|
|||
|
};
|
|||
|
|
|||
|
class SidemenuActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'SidemenuActionee';
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const sidemenu = this.node.closest(SidemenuSelector.SIDEMENU);
|
|||
|
if (sidemenu) {
|
|||
|
const title = sidemenu.querySelector(SidemenuSelector.TITLE);
|
|||
|
if (title) {
|
|||
|
const firstText = this.getFirstText(title);
|
|||
|
if (firstText) return firstText;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return 'menu Latéral';
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const ID$9 = 'sidemenu';
|
|||
|
|
|||
|
class SidemenuLinkActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(2);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'SidemenuLinkActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.detectInteractionType();
|
|||
|
this.listenActionClick();
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const firstText = this.getFirstText();
|
|||
|
if (firstText) return firstText;
|
|||
|
|
|||
|
return 'lien menu latéral';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$9;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class SidemenuSectionActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(2);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'SidemenuSectionActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this._wrapper = this.node.closest(SidemenuSelector.ITEM);
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
if (this._wrapper) {
|
|||
|
const button = this._wrapper.querySelector(SidemenuSelector.BUTTON);
|
|||
|
if (button) {
|
|||
|
const firstText = this.getFirstText(button);
|
|||
|
if (firstText) return firstText;
|
|||
|
}
|
|||
|
}
|
|||
|
const instance = this.element.getInstance('Collapse');
|
|||
|
if (instance) {
|
|||
|
const button = instance.buttons.filter(button => button.isPrimary)[0];
|
|||
|
if (button) {
|
|||
|
const firstTextBtn = this.getFirstText(button);
|
|||
|
if (firstTextBtn) return firstTextBtn;
|
|||
|
}
|
|||
|
}
|
|||
|
return 'section menu latéral';
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateSidemenu = () => {
|
|||
|
if (api.sidemenu) {
|
|||
|
api.internals.register(SidemenuSelector.SIDEMENU, SidemenuActionee);
|
|||
|
api.internals.register(SidemenuSelector.LINK, SidemenuLinkActionee);
|
|||
|
api.internals.register(api.sidemenu.SidemenuSelector.COLLAPSE, SidemenuSectionActionee);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
const SummarySelector = {
|
|||
|
SUMMARY: api.internals.ns.selector('summary'),
|
|||
|
LINK: api.internals.ns.selector('summary__link'),
|
|||
|
TITLE: api.internals.ns.selector('summary__title'),
|
|||
|
ITEM: `${api.internals.ns.selector('summary')} li`
|
|||
|
};
|
|||
|
|
|||
|
class SummaryActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'SummaryActionee';
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const title = this.node.querySelector(SummarySelector.TITLE);
|
|||
|
if (title) {
|
|||
|
const firstText = this.getFirstText(title);
|
|||
|
if (firstText) return firstText;
|
|||
|
}
|
|||
|
return 'sommaire';
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const ID$8 = 'summary';
|
|||
|
|
|||
|
class SummaryLinkActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'SummaryLinkActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.detectInteractionType();
|
|||
|
this.listenActionClick();
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const firstText = this.getFirstText();
|
|||
|
if (firstText) return firstText;
|
|||
|
return 'lien sommaire';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$8;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class SummarySectionActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(2);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'SummarySectionActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this._link = this.querySelector(SummarySelector.LINK);
|
|||
|
}
|
|||
|
|
|||
|
validate (target) {
|
|||
|
return this._link !== target;
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
if (!this._link) return null;
|
|||
|
const firstText = this.getFirstText(this._link);
|
|||
|
if (firstText) return firstText;
|
|||
|
return 'section sommaire';
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateSummary = () => {
|
|||
|
api.internals.register(SummarySelector.SUMMARY, SummaryActionee);
|
|||
|
api.internals.register(SummarySelector.LINK, SummaryLinkActionee);
|
|||
|
api.internals.register(SummarySelector.ITEM, SummarySectionActionee);
|
|||
|
};
|
|||
|
|
|||
|
const ID$7 = 'tab';
|
|||
|
|
|||
|
class TabButtonActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(2);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'TabButtonActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.isMuted = true;
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const text = this.getFirstText();
|
|||
|
if (text) return text;
|
|||
|
return 'bouton onglet';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$7;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class TabActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(2);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'TabActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.setDiscloseType();
|
|||
|
this.register(`[aria-controls="${this.id}"]`, TabButtonActionee);
|
|||
|
this._instance = this.element.getInstance('TabPanel');
|
|||
|
this.listenDisclose();
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const tabs = this.node.closest(api.tab.TabSelector.GROUP);
|
|||
|
if (tabs) {
|
|||
|
const tab = tabs.querySelector(`${api.tab.TabSelector.LIST} [aria-controls="${this.id}"]${api.tab.TabSelector.TAB}`);
|
|||
|
if (tab) {
|
|||
|
const firstTextTab = this.getFirstText(tab);
|
|||
|
if (firstTextTab) return firstTextTab;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const button = this._instance.buttons.filter(button => button.isPrimary)[0];
|
|||
|
if (button) {
|
|||
|
const firstTextBtn = this.getFirstText(button);
|
|||
|
if (firstTextBtn) return firstTextBtn;
|
|||
|
}
|
|||
|
return 'onglet';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$7;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateTab = () => {
|
|||
|
if (api.tab) {
|
|||
|
api.internals.register(api.tab.TabSelector.PANEL, TabActionee);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
const TableSelector = {
|
|||
|
TABLE: api.internals.ns.selector('table')
|
|||
|
};
|
|||
|
|
|||
|
const ID$6 = 'table';
|
|||
|
|
|||
|
class TableActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'TableActionee';
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const caption = this.node.querySelector('caption');
|
|||
|
if (caption) {
|
|||
|
const firstText = this.getFirstText(caption);
|
|||
|
if (firstText) return firstText;
|
|||
|
}
|
|||
|
return 'tableau';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$6;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateTable = () => {
|
|||
|
api.internals.register(TableSelector.TABLE, TableActionee);
|
|||
|
};
|
|||
|
|
|||
|
const TagSelector = {
|
|||
|
TAG: api.internals.ns.selector('tag'),
|
|||
|
PRESSABLE: '[aria-pressed]',
|
|||
|
DISMISSIBLE: `${api.internals.ns.selector('tag--dismiss', '')}`
|
|||
|
};
|
|||
|
|
|||
|
const ID$5 = 'tag';
|
|||
|
|
|||
|
class TagActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(2);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'TagActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
switch (true) {
|
|||
|
case this.detectPressableType():
|
|||
|
this.listenPressable();
|
|||
|
break;
|
|||
|
|
|||
|
case this.isInteractive && this.node.classList.contains(TagSelector.DISMISSIBLE):
|
|||
|
this.setDismissType();
|
|||
|
this.listenActionClick();
|
|||
|
break;
|
|||
|
|
|||
|
case this.isInteractive:
|
|||
|
this.detectInteractionType();
|
|||
|
this.listenActionClick();
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const firstText = this.getFirstText();
|
|||
|
if (firstText) return firstText;
|
|||
|
|
|||
|
return 'tag';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$5;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateTag = () => {
|
|||
|
api.internals.register(TagSelector.TAG, TagActionee);
|
|||
|
};
|
|||
|
|
|||
|
const TileSelector = {
|
|||
|
TILE: api.internals.ns.selector('tile'),
|
|||
|
LINK: `${api.internals.ns.selector('tile__title')} a, ${api.internals.ns.selector('tile__title')} button`,
|
|||
|
TITLE: api.internals.ns.selector('tile__title')
|
|||
|
};
|
|||
|
|
|||
|
const ID$4 = 'tile';
|
|||
|
|
|||
|
class TileActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'TileActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
const link = this.node.querySelector(TileSelector.LINK);
|
|||
|
if (link) {
|
|||
|
this.link = link;
|
|||
|
this.detectInteractionType(link);
|
|||
|
this.listenActionClick(link);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const tileTitle = this.node.querySelector(TileSelector.TITLE);
|
|||
|
if (tileTitle) return this.getFirstText(tileTitle);
|
|||
|
|
|||
|
const heading = this.getHeadingLabel();
|
|||
|
if (heading) return heading;
|
|||
|
|
|||
|
return 'tuile';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$4;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateTile = () => {
|
|||
|
api.internals.register(TileSelector.TILE, TileActionee);
|
|||
|
};
|
|||
|
|
|||
|
const ToggleSelector = {
|
|||
|
INPUT: api.internals.ns.selector('toggle [type="checkbox"]')
|
|||
|
};
|
|||
|
|
|||
|
const ID$3 = 'toggle';
|
|||
|
|
|||
|
class ToggleActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
this._data = {};
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'ToggleActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.detectCheckableType();
|
|||
|
this.listenCheckable();
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const label = this.node.parentNode.querySelector(api.internals.ns.selector('toggle__label'));
|
|||
|
if (label) {
|
|||
|
const firstText = this.getFirstText(label);
|
|||
|
if (firstText) return firstText;
|
|||
|
}
|
|||
|
|
|||
|
return 'interrupteur';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$3;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateToggle = () => {
|
|||
|
api.internals.register(ToggleSelector.INPUT, ToggleActionee);
|
|||
|
};
|
|||
|
|
|||
|
const TRANSCRIPTION = api.internals.ns.selector('transcription');
|
|||
|
const COLLAPSE$1 = api.internals.ns.selector('collapse');
|
|||
|
|
|||
|
const TranscriptionSelector = {
|
|||
|
TRANSCRIPTION: TRANSCRIPTION,
|
|||
|
COLLAPSE: `${TRANSCRIPTION} > ${COLLAPSE$1}, ${TRANSCRIPTION} > *:not(${TRANSCRIPTION}):not(${COLLAPSE$1}) > ${COLLAPSE$1}, ${TRANSCRIPTION} > *:not(${TRANSCRIPTION}):not(${COLLAPSE$1}) > *:not(${TRANSCRIPTION}):not(${COLLAPSE$1}) > ${COLLAPSE$1}`,
|
|||
|
COLLAPSE_LEGACY: `${TRANSCRIPTION} ${COLLAPSE$1}`,
|
|||
|
TITLE: `${TRANSCRIPTION}__title`
|
|||
|
};
|
|||
|
|
|||
|
const ID$2 = 'transcription';
|
|||
|
|
|||
|
class TranscriptionButtonActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(2);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'TranscriptionButtonActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.isMuted = true;
|
|||
|
}
|
|||
|
|
|||
|
get button () {
|
|||
|
return this.element.getInstance('CollapseButton');
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const text = this.getFirstText();
|
|||
|
if (text) return text;
|
|||
|
return 'bouton transcription';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$2;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class TranscriptionActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(2);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'TranscriptionActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.setDiscloseType();
|
|||
|
this.wrapper = this.node.closest(TranscriptionSelector.ACCORDION);
|
|||
|
this.detectLevel(this.wrapper);
|
|||
|
this.register(`[aria-controls="${this.id}"]`, TranscriptionButtonActionee);
|
|||
|
this.listenDisclose();
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
if (this.wrapper) {
|
|||
|
const title = this.wrapper.querySelector(TranscriptionSelector.TITLE);
|
|||
|
if (title) {
|
|||
|
const firstTextTitle = this.getFirstText(title);
|
|||
|
if (firstTextTitle) return firstTextTitle;
|
|||
|
}
|
|||
|
}
|
|||
|
const instance = this.element.getInstance('Collapse');
|
|||
|
if (instance) {
|
|||
|
const button = instance.buttons.filter(button => button.isPrimary)[0];
|
|||
|
if (button) {
|
|||
|
const firstTextBtn = this.getFirstText(button);
|
|||
|
if (firstTextBtn) return firstTextBtn;
|
|||
|
}
|
|||
|
}
|
|||
|
return 'transcription';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$2;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateTranscription = () => {
|
|||
|
api.internals.register(TranscriptionSelector.COLLAPSE, TranscriptionActionee);
|
|||
|
};
|
|||
|
|
|||
|
const TRANSLATE = api.internals.ns.selector('translate');
|
|||
|
const COLLAPSE = api.internals.ns.selector('collapse');
|
|||
|
|
|||
|
const TranslateSelector = {
|
|||
|
BUTTON: `${TRANSLATE}__btn`,
|
|||
|
COLLAPSE: `${TRANSLATE} > ${COLLAPSE}, ${TRANSLATE} > *:not(${TRANSLATE}):not(${COLLAPSE}) > ${COLLAPSE}, ${TRANSLATE} > *:not(${TRANSLATE}):not(${COLLAPSE}) > *:not(${TRANSLATE}):not(${COLLAPSE}) > ${COLLAPSE}`,
|
|||
|
COLLAPSE_LEGACY: `${TRANSLATE} ${COLLAPSE}`
|
|||
|
};
|
|||
|
|
|||
|
const ID$1 = 'translate';
|
|||
|
|
|||
|
class TranslateActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(2);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'TranslateActionee';
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const button = this.node.querySelector(TranslateSelector.BUTTON);
|
|||
|
if (button) {
|
|||
|
const title = button.getAttribute('title');
|
|||
|
if (title) return title;
|
|||
|
}
|
|||
|
|
|||
|
return 'sélecteur de langue';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class TranslateButtonActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(2);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'TranslateButtonActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.isMuted = true;
|
|||
|
}
|
|||
|
|
|||
|
get button () {
|
|||
|
return this.element.getInstance('CollapseButton');
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
const label = this.getInteractionLabel();
|
|||
|
if (label) return label;
|
|||
|
return 'bouton sélecteur de langue';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID$1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateTranslate = () => {
|
|||
|
api.internals.register(TranslateSelector.COLLAPSE, TranslateActionee);
|
|||
|
api.internals.register(TranslateSelector.BUTTON, TranslateButtonActionee);
|
|||
|
};
|
|||
|
|
|||
|
const UploadSelector = {
|
|||
|
UPLOAD: api.internals.ns.selector('upload')
|
|||
|
};
|
|||
|
|
|||
|
const ID = 'upload';
|
|||
|
|
|||
|
class UploadActionee extends ComponentActionee {
|
|||
|
constructor () {
|
|||
|
super(1);
|
|||
|
}
|
|||
|
|
|||
|
static get instanceClassName () {
|
|||
|
return 'UploadActionee';
|
|||
|
}
|
|||
|
|
|||
|
init () {
|
|||
|
this.setChangeType();
|
|||
|
this._label = this.node.parentNode.querySelector(api.internals.ns.selector('label'));
|
|||
|
this.listenChange();
|
|||
|
}
|
|||
|
|
|||
|
setChangeValue (e) {
|
|||
|
if (!e.target || !e.target.files) return;
|
|||
|
const value = Array.from(e.target.files).map(file => /(?:\.([^.]+))?$/.exec(file.name)[1]).filter((name, index, array) => array.indexOf(name) === index).join(' - ');
|
|||
|
if (value) this.value = value;
|
|||
|
}
|
|||
|
|
|||
|
get label () {
|
|||
|
if (this._label) {
|
|||
|
const text = this.getFirstText(this._label);
|
|||
|
if (text) return text;
|
|||
|
}
|
|||
|
|
|||
|
return 'ajout de fichier';
|
|||
|
}
|
|||
|
|
|||
|
get component () {
|
|||
|
return ID;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const integrateUpload = () => {
|
|||
|
api.internals.register(UploadSelector.UPLOAD, UploadActionee);
|
|||
|
};
|
|||
|
|
|||
|
const integrateComponents = () => {
|
|||
|
integrateAccordion();
|
|||
|
integrateBreadcrumb();
|
|||
|
integrateAlert();
|
|||
|
// integrateBadge();
|
|||
|
integrateButton();
|
|||
|
integrateCallout();
|
|||
|
integrateConnect();
|
|||
|
integrateConsent();
|
|||
|
// integrateContent();
|
|||
|
integrateCard();
|
|||
|
integrateInput();
|
|||
|
integrateCheckbox();
|
|||
|
integrateDownload();
|
|||
|
integrateFooter();
|
|||
|
integrateFollow();
|
|||
|
integrateHeader();
|
|||
|
integrateHighlight();
|
|||
|
integrateLink();
|
|||
|
integrateModal();
|
|||
|
integrateNavigation();
|
|||
|
// integrateNotice();
|
|||
|
integratePagination();
|
|||
|
integrateQuote();
|
|||
|
integrateRadio();
|
|||
|
integrateSearch();
|
|||
|
integrateSelect();
|
|||
|
integrateShare();
|
|||
|
integrateSidemenu();
|
|||
|
// integrateStepper();
|
|||
|
integrateSummary();
|
|||
|
integrateTab();
|
|||
|
integrateTable();
|
|||
|
integrateTag();
|
|||
|
integrateTile();
|
|||
|
integrateToggle();
|
|||
|
// integrateTooltip();
|
|||
|
integrateTranscription();
|
|||
|
integrateTranslate();
|
|||
|
integrateUpload();
|
|||
|
};
|
|||
|
|
|||
|
// import './core/core';
|
|||
|
|
|||
|
const integration = () => {
|
|||
|
integrateAttributes();
|
|||
|
integrateComponents();
|
|||
|
};
|
|||
|
|
|||
|
api.analytics.readiness.then(() => integration(), () => {});
|
|||
|
//# sourceMappingURL=analytics.module.js.map
|