{"version":3,"file":"app-e1e75006.981ca8458d389c9a476a.bundle.js","mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AACA;AACA;AACA;;;;;;;;;;;;;;;ACHA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;AClBA;;AAEA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;AC7BA;;AAEA;AACA;AACA;;AAEA;AACA;AAGA;AACA;;AAGA;AACA;AACA;AACA;AAEA;AADA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;AC/CA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AAGA;AAFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAIA;AAFA;AAAA;AAAA;AACA;AAAA;AAEA;AAEA;AACA;AACA;AACA;;;;;;;;;;;;;;;AClCA;;;;;;;;;;;;;;;;;;ACAA;;AAEA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAKA;AAJA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;AChDA;AACA;AACA;AACA;;;;;;;;;;;;;;;;ACHA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;AC3BA;;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;;;;;;;;;;;;;;;ACPA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;;;;;;ACpBA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;AC/BA;AACA;AACA;;;;;;;;;;;;;;;ACFA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AAKA;AAJA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAGA;;;;;;;;;;;;;;ACzBA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AAGA;AAFA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;;;;;;;;;;;;;;;ACpBA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAQA;AAPA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;ACzDA;;AAEA;AACA;AAGA;;AAIA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;;;;;;;;;;;;;;;ACzBA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAIA;AAHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAGA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;ACzDA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAIA;AAHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAGA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;ACrEA;;;;;;;;;;;;;;;;;ACAA;;AAEA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAGA;AAFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;AC3CA;AACA;AACA;;;;;;;;;;;;;;;ACFA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;AChCA;;AAEA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;ACNA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;ACpBA;AACA;;;;;;;;;;;;;;;;;ACDA;;AAEA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAKA;AAJA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;AChCA;;AAEA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AAEA;AADA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;AC1BA;AACA;AACA;;;;;;;;;;;;;;;;ACFA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;ACxBA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;;;;;;;;;;;;;;;AC1BA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;ACrBA;AACA;AACA;;;;;;;;;;;;;;;;ACFA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;AC/BA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;;;;;ACdA;;AAEA;AACA;AACA;AACA;AACA;AAGA;;;;;;;;;;;;;;;;;ACTA;AACA;;;;;;;;;;;;;;;;;ACDA;;AAEA;AACA;AACA;;AAGA;AACA;AACA;AACA;AAKA;AAJA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AASA;AARA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;;;;;;;;;;;;;AC7EA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AADA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;;;;;;;;;;;;;;;ACZA;;;;;;;;;;;;;;;ACAA;;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;;;;;;;;;;;;;;ACjBA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;ACJA;;AAEA;AACA;AACA;;AAGA;AACA;AACA;AAIA;AAHA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;AChCA;;AAEA;AACA;;AAGA;AACA;AACA;AAIA;AAHA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;ACnBA;;AAEA;AACA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AAGA;AAFA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;AClBA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;ACrBA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;;;;;;ACZA;;;;;;;;;;;;;;;;ACAA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AAGA;AAFA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AAEA;AACA;AAAA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;AChEA;AACA;;;;;;;;;;;;;;;;ACDA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AAHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;;;;;;;;;;;;;;;;AC3DA;;AAEA;AACA;AACA;;AAGA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;ACrEA;AACA;AACA;;;;;;;;;;;;;;;;ACFA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAWA;;;;;;;;;;;;;;AC1BA;;AAEA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;ACPA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAWA;;;;;;;;;;;;;;;;;ACxBA;AACA;;;;;;;;;;;;;;;;;ACDA;;AAEA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAMA;AALA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;ACpCA;;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAEA;AAAA;AACA;AACA;;;;;;;;;;;;;;;AC7BA;;;;;;;;;;;;;;;ACAA;;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACRA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;ACPA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;ACVA;;AAEA;AACA;AACA;;AAGA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;AClCA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;AClBA;;AAEA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;ACvBA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;ACtBA;;AAEA;AACA;AACA;;AAGA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;;;;;;;;;;;;;;AC1BA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;AC7DA;;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;ACbA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;ACLA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;AChBA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAIA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAKA;AAJA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;AC9EA;;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;ACVA;;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;ACZA;;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;ACXA;;AAEA;AACA;AACA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAGA;AAFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAIA;;;;;;;;;;;;;;;;;;;ACnCA;AACA;AACA;;;;;;;;;;;;;;;ACFA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAUA;;;;;;;;;;;;;;;;;ACjBA;;AAEA;AACA;AACA;;AAIA;AACA;;AAGA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;;;;;;;;;;;;;;AC1BA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;ACzBA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;ACJA;;AAEA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAMA;AALA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;;;;;;;;;;;;;;ACnCA;;AAEA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;;;;;;;;;;;;;;;ACnCA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AAGA;AAFA;AACA;AACA;AACA;AACA;AAGA;;;;;;;;;;;;;;;ACjBA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;;;;;ACZA;;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAIA;AACA;AAGA;;;;;;;;;;;;;;;;;;;;;;;ACdA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;ACJA;;AAEA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AAEA;AADA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;ACpGA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AAGA;AAFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;ACnDA;;AAEA;AACA;AACA;;AAGA;AACA;AACA;AACA;AAGA;AAFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;ACzLA;;AAEA;AACA;AACA;AACA;AAEA;AADA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;ACpBA;;AAEA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAGA;AAFA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;AC5GA;AACA;AAGA;AAGA;AAGA;AAGA;;;;;;;;;;;;;;;;;;ACbA;;AAEA;AACA;AACA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AAEA;AADA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;;;;;;;;;;;;;;;;ACnCA;;AAEA;AACA;AACA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AAEA;AADA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;;;;;;;;;;;;;;;;;;ACnCA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AAGA;AAFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;ACvCA;;AAEA;AACA;AAGA;AAGA;AACA;;AAEA;AACA;;AAKA;AACA;AACA;AACA;AAGA;AAFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;AC7CA;;AAEA;AACA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AAGA;AAFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;;;;;;;;;;;;;;;AC1BA;;AAEA;AACA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AAGA;AAFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC1BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;ACXA;;AAEA;AACA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;;;;;;;;;;;;;;;;ACnBA;;AAEA;AACA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;;;;;;;;;;;;;;ACnBA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;;;;;;ACZA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAKA;AAJA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;;;;;;;;;;;;;;;ACjCA;;AAEA;AACA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;;;;;;;;;;;;;;;ACnBA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;;;;;;;;;;;;;;ACXA;;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;ACjBA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;ACXA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;ACXA;;AAEA;AACA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAOA;AACA;AACA;AAGA;;;;;;;;;;;;;;;;ACjCA;;AAEA;AACA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;;;;;;;;;;;;;;;;;;;;;;;ACnBA;AACA;AAGA;AAGA;AAGA;;;;;;;;;;;;;;;;;;ACVA;;AAEA;AACA;AACA;;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AALA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;ACxCA;;AAEA;AACA;AAIA;;AAMA;AACA;AACA;AACA;AACA;AAOA;AANA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;ACvDA;;AAEA;AACA;AACA;;AAMA;AACA;AACA;AACA;AACA;AAKA;AAJA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;ACpCA;;AAEA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;ACNA;;AAEA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;ACNA;AACA;;;;;;;;;;;;;;;;;;ACDA;;AAEA;AACA;AACA;;AAEA;AACA;;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;ACrDA;;AAEA;AACA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;ACjBA;;;;;;;;;;;;;;;ACAA;;AAEA;AACA;AACA;AACA;AAEA;AADA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;ACtBA;;AAEA;AACA;;AAEA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;;;;;;;;;;;ACZA","sources":["webpack://latinera/./sources/utilities/activity.js","webpack://latinera/./sources/utilities/activity/compute-activity-type.js","webpack://latinera/./sources/utilities/activity/compute-analysis-activity-digest.js","webpack://latinera/./sources/utilities/activity/compute-analysis-activity-item.js","webpack://latinera/./sources/utilities/activity/sort-activities.js","webpack://latinera/./sources/utilities/analysis.js","webpack://latinera/./sources/utilities/analysis/compute-analysis-digest.js","webpack://latinera/./sources/utilities/array.js","webpack://latinera/./sources/utilities/array/de-duplicate.js","webpack://latinera/./sources/utilities/array/generate.js","webpack://latinera/./sources/utilities/array/match.js","webpack://latinera/./sources/utilities/array/move-item.js","webpack://latinera/./sources/utilities/card.js","webpack://latinera/./sources/utilities/card/can-merge-cards.js","webpack://latinera/./sources/utilities/card/can-split-card.js","webpack://latinera/./sources/utilities/card/compute-create-card-data.js","webpack://latinera/./sources/utilities/convert.js","webpack://latinera/./sources/utilities/convert/generate-conversion-functions.js","webpack://latinera/./sources/utilities/convert/generate-data-conversion-functions.js","webpack://latinera/./sources/utilities/definition.js","webpack://latinera/./sources/utilities/definition/compute-create-definition-data.js","webpack://latinera/./sources/utilities/dom.js","webpack://latinera/./sources/utilities/dom/get-caret-position.js","webpack://latinera/./sources/utilities/dom/remove-element.js","webpack://latinera/./sources/utilities/dom/set-caret-position.js","webpack://latinera/./sources/utilities/element.js","webpack://latinera/./sources/utilities/element/compute-create-element-data.js","webpack://latinera/./sources/utilities/element/compute-element-type.js","webpack://latinera/./sources/utilities/english.js","webpack://latinera/./sources/utilities/english/ordinal-number.js","webpack://latinera/./sources/utilities/english/pluralize.js","webpack://latinera/./sources/utilities/english/singularize.js","webpack://latinera/./sources/utilities/etc.js","webpack://latinera/./sources/utilities/etc/clone.js","webpack://latinera/./sources/utilities/etc/equal.js","webpack://latinera/./sources/utilities/etc/type-of.js","webpack://latinera/./sources/utilities/event.js","webpack://latinera/./sources/utilities/event/set-event-handlers.js","webpack://latinera/./sources/utilities/event/unset-event-handlers.js","webpack://latinera/./sources/utilities/hash.js","webpack://latinera/./sources/utilities/hash/cyrb53.js","webpack://latinera/./sources/utilities/index.js","webpack://latinera/./sources/utilities/index/add-document-meta-data.js","webpack://latinera/./sources/utilities/index/add-documents-meta-data.js","webpack://latinera/./sources/utilities/index/compute-expiry-time-stamp-ms.js","webpack://latinera/./sources/utilities/index/remove-document-meta-data.js","webpack://latinera/./sources/utilities/index/remove-documents-meta-data.js","webpack://latinera/./sources/utilities/inflection.js","webpack://latinera/./sources/utilities/inflection/compute-selected-inflections-flags.js","webpack://latinera/./sources/utilities/inflector.js","webpack://latinera/./sources/utilities/inflector/compare-inflectors-by.js","webpack://latinera/./sources/utilities/inflector/sort-inflectors.js","webpack://latinera/./sources/utilities/latin.js","webpack://latinera/./sources/utilities/latin/add-tonic-accents.js","webpack://latinera/./sources/utilities/latin/has-tonic-accents.js","webpack://latinera/./sources/utilities/latin/remove-tonic-accents.js","webpack://latinera/./sources/utilities/lemma.js","webpack://latinera/./sources/utilities/lemma/compute-create-lemma-data.js","webpack://latinera/./sources/utilities/lemma/compute-lemma-type.js","webpack://latinera/./sources/utilities/number.js","webpack://latinera/./sources/utilities/number/round-to-decimal.js","webpack://latinera/./sources/utilities/object.js","webpack://latinera/./sources/utilities/object/has-own-property.js","webpack://latinera/./sources/utilities/object/merge.js","webpack://latinera/./sources/utilities/object/pick-properties.js","webpack://latinera/./sources/utilities/object/pick-property.js","webpack://latinera/./sources/utilities/object/set-property.js","webpack://latinera/./sources/utilities/object/to-string.js","webpack://latinera/./sources/utilities/object/unset-falsy-properties.js","webpack://latinera/./sources/utilities/object/unset-properties.js","webpack://latinera/./sources/utilities/promise.js","webpack://latinera/./sources/utilities/promise/execute-after.js","webpack://latinera/./sources/utilities/promise/exponential-backoff.js","webpack://latinera/./sources/utilities/promise/pause.js","webpack://latinera/./sources/utilities/promise/reject-after.js","webpack://latinera/./sources/utilities/promise/resolve-after.js","webpack://latinera/./sources/utilities/promise/with-timeout.js","webpack://latinera/./sources/utilities/query.js","webpack://latinera/./sources/utilities/query/compress-query.js","webpack://latinera/./sources/utilities/query/format-query.js","webpack://latinera/./sources/utilities/query/remove-duplicate-fragments.js","webpack://latinera/./sources/utilities/sentence.js","webpack://latinera/./sources/utilities/sentence/compute-create-sentence-data.js","webpack://latinera/./sources/utilities/sentence/compute-sentence-text-from-elements.js","webpack://latinera/./sources/utilities/sentence/ensure-trailing-mark.js","webpack://latinera/./sources/utilities/sentence/normalize-marks.js","webpack://latinera/./sources/utilities/sentence/normalize-words.js","webpack://latinera/./sources/utilities/state.js","webpack://latinera/./sources/utilities/state/compute-initial-meta-data.js","webpack://latinera/./sources/utilities/state/compute-merged-card-meta-data.js","webpack://latinera/./sources/utilities/state/compute-rebased-state-data.js","webpack://latinera/./sources/utilities/state/compute-split-cards-meta-data.js","webpack://latinera/./sources/utilities/state/compute-updated-meta-data.js","webpack://latinera/./sources/utilities/storage.js","webpack://latinera/./sources/utilities/storage/get-local-storage-data.js","webpack://latinera/./sources/utilities/storage/get-session-storage-data.js","webpack://latinera/./sources/utilities/storage/patch-local-storage-data.js","webpack://latinera/./sources/utilities/storage/patch-session-storage-data.js","webpack://latinera/./sources/utilities/storage/set-local-storage-data.js","webpack://latinera/./sources/utilities/storage/set-session-storage-data.js","webpack://latinera/./sources/utilities/string.js","webpack://latinera/./sources/utilities/string/capitalize-all.js","webpack://latinera/./sources/utilities/string/capitalize-first.js","webpack://latinera/./sources/utilities/string/escape-regexp.js","webpack://latinera/./sources/utilities/string/highlight.js","webpack://latinera/./sources/utilities/string/normalize-email-address.js","webpack://latinera/./sources/utilities/string/optimize-spaces.js","webpack://latinera/./sources/utilities/string/parse-json.js","webpack://latinera/./sources/utilities/string/remove-multiple-spaces.js","webpack://latinera/./sources/utilities/string/remove-new-lines.js","webpack://latinera/./sources/utilities/string/split-tokens.js","webpack://latinera/./sources/utilities/string/uncapitalize-first.js","webpack://latinera/./sources/utilities/time.js","webpack://latinera/./sources/utilities/time/compute-absolute-time-string.js","webpack://latinera/./sources/utilities/time/compute-distance-time-string.js","webpack://latinera/./sources/utilities/time/compute-relative-time-string.js","webpack://latinera/./sources/utilities/time/get-current-time-stamp-ms.js","webpack://latinera/./sources/utilities/time/get-current-year.js","webpack://latinera/./sources/utilities/token.js","webpack://latinera/./sources/utilities/token/compute-client-token-data.js","webpack://latinera/./sources/utilities/token/decode-token.js","webpack://latinera/./sources/utilities/user.js","webpack://latinera/./sources/utilities/user/compute-display-name.js","webpack://latinera/./sources/value-converters/capitalize-first.js","webpack://latinera/./sources/styles/ps-uikit.scss?37e6"],"sourcesContent":["// Define activity-related utility functions\nexport { computeActivityType } from \"./activity/compute-activity-type\";\nexport { sortActivities } from \"./activity/sort-activities\";\nexport { \n computeAnalysisActivityItem \n} from \"./activity/compute-analysis-activity-item\";\nexport { \n computeAnalysisActivityDigest \n} from \"./activity/compute-analysis-activity-digest\";\n","// Define the \"computeActivityType\" activity utility function\n\n// computeActivityType: sync\n// returns a string representing the type (analysis|task) of the specified\n// activity data object\nexport function computeActivityType(activityData) {\n if (!activityData) {\n throw new Error(`Could not determine activity type: empty activity`);\n }\n const { type: activityType = \"?\" } = activityData;\n switch(activityType) {\n case \"a\":\n return \"analysis\";\n case \"t\":\n return \"task\";\n default:\n throw new Error(`Unknown activity type \"${activityType}\"`);\n }\n}\n","// Define the \"computeAnalysisActivityDigest\" activity utility function\n\n// Import utility modules\nimport { computeAnalysisDigest } from \"utilities/analysis\";\nimport { cyrb53 as computeHash } from \"utilities/hash\";\nimport { toString } from \"utilities/object\";\nimport { typeOf } from \"utilities/etc\";\n\n\n// computeAnalysisActivityDigest: sync\n// returns a string representing a unique digest for the specified\n// analysis activity data\nexport function computeAnalysisActivityDigest(analysisActivityData) {\n if (typeOf(analysisActivityData) !== \"object\") {\n throw new Error(`Parameter \"analysisActivityData\" should be an object`);\n }\n\n const { \n key: analysisActivityKey = \"\",\n type: analysisActivityType = \"\",\n analysis: analysisData = null\n } = analysisActivityData || {};\n const analysisActivityDigestData = {\n key: analysisActivityKey,\n type: analysisActivityType,\n analysis: computeAnalysisDigest(analysisData || {})\n };\n const analysisActivityDataString = toString(analysisActivityDigestData || {});\n return computeHash(`analysis-activity-${analysisActivityDataString}`);\n}\n","// Define the \"computeAnalysisActivityItem\" activity utility function \n\n// Import utility modules\nimport { pickProperties } from \"utilities/object\";\nimport { typeOf } from \"utilities/etc\";\n\n// Define internal parameters\nconst analysisActivityItemPropertyNames = [\n \"key\", \"type\", \"index\", \"taskIndex\", \"createdOn\", \"modifiedOn\"\n];\nconst analysisItemPropertyNames = [\"key\", \"createdOn\"];\nconst sentenceItemPropertyNames = [\"key\", \"text\", \"localeCode\"];\n\n\n// computeAnalysisActivityItem: sync\n// returns an analysis activity item object from the specified analysis\n// activity deep object\nexport function computeAnalysisActivityItem({\n analysisActivityData = null\n}) {\n if (typeOf(analysisActivityData) !== \"object\") {\n throw new Error(`Parameter \"analysisActivityData\" should be an object`);\n }\n\n const analysisActivityItemData = \n pickProperties(analysisActivityData, analysisActivityItemPropertyNames);\n const { analysis: analysisData = null } = analysisActivityData || {};\n\n const analysisItemData = \n pickProperties(analysisData, analysisItemPropertyNames);\n const { \n inSentence: inSentenceData = null,\n outSentence: outSentenceData = null \n } = analysisData || {};\n const inSentenceItemData = \n pickProperties(inSentenceData, sentenceItemPropertyNames);\n const outSentenceItemData = \n pickProperties(outSentenceData, sentenceItemPropertyNames);\n\n return {\n ...analysisActivityItemData,\n analysis: {\n ...analysisItemData,\n inSentence: inSentenceItemData,\n outSentence: outSentenceItemData\n }\n };\n}\n","// Define the \"sortActivities\" activity utility function\n\n// Import utility modules\nimport { clone, typeOf } from \"utilities/etc\";\n\n\n// sortActivities: sync\n// Returns the specified array of activities sorted in the specified \n// order (ascending or descending) of the index property\nexport function sortActivities({\n activitiesData = [],\n ascendingIndex = true\n}) {\n if (typeOf(activitiesData) !== \"array\") {\n throw new Error(`Parameter \"activitiesData\" should be an array`);\n } else if (typeOf(ascendingIndex) !== \"boolean\") {\n throw new Error(`Parameter \"ascendingIndex\" should be a boolean`);\n }\n\n // Return sorted activities\n // note: use of the \"toSorted\" array method for copy sorting is not\n // widely supported by browsers yet, so cloning and then sorting in\n // place\n const clonedActivitiesData = clone(activitiesData);\n const sortedActivitiesData = clonedActivitiesData\n .sort((\n { index: activity1Index }, \n { index: activity2Index }\n ) => {\n const preOrdering = activity1Index < activity2Index ? -1 : \n activity1Index > activity2Index ? +1 : 0;\n return ascendingIndex ? preOrdering : -preOrdering;\n })\n return sortedActivitiesData;\n}\n","// Define analysis-related utility functions\nexport { computeAnalysisDigest } from \"./analysis/compute-analysis-digest\";\n","// Define the \"computeAnalysisDigest\" analysis utility function\n\n// Import utility modules\nimport { cyrb53 as computeHash } from \"utilities/hash\";\nimport { toString } from \"utilities/object\";\nimport { typeOf } from \"utilities/etc\";\n\n\n// computeAnalysisDigest: sync\n// returns a string representing a unique digest for the specified\n// analysis data\nexport function computeAnalysisDigest(analysisData) {\n if (typeOf(analysisData) !== \"object\") {\n throw new Error(`Parameter \"analysisData\" should be an object`);\n }\n\n const {\n outSentence: outSentenceData = null,\n cards: cardsData = []\n } = analysisData || {};\n const { text: outSentenceText = \"\" } = outSentenceData || {};\n const analysisDigestData = {\n outSentence: {\n text: outSentenceText\n },\n cards: cardsData.map(({\n outElement: outElementData = null,\n lemmas: lemmasData = [],\n inInflectors: inInflectorsData = [],\n definitions: definitionsData = []\n }) => {\n const { text: outElementText = \"\" } = outElementData || {};\n return {\n outElement: { text: outElementText },\n lemmasKeys: lemmasData\n .map(lemmaData => lemmaData || {})\n .map(({ key: lemmaKey = \"\" }) => lemmaKey),\n inInflectorsKeys: inInflectorsData\n .map(inInflectorData => inInflectorData || {})\n .map(({ key: inInflectorKey = \"\" }) => inInflectorKey),\n definitionsKeys: definitionsData\n .map(definitionData => definitionData || {})\n .map(({ key: definitionKey = \"\" }) => definitionKey),\n };\n })\n };\n const analysisDigestString = toString(analysisDigestData);\n return computeHash(`analysis-${analysisDigestString}`);\n}\n","// Define array-related utility functions\nexport { deduplicate } from \"utilities/array/de-duplicate\";\nexport { moveItem } from \"utilities/array/move-item\";\nexport { generate } from \"utilities/array/generate\";\nexport { match } from \"utilities/array/match\";\n","// Define the \"deduplicate\" array utility function\n\n// Import utility modules\nimport { typeOf } from \"utilities/etc\";\n\n\n// deduplicate: sync\n// returns the specified array after removing the duplicate items \nexport function deduplicate(array) {\n if (typeOf(array) !== \"array\") {\n throw new Error(`First argument should be an array`);\n }\n\n const itemsSet = new Set();\n return array\n .reduce((accArray, item) => {\n if (itemsSet.has(item)) {\n return accArray;\n }\n itemsSet.add(item);\n return [ ...accArray, item ];\n }, []);\n\n // The following one-liner does not work (t.b.i.)\n //return [...new Set(array)];\n // this works in browser console and nodejs\n // This could be due to wrong babel transpiling settings in aurelia\n}\n","// Define the \"generate\" array utility function\n\n// generate: sync\n// returns an array having the specified length and whose elements are\n// initialized by calling the specified (map-like) callback function \nexport function generate(length, callback) {\n return Array.from({ length }, callback);\n}\n","// Export the \"match\" array utility function\n\n// Import utility modules\nimport { typeOf } from \"utilities/etc\";\n\n\n// match: sync\n// returns a boolean indicating whether all the items in the first array\n// are present in the second array, irrespective of sorting\n// note: set-based implementation runs in linear time\nexport function match(array1 = [], array2 = []) {\n if (typeOf(array1) !== \"array\") {\n throw new Error(`First argument should be an array`);\n } else if (typeOf(array2) !== \"array\") {\n throw new Error(`Second argument should be an array`);\n }\n const array1ItemsSet = new Set(array1);\n const array2ItemsSet = new Set(array2);\n return array1.every(item => array2ItemsSet.has(item)) &&\n array2.every(item => array1ItemsSet.has(item));\n}\n","// Define the \"moveItem\" array utility function\n\n// Import utility modules\nimport { clone, typeOf } from \"utilities/etc\";\n\n\n// moveItem: sync\n// returns an array obtained by cloning the specified array and then moving \n// the element from the specified \"oldIndex\" to the specified \"newIndex\"\nexport function moveItem(array, oldIndex, newIndex) {\n if (typeOf(array) !== \"array\") {\n throw new Error(`First argument should be an array`);\n } else if (typeOf(oldIndex) !== \"number\") {\n throw new Error(`Second argument (oldIndex) should be an integer index`);\n } else if (typeOf(newIndex) !== \"number\") {\n throw new Error(`Third argument (newIndex) should be an integer index`);\n }\n if (!array.length || oldIndex === newIndex) {\n return array;\n }\n oldIndex = oldIndex % array.length;\n oldIndex = oldIndex < 0 ? oldIndex + array.length : oldIndex;\n newIndex = newIndex % array.length;\n newIndex = newIndex < 0 ? newIndex + array.length : newIndex;\n\n // Splice-based implementation\n const targetElement = array[oldIndex];\n const newArray = clone(array);\n newArray.splice(newIndex > oldIndex ? newIndex+1 : newIndex, 0, targetElement);\n newArray.splice(newIndex < oldIndex ? oldIndex+1 : oldIndex, 1);\n return newArray;\n}\n","// Define card-related utility functions\nexport { computeCreateCardData } from \"./card/compute-create-card-data\";\nexport { canMergeCards } from \"./card/can-merge-cards\";\nexport { canSplitCard } from \"./card/can-split-card\";\n","// Define the \"canMergeCards\" card utility function\n\n// Define internal parameters\nconst mergeableElementTypes = [ \"wrd\", \"grp\", \"cmp\" ];\n\n\n// canMergeCards: sync\n// returns a boolean indicating whether the specified cards can be merged \n// or not\nexport function canMergeCards({\n cardData1 = null, \n cardData2 = null,\n cardMetaData1 = null, \n cardMetaData2 = null\n}) {\n if (!cardData1 || !cardData2 || !cardMetaData1 || !cardMetaData2) {\n return false;\n }\n const { inElement: { type: inElementType1 = \"\" } = {} } = cardData1;\n const { inElement: { type: inElementType2 = \"\" } = {} } = cardData2;\n const { isValid: cardDataValid1 = true } = cardMetaData1;\n const { isValid: cardDataValid2 = true } = cardMetaData2;\n return cardDataValid1 && cardDataValid2 &&\n mergeableElementTypes.includes(inElementType1) &&\n mergeableElementTypes.includes(inElementType2);\n}\n","// Define the \"canSplitCard\" card utility function\n\n// Define internal parameters\nconst splitteableElementTypes = [\"grp\"];\n\n\n// canSplitCard: sync\n// returns a boolean indicating whether the specified card can be split\n// or not\nexport function canSplitCard({ \n cardData = null,\n cardMetaData = null\n}) {\n if (!cardData || !cardMetaData) {\n return false;\n }\n const { inElement: inElementData = null } = cardData;\n const { type: inElementType = \"\" } = inElementData || {};\n const { isValid: cardDataValid = true } = cardMetaData;\n return cardDataValid && splitteableElementTypes.includes(inElementType);\n}\n","// Define the \"computeCreateCardData\" card utility function\n\n// Import utility modules\nimport { unsetFalsyProperties } from \"utilities/object\";\n\n\n// computeCreateCardData: sync\n// returns a createCard data object, i.e. an object containing the\n// data to be passed to the server when creating a new card, given the\n// specified card data object\nexport function computeCreateCardData({\n inElement: inElementData,\n outElement: outElementData,\n lemmas: lemmasData = [],\n inInflectors: inInflectorsData = [],\n outInflectors: outInflectorsData = [],\n definitions: definitionsData = [],\n function: functionData\n}) {\n const { key: inElementKey = \"\" } = inElementData || {};\n if (!inElementKey) {\n throw new Error(`Undefined input element key`);\n }\n const { key: outElementKey = \"\" } = outElementData || {};\n const lemmasKeys = lemmasData\n .map(lemmaData => {\n const { key: lemmaKey = \"\" } = lemmaData || {};\n return lemmaKey || null;\n });\n const inInflectorsKeys = inInflectorsData\n .map(inInflectorData => {\n const { key: inInflectorKey = \"\" } = inInflectorData || {};\n return inInflectorKey || null;\n });\n const outInflectorsKeys = outInflectorsData\n .map(outInflectorData => {\n const { key: outInflectorKey = \"\" } = outInflectorData || {};\n return outInflectorKey || null;\n });\n const definitionsKeys = definitionsData\n .map(definitionData => {\n const { key: definitionKey = \"\" } = definitionData || {};\n return definitionKey || null;\n });\n const { key: functionKey = \"\" } = functionData || {};\n return unsetFalsyProperties({\n inElementKey,\n outElementKey,\n lemmasKeys,\n inInflectorsKeys,\n outInflectorsKeys,\n definitionsKeys,\n functionKey\n }, {\n keepEmptyStrings: false,\n keepEmptyArrays: false\n });\n}\n","// Define conversion-related utility functions\n\n// import utility modules\nimport {\n generateDataConversionFunctions\n} from \"utilities/convert/generate-data-conversion-functions\";\nimport {\n generateConversionFunctions\n} from \"utilities/convert/generate-conversion-functions\";\n\n// Import parameter modules\nimport * as conversionParams from \"parameters/conversion\";\n\n\n// Export the convert object\nexport const convert = Object.entries(conversionParams)\n .reduce((accConversionFunctionsData, [ propertyName, conversionsData ]) => {\n accConversionFunctionsData[propertyName] =\n generateConversionFunctions({ conversionsData });\n if (propertyName.endsWith(\"PropertyName\")) {\n propertyName = propertyName.replace(\"PropertyName\", \"Data\");\n accConversionFunctionsData[propertyName] =\n generateDataConversionFunctions({ conversionsData });\n }\n return accConversionFunctionsData;\n }, {});\n","// Define the generateConversionFunctions function\n\n// Import utility modules\nimport { typeOf } from \"utilities/etc\";\n\n\n// generateConversionFunctions: sync\n// returns an object whose properties are the specified longToShort and\n// shortToLong functions operating the conversion between the specified\n// \"long\" and \"short\" properties of the conversionsData objects' array\nexport function generateConversionFunctions({\n conversionsData = [],\n longToShortConversionName = \"longToShort\",\n shortToLongConversionName = \"shortToLong\"\n}) {\n // Validate arguments\n if (arguments.length < 1 || arguments.length > 3) {\n throw new Error(\"One to three arguments expected\");\n }\n const isValidConversionsData = conversionsData\n .every(conversionData => {\n return Object.keys(conversionData)\n .every(key => ([ \"long\", \"short\" ].includes(key)));\n });\n if (typeOf(conversionsData) !== \"array\" ||\n !conversionsData.length ||\n !isValidConversionsData) {\n throw new Error(`Conversions data should be a non-empty array of ` +\n `objects having \"long\" and \"short\" properties`);\n }\n // Define internal longToShort and shortToLong conversion maps\n const longToShortConversionMap = new Map();\n const shortToLongConversionMap = new Map();\n conversionsData.forEach(({ long, short }) => {\n longToShortConversionMap.set(long, short);\n shortToLongConversionMap.set(short, long);\n });\n\n // Define and return the conversion functions' object\n const conversionFunctionsData = {};\n conversionFunctionsData[longToShortConversionName] = (long) => {\n const short = longToShortConversionMap.get(long);\n if (short) {\n return short;\n } else {\n throw new Error(`Unknown conversion for \"long\" property \"${long}\"`);\n }\n };\n conversionFunctionsData[shortToLongConversionName] = (short) => {\n const long = shortToLongConversionMap.get(short);\n if (long) {\n return long;\n } else {\n throw new Error(`Unknown conversion for \"short\" property \"${short}\"`);\n }\n };\n return conversionFunctionsData;\n}\n","// Define the generateDataConversionFunctions function\n\n// Import utility modules\nimport { typeOf } from \"utilities/etc\";\n\n\n// generateDataConversionFunctions: sync\n// returns an object whose properties are the specified longToShort and\n// shortToLong functions operating the conversion between the specified\n// \"long\" and \"short\" properties of the conversionsData objects' array\nexport function generateDataConversionFunctions({\n conversionsData = [],\n longToShortConversionName = \"longToShort\",\n shortToLongConversionName = \"shortToLong\"\n}) {\n // Validate arguments\n if (arguments.length < 1 || arguments.length > 3) {\n throw new Error(\"One to three arguments expected\");\n }\n const isValidConversionsData = conversionsData\n .every(conversionData => {\n return Object.keys(conversionData)\n .every(key => [ \"long\", \"short\" ].includes(key));\n });\n if (typeOf(conversionsData) !== \"array\" ||\n !conversionsData.length ||\n !isValidConversionsData) {\n throw new Error(`Conversions data should be long non-empty array of ` +\n `objects having \"long\" and \"short\" properties`);\n }\n // Define internal longToShort and shortToLong conversion maps\n const longToShortConversionMap = new Map();\n const shortToLongConversionMap = new Map();\n conversionsData.forEach(({ long, short }) => {\n longToShortConversionMap.set(long, short);\n shortToLongConversionMap.set(short, long);\n });\n\n // Define and return the conversion functions' object\n const dataConversionFunctionsData = {};\n dataConversionFunctionsData[longToShortConversionName] = object => {\n return Object.entries(object)\n .reduce((accObject, [ lpName, pValue ]) => {\n const spName = longToShortConversionMap.get(lpName);\n if (spName) {\n accObject[spName] = pValue;\n } else if (shortToLongConversionMap.has(lpName)) { // lpName = spName\n accObject[lpName] = pValue;\n } else {\n throw new Error(`Unknown conversion for \"long\" property \"${lpName}\"`);\n }\n return accObject;\n }, {});\n };\n dataConversionFunctionsData[shortToLongConversionName] = object => {\n return Object.entries(object)\n .reduce((accObject, [ spName, pValue ]) => {\n const lpName = shortToLongConversionMap.get(spName);\n if (lpName) {\n accObject[lpName] = pValue;\n } else if (longToShortConversionMap.has(spName)) { // spName = lpName\n accObject[spName] = pValue;\n } else {\n throw new Error(`Unknown conversion for \"short\" property \"${spName}\"`);\n }\n return accObject;\n }, {});\n };\n return dataConversionFunctionsData;\n}\n","// Define definition-related utility functions\nexport {\n computeCreateDefinitionData\n} from \"./definition/compute-create-definition-data\";\n","// Define the \"computeCreateDefinitionData\" definition utility function\n\n// Import utility modules\nimport { unsetFalsyProperties } from \"utilities/object\";\nimport { typeOf } from \"utilities/etc\";\n\n\n// computeCreateDefinitionData: sync\n// returns a createDefinition data object, i.e. an object containing the\n// data to be passed to the server when creating a new definition, given the\n// specified createDefinition data object\nexport function computeCreateDefinitionData({\n definitionData = null,\n lemmaKey\n}) {\n if (typeOf(definitionData) !== \"object\") {\n throw new Error(`Parameter \"definition\" should be an object`);\n }\n const {\n type = null,\n text = \"\",\n description = \"\",\n localeCode = \"\"\n } = definitionData;\n if (!text || typeOf(text) !== \"string\") {\n throw new Error(`Parameter \"definition.text\" should be a non-empty string`);\n } else if (!localeCode || typeOf(localeCode) !== \"string\") {\n throw new Error(`Parameter \"definition.localeCode\" should be a ` +\n `non-empty string`);\n }\n\n const createDefinitionData = {\n definitionData: {\n type,\n text,\n description,\n localeCode\n },\n lemmaKey // may be undefined\n };\n return unsetFalsyProperties(createDefinitionData, {\n keepEmptyStrings: true\n });\n}\n","// Define dom-related utility functions\nexport { removeElement } from \"utilities/dom/remove-element\";\nexport { getCaretPosition } from \"utilities/dom/get-caret-position\";\nexport { setCaretPosition } from \"utilities/dom/set-caret-position\";\n","// Define the \"getCaretPosition\" dom utility function\n\n// getCaretPosition: sync\n// returns an integer representing the current position of the input caret\n// within the spectified element\nexport function getCaretPosition(element) {\n if (!element) {\n throw new Error(`Undefined element`);\n }\n const elementTagName = element.nodeName.toLowerCase();\n switch (elementTagName) {\n case \"input\":\n case \"textarea\":\n return element.selectionEnd;\n case \"span\":\n case \"div\":\n if (window.getSelection) {\n const selection = window.getSelection();\n if (selection.rangeCount) {\n const range = selection.getRangeAt(0);\n if (range.commonAncestorContainer.parentNode == element) {\n return range.endOffset;\n }\n }\n return 0;\n }\n console.warn(`Could not determine caret position for element`, element);\n return 0;\n default:\n console.warn(`Could not determine caret position for element`, element);\n return 0;\n }\n}\n","// Define the \"removeElement\" dom utility function\n\n// removeElement: sync\n// remove the specified HTMLElement from the DOM\nexport function removeElement(element) {\n element.parentNode.removeChild(element);\n}\n","// Define the \"setCaretPosition\" dom utility function\n\n// setCaretPosition: sync\n// sets the position of the caret within the specified element, to the\n// specified integer value\nexport function setCaretPosition(element, position) {\n if (!element) {\n throw new Error(`Undefined element`);\n }\n\n element.focus();\n if (element.setSelectionRange) { // IE >= 9 and other browsers\n element.setSelectionRange(position, position);\n } else if (element.createTextRange) { // IE < 9 \n const range = element.createTextRange();\n range.collapse(true);\n range.moveEnd(\"character\", position);\n range.moveStart(\"character\", position);\n range.select();\n }\n}\n","// Define element-related utility functions\nexport {\n computeCreateElementData\n} from \"./element/compute-create-element-data\";\nexport {\n computeElementType\n} from \"./element/compute-element-type\";\n","// Define the \"computeCreateElementData\" element utility module\n\n// Import utility modules\nimport { computeElementType } from \"utilities/element/compute-element-type\";\nimport { unsetFalsyProperties } from \"utilities/object\";\n\n\n// computeCreateElementData: sync\n// returns a createElement data object, i.e. an object containing the\n// data to be passed to the server when creating a new element, given the\n// specified element data object\nexport function computeCreateElementData({\n type: elementType,\n text: elementText = \"\",\n leafElementsKeys = [],\n localeCode: elementLocaleCode\n}) {\n if (!elementLocaleCode) {\n throw new Error(`Undefined element localeCode`);\n }\n const createElementData = {\n elementData: {\n type: elementType || computeElementType({ text: elementText }),\n text: elementText,\n leafElementsKeys,\n localeCode: elementLocaleCode\n }\n };\n return unsetFalsyProperties(createElementData, {\n keepEmptyStrings: true,\n keepEmptyArrays: false\n });\n}\n","// Define the \"computeElementType\" element utility function\n\n// Define internal parameters\n/* eslint-disable no-misleading-character-class */\nconst numberRegExp = /^(\\d+|[IVXLCDM\\u0305]+)$/; // combining overline \\u0305\nconst markRegExp = /^[.,:;!?]+$/;\n/* eslint-enable no-misleading-character-class */\n\n\n// computeElementType: sync\n// returns a string representing the type of the specified element object\n// computed from its text field\nexport function computeElementType({\n text: elementText = \"\"\n}) {\n const childElementsTexts = elementText.split(/\\s+/);\n if (childElementsTexts.length === 1) {\n if (numberRegExp.test(elementText)) {\n return \"num\"; // number\n } else if (markRegExp.test(elementText)) {\n return \"mrk\"; // mark\n } else {\n return \"wrd\"; // work\n }\n }\n return \"grp\"; // group\n}\n","// Define english-language related utilities\nexport { ordinalNumber } from \"utilities/english/ordinal-number\";\nexport { singularize } from \"utilities/english/singularize\";\nexport { pluralize } from \"utilities/english/pluralize\";\n","// Define the \"ordinalNumber\" english utility function\n\n// Import utility functions\nimport { typeOf } from \"utilities/etc\";\n\n\n// ordinalNumber: sync\n// returns a string representing the ordinal number corresponding to the\n// specified (integer) cardinal number\nexport function ordinalNumber(cardinalNumber) {\n if (!cardinalNumber || typeOf(cardinalNumber) !== \"number\" ||\n cardinalNumber < 0) {\n throw new Error(\"Cardinal number should be a non-negative number\");\n }\n switch (cardinalNumber % 10) {\n case 1:\n return `${cardinalNumber}st`;\n case 2:\n return `${cardinalNumber}nd`;\n case 3:\n return `${cardinalNumber}rd`;\n default:\n return `${cardinalNumber}th`;\n }\n}\n","// Define the \"pluralize\" english utility function\n\n// Import utility functions\nimport { typeOf } from \"utilities/etc\";\n\n\n// pluralize: sync\n// returns the specified singular string pluralized according to the\n// specified (optional) count\nexport function pluralize(singularString = \"\", count = 2) {\n if (singularString && typeOf(singularString) !== \"string\") {\n throw new Error(\"First argument should be a non-empty string\");\n } else if (typeOf(count) !== \"number\") {\n throw new Error(\"Second argument should be a number\");\n }\n if (count === 1) {\n return singularString;\n } else if (singularString.match(/.+y$/)) { // e.g. city/cities\n return singularString.replace(/y$/, \"ies\");\n } else if (singularString.match(/.+sis$/)) { // e.g. analysis/analyses\n return singularString.replace(/sis$/, \"ses\");\n } else if (singularString.match(/.+us$/)) { // e.g. virus/viruses\n return singularString.replace(/us$/, \"uses\");\n } else { // e.g. task/tasks\n return `${singularString}s`;\n }\n}\n","// Define the \"singularize\" english utility function\n\n// Import utility functions\nimport { typeOf } from \"utilities/etc\";\n\n\n// singularize: sync\n// return the singular of the specified noun string\nexport function singularize(pluralString) {\n if (typeOf(pluralString) !== \"string\") {\n throw new Error(`first argument should be a string`);\n }\n if (pluralString.match(/.+ies$/)) { // e.g. city/cities\n return pluralString.replace(/ies$/, \"y\");\n } else if (pluralString.match(/.+ses$/)) { // e.g. analyses/analysis\n return pluralString.replace(/ses$/, \"sis\");\n } else if (pluralString.match(/.+uses$/)) { // e.g. viruses/virus\n return pluralString.replace(/uses$/, \"us\");\n } else if (pluralString.match(/.+s/)) { // e.g. tasks/task\n return pluralString.replace(/s$/, \"\");\n }\n}\n","// Define generic utility functions\nexport { typeOf } from \"utilities/etc/type-of\";\nexport { equal } from \"utilities/etc/equal\";\nexport { clone } from \"utilities/etc/clone\";\n","// Define the \"clone\" etc utility function\n\n// Import utility modules\nimport { typeOf } from \"utilities/etc/type-of\";\n\n\n// clone: sync\n// returns a deep clone of the specified argument\nexport function clone(arg) {\n const argType = typeOf(arg);\n switch (argType) {\n case \"string\":\n case \"number\":\n case \"boolean\":\n case \"date\":\n case \"function\":\n case \"null\":\n case \"undefined\":\n return arg;\n case \"array\":\n return arg\n .map(item => clone(item));\n case \"object\":\n return Object.entries(arg)\n .reduce((accObject, [ pName, pValue ]) => {\n accObject[pName] = clone(pValue);\n return accObject;\n }, {});\n default:\n throw new Error(`Invalid argument type \"${argType}\"`);\n }\n}\n","// Define the \"equal\" etc utility function\n\n// Import utility modules\nimport { typeOf } from \"utilities/etc/type-of\";\n\n\n// equal: sync\n// returns a boolean indicating whether the specified arguments are equal\nexport function equal(arg1, arg2) {\n if (arguments.length !== 2) {\n throw new Error(\"Two arguments expected\");\n }\n return (typeOf(arg1) === typeOf(arg2)) &&\n JSON.stringify(arg1) === JSON.stringify(arg2);\n}\n","// Define the \"typeOf\" etc utility function\n\n// typeOf: sync\n// returns the type of the argument beyond the limitations of javascript's\n// built-in typeof operator (e.g. with arrays, functions etc)\nexport function typeOf(arg) {\n return Object.prototype.toString.call(arg)\n .match(/\\[object\\s+(\\w+)\\]/)[1]\n .toLowerCase();\n}\n","// Define event-related utility functions\nexport { setEventHandlers } from \"utilities/event/set-event-handlers\";\nexport { unsetEventHandlers } from \"utilities/event/unset-event-handlers\";\n","// Define the \"setEventHandlers\" event utility function\n\n// Import utility modules\nimport { capitalizeFirst } from \"utilities/string\";\nimport { typeOf } from \"utilities/etc\";\n\n\n// setEventHandlers: sync\n// Subscribe the specified entity key/name to the event handlers specified\n// in the eventsData array, using the specified eventService instance\nexport function setEventHandlers({\n eventService,\n entityName,\n entityKey,\n eventsData = []\n}) {\n if (!this) {\n throw new Error(`Could not subscribe ${entityName} \"${entityKey}\" ` +\n `to events: unbound \"this\" object`);\n }\n\n return eventsData\n .map(eventData => setEventHandler.call(this, {\n eventService,\n entityName,\n entityKey,\n eventData\n }));\n}\n\nfunction setEventHandler({\n eventService = null,\n entityName = \"\",\n entityKey = \"\",\n eventData: { \n name: eventName, \n filterBy: eventFilterBy, \n showDebugs\n }\n}) {\n const capitalizedEventName = capitalizeFirst(eventName);\n const eventHandlerName = `handle${capitalizedEventName}Event`;\n const quotedEntityKey = entityKey ? ` \"${entityKey}\"` : \"\";\n if (!this[eventHandlerName]) {\n throw new Error(`Could not find handler for event \"${eventName}\"`);\n }\n const eventHandler = this[eventHandlerName].bind(this);\n if (![\"function\", \"asyncfunction\"].includes(typeOf(eventHandler))) {\n throw new Error(`Could not subscribe ${entityName}${quotedEntityKey} ` +\n `to event \"${eventName}\": event handler should be a function`);\n }\n if (eventFilterBy) {\n const capitalizedEventFilterBy = capitalizeFirst(eventFilterBy);\n const eventFilterName = `filterEventBy${capitalizedEventFilterBy || \"\"}`;\n const eventFilter = this[eventFilterName].bind(this);\n if (![\"function\", \"asyncfunction\"].includes(typeOf(eventFilter))) {\n throw new Error(`Could not subscribe ${entityName}${quotedEntityKey} ` +\n `to event \"${eventName}\": event filter should be a function`);\n }\n const eventSubscription = eventService.subscribe({ \n eventName, \n eventHandler, \n eventFilter,\n showDebugs\n });\n console.debug(`Subscribed ${entityName}${quotedEntityKey} ` +\n `to filtered event \"${eventName}\"`);\n return eventSubscription;\n }\n const eventSubscription = eventService.subscribe({\n eventName, \n eventHandler,\n showDebugs\n });\n console.debug(`Subscribed ${entityName}${quotedEntityKey} ` +\n `to un-filtered event \"${eventName}\"`);\n return eventSubscription;\n}\n","// Define the \"unsetEventHandlers\" event utility function\n\n// unsetEventHandlers: sync\n// Unsubscribe the specified entity key/name from the event handlers\n// specified in the eventsData array, using the specified eventService\n// instance\nexport function unsetEventHandlers({\n eventsSubscriptions = []\n}) {\n eventsSubscriptions.forEach(({ dispose: disposeEventSubscription }) => {\n disposeEventSubscription();\n });\n}\n","// Define hash-related utility functions\nexport { cyrb53 } from \"utilities/hash/cyrb53\";\n","// Define the \"cyrb53\" hash utility function\n\n// cyrb53: sync\n// returns the 64 bits long \"cyrb53\" hash of the specified string using\n// the specified (optional) seed value\n// note: see stackoverflow thread on js string hashes\nexport function cyrb53(string, seed = 0) {\n let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;\n for (let i = 0, ch; i < string.length; i++) {\n ch = string.charCodeAt(i);\n h1 = Math.imul(h1 ^ ch, 2654435761);\n h2 = Math.imul(h2 ^ ch, 1597334677);\n }\n h1 = Math.imul(h1 ^ h1>>>16, 2246822507) ^ Math.imul(h2 ^ h2>>>13, 3266489909);\n h2 = Math.imul(h2 ^ h2>>>16, 2246822507) ^ Math.imul(h1 ^ h1>>>13, 3266489909);\n return (h2>>>0).toString(16).padStart(8,0) +\n (h1>>>0).toString(16).padStart(8,0);\n}\n","// Define index-related utility functions\nexport { addDocumentMetaData } from \"./index/add-document-meta-data\";\nexport { addDocumentsMetaData } from \"./index/add-documents-meta-data\";\nexport { removeDocumentMetaData } from \"./index/remove-document-meta-data\";\nexport { removeDocumentsMetaData } from \"./index/remove-documents-meta-data\";\nexport { computeExpiryTimeStampMs } from \"./index/compute-expiry-time-stamp-ms\";\n","// Define the \"addDocumentMetaData\" index utility function\n\n// Import utility modules\nimport { getCurrentTimeStampMs } from \"utilities/time\";\nimport { computeExpiryTimeStampMs } from \"./compute-expiry-time-stamp-ms\";\n\n\n// addDocumentMetaData: sync\n// returns a document data object after setting the time-stamp metadata\nexport function addDocumentMetaData({\n collectionName = \"default\",\n documentData = null,\n documentMetaData = null\n}) {\n const { key: documentKey = \"\" } = documentData || {};\n if (!documentKey) {\n throw new Error(`Could not find \"key\" for document to be saved`);\n }\n const { _crtOn, _expOn } = documentMetaData || {};\n const currentTimeStampMs = getCurrentTimeStampMs();\n const finalDocumentMetaData = {\n ...(documentMetaData || {}),\n _crtOn: _crtOn || currentTimeStampMs,\n _expOn: _expOn || computeExpiryTimeStampMs({\n currentTimeStampMs,\n collectionName\n })\n };\n return {\n ...(documentData || {}),\n ...finalDocumentMetaData\n };\n}\n","// Define the \"addDocumentsMetaData\" index utility function\n\n// Import utility modules\nimport { addDocumentMetaData } from \"./add-document-meta-data\";\n\n\n// addDocumentsMetaData: sync\n// returns an array of documents after setting the time-stamps metadata\nexport function addDocumentsMetaData({\n collectionName,\n documentsData = [],\n documentMetaData = {}\n}) {\n return documentsData\n .map(documentData => addDocumentMetaData({\n collectionName,\n documentData,\n documentMetaData\n }));\n}\n","// Define the \"computeExpiryTimeStampMs\" index utility function\n\n// Import utility modules\nimport { getCurrentTimeStampMs } from \"utilities/time\";\n\n// Import parameter modules\nimport { expiryData } from \"parameters/index\";\n\n\n// computeExpiryTimeStampMs: sync\n// returns the expiry time-stamp from the specified current time-stamp and\n// indexDB collection name\nexport function computeExpiryTimeStampMs({\n collectionName = \"\",\n currentTimeStampMs = getCurrentTimeStampMs()\n}) {\n const expiryTimeStampMs = expiryData[collectionName || \"default\"];\n return currentTimeStampMs + expiryTimeStampMs;\n}\n","// Define the \"removeDocumentMetaData\" index utility function\n\n// Import utility modules\nimport { typeOf } from \"utilities/etc\";\n\n\n// removeDocumentMetaData: sync\n// return the document data object obtained by removing the meta-data\n// fields (i.e. fields starting with an underscore)\nexport function removeDocumentMetaData(documentData) {\n if (!documentData) {\n return null;\n }\n return Object.entries(documentData)\n .reduce((accDocumentData, [ key, value ]) => {\n if (key.startsWith(\"_\")) {\n return accDocumentData;\n }\n accDocumentData[key] = value;\n return accDocumentData;\n }, {});\n}\n","// Define the \"removeDocumentsMetaData\" index utility function\n\n// Import utility modules\nimport { removeDocumentMetaData } from \"./remove-document-meta-data\";\n\n\n// removeDocumentsMetaData: sync\n// return the array of document data objects obtained by removing the\n// metadata fields from each element\nexport function removeDocumentsMetaData(documentsData) {\n return documentsData\n .map(documentData => removeDocumentMetaData(documentData));\n}\n","// Define inflection-related utility functions\nexport { \n computeSelectedInflectionsFlags \n} from \"./inflection/compute-selected-inflections-flags\";\n","// Define the \"computeSelectedInflectionsFlags\" inflection utility function\n\n// Import utility modules\nimport { typeOf } from \"utilities/etc\";\n\n\n// computeSelectedInflectionsFlags: sync\n// returns an array of flags indicating which of the specified inflections\n// objects should be pre-selected to be applied to the specified lemma object\nexport function computeSelectedInflectionsFlags({\n lemmaData = null,\n inflectionsData = []\n}) {\n if (typeOf(lemmaData) !== \"object\") {\n throw new Error(`Parameter \"lemmaData\" should be an object`);\n } else if (\n typeOf(inflectionsData) !== \"array\" ||\n inflectionsData.some(inflectionData => typeOf(inflectionData) !== \"object\")\n ) {\n throw new Error(`Parameter \"inflectionsData\" should be an array of objects`);\n }\n\n const inflectionsCount = inflectionsData.length;\n if (inflectionsCount <= 1) { // zero or one item in inflections' array\n return inflectionsData.map(() => true);\n }\n\n const { cathegory: lemmaCathegory = \"\" } = lemmaData || {};\n switch (lemmaCathegory) {\n case \"nou\": // noun\n case \"prn\": // pronoun\n case \"adj\": // adjective\n case \"prt\": // participle\n case \"grd\": // gerundive\n case \"adv\": // adverb\n case \"prp\": // preposition (should be covered by first check)\n case \"cnj\": // conjunction (should be covered by first check)\n case \"int\": // interjection (should be covered by first check)\n case \"pfx\": // prefix (should be covered by first check)\n case \"sfx\": // suffix (should be covered by first check)\n case \"art\": // article (should never occur in Latin)\n return inflectionsData.map(() => false);\n case \"vrb\": { // verb\n const { \n isTransitive: isLemmaTransitive = false,\n isIntransitive: isLemmaIntransitive = false,\n isAnomalous: isLemmaAnomalous = false\n } = lemmaData || {};\n if (isLemmaAnomalous) { // do not preselect anything\n return inflectionsData.map(() => false);\n } else if (isLemmaTransitive) { // preselect active and passive forms\n return inflectionsData.map(({ form: inflectionForm = \"\" }) => {\n return [\"act\", \"pas\"].includes(inflectionForm);\n });\n } else if (isLemmaIntransitive) { // preselect active form only\n return inflectionsData.map(({ form: inflectionForm = \"\" }) => {\n return [\"act\"].includes(inflectionForm);\n });\n }\n return inflectionsData.map(() => false);\n }\n default:\n throw new Error(`Unknown lemma cathegory \"${lemmaCathegory}\"`);\n }\n}\n","// Define inflector-related utility functions\nexport { compareInflectorsBy } from \"./inflector/compare-inflectors-by\";\nexport { sortInflectors } from \"./inflector/sort-inflectors\";\n","// Define the \"compareInflectorsBy\" inflector-related utility function\n\n// Import utility modules\nimport { typeOf } from \"utilities/etc\";\n\n// Define internal parameters\nconst sortedFieldsValues = {\n degree: [ \"pos\", \"cmp\", \"sup\" ],\n gender: [ \"m\", \"f\", \"n\" ],\n number: [ \"s\", \"p\" ],\n case: [ \"nom\", \"gen\", \"dat\", \"acc\", \"voc\", \"abl\", \"loc\", \"ins\" ],\n age: [ \"cl\", \"ar\", \"pc\", \"po\" ],\n form: [ \"act\", \"pas\", \"dep\", \"sdp\" ],\n mood: [ \"ind\", \"sub\", \"imp\", \"inf\", \"prt\", \"grd\", \"grn\", \"sup\" ],\n tense: [ \"pre\", \"imp\", \"fut\", \"per\", \"ppe\", \"fpe\" ],\n person: [ \"f\", \"s\", \"t\" ]\n};\n\n\n// compareInflectorsBy: sync\n// returns a number representing the result of comparing the specified\n// inflectors according to the specified array of fields. The returned\n// number is:\n// -1 -> inflector1 sorts before inflector2\n// +1 -> inflector1 sorts after inflector2\n// 0 -> inflector1 sorts the same as inflector2\nexport function compareInflectorsBy({\n inflectorData1,\n inflectorData2,\n fieldsNames = []\n}) {\n if (typeOf(inflectorData1) !== \"object\") {\n throw new Error(`Parameter \"inflectorData1\" should be an object`);\n } else if (typeOf(inflectorData2) !== \"object\") {\n throw new Error(`Parameter \"inflectorData2\" should be an object`);\n } else if (typeOf(fieldsNames) !== \"array\" ||\n fieldsNames.some(fieldName => typeOf(fieldName) !== \"string\")) {\n throw new Error(`Parameter \"fieldsNames\" should be an array of strings`);\n }\n\n const fieldsCount = fieldsNames.length;\n const compareResult = fieldsNames\n .reduce((accCompareResult, fieldName, fieldIndex) => {\n if (accCompareResult !== 0) {\n return accCompareResult; // use non-null compare result from prev field\n }\n const fieldValue1 = inflectorData1[fieldName] || \"???\";\n const fieldValue2 = inflectorData2[fieldName] || \"???\";\n const sortedFieldValues = sortedFieldsValues[fieldName] || [];\n const fieldIndex1 = sortedFieldValues.includes(fieldValue1) ?\n sortedFieldValues.indexOf(fieldValue1) : sortedFieldValues.length;\n const fieldIndex2 = sortedFieldValues.includes(fieldValue2) ?\n sortedFieldValues.indexOf(fieldValue2) : sortedFieldValues.length;\n const fieldIndexesDifference = fieldIndex1 - fieldIndex2;\n return accCompareResult + fieldIndexesDifference;\n }, 0);\n const finalCompareResult = compareResult < 0 ? -1 : \n compareResult > 0 ? +1 : 0;\n return finalCompareResult;\n}\n","// Define the \"sortInflectors\" inflector-related utility function\n\n// Import utility modules\nimport { compareInflectorsBy } from \"./compare-inflectors-by\";\nimport { clone, typeOf } from \"utilities/etc\";\n\n\n// sortInflectors: sync\n// returns the specified array of inflectors sorted by grammar order\nexport function sortInflectors(inflectorsData = []) {\n if (typeOf(inflectorsData) !== \"array\") {\n throw new Error(`Parameter \"inflectorsData\" should be ` +\n `an array of inflectors`);\n }\n\n // Return array of sorted inflectors\n // note: use of the \"toSorted\" array method for copy sorting is not\n // widely supported by browsers yet, so cloning and then sorting in\n // place\n const clonedInflectosData = clone(inflectorsData);\n const sortedInflectorsData = clonedInflectosData\n .sort((inflectorData1, inflectorData2) => {\n const { cathegory: inflectorCathegory1 = \"\" } = inflectorData1 || {};\n const { cathegory: inflectorCathegory2 = \"\" } = inflectorData2 || {};\n if (inflectorCathegory1 !== inflectorCathegory2) {\n return inflectorCathegory1.localeCompare(inflectorCathegory2, \"en\");\n }\n\n switch (inflectorCathegory1) {\n case \"adj\": // adjective\n return compareInflectorsBy({\n inflectorData1, \n inflectorData2,\n fieldsNames: [ \"degree\", \"gender\", \"number\", \"case\", \"age\" ]\n });\n case \"adv\": // adverb\n return compareInflectorsBy({\n inflectorData1, \n inflectorData2,\n fieldsNames: [ \"degree\" ]\n });\n case \"nou\": // noun\n case \"prn\": // pronoun\n case \"prt\": // participle\n case \"grd\": // gerundive\n return compareInflectorsBy({\n inflectorData1, \n inflectorData2,\n fieldsNames: [ \"gender\", \"number\", \"case\", \"age\" ]\n });\n case \"vrb\": // verb\n return compareInflectorsBy({\n inflectorData1, \n inflectorData2,\n fieldsNames: [ \"form\", \"mood\", \"tense\", \"number\", \"person\", \"age\" ]\n });\n case \"prp\": // preposition\n case \"cnj\": // conjunction\n case \"int\": // interjection\n case \"art\": // article\n case \"pfx\": // prefix\n case \"sfx\": // suffix\n return 0;\n default:\n console.warn(`Unknown inflector cathegory \"${inflectorCathegory1}\"`);\n return 0;\n }\n });\n return sortedInflectorsData;\n}\n","// Define latin-related utility functions\nexport { removeTonicAccents } from \"./latin/remove-tonic-accents\";\nexport { addTonicAccents } from \"./latin/add-tonic-accents\";\nexport { hasTonicAccents } from \"./latin/has-tonic-accents\";\n","// Define the \"addTonicAccents\" utility function\n\n// Import utility modules\nimport { typeOf } from \"utilities/etc\";\n\n\n// addTonicAccents: sync\n// returns the specified text string after replacing un-accented vowels\n// with the specified short/long accented vowels\nexport function addTonicAccents(textString, accentType = \"short\") {\n if (typeOf(textString) !== \"string\") {\n throw new Error(\"First argument should be a string\");\n } else if (![ \"short\", \"long\" ].includes(accentType)) {\n throw new Error(`Second argument should be a either \"long\" or \"short\"`);\n }\n return textString\n .replace(/[a]/g, accentType === \"short\" ? \"ă\" : \"ā\")\n .replace(/[e]/g, accentType === \"short\" ? \"ĕ\" : \"ē\")\n .replace(/[i]/g, accentType === \"short\" ? \"ĭ\" : \"ī\")\n .replace(/[o]/g, accentType === \"short\" ? \"ŏ\" : \"ō\")\n .replace(/[u]/g, accentType === \"short\" ? \"ŭ\" : \"ū\")\n .replace(/[A]/g, accentType === \"short\" ? \"Ă\" : \"Ā\")\n .replace(/[E]/g, accentType === \"short\" ? \"Ĕ\" : \"Ē\")\n .replace(/[I]/g, accentType === \"short\" ? \"Ĭ\" : \"Ī\")\n .replace(/[O]/g, accentType === \"short\" ? \"Ŏ\" : \"Ō\")\n .replace(/[U]/g, accentType === \"short\" ? \"Ŭ\" : \"Ū\");\n}\n","// Define the \"hasTonicAccents\" utility function\n\n// hasTonicAccents: sync\n// returns a boolean indicating whether the specified string contains\n// tonic accents or not\nexport function hasTonicAccents(textString) {\n return /[ăĕĭŏŭāēīōūĂĔĬŎŬĀĒĪŌŪ]/.test(textString);\n}\n","// Define the \"removeTonicAccents\" italian utility function\n\n// Import utility modules\nimport { typeOf } from \"utilities/etc\";\n\n\n// removeTonicAccents: sync\n// returns the specified text string after replacing all tonic-accented\n// vowels with their un-accented counterparts\nexport function removeTonicAccents(textString) {\n if (typeOf(textString) !== \"string\") {\n throw new Error(\"First argument should be a string\");\n }\n return textString\n .replace(/[ăā]/g, \"a\")\n .replace(/[ĕē]/g, \"e\")\n .replace(/[ĭī]/g, \"i\")\n .replace(/[ŏō]/g, \"o\")\n .replace(/[ŭū]/g, \"u\")\n .replace(/[ĂĀ]/g, \"A\")\n .replace(/[ĔĒ]/g, \"E\")\n .replace(/[ĬĪ]/g, \"I\")\n .replace(/[ŎŌ]/g, \"O\")\n .replace(/[ŬŪ]/g, \"U\");\n}\n","// Define lemma-related utility functions\nexport { computeLemmaType } from \"./lemma/compute-lemma-type\";\nexport { computeCreateLemmaData } from \"./lemma/compute-create-lemma-data\";\n","// Define the \"computeCreateLemmaData\" lemma utility function\n\n// Import utility modules\nimport { unsetFalsyProperties } from \"utilities/object\";\nimport { typeOf } from \"utilities/etc\";\n\n\n// computeCreateLemmaData: sync\n// returns a createLemma data object, i.e. an object containing the\n// data to be passed to the server when creating a new lemma, given the\n// specified createLemma data object\nexport function computeCreateLemmaData({\n lemmaData = null,\n referenceData = null,\n inflectionsKeys = [],\n definitionsKeys = [],\n rootLemmaKey\n}) {\n if (typeOf(lemmaData) !== \"object\") {\n throw new Error(`Parameter \"lemma\" should be an object`);\n } else if (inflectionsKeys.length === 0) {\n throw new Error(`Parameter \"inflectionsKeys\" should be a non-empty array`);\n } else if (definitionsKeys.length === 0) {\n throw new Error(`Parameter \"definitionsKeys\" should be a non-empty array`);\n }\n\n const createLemmaData = {\n lemmaData,\n referenceData, // may be null\n inflectionsKeys,\n definitionsKeys,\n rootLemmaKey // may be undefined\n };\n return unsetFalsyProperties(createLemmaData, {\n keepEmptyStrings: true\n });\n}\n","// Define the \"computeLemmaType\" lemma utility function\n\n// computeLemmaType: sync\n// returns the type of the specified lemma, computed from its cathegory\nexport function computeLemmaType(lemmaData) {\n const { cathegory: lemmaCathegory = \"?\" } = lemmaData || {};\n\n switch (lemmaCathegory) {\n case \"vrb\":\n return \"cnj\"; // conjugation\n case \"nou\":\n case \"prn\":\n case \"adj\":\n case \"adv\":\n case \"prt\":\n case \"grd\":\n return \"dcl\"; // declension\n case \"prp\":\n case \"cnj\":\n case \"int\":\n case \"art\":\n case \"pfx\":\n case \"sfx\":\n return \"mir\"; // mirror\n default:\n console.warn(`Unknown lemma cathegory \"${lemmaCathegory}\" ` +\n `for lemma \"${lemmaData.text}\"`);\n return \"?\"; // unknown\n }\n}\n","// Define number-related utility functions\nexport { roundToDecimal } from \"./number/round-to-decimal\";\n","// Define the \"roundToDecimal\" number utiiity module\n\n// roundToDecimal: sync\n// returns the specified number after rounding it to the specified number\n// of decimal places\nexport function roundToDecimal(number, decimals = 0) {\n const factor = Math.pow(10, decimals);\n return Math.round(number*factor)/factor;\n}\n","// Define object-related utility functions\nexport { hasOwnProperty } from \"utilities/object/has-own-property\";\nexport { pickProperty } from \"utilities/object/pick-property\";\nexport { pickProperties } from \"utilities/object/pick-properties\";\nexport { setProperty } from \"utilities/object/set-property\";\nexport { unsetProperties } from \"utilities/object/unset-properties\";\nexport { unsetFalsyProperties } from \"utilities/object/unset-falsy-properties\";\nexport { toString } from \"utilities/object/to-string\";\nexport { merge } from \"utilities/object/merge\";\n","// Define the \"hasOwnProperty\" object utility function\n\n// hasOwnProperty: sync\n// returns a boolean indicating whether the specified object has the\n// specified (own, i.e. not inherited) property\n// note: calling \"hasOwnProperty\" directly on an object is a potential\n// security vulnerability because it could not have such method defined\n// (see eslint's \"no-prototype-builtins\" rule)\nexport function hasOwnProperty(object, propertyName) {\n return Object.prototype.hasOwnProperty.call(object, propertyName);\n}\n","// Define the \"merge\" object utility function\n\n// Import utility modules\nimport { hasOwnProperty } from \"utilities/object/has-own-property\";\nimport { typeOf } from \"utilities/etc\";\n\n\n// merge: sync\n// returns an object resulting from merging the specified objects\nexport function merge(...objects) {\n const allObjects = objects\n .every(object => typeOf(object) === \"object\");\n if (!allObjects) {\n throw new Error(`All parameters should be objects`);\n }\n\n return objects\n .reduce((accObject, object) => {\n return Object.entries(object)\n .reduce((accObject, [ pName, pValue ]) => {\n if (!hasOwnProperty(accObject, pName)) {\n accObject[pName] = pValue;\n return accObject;\n }\n const accPValue = accObject[pName];\n if (typeOf(pValue) !== typeOf(accPValue) ||\n typeOf(pValue) !== \"object\") {\n accObject[pName] = pValue;\n return accObject;\n }\n accObject[pName] = merge(accPValue, pValue);\n return accObject;\n }, accObject);\n }, {});\n}\n","// Define the \"pickProperties\" object utility function\n\n// Import utility modules\nimport { hasOwnProperty } from \"utilities/object\";\n\n\n// pickProperties: sync\n// return an object obtained by picking the specified properties from the\n// specified object\nexport function pickProperties(object, propertyNames = []) {\n propertyNames = propertyNames || []; // for falsy argument\n return propertyNames\n .reduce((accObject, propertyName) => {\n if (hasOwnProperty(object, propertyName)) {\n accObject[propertyName] = object[propertyName];\n }\n return accObject;\n }, {});\n}\n","// Define the \"pickProperty\" object utility function\n\n// Import utility modules\nimport { hasOwnProperty } from \"utilities/object/has-own-property\";\nimport { typeOf } from \"utilities/etc\";\n\n\n// pickProperty: sync\n// returns the value of the specified property path (dot separated) within\n// the specified object\nexport function pickProperty(object, path) {\n return path\n .split(\".\")\n .reduce((value, pathItem) => {\n if (typeOf(value) === \"object\") {\n if (hasOwnProperty(value, pathItem)) {\n value = value[pathItem];\n } else {\n value = undefined;\n }\n }\n return value;\n }, object);\n}\n","// Define the \"setProperty\" object utility function\n\n// setProperty: sync\n// returns the specified object after setting the specified property path\n// to the specified value\n// note: the specified object is changed in-place, i.e. it is mutated\n// note: a warning is displayed if the property path does not exist in the\n// passed-in object already\nexport function setProperty(object, path, value) {\n const pathItems = path.split(\".\");\n const tempObject = pathItems\n .slice(0, -1)\n .reduce((tempObject, pathItem, pathIndex) => {\n if (!(pathItem in tempObject)) {\n //console.debug(`Creating missing object sub-property ` +\n //`\"${pathItems.slice(0, pathIndex + 1).join(\".\")}\"`);\n tempObject[pathItem] = {};\n }\n return tempObject[pathItem];\n }, object);\n tempObject[pathItems.at(-1)] = value;\n return object;\n}\n","// Define the \"toString\" object utility function\n\n// Import utility modules\nimport { unsetProperties } from \"utilities/object/unset-properties\";\nimport { typeOf } from \"utilities/etc\";\n\n\n// toString: sync\n// converts the specified object to string according to the specified\n// parameters\nexport function toString(object, params = {}) {\n if (typeOf(object) !== \"object\") {\n throw new Error(\"First argument should be an object\");\n } else if (typeOf(params) !== \"object\") {\n throw new Error(\"Second argument should be an object\");\n }\n const {\n excludeProperties = [],\n multiline = true\n } = params;\n if (typeOf(excludeProperties) !== \"array\") {\n throw new Error(\"excludeProperties parameter should be an array\");\n }\n\n object = unsetProperties(object, excludeProperties);\n return multiline ? JSON.stringify(object, null, 2) : JSON.stringify(object);\n}\n","// Define the \"unsetFalsyProperties\" object utility function\n\n// Import utility modules\nimport { typeOf } from \"utilities/etc\";\n\n// unsetFalsyProperties: sync\n// returns the specified data object after recursively un-setting the\n// specified its falsy properties\nexport function unsetFalsyProperties(data, options) {\n const {\n keepNullProperties = false,\n keepUndefinedProperties = false,\n keepEmptyStrings = false,\n keepNullNumbers = false,\n keepEmptyObjects = false,\n keepEmptyArrays = false,\n } = options || {};\n return Object.entries(data)\n .reduce((accObject, [ pName, pValue ]) => {\n switch (typeOf(pValue)) {\n case \"string\":\n if (pValue !== \"\" || keepEmptyStrings) {\n accObject[pName] = pValue;\n }\n return accObject;\n case \"number\":\n if (pValue !== 0 || keepNullNumbers) {\n accObject[pName] = pValue;\n }\n return accObject;\n case \"date\":\n accObject[pName] = pValue;\n return accObject;\n case \"object\":\n if (pValue !== {} || keepEmptyObjects) {\n accObject[pName] = unsetFalsyProperties(pValue, options);\n }\n return accObject;\n case \"array\":\n if (pValue.length || keepEmptyArrays) {\n accObject[pName] = pValue\n .map(item => typeOf(item) === \"object\" ?\n unsetFalsyProperties(item, options) : item\n );\n }\n return accObject;\n case \"null\":\n if (keepNullProperties) {\n accObject[pName] = pValue;\n }\n return accObject;\n case \"undefined\":\n if (keepUndefinedProperties) {\n accObject[pName] = pValue;\n }\n return accObject;\n default:\n accObject[pName] = pValue;\n return accObject;\n }\n }, {});\n}\n","// Define the \"unsetProperties\" object utility function\n\n// unsetProperties: sync\n// returns the specified object after the unsetting the specified property\n// names\nexport function unsetProperties(object, propertyNames = []) {\n return Object.entries(object)\n .reduce((accObject, [ propertyName, propertyValue ]) => {\n if (!(propertyNames.includes(propertyName))) {\n accObject[propertyName] = propertyValue;\n }\n return accObject;\n }, {});\n}\n","// Define promise-related utility functions\nexport { pause } from \"utilities/promise/pause\";\nexport { resolveAfter } from \"utilities/promise/resolve-after\";\nexport { rejectAfter } from \"utilities/promise/reject-after\";\nexport { executeAfter } from \"utilities/promise/execute-after\";\nexport { withTimeout } from \"utilities/promise/with-timeout\";\nexport { exponentialBackoff } from \"utilities/promise/exponential-backoff\";\n","// Define the \"executeAfter\" promise utility function\n\n// executeAfter: async\n// returns a promise which resolves/rejects with the result returned by\n// the specified promise after the specified timeout (in milliseconds)\nexport async function executeAfter(timeoutMs, promise) {\n return new Promise((resolve, reject) => {\n setTimeout(async () => {\n try {\n const result = await promise;\n resolve(result);\n } catch(error) {\n reject(error);\n }\n }, timeoutMs);\n });\n}\n","// Define the \"exponentialBackoff\" promise utility function\n\n// Import library modules\nimport ms from \"ms\";\n\n// Import utility modules\nimport { pause } from \"utilities/promise/pause\";\nimport { typeOf } from \"utilities/etc\";\n\n// Import parameter modules\nimport {\n backoffTimeoutsMs as defaultBackoffTimeoutsMs\n} from \"parameters/promise\";\n\n// Define internal parameters\nfunction defaultBackoffCallback({ backoffTimeoutMs, error }) {\n console.error(`Exponentially backing off by ${ms(backoffTimeoutMs)} ` +\n `upon error`, error);\n return true;\n}\n\n\n// exponentialBackoff: async\n// returns a promise which either resolves with the return value of the\n// specified promise (or async function) within the specified back-off\n// timeouts, or rejects with an error\nexport async function exponentialBackoff(promise, {\n backoffCallback = defaultBackoffCallback,\n backoffTimeoutsMs = defaultBackoffTimeoutsMs,\n backoffFailureError = new Error(`Exponential back-off failed`),\n cumulatedBackoffTimeoutMs = 0\n} = {}) {\n if (!promise) {\n throw new Error(`Missing required parameter \"promise\"`);\n } else if (typeOf(backoffTimeoutsMs) !== \"array\") {\n throw new Error(`Parameter \"backoffTimeoutsMs\" should be an array`);\n }\n\n let finalPromise;\n switch (typeOf(promise)) {\n case \"function\":\n case \"asyncfunction\":\n finalPromise = promise();\n break;\n case \"promise\":\n finalPromise = promise;\n break;\n default:\n throw new Error(`First argument should be a promise or async function`);\n }\n\n try {\n return await finalPromise;\n } catch(error) {\n const [\n currentBackoffTimeoutMs,\n ...restBackoffTimeoutsMs\n ] = backoffTimeoutsMs;\n if (currentBackoffTimeoutMs === undefined) {\n throw backoffFailureError;\n }\n const performBackoff = await backoffCallback({\n backoffTimeoutsMs: currentBackoffTimeoutMs,\n cumulatedBackoffTimeoutMs,\n error\n });\n if (performBackoff) {\n await pause(currentBackoffTimeoutMs);\n cumulatedBackoffTimeoutMs += currentBackoffTimeoutMs;\n return await exponentialBackoff(promise, {\n backoffTimeoutsMs: restBackoffTimeoutsMs,\n cumulatedBackoffTimeoutMs,\n backoffCallback\n });\n } else {\n throw error;\n }\n }\n}\n","// Define the \"pause\" promise utility function\n\n// pause: async\n// returns a promise which resolves after the specified timeout (in ms)\nexport async function pause(timeoutMs = 0) {\n return new Promise(resolve => {\n setTimeout(() => {\n resolve();\n }, timeoutMs);\n });\n}\n","// Define the \"rejectAfter\" promise utility function\n\n// rejectAfter: async\n// returns a promise which rejects with the specified error instance after\n// the specified timeout (in milliseconds)\nexport async function rejectAfter(timeoutMs, error = null) {\n error = error || new Error(`Promise rejected at ${timeoutMs}ms timeout`);\n return new Promise((_, reject) => {\n setTimeout(() => {\n reject(error);\n }, timeoutMs);\n });\n}\n","// Define the \"resolveAfter\" promise utility function\n\n// resolveAfter: async\n// returns a promise which resolves with the specified data after the\n// specified timeout (in milliseconds)\nexport async function resolveAfter(timeoutMs, data = null) {\n return new Promise(resolve => {\n setTimeout(() => {\n resolve(data);\n }, timeoutMs);\n });\n}\n","// Define the \"withTimeout\" promise utility function\n\n// Import utility modules\nimport { rejectAfter } from \"utilities/promise/reject-after\";\nimport { typeOf } from \"utilities/etc\";\n\n// import parameter modules\nimport { timeoutMs as defaultTimeoutMs } from \"parameters/promise\";\n\n\n// withTimeout: async\n// returns a promise which either resolves/rejects with the result\n// returned by the specified promise, or rejects with the specified error\n// after the specified timeout has expired\nexport async function withTimeout(promise, {\n timeoutMs = defaultTimeoutMs,\n error = new Error(`Promise time-out elapsed`)\n}) {\n let finalPromise;\n switch (typeOf(promise)) {\n case \"function\":\n case \"asyncfunction\":\n finalPromise = promise();\n break;\n case \"promise\":\n finalPromise = promise;\n break;\n default:\n throw new Error(`First argument should be a promise or async function`);\n }\n\n return Promise.race([\n finalPromise,\n rejectAfter(timeoutMs, error)\n ]);\n}\n","// Define graphQL-query-related utility functions\nexport { formatQuery } from \"utilities/query/format-query\";\nexport { compressQuery } from \"utilities/query/compress-query\";\nexport {\n removeDuplicateFragments\n} from \"utilities/query/remove-duplicate-fragments\";\n","// Define the \"compressQuery\" query utility modules\n\n// compressQuery: sync\n// returns the specified graphQL query string after compressing it by\n// shortening shallow/deep fragment namess and removing un-necessary\n// white-spaces\nexport function compressQuery(queryString) {\n return queryString\n .replace(/Shallow/g, \"Sh\")\n .replace(/Deep/g, \"Dp\")\n .replace(/Item/g, \"It\")\n .replace(/Fragment/g, \"\")\n .replace(/\\(\\s+/g, \"(\")\n .replace(/\\s+\\)/g, \")\")\n .replace(/\\{\\s+/g, \"{\")\n .replace(/\\s+}/g, \"}\")\n .trim();\n}\n","// Define the \"formatQuery\" query utility function\n\n// Import utility modules\nimport { compressQuery } from \"utilities/query/compress-query\";\nimport {\n removeDuplicateFragments\n} from \"utilities/query/remove-duplicate-fragments\";\n\n// Import parameter modules\nimport { runMode as defaultRunMode } from \"parameters/environment\";\n\n\n// formatQuery: sync\n// returns the specified graphQL query string after removing new-lines and\n// multiple spaces from it, depending on the runMode\nexport function formatQuery(queryString, optionsData = null) {\n const { runMode = defaultRunMode } = optionsData || {};\n\n const dedupedQueryString = removeDuplicateFragments(queryString);\n const compressedQuery = compressQuery(dedupedQueryString);\n if ([\"dev\"].includes(runMode)) {\n const keywordRegExp = / (query|mutation|fragment)/g;\n return compressedQuery\n .replace(keywordRegExp, (_, keyword) => `\\n${keyword}`);\n }\n return compressedQuery;\n}\n","// Define the \"removeDuplicateFragments\" query utility function\n\n// Import utility modules\nimport { removeMultipleSpaces, removeNewLines } from \"utilities/string\";\n\n\n// removeDuplicateFragments: sync\n// returns the specified graphQL query string after removing duplicate\n// fragments from it\nexport function removeDuplicateFragments(queryString) {\n const fragmentsSet = new Map();\n const keywordRegExp = / ?(fragment)/g;\n const queryRegExp = /(\\w|\\n)fragment (\\S+) on (\\S+) [^\\n]+/g;\n const compactQueryString = removeMultipleSpaces(removeNewLines(queryString));\n const dedupedQueryString = compactQueryString\n .replace(keywordRegExp, (_, keyword) => `\\n${keyword}`)\n .replace(queryRegExp, (match, _, fragmentName, entityType) => {\n const fragmentKey = `${fragmentName}-${entityType}`;\n if (fragmentsSet.has(fragmentKey)) { // duplicate fragment\n return \"\";\n }\n fragmentsSet.set(fragmentKey);\n return match;\n });\n return removeMultipleSpaces(removeNewLines(dedupedQueryString));\n}\n","// Define sentence-related utility functions\nexport { ensureTrailingMark } from \"./sentence/ensure-trailing-mark\";\nexport { normalizeWords } from \"./sentence/normalize-words\";\nexport { normalizeMarks } from \"./sentence/normalize-marks\";\nexport {\n computeCreateSentenceData\n} from \"./sentence/compute-create-sentence-data\";\nexport {\n computeSentenceTextFromElements\n} from \"./sentence/compute-sentence-text-from-elements\";\n","// Define the \"computeCreateSentenceData\" sentence utility function\n\n// Import utility modules\nimport { unsetFalsyProperties } from \"utilities/object\";\nimport { typeOf } from \"utilities/etc\";\n\n\n// computeCreateSentenceData: sync\n// returns a createSentence data object, i.e. an object containing the\n// data to be passed to the server when creating a new sentence, given the\n// specified sentence data object\nexport function computeCreateSentenceData({\n sentenceData: {\n text: sentenceText = \"\",\n localeCode: sentenceLocaleCode\n } = {},\n authorKey = \"\"\n}) {\n if (typeOf(sentenceText) !== \"string\") {\n throw new Error(`Parameter \"text\" should be a string`);\n } else if (typeOf(sentenceLocaleCode) !== \"string\") {\n throw new Error(`Parameter \"localeCode\" should be a string`);\n } else if (typeOf(authorKey) !== \"string\") {\n throw new Error(`Parameter \"authorKey\" should be a string`);\n }\n\n return {\n sentenceData: unsetFalsyProperties({\n text: sentenceText,\n localeCode: sentenceLocaleCode\n }, {\n keepEmptyStrings: true\n }),\n ...(authorKey ? { authorKey } : {})\n };\n}\n","// Define the \"computeSentenceTextFromElements\" sentence utility function\n\n// Define internal parameters\nconst prefixRegExp = /<\\|pfx\\|>\\s+/g;\nconst whitespaceRegExp = /\\s+/g;\nconst markRegExp = /\\s+([.,:;!?])/g;\n\n\n// computeSentenceTextFromElements: sync\n// returns the sentence text string obtained by joining the specified\n// array of elements according to their types\nexport function computeSentenceTextFromElements(elementsData) {\n return elementsData\n .filter(elementData => Boolean(elementData))\n .map(({ type: elementType, text: elementText = \"\" }) => {\n switch(elementType) {\n case \"wrd\":\n case \"grp\":\n case \"cmp\":\n return ` ${elementText || \"…\"}`;\n case \"num\":\n return ` ${elementText}`;\n case \"pfx\":\n return ` ${elementText}-<|pfx|>`; // prefix placeholder\n case \"sfx\":\n return `-${elementText}`;\n case \"mrk\":\n return `${elementText}`;\n }\n })\n .join(\"\")\n .replaceAll(prefixRegExp, \"\") // remove prefix placeholder\n .replaceAll(whitespaceRegExp, \" \") // remove multiple whitespaces\n .replaceAll(markRegExp, (_, mark) => mark) // remove whitespaces before marks\n .trim();\n}\n","// Define the \"ensureTrailingMark\" string utility function \n\n// Import parameter modules\nimport { endsWithMarkRegExp } from \"parameters/sentence\";\n\n\n// ensureTrailingMark: sync\n// returns the specified sentence text after ensuring it ends with a\n// trailing mark (either the original one, or the specified one)\nexport function ensureTrailingMark({\n sentenceText = \"\",\n trailingMark = \".\"\n}) {\n const trimmedSentenceText = sentenceText.trim();\n return !trimmedSentenceText ? \"\" :\n endsWithMarkRegExp.test(trimmedSentenceText) ?\n trimmedSentenceText : `${trimmedSentenceText}${trailingMark}`;\n}\n","// Define the \"normalizeMarks\" sentence utility function;\n\n// Import parameter modules\nimport { normalizeMarksRegExp } from \"parameters/sentence\";\n\n\n// normalizeMarks: sync\n// returns the specified sentence after converting non-leading characters \n// of each word to lower case\nexport function normalizeMarks(sentenceText) {\n return sentenceText\n .replace(normalizeMarksRegExp, (_, mark) => mark);\n}\n","// Define the \"normalizeWords\" sentence utility function;\n\n// normalizeWords: sync\n// returns the specified sentence after converting non-leading characters \n// of each word to lower case\nexport function normalizeWords(sentenceText = \"\") {\n return sentenceText ?\n sentenceText\n .split(/\\s+/g)\n .map(word => word.replaceAll(/\\b(\\w)(\\w+)/g, (_, leading, following) => {\n return `${leading}${following.toLowerCase()}`;\n }))\n .join(\" \") :\n sentenceText;\n}\n","// Define state-related utility functions\nexport { computeInitialMetaData } from \"./state/compute-initial-meta-data\";\nexport { computeUpdatedMetaData } from \"./state/compute-updated-meta-data\";\nexport { computeMergedCardMetaData } from \"./state/compute-merged-card-meta-data\";\nexport { computeSplitCardsMetaData } from \"./state/compute-split-cards-meta-data\";\nexport { computeRebasedStateData } from \"./state/compute-rebased-state-data\";\n","// Define the \"computeInitialMetaData\" state utility function \n\n// Import utility modules\nimport { computeAnalysisActivityDigest } from \"utilities/activity\"\nimport { canMergeCards, canSplitCard } from \"utilities/card\";\nimport { computeAnalysisDigest } from \"utilities/analysis\";\nimport { typeOf } from \"utilities/etc\";\n\n\n// computeInitialMetaData: sync\n// returns an initial metadata object corresponding to the specified\n// analysis activity data object\nexport function computeInitialMetaData({\n analysisActivityData = null\n}) {\n if (typeOf(analysisActivityData) !== \"object\") {\n throw new Error(`Parameter \"analysisActivityData\" should be an object`);\n }\n\n const { analysis: analysisData = null } = analysisActivityData || {};\n const { \n outSentence: outSentenceData = null,\n cards: cardsData = [] \n } = analysisData || {};\n const { text: outSentenceText = \"\" } = outSentenceData || {};\n\n // Return initial metadata\n return {\n savedDigest: computeAnalysisActivityDigest(analysisActivityData),\n isModified: false,\n isValid: true,\n action: {\n name: \"saveAnalysisActivity\"\n },\n analysis: {\n savedDigest: computeAnalysisDigest(analysisData),\n isModified: false,\n isValid: true,\n outSentence: { \n savedText: outSentenceText,\n isModified: false, \n isValid: true \n },\n cards: cardsData.map((cardData, cardIndex) => {\n const { \n outElement: outElementData = null,\n lemmas: lemmasData = [],\n inInflectors: inInflectorsData = [],\n definitions: definitionsData = []\n } = cardData || {};\n const previousCardData = cardIndex === 0 ? null :\n cardsData.at(cardIndex-1) || null;\n const nextCardData = cardsData.at(cardIndex+1) || null;\n const { text: outElementText = \"\" } = outElementData || {};\n return {\n isModified: false,\n isValid: true,\n canMergePreviousCard: canMergeCards({\n cardData1: previousCardData,\n cardData2: cardData,\n cardMetaData1: { isValid: true }, // valid upon init\n cardMetaData2: { isValid: true }, // valid upon init\n }),\n canMergeNextCard: canMergeCards({\n cardData1: cardData,\n cardData2: nextCardData,\n cardMetaData1: { isValid: true }, // valid upon init\n cardMetaData2: { isValid: true }, // valid upon init\n }),\n canSplitCard: canSplitCard({ \n cardData,\n cardMetaData: { isValid: true } // valid upon init\n }),\n outElement: { \n savedText: outElementText,\n isModified: false, \n isValid: true \n },\n lemmas: lemmasData\n .map(lemmaData => lemmaData || {})\n .map(({ key: lemmaKey = \"\" }) => ({\n savedKey: lemmaKey,\n isModified: false\n })),\n inInflectors: inInflectorsData\n .map(inInflectorData => inInflectorData || {})\n .map(({ key: inInflectorKey = \"\" }) => ({\n savedKey: inInflectorKey,\n isModified: false\n })),\n definitions: definitionsData\n .map(definitionData => definitionData || {})\n .map(({ key: definitionKey = \"\" }) => ({\n savedKey: definitionKey,\n isModified: false\n }))\n };\n })\n }\n };\n}\n","// Define the \"computeMergedCardMetaData\" state utility function\n\n// Import utility modules\nimport { typeOf } from \"utilities/etc\";\n\n\n// computeMergedCardMetaData: sync\n// returns a card's metadata object obtained by merging the specified cards'\n// metadata objects\nexport function computeMergedCardMetaData({\n cardMetaData1 = null,\n cardMetaData2 = null\n}) {\n if (typeOf(cardMetaData1) !== \"object\") {\n throw new Error(`Parameter \"cardMetaData1\" should be an object`);\n } else if (typeOf(cardMetaData2) !== \"object\") {\n throw new Error(`Parameter \"cardMetaData2\" should be an object`);\n }\n\n const {\n isModified: cardDataModified1 = false,\n isValid: cardDataValid1 = true,\n outElement: {\n savedText: savedOutElementText1 = \"\",\n isModified: outElementDataModified1 = false,\n isValid: outElementDataValid1 = true,\n }\n } = cardMetaData1 || {};\n const {\n isModified: cardDataModified2 = false,\n isValid: cardDataValid2 = true,\n outElement: {\n savedText: savedOutElementText2 = \"\",\n isModified: outElementDataModified2 = false,\n isValid: outElementDataValid2 = true,\n }\n } = cardMetaData2 || {};\n \n const mergedSavedOutElementText = [savedOutElementText1, savedOutElementText2]\n .map(outElementText => outElementText.trim())\n .join(\" \")\n .trim();\n return {\n isModified: cardDataModified1 || cardDataModified2,\n isValid: cardDataValid1 && cardDataValid2,\n outElement: {\n savedText: mergedSavedOutElementText,\n isModified: outElementDataModified1 || outElementDataModified2,\n isValid: outElementDataValid1 || outElementDataValid2\n }\n };\n}\n","// Define the \"computeRebasedStateData\" state utility function\n\n// Import utility modules\nimport { computeAnalysisActivityDigest } from \"utilities/activity\"\nimport { computeAnalysisDigest } from \"utilities/analysis\";\n\n\n// computeRebasedStateData: sync\n// returns an object representing the rebased version of the specified\n// state object with respect to the specified saved state object\nexport function computeRebasedStateData({\n stateData = null,\n savedStateData = null,\n}) {\n // Destructure state data\n const { \n liveData: analysisActivityData = null,\n metaData: analysisActivityMetaData = null\n } = stateData || {};\n\n const { analysis: analysisData = null } = analysisActivityData || {};\n const { analysis: analysisMetaData = null } = analysisActivityMetaData || {};\n\n const { \n outSentence: outSentenceData = null,\n cards: cardsData = [] \n } = analysisData || {};\n const { \n outSentence: outSentenceMetaData = null,\n cards: cardsMetaData = []\n } = analysisMetaData || {};\n\n // Destructure saved state data\n const { \n metaData: savedAnalysisActivityMetaData = null\n } = savedStateData || {};\n const { \n savedDigest: savedAnalysisActivityDigest = \"\",\n analysis: savedAnalysisMetaData = null \n } = savedAnalysisActivityMetaData || {};\n\n const { \n savedDigest: savedAnalysisDigest = \"\",\n outSentence: savedOutSentenceMetaData = null,\n cards: savedCardsMetaData = []\n } = savedAnalysisMetaData || {};\n\n // Compute rebased output sentence data\n const { text: outSentenceText = \"\" } = outSentenceData || {};\n const { \n savedText: savedOutSentenceText = \"\" \n } = savedOutSentenceMetaData || {};\n const rebasedOutSentenceMetaData = {\n ...(outSentenceMetaData || {}),\n savedText: savedOutSentenceText,\n isModified: outSentenceText !== savedOutSentenceText\n };\n\n // Compute rebased cards data\n const rebasedCardsMetaData = cardsMetaData.map((cardMetaData, cardIndex) => {\n const cardData = cardsData.at(cardIndex) || null;\n const savedCardMetaData = savedCardsMetaData.at(cardIndex) || null;\n\n const { outElement: outElementData = null } = cardData || {};\n const { \n outElement: outElementMetaData = null,\n lemmas: lemmasMetaData = [],\n inInflectors: inInflectorsMetaData = [],\n definitions: definitionsMetaData = []\n } = cardMetaData || {};\n const { \n outElement: savedOutElementMetaData = null,\n lemmas: savedLemmasMetaData = [],\n inInflectors: savedInInflectorsMetaData = [],\n definitions: savedDefinitionsMetaData = []\n } = savedCardMetaData || {};\n\n // Compute rebased output element's metadata\n const { text: outElementText = \"\" } = outElementData || {};\n const { \n savedText: savedOutElementText = \"\" \n } = savedOutElementMetaData || {};\n const rebasedOutElementMetaData = {\n ...(outElementMetaData || {}),\n savedText: savedOutElementText,\n isModified: outElementText !== savedOutElementText\n };\n\n // Compute rebased lemmas' metadata\n const { lemmas: lemmasData = [] } = cardData || {};\n const rebasedLemmasMetaData = lemmasData\n .map(lemmaData => lemmaData || {})\n .map(({ key: lemmaKey = \"\" }, lemmaIndex) => {\n const lemmaMetaData = lemmasMetaData.at(lemmaIndex) || null;\n const savedLemmaMetaData = \n savedLemmasMetaData.at(lemmaIndex) || null;\n const { \n savedKey: savedLemmaKey = \"\" \n } = savedLemmaMetaData || {};\n return {\n ...(lemmaMetaData || {}),\n savedKey: savedLemmaKey,\n isModified: lemmaKey !== savedLemmaKey\n };\n });\n\n // Compute rebased input inflectors' metadata\n const { inInflectors: inInflectorsData = [] } = cardData || {};\n const rebasedInInflectorsMetaData = inInflectorsData\n .map(inInflectorData => inInflectorData || {})\n .map(({ key: inInflectorKey = \"\" }, inInflectorIndex) => {\n const inInflectorMetaData = \n inInflectorsMetaData.at(inInflectorIndex) || null;\n const savedInInflectorMetaData = \n savedInInflectorsMetaData.at(inInflectorIndex) || null;\n const { \n savedKey: savedInInflectorKey = \"\" \n } = savedInInflectorMetaData || {};\n return {\n ...(inInflectorMetaData || {}),\n savedKey: savedInInflectorKey,\n isModified: inInflectorKey !== savedInInflectorKey\n };\n });\n\n // Compute rebased definitions' metadata\n const { definitions: definitionsData = [] } = cardData || {};\n const rebasedDefinitionsMetaData = definitionsData\n .map(definitionData => definitionData || {})\n .map(({ key: definitionKey = \"\" }, definitionIndex) => {\n const definitionMetaData = \n definitionsMetaData.at(definitionIndex) || null;\n const savedDefinitionMetaData = \n savedDefinitionsMetaData.at(definitionIndex) || null;\n const { \n savedKey: savedDefinitionKey = \"\" \n } = savedDefinitionMetaData || {};\n return {\n ...(definitionMetaData || {}),\n savedKey: savedDefinitionKey,\n isModified: definitionKey !== savedDefinitionKey\n };\n });\n\n const { \n isModified: outElementDataModified = false \n } = rebasedOutElementMetaData || {};\n const someLemmaDataModified = rebasedLemmasMetaData.some(({ \n isModified: lemmaDataModified = false \n }) => lemmaDataModified);\n const someInInflectorDataModified = rebasedInInflectorsMetaData.some(({ \n isModified: inInflectorDataModified = false\n }) => inInflectorDataModified);\n const someDefinitionDataModified = rebasedDefinitionsMetaData.some(({ \n isModified: definitionDataModified = false\n }) => definitionDataModified);\n return {\n ...cardMetaData,\n isModified: outElementDataModified || someLemmaDataModified ||\n someInInflectorDataModified || someDefinitionDataModified,\n outElement: rebasedOutElementMetaData,\n lemmas: rebasedLemmasMetaData,\n inInflectors: rebasedInInflectorsMetaData,\n definitions: rebasedDefinitionsMetaData \n };\n });\n const analysisActivityDigest = \n computeAnalysisActivityDigest(analysisActivityData)\n const analysisDigest = \n computeAnalysisDigest(analysisData);\n\n // Return rebased state data\n return {\n liveData: analysisActivityData,\n metaData: {\n ...(analysisActivityMetaData || {}),\n isModified: analysisActivityDigest !== savedAnalysisActivityDigest,\n analysis: {\n ...(analysisMetaData || {}),\n isModified: analysisDigest !== savedAnalysisDigest,\n outSentence: rebasedOutSentenceMetaData,\n cards: rebasedCardsMetaData\n }\n }\n };\n}\n","// Define the \"computeSplitCardsMetaData\" state utility function \n\n// computeSplitCardsMetaData: sync\n// returns an array of split cards metadata corresponding to the\n// specified array of splitted cards\nexport function computeSplitCardsMetaData({\n splitCardsData = []\n}) {\n return splitCardsData.map(_ => ({\n isModified: false,\n isValid: true,\n canMergePreviousCard: false, // will be re-computed\n canMergeNextCard: false, // will be re-computed\n canSplitCard: false, // will be re-computed\n outElement: {\n savedText: \"\",\n isModified: false,\n isValid: true\n }\n }));\n}\n","// Define the \"computeUpdatedMetaData\" state utility function\n\n// Import utility modules\nimport { computeAnalysisActivityDigest } from \"utilities/activity\"\nimport { canMergeCards, canSplitCard } from \"utilities/card\";\nimport { computeAnalysisDigest } from \"utilities/analysis\";\n\n\n// computeUpdatedMetaData: sync\n// returns an object representing the updated version of the specified\n// analysis activity's metadata object, in which \"isModified\" and \"isValid\"\n// fields are recursively updated in a bottom-up fashion\nexport function computeUpdatedMetaData({\n analysisActivityData = null,\n analysisActivityMetaData = null\n}) {\n const { analysis: analysisData = null } = analysisActivityData || {};\n const { cards: cardsData = [] } = analysisData || {};\n const { \n savedDigest: savedAnalysisActivityDigest = \"\",\n analysis: analysisMetaData = null \n } = analysisActivityMetaData || {};\n const { \n savedDigest: savedAnalysisDigest = \"\",\n outSentence: outSentenceMetaData = null,\n cards: cardsMetaData = []\n } = analysisMetaData || {};\n\n const { \n isValid: outSentenceDataValid = false \n } = outSentenceMetaData || {};\n const updatedCardsMetaData = cardsMetaData\n .map(cardMetaData => {\n const { \n outElement: outElementMetaData = null,\n lemmas: lemmasMetaData = [],\n inInflectors: inInflectorsMetaData = [],\n definitions: definitionsMetaData = []\n } = cardMetaData || {};\n const { \n isModified: outElementDataModified = false,\n isValid: outElementDataValid = true \n } = outElementMetaData || {};\n const someLemmaDataModified = lemmasMetaData.some(({ \n isModified: lemmaDataModified = false \n }) => lemmaDataModified);\n const someInInflectorDataModified = inInflectorsMetaData.some(({ \n isModified: inInflectorDataModified = false\n }) => inInflectorDataModified);\n const someDefinitionDataModified = definitionsMetaData.some(({ \n isModified: definitionDataModified = false\n }) => definitionDataModified);\n return {\n ...cardMetaData,\n isModified: outElementDataModified || someLemmaDataModified ||\n someInInflectorDataModified || someDefinitionDataModified,\n isValid: outElementDataValid\n };\n })\n .map((cardMetaData, cardIndex, cardsMetaData) => {\n const cardData = cardsData.at(cardIndex) || null;\n const previousCardData = cardIndex === 0 ? null :\n cardsData.at(cardIndex-1) || null;\n const previousCardMetaData = cardIndex === 0 ? null :\n cardsMetaData.at(cardIndex-1) || null;\n const nextCardData = cardsData.at(cardIndex+1) || null;\n const nextCardMetaData = cardsMetaData.at(cardIndex+1) || null;\n return {\n ...cardMetaData,\n canMergePreviousCard: canMergeCards({\n cardData1: previousCardData,\n cardData2: cardData,\n cardMetaData1: previousCardMetaData,\n cardMetaData2: cardMetaData,\n }),\n canMergeNextCard: canMergeCards({\n cardData1: cardData,\n cardData2: nextCardData,\n cardMetaData1: cardMetaData,\n cardMetaData2: nextCardMetaData,\n }),\n canSplitCard: canSplitCard({ \n cardData,\n cardMetaData\n }),\n };\n });\n const analysisActivityDigest = \n computeAnalysisActivityDigest(analysisActivityData)\n const analysisActivityDataModified = \n analysisActivityDigest !== savedAnalysisActivityDigest;\n const analysisDigest = computeAnalysisDigest(analysisData);\n const analysisDataModified = analysisDigest !== savedAnalysisDigest;\n const everyCardDataValid = updatedCardsMetaData\n .every(({ isValid: cardDataValid = true }) => cardDataValid);\n const analysisDataValid = outSentenceDataValid && everyCardDataValid;\n return {\n ...analysisActivityMetaData,\n isModified: analysisActivityDataModified,\n isValid: analysisDataValid,\n analysis: {\n ...analysisMetaData,\n isModified: analysisDataModified,\n isValid: analysisDataValid,\n outSentence: outSentenceMetaData,\n cards: updatedCardsMetaData\n }\n };\n}\n","// Define (local/session) storage-related utility functions\nexport {\n getLocalStorageData\n} from \"utilities/storage/get-local-storage-data\";\nexport {\n setLocalStorageData\n} from \"utilities/storage/set-local-storage-data\";\nexport {\n patchLocalStorageData\n} from \"utilities/storage/patch-local-storage-data\";\nexport {\n getSessionStorageData\n} from \"utilities/storage/get-session-storage-data\";\nexport {\n setSessionStorageData\n} from \"utilities/storage/set-session-storage-data\";\nexport {\n patchSessionStorageData\n} from \"utilities/storage/patch-session-storage-data\";\n","// Define the \"getLocalStorageData\" storage utility function\n\n// Import utility modules\nimport { parseJSON } from \"utilities/string\";\nimport { typeOf } from \"utilities/etc\";\n\n// Import parameter modules\nimport { localStorageKey as defaultLocalStorageKey } from \"parameters/state\";\n\n\n// getLocalStorageData: sync\n// returns the data object stored under the specified key of the\n// user-agent's localStorage\nexport function getLocalStorageData({\n key: localStorageKey = defaultLocalStorageKey\n} = {}) {\n if (typeOf(localStorageKey) !== \"string\") {\n throw new Error(`Parameter \"key\" should be a non-empty string`);\n } else if (!window.localStorage) {\n throw new Error(`User agent does not support local storage`);\n }\n\n const dataString = window.localStorage.getItem(localStorageKey) || \"\";\n if (!dataString) {\n return null;\n }\n let data;\n try {\n data = parseJSON(dataString);\n } catch(error) {\n console.error(error);\n throw new Error(`Error parsing local storage's string data ` +\n `for key \"${localStorageKey}\"`);\n }\n return data;\n}\n","// Define the \"getSessionStorageData\" storage utility function\n\n// Import utility modules\nimport { parseJSON } from \"utilities/string\";\nimport { typeOf } from \"utilities/etc\";\n\n// Import parameter modules\nimport { sessionStorageKey as defaultSessionStorageKey } from \"parameters/state\";\n\n\n// getSessionStorageData: sync\n// returns the data object stored undet the specified key of the\n// user-agent's sessionStorage\nexport function getSessionStorageData({\n key: sessionStorageKey = defaultSessionStorageKey\n} = {}) {\n if (typeOf(sessionStorageKey) !== \"string\") {\n throw new Error(`Parameter \"key\" should be a non-empty string`);\n } else if (!window.sessionStorage) {\n throw new Error(`User agent does not support session storage`);\n }\n\n const dataString = window.sessionStorage.getItem(sessionStorageKey) || \"\";\n if (!dataString) {\n return null;\n }\n let data;\n try {\n data = parseJSON(dataString);\n } catch(error) {\n console.error(error);\n throw new Error(`Error parsing session storage's string data ` +\n `for key \"${sessionStorageKey}\"`);\n }\n return data;\n}\n","// Define the \"patchLocalStorageData\" storage utility function\n\n// Import utility modules\nimport { getLocalStorageData } from \"./get-local-storage-data\";\nimport { setLocalStorageData } from \"./set-local-storage-data\";\nimport { merge } from \"utilities/object\";\nimport { typeOf } from \"utilities/etc\";\n\n// Import parameter modules\nimport { localStorageKey as defaultLocalStorageKey } from \"parameters/state\";\n\n\n// patchLocalStorageData: sync\n// patches the data object already available under the specified key of\n// the user-agent's localStorage with the specified data object \nexport function patchLocalStorageData({\n key: localStorageKey = defaultLocalStorageKey,\n data: patchData = {}\n}) {\n if (typeOf(localStorageKey) !== \"string\") {\n throw new Error(`Parameter \"key\" should be a non-empty string`);\n } else if (![\"object\", \"null\"].includes(typeOf(patchData))) {\n throw new Error(`Parameter \"data\" should be an object or null`);\n } else if (!window.localStorage) {\n throw new Error(`User agent does not support local storage`);\n }\n\n const existingData = getLocalStorageData({ \n key: localStorageKey \n });\n if (!existingData) {\n throw new Error(`Could not find local storage data ` +\n `for key \"${localStorageKey}\"`);\n }\n const patchedData = merge(existingData, patchData);\n setLocalStorageData({ \n key: localStorageKey, \n data: patchedData \n });\n}\n","// Define the \"patchSessionStorageData\" storage utility function\n\n// Import utility modules\nimport {\n getSessionStorageData\n} from \"utilities/storage/get-session-storage-data\";\nimport {\n setSessionStorageData\n} from \"utilities/storage/set-session-storage-data\";\nimport { merge } from \"utilities/object\";\nimport { typeOf } from \"utilities/etc\";\n\n// Import parameter modules\nimport { \n sessionStorageKey as defaultSessionStorageKey \n} from \"parameters/state\";\n\n\n// patchSessionStorageData: sync\n// patches the data object already present under the specified key of the\n// user-agent's sessionStorage with the specified data object\nexport function patchSessionStorageData({\n key: sessionStorageKey = defaultSessionStorageKey,\n data: patchData = {}\n}) {\n if (typeOf(sessionStorageKey) !== \"string\") {\n throw new Error(`Parameter \"key\" should be a non-empty string`);\n } else if (![\"object\", \"null\"].includes(typeOf(patchData))) {\n throw new Error(`Parameter \"data\" should be an object or null`);\n } else if (!window.sessionStorage) {\n throw new Error(`User agent does not support session storage`);\n }\n\n const existingData = getSessionStorageData({ \n key: sessionStorageKey \n });\n if (!existingData) {\n throw new Error(`Could not find session storage data ` +\n `for key \"${sessionStorageKey}\"`);\n }\n const patchedData = merge(existingData, patchData);\n setSessionStorageData({ \n key: sessionStorageKey, \n data: patchedData \n });\n}\n","// Define the \"setLocalStorageData\" storage utility function\n\n// Import utility modules\nimport { typeOf } from \"utilities/etc\";\n\n// Import parameter modules\nimport { localStorageKey as defaultLocalStorageKey } from \"parameters/state\";\n\n\n// setLocalStorageData: sync\n// sets the specified key of the user-agent's localStorage to the\n// specified data object \nexport function setLocalStorageData({\n key = defaultLocalStorageKey,\n data\n}) {\n if (typeOf(key) !== \"string\") {\n throw new Error(`Parameter \"key\" should be a non-empty string`);\n } else if (![\"object\", \"null\"].includes(typeOf(data))) {\n throw new Error(`Parameter \"data\" should be an object or null`);\n } else if (!window.localStorage) {\n throw new Error(`User agent does not support local storage`);\n }\n\n const dataString = JSON.stringify(data);\n window.localStorage.setItem(key, dataString);\n}\n","// Define the \"setSessionStorageData\" storage utility function\n\n// Import utility modules\nimport { typeOf } from \"utilities/etc\";\n\n// Import parameter modules\nimport { sessionStorageKey as defaultSessionStorageKey } from \"parameters/state\";\n\n\n// setSessionStorageData: sync\n// sets the specified key of the user-agent's sessionStorage to the\n// specified data object\nexport function setSessionStorageData({\n key = defaultSessionStorageKey,\n data\n}) {\n if (typeOf(key) !== \"string\") {\n throw new Error(`Parameter \"key\" should be a non-empty string`);\n } else if (![\"object\", \"null\"].includes(typeOf(data))) {\n throw new Error(`Parameter \"data\" should be an object or null`);\n } else if (!window.sessionStorage) {\n throw new Error(`User agent does not support session storage`);\n }\n\n const dataString = JSON.stringify(data);\n window.sessionStorage.setItem(key, dataString);\n}\n","// Define string-related utility functions\nexport { capitalizeFirst } from \"./string/capitalize-first\";\nexport { capitalizeAll } from \"./string/capitalize-all\";\nexport { uncapitalizeFirst } from \"./string/uncapitalize-first\";\nexport { escapeRegExp } from \"./string/escape-regexp\";\nexport { highlight } from \"./string/highlight\";\nexport { parseJSON } from \"./string/parse-json\";\nexport { removeNewLines } from \"./string/remove-new-lines\";\nexport { removeMultipleSpaces } from \"./string/remove-multiple-spaces\";\nexport { optimizeSpaces } from \"./string/optimize-spaces\";\nexport { splitTokens } from \"./string/split-tokens\";\nexport { normalizeEmailAddress } from \"./string/normalize-email-address\";\nexport {\n normalizeWords as uncapitalizeNonLeading \n} from \"./sentence/normalize-words\";\n","// Define the \"capitalizeAll\" string utility function\n\n// Import utility functions\nimport { typeOf } from \"utilities/etc\";\n\n// Import parameter modules\nimport { uncapitalizedWordsRegExp } from \"parameters/sentence\";\n\n\n// capitalizeAll: sync\n// returns the specified string with the first letters of each word \n// converted to upper case\nexport function capitalizeAll(string) {\n if (typeOf(string) !== \"string\") {\n throw new Error(\"First argument should be a string\");\n }\n\n return string\n .replace(uncapitalizedWordsRegExp, (_, match) => match.toUpperCase());\n}\n","// Define the \"capitalizeFirst\" string utility function\n\n// Import utility functions\nimport { typeOf } from \"utilities/etc\";\n\n// Import parameter modules\nimport { startsWithLowerCaseRegExp } from \"parameters/sentence\";\n\n\n// capitalizeFirst: sync\n// returns the specified string with the first letter converted to\n// locale's upper case\nexport function capitalizeFirst(string) {\n if (typeOf(string) !== \"string\") {\n throw new Error(\"First argument should be a string\");\n }\n\n return string\n .replace(startsWithLowerCaseRegExp, (_, match) => match.toUpperCase());\n}\n","// Define the \"escapeRegExp\" sttring utility function\n\n// Define internal parameters\nconst specialCharsRegExp = /[.+*?()[\\]{}\\\\^$]/g;\n\n\n// escapeRegExp: sync\n// returns the specified string regexp after escaping the special\n// characters in it (e.g. \".\" -> \"\\.\", of \"+\" -> \"\\+\"\nexport function escapeRegExp(string) {\n return string\n .replace(specialCharsRegExp, match => `\\\\${match}`);\n}\n","// Define the \"highlight\" string utility function\n\n// Import utility modules\nimport { escapeRegExp } from \"utilities/string/escape-regexp\";\n\n\n// highlight: sync\n// returns the specified text string after highlighting the parts\n// appearing in the specified highlight text using the specified opening\n// and closing markups\nexport function highlight({\n text = \"\",\n highlightText = \"\",\n openingHighlightMarkup = \"\",\n closingHighlightMarkup = \"\"\n}) {\n const markTextTargets = highlightText.split(/\\s+|\\b/);\n const markedText = markTextTargets\n .reduce((accText, markTextTarget) => {\n const escapedHighlightTextTarget =\n escapeRegExp(markTextTarget.toLowerCase());\n const markTargetRegExp =\n new RegExp(escapedHighlightTextTarget, \"i\");\n const [textMatch] = accText.match(markTargetRegExp) || [];\n if (textMatch) {\n const markedTextMatch = `<|o|>${textMatch}<|c|>`;\n return accText.replace(markTargetRegExp, markedTextMatch);\n }\n return accText;\n }, text);\n return markedText\n .replace(/<\\|o\\|>/g, openingHighlightMarkup)\n .replace(/<\\|c\\|>/g, closingHighlightMarkup);\n}\n","// Define the \"normailzeEmailAddress\" string utility function\n\n// Import utility modules\nimport { typeOf } from \"utilities/etc\";\n\n// Define internal parameters\nconst whitespacesRegExp = /\\s+/g;\n\n\n// normailzeEmailAddress: sync\n// returns a normalized version of the specified e-mail address\nexport function normalizeEmailAddress(emailAddress) {\n if (typeOf(emailAddress) !== \"string\") {\n throw new Error(`E-mail address should be a string`);\n }\n\n return emailAddress\n .replace(whitespacesRegExp, \"\")\n .toLowerCase();\n}\n","// Define the \"optimizeSpaces\" string utility function\n\n// Import utility methods\nimport { removeMultipleSpaces } from \"./remove-multiple-spaces\";\n\n\n// optimizeSpaces: sync\n// returns the input string after removing multiple consecutive spaces \n// as well as leading/trailing spaces\nexport function optimizeSpaces(string = \"\") {\n return removeMultipleSpaces(string.trim());\n}\n","// Define the \"parseJSON\" string utility function\n\n// parseJSON: sync\n// returns the result of parsing the specified JSON string, or null if\n// parsing fails\nexport function parseJSON(string = \"\") {\n if (!string) {\n console.warn(`Returning null upon parsing an empty string`);\n return null;\n }\n try {\n return JSON.parse(string);\n } catch(error) {\n console.warn(`Error while parsing JSON string \"${string}\"`);\n console.debug(error);\n return null;\n }\n}\n","// Define the \"removeMultipleSpaces\" string utility function\n\n// Define internal parameters\nconst multipleSpacesRegExp = /[\\s\\t]+/g;\n\n\n// removeMultipleSpaces: sync\n// returns the input string after replacing multiple spaces with a single\n// one\nexport function removeMultipleSpaces(string) {\n return string.replace(multipleSpacesRegExp, \" \");\n}\n","// Define the \"removeNewLines\" string utility function\n\n// Define internal parameters\nconst newLinesRegExp = /(\\r\\n)+|\\r+|\\n+/g;\n\n\n// removeNewLines: sync\n// returns the specified string after replacing new-line characters with\n// spaces\nexport function removeNewLines(string) {\n return string.replace(newLinesRegExp, \" \");\n}\n","// Define the \"splitTokens\" string utility function\n\n// Import library modules\nimport XRegExp from \"xregexp\";\n\n// Import utility modules\nimport { typeOf } from \"utilities/etc\";\n\n\n// splitTokens: sync\n// returns an array containing the tokens (i.e. words, marks and symbols)\n// present within the specified string, according to the specified split\n// and non-split characters\n// note: use regexp syntax from XRegExp library\nexport function splitTokens(string, params = {}) {\n if (typeOf(string) !== \"string\") {\n throw new Error(\"String parameter should be a string\");\n }\n const { splitChars = \"\\\\pM\\\\pP\\\\pS\", nonSplitChars = \"\\\\pL\\\\pZ\" } = params;\n const splitRegExpString = splitChars ? `[${splitChars}]` : ``;\n const nonSplitRegExpString = nonSplitChars ? `[^${nonSplitChars}]+` : ``;\n const fullSplitRegExpString = [\n \"(?\",\n [ splitRegExpString, nonSplitRegExpString ]\n .filter(sreString => sreString)\n .join(\"|\"),\n \")\"\n ].join(\"\");\n const splitRegExp = XRegExp(fullSplitRegExpString, \"g\");\n const wordsString = XRegExp.replace(string, splitRegExp, \" ${token} \");\n return XRegExp.replace(wordsString, XRegExp(\"\\\\pZ+\", \"g\"), \" \")\n .split(\" \")\n .filter(token => token);\n}\n","// Define the \"uncapitalizeFirst\" string utility function\n\n// Import utility functions\nimport { typeOf } from \"utilities/etc\";\n\n// Import parameter modules\nimport { startsWithUpperCaseRegExp } from \"parameters/sentence\";\n\n\n// uncapitalizeFirst: sync\n// returns the specified string with the first letter converted to\n// locale's lower case\nexport function uncapitalizeFirst(string) {\n if (typeOf(string) !== \"string\") {\n throw new Error(\"First argument should be a string\");\n }\n\n return string\n .replace(startsWithUpperCaseRegExp, (_, first) => first.toLowerCase());\n}\n","// Define time-related utility functions\nexport {\n getCurrentYear\n} from \"utilities/time/get-current-year\";\nexport {\n getCurrentTimeStampMs\n} from \"utilities/time/get-current-time-stamp-ms\";\nexport {\n computeAbsoluteTimeString\n} from \"utilities/time/compute-absolute-time-string\";\nexport {\n computeRelativeTimeString\n} from \"utilities/time/compute-relative-time-string\";\nexport {\n computeDistanceTimeString\n} from \"utilities/time/compute-distance-time-string\";\n","// Define the \"computeAbsoluteTimeString\" time utility function\n\n// Import library modules\nimport { format } from \"date-fns\";\nimport {\n it as itLocaleData,\n enGB as enGBLocaleData\n} from \"date-fns/locale\";\n\n\n// computeAbsoluteTimeString: sync\n// returns a localized time string representing the specified time-stamp\n// (in ms) in absolute terms accrding to the specified format (see\n// date-fns reference for details), e.g. yasterday, at 03:20 PM\n// note: by removing the time-zone (last) character from the ISO string,\n// date-fns assumes local time-zone for the spefified date\nexport function computeAbsoluteTimeString({\n timeStampMs,\n formatString = \"Pp\", // date/time format, e.g. 03/05/2020, 14:20PM\n localeCode = \"en-gb\",\n weekStartsOn = 0, // week start index, i.e. 0 -> Sunday, 1 -> Monday, ...\n useLocalTimeZone = true\n}) {\n if (!timeStampMs) {\n throw new Error(`Missing required parameter \"timeStampsMs\"`);\n }\n const localeData = [ \"it-it\", \"it\" ].includes(localeCode) ? itLocaleData :\n [ \"en-gb\", \"en\" ].includes(localeCode) ? enGBLocaleData : null;\n if (!localeData) {\n throw new Error(`Un-supported locale \"${localeCode}\"`);\n }\n const rawDate = new Date(timeStampMs);\n const finalDate = useLocalTimeZone ? \n new Date(rawDate.toISOString().slice(0, -1)) : rawDate;\n\n // Return localized absolute time string\n return format(finalDate, formatString, {\n locale: localeData,\n weekStartsOn\n });\n}\n","// Define the \"computeDistanceTimeString\" time utility function\n\n// Import library modules\nimport { \n formatDistance,\n formatDistanceStrict\n} from \"date-fns\";\nimport {\n it as itLocaleData,\n enGB as enGBLocaleData\n} from \"date-fns/locale\";\n\n\n// computeDistanceTimeString: sync\n// returns a localized time string representing the distance of the\n// specified time-stamp (in ms) from the specified base time-stamp (in\n// ms), e.g. 3 days ago\nexport function computeDistanceTimeString({\n timeStampMs,\n baseTimeStampMs = Date.now(),\n stringFormat = \"strict\",\n localeCode = \"en-gb\",\n includeSeconds = false, // include details for distances below 1 minute\n addSuffix = true // true -> 3 days ago | false -> 3 days\n}) {\n if (!timeStampMs) {\n throw new Error(`Missing required parameter \"timeStampMs\"`);\n } else if (!baseTimeStampMs) {\n throw new Error(`Missing required parameter \"baseTimeStampMs\"`);\n }\n const localeData = [ \"it-it\", \"it\" ].includes(localeCode) ? itLocaleData :\n [ \"en-gb\", \"en\" ].includes(localeCode) ? enGBLocaleData : null;\n if (!localeData) {\n throw new Error(`Un-supported locale \"${localeCode}\"`);\n }\n\n // Return localized distance time string\n switch(stringFormat) {\n case \"strict\":\n return formatDistanceStrict(\n new Date(timeStampMs), \n new Date(baseTimeStampMs), {\n locale: localeData,\n includeSeconds,\n addSuffix\n });\n case \"words\":\n return formatDistance(\n new Date(timeStampMs), \n new Date(baseTimeStampMs), {\n locale: localeData,\n includeSeconds,\n addSuffix\n });\n }\n}\n","// Define the \"computeRelativeTimeString\" time utility function\n\n// Import library modules\nimport { formatRelative } from \"date-fns\";\nimport {\n it as itLocaleData,\n enGB as enGBLocaleData\n} from \"date-fns/locale\";\n\n\n// computeRelativeTimeString: sync\n// returns a localized time string representing the relation between the\n// specified time-stamp (in ms) from the specified base time-stamp (in\n// ms), e.g. yasterday, at 03:20 PM\nexport function computeRelativeTimeString({\n timeStampMs,\n baseTimeStampMs = Date.now(),\n localeCode = \"en-gb\",\n weekStartsOn = 0 // 0 -> Sunday, 1 -> Monday, ...\n}) {\n if (!timeStampMs) {\n throw new Error(`Missing required parameter \"timeStampMs\"`);\n } else if (!baseTimeStampMs) {\n throw new Error(`Missing required parameter \"baseTimeStampMs\"`);\n }\n const localeData = [ \"it-it\", \"it\" ].includes(localeCode) ? itLocaleData :\n [ \"en-gb\", \"en\" ].includes(localeCode) ? enGBLocaleData : null;\n if (!localeData) {\n throw new Error(`Un-supported locale \"${localeCode}\"`);\n }\n\n // Return localized relative time string\n return formatRelative(new Date(timeStampMs), new Date(baseTimeStampMs), {\n locale: localeData,\n weekStartsOn\n });\n}\n","// Define the \"getCurrentTimeStampMs\" time utility function\n\n// getCurrentTimeStampMs: sync\n// returns the current time-stamp in milliseconds from the unix epoch\nexport function getCurrentTimeStampMs() {\n return Date.now();\n}\n","// Define the \"getCurrentYear\" time utility function\n\n// getCurrentYear: sync\n// returns the current year\nexport function getCurrentYear() {\n return (new Date()).getFullYear();\n}\n","// Define json-web-token-related utility functions\nexport { decodeToken } from \"utilities/token/decode-token\";\nexport {\n computeClientTokenData\n} from \"utilities/token/compute-client-token-data\";\n","// Define the \"computeClientTokenData\" token utility function\n\n// Import utility modules\nimport { getCurrentTimeStampMs } from \"utilities/time\";\nimport { typeOf } from \"utilities/etc\";\n\n// Import parameter modules\nimport {\n sessionDurationMs,\n expiryMarginMs\n} from \"parameters/session\";\n\n\n// computeClientTokenData: sync\n// returns a client-side token data object obtained by adjusting the\n// time-stamps of the specified server-side token data object with respect\n// to the client's current time and expiry margin\nexport function computeClientTokenData(serverTokenData, sessionOperation) {\n if (!serverTokenData) {\n throw new Error(`Undefined server token data`);\n } else if (typeOf(serverTokenData) !== \"object\") {\n throw new Error(`Server token data should be an object with ` +\n `\"iat\" and \"exp\" properties`);\n }\n const {\n iat: serverIssuedAtMs,\n exp: serverExpiresOnMs\n } = serverTokenData;\n const currentTimeStampMs = getCurrentTimeStampMs();\n let timeAdjustmentMs = 0;\n switch (sessionOperation) {\n case \"open\":\n timeAdjustmentMs = serverIssuedAtMs - currentTimeStampMs;\n return Object.assign({}, serverTokenData, {\n iat: serverIssuedAtMs - timeAdjustmentMs,\n exp: serverExpiresOnMs - timeAdjustmentMs - expiryMarginMs\n });\n case \"extend\":\n timeAdjustmentMs =\n serverExpiresOnMs - sessionDurationMs - currentTimeStampMs;\n return Object.assign({}, serverTokenData, {\n iat: serverIssuedAtMs - timeAdjustmentMs,\n exp: serverExpiresOnMs - timeAdjustmentMs - expiryMarginMs\n });\n case \"close\":\n timeAdjustmentMs = serverExpiresOnMs - currentTimeStampMs;\n return Object.assign({}, serverTokenData, {\n iat: serverIssuedAtMs - timeAdjustmentMs,\n exp: serverExpiresOnMs - timeAdjustmentMs\n });\n default:\n throw new Error(`Unknown session operation \"${sessionOperation}\"`);\n }\n}\n","// Define the \"decodeToken\" token utility function\n\n// Import library modules\nimport jwt_decode from \"jwt-decode\";\n\n// Import utility modules\nimport { typeOf } from \"utilities/etc\";\n\n\n// decodeToken: sync\n// returns an object containing the decoded payload of the specified json\n// web token, or throws a decoding failure error\nexport function decodeToken(jwtString) {\n if (jwtString && typeOf(jwtString) === \"string\") {\n return jwt_decode(jwtString);\n }\n throw new Error(`Invalid json web token \"${jwtString}\"`);\n}\n","// Define user-related utility functions\nexport { computeDisplayName } from \"./user/compute-display-name\";\n","// Define the \"computeDisplayName\" user utility function \n\n// computeDisplayName: sync\n// returns a string representing the display name of the user from the\n// specified profile object\nexport function computeDisplayName({\n profileData = null\n}) {\n const { \n displayName = \"\", \n firstName = \"\", \n lastName = \"\" \n } = profileData || {};\n if (displayName) {\n return displayName;\n } else if (firstName && lastName) {\n const firstNameInitial = firstName.slice(0, 1).toUpperCase();\n const lastNameInitial = lastName.slice(0, 1).toUpperCase();\n return `${firstNameInitial}${lastNameInitial}`;\n } else {\n return \"\"\n }\n}\n","// Define the capitalizeFirst value converter\n\n// Import utility modules\nimport { capitalizeFirst } from \"utilities/string\";\n\n// Export value converter class\nexport class CapitalizeFirstValueConverter {\n\n toView(string = \"\") {\n return capitalizeFirst(string || \"\");\n }\n\n}\n","// extracted by mini-css-extract-plugin\nexport {};"],"names":[],"sourceRoot":""}