{"version":3,"file":"js/vendor.js","mappings":";;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AAEA;AAEA;;AAEA;AAEA;AAEA;AACA;AAAA;AAEA;AAuBA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAIA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAGA;AACA;;AAEA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAKA;AACA;AAKA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAKA;AACA;AAIA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAKA;AACA;AAKA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAKA;AACA;AAIA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AAAA;AACA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;;AAEA;AACA;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;;AAEA;AACA;AACA;AAIA;AAEA;AACA;AAIA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AAIA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AASA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAGA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAGA;AAEA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAAA;AACA;AAEA;;AAEA;AACA;AAEA;AAAA;AACA;AAEA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAGA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAKA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAKA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAIA;AAEA;AACA;AACA;AACA;AAEA;AAMA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAIA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAAA;AAEA;AACA;AACA;AAAA;AACA;AAEA;AAEA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AAEA;AACA;AAIA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AAEA;AACA;AAEA;AAAA;AACA;AAEA;AACA;AAEA;AAAA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;;AAEA;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAGA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;;AAEA;AACA;AAEA;AACA;;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAGA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AAAA;;AAGA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAGA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAGA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAKA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;AAMA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;;AAEA;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;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;;AAEA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;;AAEA;AAAA;;AAEA;AAAA;;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAAA;;AAEA;AAAA;;AAEA;AACA;AAEA;AAEA;AACA;AAEA;;AAEA;;AAEA;;AAEA;;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AACA;AACA;AACA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AACA;AACA;AACA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AACA;AACA;AACA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AACA;AACA;AACA;AAAA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AAAA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AAIA;;AAMA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;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;AAEA;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;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;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;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;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAIA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAAA;AAAA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAOA;AACA;AACA;AACA;;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AAEA;;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAQA;AAEA;AAEA;AAAA;AAAA;AAEA;AAEA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;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;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAIA;AACA;;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAIA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAuBA;;AAEA;AACA;AACA;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;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAIA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AAYA;AACA;AAQA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AAEA;AAAA;AAEA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AAAA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;;AAEA;;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAKA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAGA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AASA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AAEA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;;AAEA;AACA;AAEA;AAAA;AACA;AAEA;AAAA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AAAA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AAEA;AAIA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAGA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AAEA;AAEA;AAGA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;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;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;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;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAGA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;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;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAGA;AACA;AACA;AAAA;AAGA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAAA;AAGA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAAA;AAGA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAIA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;;AAEA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAGA;AAEA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;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;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAAA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;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;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;;AAIA;AACA;AACA;AACA;AACA;AAGA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AAKA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAGA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;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;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;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;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAGA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;;AAGA;AACA;;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAIA;AAEA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;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;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;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;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;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;AAEA;AAEA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;;AAEA;AACA;;AAEA;;AAEA;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;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;;AAEA;AAAA;AAGA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;;AAEA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAGA;AAEA;AACA;AAEA;AAEA;AAEA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AAAA;;AAEA;AAEA;AAGA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAAA;AAAA;AAAA;;AAEA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAGA;AACA;AACA;AACA;AACA;AACA;AAAA;AAGA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAIA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AAEA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;AAAA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAEA;;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AAEA;;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;;AAGA;AACA;;AAEA;AAEA;AACA;AAWA;AAeA;AAEA;AACA;AACA;AACA;AAUA;AAMA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;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;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;;AAEA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;;AAEA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAAA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAGA;AACA;;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;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;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAIA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;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;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AAAA;AAAA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAOA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;;AAEA;AACA;;AAEA;AACA;AAAA;AAAA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAEA;AAEA;AACA;AAIA;AAEA;AAEA;AACA;AAIA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;;AAEA;AACA;;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AAAA;AAGA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAKA;AAEA;AACA;AAKA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAIA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAIA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;;AAEA;AAEA;;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAIA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AAAA;AAGA;AACA;AAAA;AAGA;AAEA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAGA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAEA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AAGA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAGA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAGA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AAEA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AAAA;AAAA;AAEA;AAAA;AAAA;AAEA;AAAA;AAAA;AAEA;;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAMA;;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AAEA;AAEA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAIA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAAA;AAEA;AAAA;AAEA;AAEA;AACA;;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;AAAA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAMA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAKA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;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;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;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;AAAA;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;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;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;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;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;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAEA;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;AACA;AACA;AAEA;AACA;AACA;AAOA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAIA;;AAEA;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;AACA;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;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;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;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;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;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;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;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;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;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;AAEA;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;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAEA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAUA;AAAA;AAAA;AACA;AACA;AACA;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;AAEA;AACA;AACA;AACA;AACA;AACA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;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;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;AACA;AACA;AACA;AACA;AACA;AACA;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;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;AACA;AACA;AACA;AACA;AAGA;AAEA;AAIA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAAA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAGA;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AAIA;;AAEA;;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAIA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;;AAEA;AAAA;AAEA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AACA;AAEA;;AAEA;;AAEA;AAAA;AACA;AAAA;;AAEA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAAA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAAA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AAAA;AACA;;AAEA;;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AAAA;AACA;AACA;AAEA;;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AAEA;;AAEA;AAEA;AAEA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;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;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AAEA;AACA;;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;;AAEA;AAAA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAAA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAAA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;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;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAKA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AAAA;AAEA;AAAA;AAEA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AAIA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAIA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;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;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AAEA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AAKA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAIA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAGA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AACA;AAAA;AAAA;AACA;AAEA;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;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAQA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;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;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;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;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AACA;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;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;AAEA;AACA;AAAA;AAAA;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;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;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;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;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;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;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;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AAAA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;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;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAEA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AASA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAGA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;;AAEA;;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAWA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAMA;AAWA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AAAA;AAAA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAGA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAMA;AAEA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;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;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAGA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAGA;AAGA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAOA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AAEA;AAEA;AAkBA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AAEA;AAEA;;AAEA;AACA;AAQA;AAAA;AAAA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;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;AAEA;AAEA;;AAEA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;;AAEA;AACA;AACA;AAAA;AACA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AAGA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAYA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;;AAEA;AACA;AAAA;AAAA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AAAA;AAAA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AAEA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAEA;AACA;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AAEA;;AAEA;AACA;;AAEA;AACA;AAEA;;AAGA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAGA;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;;AAGA;AACA;AAEA;AAcA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;;AAEA;AAAA;AAAA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;;AAEA;;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;;AAEA;AACA;AAEA;AAOA;AAEA;AAAA;AAAA;AAAA;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;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;;AAEA;AACA;AAEA;AAOA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;;AAEA;;AAEA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;;AAEA;;AAEA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;;AAEA;;AAEA;AAEA;AAOA;AAEA;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;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;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAGA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;;AAEA;AAEA;AAEA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;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;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAQA;AAEA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAIA;AAIA;AAEA;;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;;AAEA;;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AAEA;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;AAEA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAGA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;;AAEA;AACA;;AAEA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAGA;;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAEA;AAEA;AAEA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAIA;;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAGA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AAAA;;AAGA;AAAA;AAEA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAOA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAIA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAGA;AAEA;AACA;AAEA;AAEA;AAEA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAEA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAAA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AAAA;AAAA;;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAGA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;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;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAGA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAIA;;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAMA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;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;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAGA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAXA;AAcA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAGA;AAAA;AACA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAKA;AAIA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AAEA;AAAA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAAA;AAAA;AAEA;AACA;AAAA;AAAA;AAAA;AAEA;AAEA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AAEA;;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;;AAEA;AACA;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;;AAEA;AAGA;AAEA;AAEA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;;AAEA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAIA;AAIA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAIA;AAEA;AAIA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;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;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AAEA;AAEA;AAAA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AAAA;AAAA;AAAA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAOA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAIA;AAIA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AAKA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AAOA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;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;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AAEA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;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;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAGA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAAA;AAEA;AAEA;AACA;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;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAIA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AAAA;AAAA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAGA;AACA;AACA;AACA;AAEA;AACA;AACA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AASA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAIA;AAEA;AAGA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAIA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AAAA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;;AAEA;;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAAA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAAA;AAAA;;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;;AAEA;AAEA;AAEA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAAA;AAAA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AAIA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;;AAEA;;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AAIA;AACA;AACA;AACA;AACA;AACA;AAEA;AAKA;AAEA;AACA;AAGA;AAGA;AAGA;AAMA;AAEA;AACA;AAEA;AAEA;AAKA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;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;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AAEA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAGA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;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;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AAEA;AAEA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;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;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAIA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;;AAEA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;;AAEA;AACA;AAEA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AAQA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AAEA;AAAA;AAAA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AAEA;AASA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AAAA;AAAA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;AAEA;AAEA;AACA;AAAA;AAAA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AAEA;AAEA;;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAAA;AAAA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAAA;AAAA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAAA;;AAEA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AAEA;AACA;AACA;;AAEA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;;AAGA;;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;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;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAKA;AAKA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAOA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAKA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;;AAEA;AAAA;AAGA;;AAEA;AAAA;AAGA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAKA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAIA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAKA;AAMA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAKA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAIA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAKA;AAMA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAIA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAMA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAIA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAMA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAKA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAIA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAKA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAIA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAKA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAKA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAIA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAKA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAKA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAIA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;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;AACA;AACA;AACA;AAAA;AAEA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;;AAOA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AAEA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAGA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AAEA;AAmBA;AAkBA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAIA;AAGA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAGA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAGA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAGA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;;AAEA;AAAA;AAKA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AAEA;AAEA;;AAEA;AACA;AAAA;AACA;AACA;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;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;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;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AAKA;AACA;AAEA;AACA;AACA;AACA;AAKA;AACA;AAEA;AACA;AACA;AACA;AAKA;AACA;;AAGA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;;AAEA;AAAA;AAKA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AAAA;AAAA;AAAA;AACA;;AAEA;AAAA;AAKA;AAEA;AAEA;AACA;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;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;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;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;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;AAEA;AACA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAAA;AAAA;;AAEA;AACA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;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;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAIA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;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;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAQA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAIA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAAA;;AAGA;AACA;AACA;AAAA;;AAGA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AACA;AAAA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AACA;AAAA;AACA;;AAEA;AACA;AACA;AAMA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAWA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;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;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAGA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;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;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAIA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;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;AACA;AACA;AAEA;AACA;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;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;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;;AAEA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AAIA;AACA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAIA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAGA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAIA;AACA;AACA;AACA;AACA;AAEA;AAGA;AAGA;AAGA;AAIA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAGA;AAIA;AACA;AACA;AACA;AAGA;AACA;AACA;AAGA;AAEA;AAIA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAAA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAQA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAEA;AACA;;AAEA;AACA;AACA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;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;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAAA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAAA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAAA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAAA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAAA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAAA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAKA;AACA;AACA;AAKA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;;AAEA;AAGA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AAAA;AAAA;;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAEA;AAEA;AAEA;AAEA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAcA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;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;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;;AAEA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;;AAEA;AACA;;AAEA;AAEA;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AAEA;AACA;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAEA;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;;AAEA;AACA;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AAEA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AAAA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAGA;AACA;AAEA;AAEA","sources":["webpack://@fortressiq/trinity/./client/vendor/rappid.js"],"sourcesContent":["/*! Rappid v2.3.3 - HTML5 Diagramming Framework\n\nCopyright (c) 2015 client IO\n\n 2018-08-03 \n\n\nThis Source Code Form is subject to the terms of the Rappid License\n, v. 2.0. If a copy of the Rappid License was not distributed with this\nfile, You can obtain one at http://jointjs.com/license/rappid_v2.txt\n or from the Rappid archive as was distributed by client IO. See the LICENSE file.*/\n\n\n(function(root, factory) {\n\n if (typeof define === 'function' && define.amd) {\n\n // For AMD.\n\n define(['backbone', 'lodash', 'jquery'], function(Backbone, _, $) {\n\n Backbone.$ = $\n\n return factory(root, Backbone, _, $)\n })\n\n } else if (typeof exports !== 'undefined') {\n\n // For Node.js or CommonJS.\n\n var Backbone = require('backbone')\n var _ = require('lodash')\n var $ = Backbone.$ = require('jquery')\n\n module.exports = factory(root, Backbone, _, $)\n\n } else {\n\n // As a browser global.\n\n var {Backbone} = root\n var {_} = root\n var $ = Backbone.$ = root.jQuery || root.$\n\n root.joint = factory(root, Backbone, _, $)\n root.g = root.joint.g\n root.V = root.Vectorizer = root.joint.V\n }\n\n}(this, function(root, Backbone, _, $) {\n\n(function() {\n\n /**\n * version: 0.3.0\n * git://github.com/davidchambers/Base64.js.git\n */\n\n const object = typeof exports !== 'undefined' ? exports : this // #8: web workers\n const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='\n\n function InvalidCharacterError(message) {\n this.message = message\n }\n\n InvalidCharacterError.prototype = new Error\n InvalidCharacterError.prototype.name = 'InvalidCharacterError'\n\n // encoder\n // [https://gist.github.com/999166] by [https://github.com/nignag]\n object.btoa || (\n object.btoa = function(input) {\n const str = String(input)\n for (\n // initialize result and counter\n var block, charCode, idx = 0, map = chars, output = '';\n // if the next str index does not exist:\n // change the mapping table to \"=\"\n // check if d has no fractional digits\n str.charAt(idx | 0) || (map = '=', idx % 1);\n // \"8 - idx % 1 * 8\" generates the sequence 2, 4, 6, 8\n output += map.charAt(63 & block >> 8 - idx % 1 * 8)\n ) {\n charCode = str.charCodeAt(idx += 3 / 4)\n if (charCode > 0xFF) {\n throw new InvalidCharacterError('\\'btoa\\' failed: The string to be encoded contains characters outside of the Latin1 range.')\n }\n block = block << 8 | charCode\n }\n return output\n })\n\n // decoder\n // [https://gist.github.com/1020396] by [https://github.com/atk]\n object.atob || (\n object.atob = function(input) {\n const str = String(input).replace(/=+$/, '')\n if (str.length % 4 == 1) {\n throw new InvalidCharacterError('\\'atob\\' failed: The string to be decoded is not correctly encoded.')\n }\n for (\n // initialize result and counters\n var bc = 0, bs, buffer, idx = 0, output = '';\n // get next character\n // eslint-disable-next-line no-cond-assign\n buffer = str.charAt(idx++);\n // character found in table? initialize bit storage and add its ascii value;\n ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,\n // and if not first of each 4 characters,\n // convert the first 8 bits to one ascii character\n bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0\n ) {\n // try to find character in table (0-63, not found => -1)\n buffer = chars.indexOf(buffer)\n }\n return output\n })\n\n}());\n\n(function() {\n\n if (typeof Uint8Array !== 'undefined' || typeof window === 'undefined') {\n return\n }\n\n function subarray(start, end) {\n return this.slice(start, end)\n }\n\n function set_(array, offset) {\n\n if (arguments.length < 2) {\n offset = 0\n }\n for (let i = 0, n = array.length; i < n; ++i, ++offset) {\n this[offset] = array[i] & 0xFF\n }\n }\n\n // we need typed arrays\n function TypedArray(arg1) {\n\n let result\n if (typeof arg1 === 'number') {\n result = new Array(arg1)\n for (let i = 0; i < arg1; ++i) {\n result[i] = 0\n }\n } else {\n result = arg1.slice(0)\n }\n result.subarray = subarray\n result.buffer = result\n result.byteLength = result.length\n result.set = set_\n if (typeof arg1 === 'object' && arg1.buffer) {\n result.buffer = arg1.buffer\n }\n\n return result\n }\n\n window.Uint8Array = TypedArray\n window.Uint32Array = TypedArray\n window.Int32Array = TypedArray\n})();\n\n/**\n * make xhr.response = 'arraybuffer' available for the IE9\n */\n(function() {\n\n if (typeof XMLHttpRequest === 'undefined') {\n return\n }\n\n if ('response' in XMLHttpRequest.prototype ||\n 'mozResponseArrayBuffer' in XMLHttpRequest.prototype ||\n 'mozResponse' in XMLHttpRequest.prototype ||\n 'responseArrayBuffer' in XMLHttpRequest.prototype) {\n return\n }\n\n Object.defineProperty(XMLHttpRequest.prototype, 'response', {\n get: function() {\n /* global VBArray:true */\n return new Uint8Array(new VBArray(this.responseBody).toArray())\n }\n })\n})()\n\n// https://tc39.github.io/ecma262/#sec-array.prototype.includes\nif (!Array.prototype.includes) {\n Object.defineProperty(Array.prototype, 'includes', {\n value: function(searchElement, fromIndex) {\n\n // 1. Let O be ? ToObject(this value).\n if (this == null) {\n throw new TypeError('\"this\" is null or not defined')\n }\n\n const o = Object(this)\n\n // 2. Let len be ? ToLength(? Get(O, \"length\")).\n const len = o.length >>> 0\n\n // 3. If len is 0, return false.\n if (len === 0) {\n return false\n }\n\n // 4. Let n be ? ToInteger(fromIndex).\n // (If fromIndex is undefined, this step produces the value 0.)\n const n = fromIndex | 0\n\n // 5. If n ≥ 0, then\n // a. Let k be n.\n // 6. Else n < 0,\n // a. Let k be len + n.\n // b. If k < 0, let k be 0.\n let k = Math.max(n >= 0 ? n : len - Math.abs(n), 0)\n\n function sameValueZero(x, y) {\n return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y))\n }\n\n // 7. Repeat, while k < len\n while (k < len) {\n // a. Let elementK be the result of ? Get(O, ! ToString(k)).\n // b. If SameValueZero(searchElement, elementK) is true, return true.\n // c. Increase k by 1.\n if (sameValueZero(o[k], searchElement)) {\n return true\n }\n k++\n }\n\n // 8. Return false\n return false\n }\n })\n}\n\n// https://tc39.github.io/ecma262/#sec-array.prototype.find\nif (!Array.prototype.find) {\n Object.defineProperty(Array.prototype, 'find', {\n value: function(predicate) {\n // 1. Let O be ? ToObject(this value).\n if (this == null) {\n throw new TypeError('\"this\" is null or not defined')\n }\n\n const o = Object(this)\n\n // 2. Let len be ? ToLength(? Get(O, \"length\")).\n const len = o.length >>> 0\n\n // 3. If IsCallable(predicate) is false, throw a TypeError exception.\n if (typeof predicate !== 'function') {\n throw new TypeError('predicate must be a function')\n }\n\n // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.\n const thisArg = arguments[1]\n\n // 5. Let k be 0.\n let k = 0\n\n // 6. Repeat, while k < len\n while (k < len) {\n // a. Let Pk be ! ToString(k).\n // b. Let kValue be ? Get(O, Pk).\n // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).\n // d. If testResult is true, return kValue.\n const kValue = o[k]\n if (predicate.call(thisArg, kValue, k, o)) {\n return kValue\n }\n // e. Increase k by 1.\n k++\n }\n\n // 7. Return undefined.\n return undefined\n }\n })\n}\n\n// Production steps of ECMA-262, Edition 6, 22.1.2.1\nif (!Array.from) {\n Array.from = (function() {\n const toStr = Object.prototype.toString\n const isCallable = function(fn) {\n return typeof fn === 'function' || toStr.call(fn) === '[object Function]'\n }\n const toInteger = function(value) {\n const number = Number(value)\n if (isNaN(number)) { return 0 }\n if (number === 0 || !isFinite(number)) { return number }\n return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number))\n }\n const maxSafeInteger = Math.pow(2, 53) - 1\n const toLength = function(value) {\n const len = toInteger(value)\n return Math.min(Math.max(len, 0), maxSafeInteger)\n }\n\n // The length property of the from method is 1.\n return function from(arrayLike/*, mapFn, thisArg */) {\n // 1. Let C be the this value.\n const C = this\n\n // 2. Let items be ToObject(arrayLike).\n const items = Object(arrayLike)\n\n // 3. ReturnIfAbrupt(items).\n if (arrayLike == null) {\n throw new TypeError('Array.from requires an array-like object - not null or undefined')\n }\n\n // 4. If mapfn is undefined, then let mapping be false.\n const mapFn = arguments.length > 1 ? arguments[1] : void undefined\n let T\n if (typeof mapFn !== 'undefined') {\n // 5. else\n // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.\n if (!isCallable(mapFn)) {\n throw new TypeError('Array.from: when provided, the second argument must be a function')\n }\n\n // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.\n if (arguments.length > 2) {\n T = arguments[2]\n }\n }\n\n // 10. Let lenValue be Get(items, \"length\").\n // 11. Let len be ToLength(lenValue).\n const len = toLength(items.length)\n\n // 13. If IsConstructor(C) is true, then\n // 13. a. Let A be the result of calling the [[Construct]] internal method\n // of C with an argument list containing the single item len.\n // 14. a. Else, Let A be ArrayCreate(len).\n const A = isCallable(C) ? Object(new C(len)) : new Array(len)\n\n // 16. Let k be 0.\n let k = 0\n // 17. Repeat, while k < len… (also steps a - h)\n let kValue\n while (k < len) {\n kValue = items[k]\n if (mapFn) {\n A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k)\n } else {\n A[k] = kValue\n }\n k += 1\n }\n // 18. Let putStatus be Put(A, \"length\", len, true).\n A.length = len\n // 20. Return A.\n return A\n }\n }())\n}\n\n// https://tc39.github.io/ecma262/#sec-array.prototype.findIndex\nif (!Array.prototype.findIndex) {\n Object.defineProperty(Array.prototype, 'findIndex', {\n value: function(predicate) {\n // 1. Let O be ? ToObject(this value).\n if (this == null) {\n throw new TypeError('\"this\" is null or not defined')\n }\n\n const o = Object(this)\n\n // 2. Let len be ? ToLength(? Get(O, \"length\")).\n const len = o.length >>> 0\n\n // 3. If IsCallable(predicate) is false, throw a TypeError exception.\n if (typeof predicate !== 'function') {\n throw new TypeError('predicate must be a function')\n }\n\n // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.\n const thisArg = arguments[1]\n\n // 5. Let k be 0.\n let k = 0\n\n // 6. Repeat, while k < len\n while (k < len) {\n // a. Let Pk be ! ToString(k).\n // b. Let kValue be ? Get(O, Pk).\n // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).\n // d. If testResult is true, return k.\n const kValue = o[k]\n if (predicate.call(thisArg, kValue, k, o)) {\n return k\n }\n // e. Increase k by 1.\n k++\n }\n\n // 7. Return -1.\n return -1\n }\n })\n}\n\nif (!String.prototype.includes) {\n String.prototype.includes = function(search, start) {\n \n if (typeof start !== 'number') {\n start = 0\n }\n\n if (start + search.length > this.length) {\n return false\n } else {\n return this.indexOf(search, start) !== -1\n }\n }\n}\n\nif (!String.prototype.startsWith) {\n String.prototype.startsWith = function(searchString, position){\n return this.substr(position || 0, searchString.length) === searchString\n }\n}\n\nNumber.isFinite = Number.isFinite || function(value) {\n return typeof value === 'number' && isFinite(value)\n}\n\n//The following works because NaN is the only value in javascript which is not equal to itself.\nNumber.isNaN = Number.isNaN || function(value) {\n return value !== value\n}\n\n\n// Geometry library.\n// -----------------\n\nconst g = {};\n\n(function(g) {\n\n // Declare shorthands to the most used math functions.\n const math = Math\n const {abs} = math\n const {cos} = math\n const {sin} = math\n const {sqrt} = math\n const {min} = math\n const {max} = math\n const {atan2} = math\n const {round} = math\n const {floor} = math\n const {PI} = math\n const {random} = math\n const {pow} = math\n\n g.bezier = {\n\n // Cubic Bezier curve path through points.\n // @deprecated\n // @param {array} points Array of points through which the smooth line will go.\n // @return {array} SVG Path commands as an array\n curveThroughPoints: function(points) {\n\n console.warn('deprecated')\n\n return new Path(Curve.throughPoints(points)).serialize()\n },\n\n // Get open-ended Bezier Spline Control Points.\n // @deprecated\n // @param knots Input Knot Bezier spline points (At least two points!).\n // @param firstControlPoints Output First Control points. Array of knots.length - 1 length.\n // @param secondControlPoints Output Second Control points. Array of knots.length - 1 length.\n getCurveControlPoints: function(knots) {\n\n console.warn('deprecated')\n\n const firstControlPoints = []\n const secondControlPoints = []\n const n = knots.length - 1\n let i\n\n // Special case: Bezier curve should be a straight line.\n if (n == 1) {\n // 3P1 = 2P0 + P3\n firstControlPoints[0] = new Point(\n (2 * knots[0].x + knots[1].x) / 3,\n (2 * knots[0].y + knots[1].y) / 3\n )\n\n // P2 = 2P1 – P0\n secondControlPoints[0] = new Point(\n 2 * firstControlPoints[0].x - knots[0].x,\n 2 * firstControlPoints[0].y - knots[0].y\n )\n\n return [firstControlPoints, secondControlPoints]\n }\n\n // Calculate first Bezier control points.\n // Right hand side vector.\n const rhs = []\n\n // Set right hand side X values.\n for (i = 1; i < n - 1; i++) {\n rhs[i] = 4 * knots[i].x + 2 * knots[i + 1].x\n }\n\n rhs[0] = knots[0].x + 2 * knots[1].x\n rhs[n - 1] = (8 * knots[n - 1].x + knots[n].x) / 2.0\n\n // Get first control points X-values.\n const x = this.getFirstControlPoints(rhs)\n\n // Set right hand side Y values.\n for (i = 1; i < n - 1; ++i) {\n rhs[i] = 4 * knots[i].y + 2 * knots[i + 1].y\n }\n\n rhs[0] = knots[0].y + 2 * knots[1].y\n rhs[n - 1] = (8 * knots[n - 1].y + knots[n].y) / 2.0\n\n // Get first control points Y-values.\n const y = this.getFirstControlPoints(rhs)\n\n // Fill output arrays.\n for (i = 0; i < n; i++) {\n // First control point.\n firstControlPoints.push(new Point(x[i], y[i]))\n\n // Second control point.\n if (i < n - 1) {\n secondControlPoints.push(new Point(\n 2 * knots [i + 1].x - x[i + 1],\n 2 * knots[i + 1].y - y[i + 1]\n ))\n\n } else {\n secondControlPoints.push(new Point(\n (knots[n].x + x[n - 1]) / 2,\n (knots[n].y + y[n - 1]) / 2)\n )\n }\n }\n\n return [firstControlPoints, secondControlPoints]\n },\n\n // Solves a tridiagonal system for one of coordinates (x or y) of first Bezier control points.\n // @deprecated\n // @param rhs Right hand side vector.\n // @return Solution vector.\n getFirstControlPoints: function(rhs) {\n\n console.warn('deprecated')\n\n const n = rhs.length\n // `x` is a solution vector.\n const x = []\n const tmp = []\n let b = 2.0\n\n x[0] = rhs[0] / b\n\n // Decomposition and forward substitution.\n for (var i = 1; i < n; i++) {\n tmp[i] = 1 / b\n b = (i < n - 1 ? 4.0 : 3.5) - tmp[i]\n x[i] = (rhs[i] - x[i - 1]) / b\n }\n\n for (i = 1; i < n; i++) {\n // Backsubstitution.\n x[n - i - 1] -= tmp[n - i] * x[n - i]\n }\n\n return x\n },\n\n // Divide a Bezier curve into two at point defined by value 't' <0,1>.\n // Using deCasteljau algorithm. http://math.stackexchange.com/a/317867\n // @deprecated\n // @param control points (start, control start, control end, end)\n // @return a function that accepts t and returns 2 curves.\n getCurveDivider: function(p0, p1, p2, p3) {\n\n console.warn('deprecated')\n\n const curve = new Curve(p0, p1, p2, p3)\n\n return function divideCurve(t) {\n\n const divided = curve.divide(t)\n\n return [{\n p0: divided[0].start,\n p1: divided[0].controlPoint1,\n p2: divided[0].controlPoint2,\n p3: divided[0].end\n }, {\n p0: divided[1].start,\n p1: divided[1].controlPoint1,\n p2: divided[1].controlPoint2,\n p3: divided[1].end\n }]\n }\n },\n\n // Solves an inversion problem -- Given the (x, y) coordinates of a point which lies on\n // a parametric curve x = x(t)/w(t), y = y(t)/w(t), find the parameter value t\n // which corresponds to that point.\n // @deprecated\n // @param control points (start, control start, control end, end)\n // @return a function that accepts a point and returns t.\n getInversionSolver: function(p0, p1, p2, p3) {\n\n console.warn('deprecated')\n\n const curve = new Curve(p0, p1, p2, p3)\n\n return function solveInversion(p) {\n\n return curve.closestPointT(p)\n }\n }\n }\n\n var Curve = g.Curve = function(p1, p2, p3, p4) {\n\n if (!(this instanceof Curve)) {\n return new Curve(p1, p2, p3, p4)\n }\n\n if (p1 instanceof Curve) {\n return new Curve(p1.start, p1.controlPoint1, p1.controlPoint2, p1.end)\n }\n\n this.start = new Point(p1)\n this.controlPoint1 = new Point(p2)\n this.controlPoint2 = new Point(p3)\n this.end = new Point(p4)\n }\n\n // Curve passing through points.\n // Ported from C# implementation by Oleg V. Polikarpotchkin and Peter Lee (http://www.codeproject.com/KB/graphics/BezierSpline.aspx).\n // @param {array} points Array of points through which the smooth line will go.\n // @return {array} curves.\n Curve.throughPoints = (function() {\n\n // Solves a tridiagonal system for one of coordinates (x or y) of first Bezier control points.\n // @param rhs Right hand side vector.\n // @return Solution vector.\n function getFirstControlPoints(rhs) {\n\n const n = rhs.length\n // `x` is a solution vector.\n const x = []\n const tmp = []\n let b = 2.0\n\n x[0] = rhs[0] / b\n\n // Decomposition and forward substitution.\n for (var i = 1; i < n; i++) {\n tmp[i] = 1 / b\n b = (i < n - 1 ? 4.0 : 3.5) - tmp[i]\n x[i] = (rhs[i] - x[i - 1]) / b\n }\n\n for (i = 1; i < n; i++) {\n // Backsubstitution.\n x[n - i - 1] -= tmp[n - i] * x[n - i]\n }\n\n return x\n }\n\n // Get open-ended Bezier Spline Control Points.\n // @param knots Input Knot Bezier spline points (At least two points!).\n // @param firstControlPoints Output First Control points. Array of knots.length - 1 length.\n // @param secondControlPoints Output Second Control points. Array of knots.length - 1 length.\n function getCurveControlPoints(knots) {\n\n const firstControlPoints = []\n const secondControlPoints = []\n const n = knots.length - 1\n let i\n\n // Special case: Bezier curve should be a straight line.\n if (n == 1) {\n // 3P1 = 2P0 + P3\n firstControlPoints[0] = new Point(\n (2 * knots[0].x + knots[1].x) / 3,\n (2 * knots[0].y + knots[1].y) / 3\n )\n\n // P2 = 2P1 – P0\n secondControlPoints[0] = new Point(\n 2 * firstControlPoints[0].x - knots[0].x,\n 2 * firstControlPoints[0].y - knots[0].y\n )\n\n return [firstControlPoints, secondControlPoints]\n }\n\n // Calculate first Bezier control points.\n // Right hand side vector.\n const rhs = []\n\n // Set right hand side X values.\n for (i = 1; i < n - 1; i++) {\n rhs[i] = 4 * knots[i].x + 2 * knots[i + 1].x\n }\n\n rhs[0] = knots[0].x + 2 * knots[1].x\n rhs[n - 1] = (8 * knots[n - 1].x + knots[n].x) / 2.0\n\n // Get first control points X-values.\n const x = getFirstControlPoints(rhs)\n\n // Set right hand side Y values.\n for (i = 1; i < n - 1; ++i) {\n rhs[i] = 4 * knots[i].y + 2 * knots[i + 1].y\n }\n\n rhs[0] = knots[0].y + 2 * knots[1].y\n rhs[n - 1] = (8 * knots[n - 1].y + knots[n].y) / 2.0\n\n // Get first control points Y-values.\n const y = getFirstControlPoints(rhs)\n\n // Fill output arrays.\n for (i = 0; i < n; i++) {\n // First control point.\n firstControlPoints.push(new Point(x[i], y[i]))\n\n // Second control point.\n if (i < n - 1) {\n secondControlPoints.push(new Point(\n 2 * knots [i + 1].x - x[i + 1],\n 2 * knots[i + 1].y - y[i + 1]\n ))\n\n } else {\n secondControlPoints.push(new Point(\n (knots[n].x + x[n - 1]) / 2,\n (knots[n].y + y[n - 1]) / 2\n ))\n }\n }\n\n return [firstControlPoints, secondControlPoints]\n }\n\n return function(points) {\n\n if (!points || (Array.isArray(points) && points.length < 2)) {\n throw new Error('At least 2 points are required')\n }\n\n const controlPoints = getCurveControlPoints(points)\n\n const curves = []\n const n = controlPoints[0].length\n for (let i = 0; i < n; i++) {\n\n const controlPoint1 = new Point(controlPoints[0][i].x, controlPoints[0][i].y)\n const controlPoint2 = new Point(controlPoints[1][i].x, controlPoints[1][i].y)\n\n curves.push(new Curve(points[i], controlPoint1, controlPoint2, points[i + 1]))\n }\n\n return curves\n }\n })()\n\n Curve.prototype = {\n\n // Returns a bbox that tightly envelops the curve.\n bbox: function() {\n\n const {start} = this\n const {controlPoint1} = this\n const {controlPoint2} = this\n const {end} = this\n\n const x0 = start.x\n const y0 = start.y\n const x1 = controlPoint1.x\n const y1 = controlPoint1.y\n const x2 = controlPoint2.x\n const y2 = controlPoint2.y\n const x3 = end.x\n const y3 = end.y\n\n const points = new Array() // local extremes\n const tvalues = new Array() // t values of local extremes\n const bounds = [new Array(), new Array()]\n\n let a; let b; let c; let t\n let t1; let t2\n let b2ac; let sqrtb2ac\n\n for (let i = 0; i < 2; ++i) {\n\n if (i === 0) {\n b = 6 * x0 - 12 * x1 + 6 * x2\n a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3\n c = 3 * x1 - 3 * x0\n\n } else {\n b = 6 * y0 - 12 * y1 + 6 * y2\n a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3\n c = 3 * y1 - 3 * y0\n }\n\n if (abs(a) < 1e-12) { // Numerical robustness\n if (abs(b) < 1e-12) { // Numerical robustness\n continue\n }\n\n t = -c / b\n if ((t > 0) && (t < 1)) tvalues.push(t)\n\n continue\n }\n\n b2ac = b * b - 4 * c * a\n sqrtb2ac = sqrt(b2ac)\n\n if (b2ac < 0) continue\n\n t1 = (-b + sqrtb2ac) / (2 * a)\n if ((t1 > 0) && (t1 < 1)) tvalues.push(t1)\n\n t2 = (-b - sqrtb2ac) / (2 * a)\n if ((t2 > 0) && (t2 < 1)) tvalues.push(t2)\n }\n\n let j = tvalues.length\n const jlen = j\n let mt\n let x; let y\n\n while (j--) {\n t = tvalues[j]\n mt = 1 - t\n\n x = (mt * mt * mt * x0) + (3 * mt * mt * t * x1) + (3 * mt * t * t * x2) + (t * t * t * x3)\n bounds[0][j] = x\n\n y = (mt * mt * mt * y0) + (3 * mt * mt * t * y1) + (3 * mt * t * t * y2) + (t * t * t * y3)\n bounds[1][j] = y\n\n points[j] = { X: x, Y: y }\n }\n\n tvalues[jlen] = 0\n tvalues[jlen + 1] = 1\n\n points[jlen] = { X: x0, Y: y0 }\n points[jlen + 1] = { X: x3, Y: y3 }\n\n bounds[0][jlen] = x0\n bounds[1][jlen] = y0\n\n bounds[0][jlen + 1] = x3\n bounds[1][jlen + 1] = y3\n\n tvalues.length = jlen + 2\n bounds[0].length = jlen + 2\n bounds[1].length = jlen + 2\n points.length = jlen + 2\n\n const left = min.apply(null, bounds[0])\n const top = min.apply(null, bounds[1])\n const right = max.apply(null, bounds[0])\n const bottom = max.apply(null, bounds[1])\n\n return new Rect(left, top, (right - left), (bottom - top))\n },\n\n clone: function() {\n\n return new Curve(this.start, this.controlPoint1, this.controlPoint2, this.end)\n },\n\n // Returns the point on the curve closest to point `p`\n closestPoint: function(p, opt) {\n\n return this.pointAtT(this.closestPointT(p, opt))\n },\n\n closestPointLength: function(p, opt) {\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision\n const subdivisions = (opt.subdivisions === undefined) ? this.getSubdivisions({ precision: precision }) : opt.subdivisions\n const localOpt = { precision: precision, subdivisions: subdivisions }\n\n return this.lengthAtT(this.closestPointT(p, localOpt), localOpt)\n },\n\n closestPointNormalizedLength: function(p, opt) {\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision\n const subdivisions = (opt.subdivisions === undefined) ? this.getSubdivisions({ precision: precision }) : opt.subdivisions\n const localOpt = { precision: precision, subdivisions: subdivisions }\n\n const cpLength = this.closestPointLength(p, localOpt)\n if (!cpLength) return 0\n\n const length = this.length(localOpt)\n if (length === 0) return 0\n\n return cpLength / length\n },\n\n // Returns `t` of the point on the curve closest to point `p`\n closestPointT: function(p, opt) {\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision\n const subdivisions = (opt.subdivisions === undefined) ? this.getSubdivisions({ precision: precision }) : opt.subdivisions\n // does not use localOpt\n\n // identify the subdivision that contains the point:\n let investigatedSubdivision\n let investigatedSubdivisionStartT // assume that subdivisions are evenly spaced\n let investigatedSubdivisionEndT\n let distFromStart // distance of point from start of baseline\n let distFromEnd // distance of point from end of baseline\n let minSumDist // lowest observed sum of the two distances\n const n = subdivisions.length\n let subdivisionSize = (n ? (1 / n) : 0)\n for (let i = 0; i < n; i++) {\n\n const currentSubdivision = subdivisions[i]\n\n const startDist = currentSubdivision.start.distance(p)\n const endDist = currentSubdivision.end.distance(p)\n const sumDist = startDist + endDist\n\n // check that the point is closest to current subdivision and not any other\n if (!minSumDist || (sumDist < minSumDist)) {\n investigatedSubdivision = currentSubdivision\n\n investigatedSubdivisionStartT = i * subdivisionSize\n investigatedSubdivisionEndT = (i + 1) * subdivisionSize\n\n distFromStart = startDist\n distFromEnd = endDist\n\n minSumDist = sumDist\n }\n }\n\n const precisionRatio = pow(10, -precision)\n\n // recursively divide investigated subdivision:\n // until distance between baselinePoint and closest path endpoint is within 10^(-precision)\n // then return the closest endpoint of that final subdivision\n while (true) {\n\n // check if we have reached required observed precision\n var startPrecisionRatio\n var endPrecisionRatio\n\n startPrecisionRatio = (distFromStart ? (abs(distFromStart - distFromEnd) / distFromStart) : 0)\n endPrecisionRatio = (distFromEnd ? (abs(distFromStart - distFromEnd) / distFromEnd) : 0)\n if ((startPrecisionRatio < precisionRatio) || (endPrecisionRatio) < precisionRatio) {\n return ((distFromStart <= distFromEnd) ? investigatedSubdivisionStartT : investigatedSubdivisionEndT)\n }\n\n // otherwise, set up for next iteration\n const divided = investigatedSubdivision.divide(0.5)\n subdivisionSize /= 2\n\n const startDist1 = divided[0].start.distance(p)\n const endDist1 = divided[0].end.distance(p)\n const sumDist1 = startDist1 + endDist1\n\n const startDist2 = divided[1].start.distance(p)\n const endDist2 = divided[1].end.distance(p)\n const sumDist2 = startDist2 + endDist2\n\n if (sumDist1 <= sumDist2) {\n investigatedSubdivision = divided[0]\n\n investigatedSubdivisionEndT -= subdivisionSize // subdivisionSize was already halved\n\n distFromStart = startDist1\n distFromEnd = endDist1\n\n } else {\n investigatedSubdivision = divided[1]\n\n investigatedSubdivisionStartT += subdivisionSize // subdivisionSize was already halved\n\n distFromStart = startDist2\n distFromEnd = endDist2\n }\n }\n },\n\n closestPointTangent: function(p, opt) {\n\n return this.tangentAtT(this.closestPointT(p, opt))\n },\n\n // Divides the curve into two at point defined by `t` between 0 and 1.\n // Using de Casteljau's algorithm (http://math.stackexchange.com/a/317867).\n // Additional resource: https://pomax.github.io/bezierinfo/#decasteljau\n divide: function(t) {\n\n const {start} = this\n const {controlPoint1} = this\n const {controlPoint2} = this\n const {end} = this\n\n // shortcuts for `t` values that are out of range\n if (t <= 0) {\n return [\n new Curve(start, start, start, start),\n new Curve(start, controlPoint1, controlPoint2, end)\n ]\n }\n\n if (t >= 1) {\n return [\n new Curve(start, controlPoint1, controlPoint2, end),\n new Curve(end, end, end, end)\n ]\n }\n\n const dividerPoints = this.getSkeletonPoints(t)\n\n const startControl1 = dividerPoints.startControlPoint1\n const startControl2 = dividerPoints.startControlPoint2\n const {divider} = dividerPoints\n const dividerControl1 = dividerPoints.dividerControlPoint1\n const dividerControl2 = dividerPoints.dividerControlPoint2\n\n // return array with two new curves\n return [\n new Curve(start, startControl1, startControl2, divider),\n new Curve(divider, dividerControl1, dividerControl2, end)\n ]\n },\n\n // Returns the distance between the curve's start and end points.\n endpointDistance: function() {\n\n return this.start.distance(this.end)\n },\n\n // Checks whether two curves are exactly the same.\n equals: function(c) {\n\n return !!c &&\n this.start.x === c.start.x &&\n this.start.y === c.start.y &&\n this.controlPoint1.x === c.controlPoint1.x &&\n this.controlPoint1.y === c.controlPoint1.y &&\n this.controlPoint2.x === c.controlPoint2.x &&\n this.controlPoint2.y === c.controlPoint2.y &&\n this.end.x === c.end.x &&\n this.end.y === c.end.y\n },\n\n // Returns five helper points necessary for curve division.\n getSkeletonPoints: function(t) {\n\n const {start} = this\n const control1 = this.controlPoint1\n const control2 = this.controlPoint2\n const {end} = this\n\n // shortcuts for `t` values that are out of range\n if (t <= 0) {\n return {\n startControlPoint1: start.clone(),\n startControlPoint2: start.clone(),\n divider: start.clone(),\n dividerControlPoint1: control1.clone(),\n dividerControlPoint2: control2.clone()\n }\n }\n\n if (t >= 1) {\n return {\n startControlPoint1: control1.clone(),\n startControlPoint2: control2.clone(),\n divider: end.clone(),\n dividerControlPoint1: end.clone(),\n dividerControlPoint2: end.clone()\n }\n }\n\n const midpoint1 = (new Line(start, control1)).pointAt(t)\n const midpoint2 = (new Line(control1, control2)).pointAt(t)\n const midpoint3 = (new Line(control2, end)).pointAt(t)\n\n const subControl1 = (new Line(midpoint1, midpoint2)).pointAt(t)\n const subControl2 = (new Line(midpoint2, midpoint3)).pointAt(t)\n\n const divider = (new Line(subControl1, subControl2)).pointAt(t)\n\n const output = {\n startControlPoint1: midpoint1,\n startControlPoint2: subControl1,\n divider: divider,\n dividerControlPoint1: subControl2,\n dividerControlPoint2: midpoint3\n }\n\n return output\n },\n\n // Returns a list of curves whose flattened length is better than `opt.precision`.\n // That is, observed difference in length between recursions is less than 10^(-3) = 0.001 = 0.1%\n // (Observed difference is not real precision, but close enough as long as special cases are covered)\n // (That is why skipping iteration 1 is important)\n // As a rule of thumb, increasing `precision` by 1 requires two more division operations\n // - Precision 0 (endpointDistance) - total of 2^0 - 1 = 0 operations (1 subdivision)\n // - Precision 1 (<10% error) - total of 2^2 - 1 = 3 operations (4 subdivisions)\n // - Precision 2 (<1% error) - total of 2^4 - 1 = 15 operations requires 4 division operations on all elements (15 operations total) (16 subdivisions)\n // - Precision 3 (<0.1% error) - total of 2^6 - 1 = 63 operations - acceptable when drawing (64 subdivisions)\n // - Precision 4 (<0.01% error) - total of 2^8 - 1 = 255 operations - high resolution, can be used to interpolate `t` (256 subdivisions)\n // (Variation of 1 recursion worse or better is possible depending on the curve, doubling/halving the number of operations accordingly)\n getSubdivisions: function(opt) {\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision\n // not using opt.subdivisions\n // not using localOpt\n\n let subdivisions = [new Curve(this.start, this.controlPoint1, this.controlPoint2, this.end)]\n if (precision === 0) return subdivisions\n\n let previousLength = this.endpointDistance()\n\n const precisionRatio = pow(10, -precision)\n\n // recursively divide curve at `t = 0.5`\n // until the difference between observed length at subsequent iterations is lower than precision\n let iteration = 0\n while (true) {\n iteration += 1\n\n // divide all subdivisions\n const newSubdivisions = []\n const numSubdivisions = subdivisions.length\n for (let i = 0; i < numSubdivisions; i++) {\n\n const currentSubdivision = subdivisions[i]\n const divided = currentSubdivision.divide(0.5) // dividing at t = 0.5 (not at middle length!)\n newSubdivisions.push(divided[0], divided[1])\n }\n\n // measure new length\n let length = 0\n const numNewSubdivisions = newSubdivisions.length\n for (let j = 0; j < numNewSubdivisions; j++) {\n\n const currentNewSubdivision = newSubdivisions[j]\n length += currentNewSubdivision.endpointDistance()\n }\n\n // check if we have reached required observed precision\n // sine-like curves may have the same observed length in iteration 0 and 1 - skip iteration 1\n // not a problem for further iterations because cubic curves cannot have more than two local extrema\n // (i.e. cubic curves cannot intersect the baseline more than once)\n // therefore two subsequent iterations cannot produce sampling with equal length\n const observedPrecisionRatio = ((length !== 0) ? ((length - previousLength) / length) : 0)\n if (iteration > 1 && observedPrecisionRatio < precisionRatio) {\n return newSubdivisions\n }\n\n // otherwise, set up for next iteration\n subdivisions = newSubdivisions\n previousLength = length\n }\n },\n\n isDifferentiable: function() {\n\n const {start} = this\n const control1 = this.controlPoint1\n const control2 = this.controlPoint2\n const {end} = this\n\n return !(start.equals(control1) && control1.equals(control2) && control2.equals(end))\n },\n\n // Returns flattened length of the curve with precision better than `opt.precision`; or using `opt.subdivisions` provided.\n length: function(opt) {\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision // opt.precision only used in getSubdivisions() call\n const subdivisions = (opt.subdivisions === undefined) ? this.getSubdivisions({ precision: precision }) : opt.subdivisions\n // not using localOpt\n\n let length = 0\n const n = subdivisions.length\n for (let i = 0; i < n; i++) {\n\n const currentSubdivision = subdivisions[i]\n length += currentSubdivision.endpointDistance()\n }\n\n return length\n },\n\n // Returns distance along the curve up to `t` with precision better than requested `opt.precision`. (Not using `opt.subdivisions`.)\n lengthAtT: function(t, opt) {\n\n if (t <= 0) return 0\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision\n // not using opt.subdivisions\n // not using localOpt\n\n const subCurve = this.divide(t)[0]\n const subCurveLength = subCurve.length({ precision: precision })\n\n return subCurveLength\n },\n\n // Returns point at requested `ratio` between 0 and 1 with precision better than `opt.precision`; optionally using `opt.subdivisions` provided.\n // Mirrors Line.pointAt() function.\n // For a function that tracks `t`, use Curve.pointAtT().\n pointAt: function(ratio, opt) {\n\n if (ratio <= 0) return this.start.clone()\n if (ratio >= 1) return this.end.clone()\n\n const t = this.tAt(ratio, opt)\n\n return this.pointAtT(t)\n },\n\n // Returns point at requested `length` with precision better than requested `opt.precision`; optionally using `opt.subdivisions` provided.\n pointAtLength: function(length, opt) {\n\n const t = this.tAtLength(length, opt)\n\n return this.pointAtT(t)\n },\n\n // Returns the point at provided `t` between 0 and 1.\n // `t` does not track distance along curve as it does in Line objects.\n // Non-linear relationship, speeds up and slows down as curve warps!\n // For linear length-based solution, use Curve.pointAt().\n pointAtT: function(t) {\n\n if (t <= 0) return this.start.clone()\n if (t >= 1) return this.end.clone()\n\n return this.getSkeletonPoints(t).divider\n },\n\n // Default precision\n PRECISION: 3,\n\n scale: function(sx, sy, origin) {\n\n this.start.scale(sx, sy, origin)\n this.controlPoint1.scale(sx, sy, origin)\n this.controlPoint2.scale(sx, sy, origin)\n this.end.scale(sx, sy, origin)\n return this\n },\n\n // Returns a tangent line at requested `ratio` with precision better than requested `opt.precision`; or using `opt.subdivisions` provided.\n tangentAt: function(ratio, opt) {\n\n if (!this.isDifferentiable()) return null\n\n if (ratio < 0) ratio = 0\n else if (ratio > 1) ratio = 1\n\n const t = this.tAt(ratio, opt)\n\n return this.tangentAtT(t)\n },\n\n // Returns a tangent line at requested `length` with precision better than requested `opt.precision`; or using `opt.subdivisions` provided.\n tangentAtLength: function(length, opt) {\n\n if (!this.isDifferentiable()) return null\n\n const t = this.tAtLength(length, opt)\n\n return this.tangentAtT(t)\n },\n\n // Returns a tangent line at requested `t`.\n tangentAtT: function(t) {\n\n if (!this.isDifferentiable()) return null\n\n if (t < 0) t = 0\n else if (t > 1) t = 1\n\n const skeletonPoints = this.getSkeletonPoints(t)\n\n const p1 = skeletonPoints.startControlPoint2\n const p2 = skeletonPoints.dividerControlPoint1\n\n const tangentStart = skeletonPoints.divider\n\n const tangentLine = new Line(p1, p2)\n tangentLine.translate(tangentStart.x - p1.x, tangentStart.y - p1.y) // move so that tangent line starts at the point requested\n\n return tangentLine\n },\n\n // Returns `t` at requested `ratio` with precision better than requested `opt.precision`; optionally using `opt.subdivisions` provided.\n tAt: function(ratio, opt) {\n\n if (ratio <= 0) return 0\n if (ratio >= 1) return 1\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision\n const subdivisions = (opt.subdivisions === undefined) ? this.getSubdivisions({ precision: precision }) : opt.subdivisions\n const localOpt = { precision: precision, subdivisions: subdivisions }\n\n const curveLength = this.length(localOpt)\n const length = curveLength * ratio\n\n return this.tAtLength(length, localOpt)\n },\n\n // Returns `t` at requested `length` with precision better than requested `opt.precision`; optionally using `opt.subdivisions` provided.\n // Uses `precision` to approximate length within `precision` (always underestimates)\n // Then uses a binary search to find the `t` of a subdivision endpoint that is close (within `precision`) to the `length`, if the curve was as long as approximated\n // As a rule of thumb, increasing `precision` by 1 causes the algorithm to go 2^(precision - 1) deeper\n // - Precision 0 (chooses one of the two endpoints) - 0 levels\n // - Precision 1 (chooses one of 5 points, <10% error) - 1 level\n // - Precision 2 (<1% error) - 3 levels\n // - Precision 3 (<0.1% error) - 7 levels\n // - Precision 4 (<0.01% error) - 15 levels\n tAtLength: function(length, opt) {\n\n let fromStart = true\n if (length < 0) {\n fromStart = false // negative lengths mean start calculation from end point\n length = -length // absolute value\n }\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision\n const subdivisions = (opt.subdivisions === undefined) ? this.getSubdivisions({ precision: precision }) : opt.subdivisions\n const localOpt = { precision: precision, subdivisions: subdivisions }\n\n // identify the subdivision that contains the point at requested `length`:\n let investigatedSubdivision\n let investigatedSubdivisionStartT // assume that subdivisions are evenly spaced\n let investigatedSubdivisionEndT\n //var baseline; // straightened version of subdivision to investigate\n //var baselinePoint; // point on the baseline that is the requested distance away from start\n let baselinePointDistFromStart // distance of baselinePoint from start of baseline\n let baselinePointDistFromEnd // distance of baselinePoint from end of baseline\n let l = 0 // length so far\n const n = subdivisions.length\n let subdivisionSize = 1 / n\n for (let i = (fromStart ? (0) : (n - 1)); (fromStart ? (i < n) : (i >= 0)); (fromStart ? (i++) : (i--))) {\n\n const currentSubdivision = subdivisions[i]\n const d = currentSubdivision.endpointDistance() // length of current subdivision\n\n if (length <= (l + d)) {\n investigatedSubdivision = currentSubdivision\n\n investigatedSubdivisionStartT = i * subdivisionSize\n investigatedSubdivisionEndT = (i + 1) * subdivisionSize\n\n baselinePointDistFromStart = (fromStart ? (length - l) : ((d + l) - length))\n baselinePointDistFromEnd = (fromStart ? ((d + l) - length) : (length - l))\n\n break\n }\n\n l += d\n }\n\n if (!investigatedSubdivision) return (fromStart ? 1 : 0) // length requested is out of range - return maximum t\n // note that precision affects what length is recorded\n // (imprecise measurements underestimate length by up to 10^(-precision) of the precise length)\n // e.g. at precision 1, the length may be underestimated by up to 10% and cause this function to return 1\n\n const curveLength = this.length(localOpt)\n\n const precisionRatio = pow(10, -precision)\n\n // recursively divide investigated subdivision:\n // until distance between baselinePoint and closest path endpoint is within 10^(-precision)\n // then return the closest endpoint of that final subdivision\n while (true) {\n\n // check if we have reached required observed precision\n var observedPrecisionRatio\n\n observedPrecisionRatio = ((curveLength !== 0) ? (baselinePointDistFromStart / curveLength) : 0)\n if (observedPrecisionRatio < precisionRatio) return investigatedSubdivisionStartT\n observedPrecisionRatio = ((curveLength !== 0) ? (baselinePointDistFromEnd / curveLength) : 0)\n if (observedPrecisionRatio < precisionRatio) return investigatedSubdivisionEndT\n\n // otherwise, set up for next iteration\n var newBaselinePointDistFromStart\n var newBaselinePointDistFromEnd\n\n const divided = investigatedSubdivision.divide(0.5)\n subdivisionSize /= 2\n\n const baseline1Length = divided[0].endpointDistance()\n const baseline2Length = divided[1].endpointDistance()\n\n if (baselinePointDistFromStart <= baseline1Length) { // point at requested length is inside divided[0]\n investigatedSubdivision = divided[0]\n\n investigatedSubdivisionEndT -= subdivisionSize // sudivisionSize was already halved\n\n newBaselinePointDistFromStart = baselinePointDistFromStart\n newBaselinePointDistFromEnd = baseline1Length - newBaselinePointDistFromStart\n\n } else { // point at requested length is inside divided[1]\n investigatedSubdivision = divided[1]\n\n investigatedSubdivisionStartT += subdivisionSize // subdivisionSize was already halved\n\n newBaselinePointDistFromStart = baselinePointDistFromStart - baseline1Length\n newBaselinePointDistFromEnd = baseline2Length - newBaselinePointDistFromStart\n }\n\n baselinePointDistFromStart = newBaselinePointDistFromStart\n baselinePointDistFromEnd = newBaselinePointDistFromEnd\n }\n },\n\n translate: function(tx, ty) {\n\n this.start.translate(tx, ty)\n this.controlPoint1.translate(tx, ty)\n this.controlPoint2.translate(tx, ty)\n this.end.translate(tx, ty)\n return this\n },\n\n // Returns an array of points that represents the curve when flattened, up to `opt.precision`; or using `opt.subdivisions` provided.\n // Flattened length is no more than 10^(-precision) away from real curve length.\n toPoints: function(opt) {\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision // opt.precision only used in getSubdivisions() call\n const subdivisions = (opt.subdivisions === undefined) ? this.getSubdivisions({ precision: precision }) : opt.subdivisions\n // not using localOpt\n\n const points = [subdivisions[0].start.clone()]\n const n = subdivisions.length\n for (let i = 0; i < n; i++) {\n\n const currentSubdivision = subdivisions[i]\n points.push(currentSubdivision.end.clone())\n }\n\n return points\n },\n\n // Returns a polyline that represents the curve when flattened, up to `opt.precision`; or using `opt.subdivisions` provided.\n // Flattened length is no more than 10^(-precision) away from real curve length.\n toPolyline: function(opt) {\n\n return new Polyline(this.toPoints(opt))\n },\n\n toString: function() {\n\n return `${this.start } ${ this.controlPoint1 } ${ this.controlPoint2 } ${ this.end}`\n }\n }\n\n var Ellipse = g.Ellipse = function(c, a, b) {\n\n if (!(this instanceof Ellipse)) {\n return new Ellipse(c, a, b)\n }\n\n if (c instanceof Ellipse) {\n return new Ellipse(new Point(c.x, c.y), c.a, c.b)\n }\n\n c = new Point(c)\n this.x = c.x\n this.y = c.y\n this.a = a\n this.b = b\n }\n\n Ellipse.fromRect = function(rect) {\n\n rect = new Rect(rect)\n return new Ellipse(rect.center(), rect.width / 2, rect.height / 2)\n }\n\n Ellipse.prototype = {\n\n bbox: function() {\n\n return new Rect(this.x - this.a, this.y - this.b, 2 * this.a, 2 * this.b)\n },\n\n clone: function() {\n\n return new Ellipse(this)\n },\n\n /**\n * @param {g.Point} point\n * @returns {number} result < 1 - inside ellipse, result == 1 - on ellipse boundary, result > 1 - outside\n */\n normalizedDistance: function(point) {\n\n const x0 = point.x\n const y0 = point.y\n const {a} = this\n const {b} = this\n const {x} = this\n const {y} = this\n\n return ((x0 - x) * (x0 - x)) / (a * a ) + ((y0 - y) * (y0 - y)) / (b * b)\n },\n\n // inflate by dx and dy\n // @param dx {delta_x} representing additional size to x\n // @param dy {delta_y} representing additional size to y -\n // dy param is not required -> in that case y is sized by dx\n inflate: function(dx, dy) {\n if (dx === undefined) {\n dx = 0\n }\n\n if (dy === undefined) {\n dy = dx\n }\n\n this.a += 2 * dx\n this.b += 2 * dy\n\n return this\n },\n\n\n /**\n * @param {g.Point} p\n * @returns {boolean}\n */\n containsPoint: function(p) {\n\n return this.normalizedDistance(p) <= 1\n },\n\n /**\n * @returns {g.Point}\n */\n center: function() {\n\n return new Point(this.x, this.y)\n },\n\n /** Compute angle between tangent and x axis\n * @param {g.Point} p Point of tangency, it has to be on ellipse boundaries.\n * @returns {number} angle between tangent and x axis\n */\n tangentTheta: function(p) {\n\n const refPointDelta = 30\n const x0 = p.x\n const y0 = p.y\n const {a} = this\n const {b} = this\n const center = this.bbox().center()\n const m = center.x\n const n = center.y\n\n const q1 = x0 > center.x + a / 2\n const q3 = x0 < center.x - a / 2\n\n let y; let x\n if (q1 || q3) {\n y = x0 > center.x ? y0 - refPointDelta : y0 + refPointDelta\n x = (a * a / (x0 - m)) - (a * a * (y0 - n) * (y - n)) / (b * b * (x0 - m)) + m\n\n } else {\n x = y0 > center.y ? x0 + refPointDelta : x0 - refPointDelta\n y = ( b * b / (y0 - n)) - (b * b * (x0 - m) * (x - m)) / (a * a * (y0 - n)) + n\n }\n\n return (new Point(x, y)).theta(p)\n\n },\n\n equals: function(ellipse) {\n\n return !!ellipse &&\n ellipse.x === this.x &&\n ellipse.y === this.y &&\n ellipse.a === this.a &&\n ellipse.b === this.b\n },\n\n intersectionWithLine: function(line) {\n\n const intersections = []\n const a1 = line.start\n const a2 = line.end\n const rx = this.a\n const ry = this.b\n const dir = line.vector()\n const diff = a1.difference(new Point(this))\n const mDir = new Point(dir.x / (rx * rx), dir.y / (ry * ry))\n const mDiff = new Point(diff.x / (rx * rx), diff.y / (ry * ry))\n\n const a = dir.dot(mDir)\n const b = dir.dot(mDiff)\n const c = diff.dot(mDiff) - 1.0\n const d = b * b - a * c\n\n if (d < 0) {\n return null\n } else if (d > 0) {\n const root = sqrt(d)\n const ta = (-b - root) / a\n const tb = (-b + root) / a\n\n if ((ta < 0 || ta > 1) && (tb < 0 || tb > 1)) {\n // if ((ta < 0 && tb < 0) || (ta > 1 && tb > 1)) outside else inside\n return null\n } else {\n if (ta >= 0 && ta <= 1) intersections.push(a1.lerp(a2, ta))\n if (tb >= 0 && tb <= 1) intersections.push(a1.lerp(a2, tb))\n }\n } else {\n const t = -b / a\n if (t >= 0 && t <= 1) {\n intersections.push(a1.lerp(a2, t))\n } else {\n // outside\n return null\n }\n }\n\n return intersections\n },\n\n // Find point on me where line from my center to\n // point p intersects my boundary.\n // @param {number} angle If angle is specified, intersection with rotated ellipse is computed.\n intersectionWithLineFromCenterToPoint: function(p, angle) {\n\n p = new Point(p)\n\n if (angle) p.rotate(new Point(this.x, this.y), angle)\n\n const dx = p.x - this.x\n const dy = p.y - this.y\n let result\n\n if (dx === 0) {\n result = this.bbox().pointNearestToPoint(p)\n if (angle) return result.rotate(new Point(this.x, this.y), -angle)\n return result\n }\n\n const m = dy / dx\n const mSquared = m * m\n const aSquared = this.a * this.a\n const bSquared = this.b * this.b\n\n let x = sqrt(1 / ((1 / aSquared) + (mSquared / bSquared)))\n x = dx < 0 ? -x : x\n\n const y = m * x\n result = new Point(this.x + x, this.y + y)\n\n if (angle) return result.rotate(new Point(this.x, this.y), -angle)\n return result\n },\n\n toString: function() {\n\n return `${(new Point(this.x, this.y)).toString() } ${ this.a } ${ this.b}`\n }\n }\n\n var Line = g.Line = function(p1, p2) {\n\n if (!(this instanceof Line)) {\n return new Line(p1, p2)\n }\n\n if (p1 instanceof Line) {\n return new Line(p1.start, p1.end)\n }\n\n this.start = new Point(p1)\n this.end = new Point(p2)\n }\n\n Line.prototype = {\n\n bbox: function() {\n\n const left = min(this.start.x, this.end.x)\n const top = min(this.start.y, this.end.y)\n const right = max(this.start.x, this.end.x)\n const bottom = max(this.start.y, this.end.y)\n\n return new Rect(left, top, (right - left), (bottom - top))\n },\n\n // @return the bearing (cardinal direction) of the line. For example N, W, or SE.\n // @returns {String} One of the following bearings : NE, E, SE, S, SW, W, NW, N.\n bearing: function() {\n\n const lat1 = toRad(this.start.y)\n const lat2 = toRad(this.end.y)\n const lon1 = this.start.x\n const lon2 = this.end.x\n const dLon = toRad(lon2 - lon1)\n const y = sin(dLon) * cos(lat2)\n const x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon)\n const brng = toDeg(atan2(y, x))\n\n const bearings = ['NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'N']\n\n let index = brng - 22.5\n if (index < 0)\n index += 360\n index = parseInt(index / 45)\n\n return bearings[index]\n },\n\n clone: function() {\n\n return new Line(this.start, this.end)\n },\n\n // @return {point} the closest point on the line to point `p`\n closestPoint: function(p) {\n\n return this.pointAt(this.closestPointNormalizedLength(p))\n },\n\n closestPointLength: function(p) {\n\n return this.closestPointNormalizedLength(p) * this.length()\n },\n\n // @return {number} the normalized length of the closest point on the line to point `p`\n closestPointNormalizedLength: function(p) {\n\n const product = this.vector().dot((new Line(this.start, p)).vector())\n const cpNormalizedLength = min(1, max(0, product / this.squaredLength()))\n\n // cpNormalizedLength returns `NaN` if this line has zero length\n // we can work with that - if `NaN`, return 0\n if (cpNormalizedLength !== cpNormalizedLength) return 0 // condition evaluates to `true` if and only if cpNormalizedLength is `NaN`\n // (`NaN` is the only value that is not equal to itself)\n\n return cpNormalizedLength\n },\n\n closestPointTangent: function(p) {\n\n return this.tangentAt(this.closestPointNormalizedLength(p))\n },\n\n equals: function(l) {\n\n return !!l &&\n this.start.x === l.start.x &&\n this.start.y === l.start.y &&\n this.end.x === l.end.x &&\n this.end.y === l.end.y\n },\n\n intersectionWithLine: function(line) {\n\n const pt1Dir = new Point(this.end.x - this.start.x, this.end.y - this.start.y)\n const pt2Dir = new Point(line.end.x - line.start.x, line.end.y - line.start.y)\n const det = (pt1Dir.x * pt2Dir.y) - (pt1Dir.y * pt2Dir.x)\n const deltaPt = new Point(line.start.x - this.start.x, line.start.y - this.start.y)\n const alpha = (deltaPt.x * pt2Dir.y) - (deltaPt.y * pt2Dir.x)\n const beta = (deltaPt.x * pt1Dir.y) - (deltaPt.y * pt1Dir.x)\n\n if (det === 0 || alpha * det < 0 || beta * det < 0) {\n // No intersection found.\n return null\n }\n\n if (det > 0) {\n if (alpha > det || beta > det) {\n return null\n }\n\n } else if (alpha < det || beta < det) {\n return null\n }\n\n return [new Point(\n this.start.x + (alpha * pt1Dir.x / det),\n this.start.y + (alpha * pt1Dir.y / det)\n )]\n },\n\n // @return {point} Point where I'm intersecting a line.\n // @return [point] Points where I'm intersecting a rectangle.\n // @see Squeak Smalltalk, LineSegment>>intersectionWith:\n intersect: function(shape, opt) {\n\n if (shape instanceof Line ||\n shape instanceof Rect ||\n shape instanceof Polyline ||\n shape instanceof Ellipse ||\n shape instanceof Path\n ) {\n let intersection = shape.intersectionWithLine(this, opt)\n\n // Backwards compatibility\n if (intersection && (shape instanceof Line)) {\n intersection = intersection[0]\n }\n\n return intersection\n }\n\n return null\n },\n\n isDifferentiable: function() {\n\n return !this.start.equals(this.end)\n },\n\n // @return {double} length of the line\n length: function() {\n\n return sqrt(this.squaredLength())\n },\n\n // @return {point} my midpoint\n midpoint: function() {\n\n return new Point(\n (this.start.x + this.end.x) / 2,\n (this.start.y + this.end.y) / 2\n )\n },\n\n // @return {point} my point at 't' <0,1>\n pointAt: function(t) {\n\n const {start} = this\n const {end} = this\n\n if (t <= 0) return start.clone()\n if (t >= 1) return end.clone()\n\n return start.lerp(end, t)\n },\n\n pointAtLength: function(length) {\n\n const {start} = this\n const {end} = this\n\n let fromStart = true\n if (length < 0) {\n fromStart = false // negative lengths mean start calculation from end point\n length = -length // absolute value\n }\n\n const lineLength = this.length()\n if (length >= lineLength) return (fromStart ? end.clone() : start.clone())\n\n return this.pointAt((fromStart ? (length) : (lineLength - length)) / lineLength)\n },\n\n // @return {number} the offset of the point `p` from the line. + if the point `p` is on the right side of the line, - if on the left and 0 if on the line.\n pointOffset: function(p) {\n\n // Find the sign of the determinant of vectors (start,end), where p is the query point.\n p = new g.Point(p)\n const {start} = this\n const {end} = this\n const determinant = ((end.x - start.x) * (p.y - start.y) - (end.y - start.y) * (p.x - start.x))\n\n return determinant / this.length()\n },\n\n rotate: function(origin, angle) {\n\n this.start.rotate(origin, angle)\n this.end.rotate(origin, angle)\n return this\n },\n\n round: function(precision) {\n\n const f = pow(10, precision || 0)\n this.start.x = round(this.start.x * f) / f\n this.start.y = round(this.start.y * f) / f\n this.end.x = round(this.end.x * f) / f\n this.end.y = round(this.end.y * f) / f\n return this\n },\n\n scale: function(sx, sy, origin) {\n\n this.start.scale(sx, sy, origin)\n this.end.scale(sx, sy, origin)\n return this\n },\n\n // @return {number} scale the line so that it has the requested length\n setLength: function(length) {\n\n const currentLength = this.length()\n if (!currentLength) return this\n\n const scaleFactor = length / currentLength\n return this.scale(scaleFactor, scaleFactor, this.start)\n },\n\n // @return {integer} length without sqrt\n // @note for applications where the exact length is not necessary (e.g. compare only)\n squaredLength: function() {\n\n let x0 = this.start.x\n let y0 = this.start.y\n const x1 = this.end.x\n const y1 = this.end.y\n return (x0 -= x1) * x0 + (y0 -= y1) * y0\n },\n\n tangentAt: function(t) {\n\n if (!this.isDifferentiable()) return null\n\n const {start} = this\n const {end} = this\n\n const tangentStart = this.pointAt(t) // constrains `t` between 0 and 1\n\n const tangentLine = new Line(start, end)\n tangentLine.translate(tangentStart.x - start.x, tangentStart.y - start.y) // move so that tangent line starts at the point requested\n\n return tangentLine\n },\n\n tangentAtLength: function(length) {\n\n if (!this.isDifferentiable()) return null\n\n const {start} = this\n const {end} = this\n\n const tangentStart = this.pointAtLength(length)\n\n const tangentLine = new Line(start, end)\n tangentLine.translate(tangentStart.x - start.x, tangentStart.y - start.y) // move so that tangent line starts at the point requested\n\n return tangentLine\n },\n\n translate: function(tx, ty) {\n\n this.start.translate(tx, ty)\n this.end.translate(tx, ty)\n return this\n },\n\n // @return vector {point} of the line\n vector: function() {\n\n return new Point(this.end.x - this.start.x, this.end.y - this.start.y)\n },\n\n toString: function() {\n\n return `${this.start.toString() } ${ this.end.toString()}`\n }\n }\n\n // For backwards compatibility:\n Line.prototype.intersection = Line.prototype.intersect\n\n // Accepts path data string, array of segments, array of Curves and/or Lines, or a Polyline.\n // Path created is not guaranteed to be a valid (serializable) path (might not start with an M).\n var Path = g.Path = function(arg) {\n\n if (!(this instanceof Path)) {\n return new Path(arg)\n }\n\n if (typeof arg === 'string') { // create from a path data string\n return new Path.parse(arg)\n }\n\n this.segments = []\n\n let i\n let n\n\n if (!arg) {\n // don't do anything\n\n } else if (Array.isArray(arg) && arg.length !== 0) { // if arg is a non-empty array\n n = arg.length\n if (arg[0].isSegment) { // create from an array of segments\n for (i = 0; i < n; i++) {\n\n const segment = arg[i]\n\n this.appendSegment(segment)\n }\n\n } else { // create from an array of Curves and/or Lines\n let previousObj = null\n for (i = 0; i < n; i++) {\n\n const obj = arg[i]\n\n if (!((obj instanceof Line) || (obj instanceof Curve))) {\n throw new Error('Cannot construct a path segment from the provided object.')\n }\n\n if (i === 0) this.appendSegment(Path.createSegment('M', obj.start))\n\n // if objects do not link up, moveto segments are inserted to cover the gaps\n if (previousObj && !previousObj.end.equals(obj.start)) this.appendSegment(Path.createSegment('M', obj.start))\n\n if (obj instanceof Line) {\n this.appendSegment(Path.createSegment('L', obj.end))\n\n } else if (obj instanceof Curve) {\n this.appendSegment(Path.createSegment('C', obj.controlPoint1, obj.controlPoint2, obj.end))\n }\n\n previousObj = obj\n }\n }\n\n } else if (arg.isSegment) { // create from a single segment\n this.appendSegment(arg)\n\n } else if (arg instanceof Line) { // create from a single Line\n this.appendSegment(Path.createSegment('M', arg.start))\n this.appendSegment(Path.createSegment('L', arg.end))\n\n } else if (arg instanceof Curve) { // create from a single Curve\n this.appendSegment(Path.createSegment('M', arg.start))\n this.appendSegment(Path.createSegment('C', arg.controlPoint1, arg.controlPoint2, arg.end))\n\n } else if (arg instanceof Polyline && arg.points && arg.points.length !== 0) { // create from a Polyline\n n = arg.points.length\n for (i = 0; i < n; i++) {\n\n const point = arg.points[i]\n\n if (i === 0) this.appendSegment(Path.createSegment('M', point))\n else this.appendSegment(Path.createSegment('L', point))\n }\n }\n }\n\n // More permissive than V.normalizePathData and Path.prototype.serialize.\n // Allows path data strings that do not start with a Moveto command (unlike SVG specification).\n // Does not require spaces between elements; commas are allowed, separators may be omitted when unambiguous (e.g. 'ZM10,10', 'L1.6.8', 'M100-200').\n // Allows for command argument chaining.\n // Throws an error if wrong number of arguments is provided with a command.\n // Throws an error if an unrecognized path command is provided (according to Path.segmentTypes). Only a subset of SVG commands is currently supported (L, C, M, Z).\n Path.parse = function(pathData) {\n\n if (!pathData) return new Path()\n\n const path = new Path()\n\n const commandRe = /(?:[a-zA-Z] *)(?:(?:-?\\d+(?:\\.\\d+)? *,? *)|(?:-?\\.\\d+ *,? *))+|(?:[a-zA-Z] *)(?! |\\d|-|\\.)/g\n const commands = pathData.match(commandRe)\n\n const numCommands = commands.length\n for (let i = 0; i < numCommands; i++) {\n\n const command = commands[i]\n const argRe = /(?:[a-zA-Z])|(?:(?:-?\\d+(?:\\.\\d+)?))|(?:(?:-?\\.\\d+))/g\n const args = command.match(argRe)\n\n const segment = Path.createSegment.apply(this, args) // args = [type, coordinate1, coordinate2...]\n path.appendSegment(segment)\n }\n\n return path\n }\n\n // Create a segment or an array of segments.\n // Accepts unlimited points/coords arguments after `type`.\n Path.createSegment = function(type) {\n\n if (!type) throw new Error('Type must be provided.')\n\n const segmentConstructor = Path.segmentTypes[type]\n if (!segmentConstructor) throw new Error(`${type } is not a recognized path segment type.`)\n\n const args = []\n const n = arguments.length\n for (let i = 1; i < n; i++) { // do not add first element (`type`) to args array\n args.push(arguments[i])\n }\n\n return applyToNew(segmentConstructor, args)\n },\n\n Path.prototype = {\n\n // Accepts one segment or an array of segments as argument.\n // Throws an error if argument is not a segment or an array of segments.\n appendSegment: function(arg) {\n\n const {segments} = this\n const numSegments = segments.length\n // works even if path has no segments\n\n let currentSegment\n\n let previousSegment = ((numSegments !== 0) ? segments[numSegments - 1] : null) // if we are appending to an empty path, previousSegment is null\n const nextSegment = null\n\n if (!Array.isArray(arg)) { // arg is a segment\n if (!arg || !arg.isSegment) throw new Error('Segment required.')\n\n currentSegment = this.prepareSegment(arg, previousSegment, nextSegment)\n segments.push(currentSegment)\n\n } else { // arg is an array of segments\n if (!arg[0].isSegment) throw new Error('Segments required.')\n\n const n = arg.length\n for (let i = 0; i < n; i++) {\n\n const currentArg = arg[i]\n currentSegment = this.prepareSegment(currentArg, previousSegment, nextSegment)\n segments.push(currentSegment)\n previousSegment = currentSegment\n }\n }\n },\n\n // Returns the bbox of the path.\n // If path has no segments, returns null.\n // If path has only invisible segments, returns bbox of the end point of last segment.\n bbox: function() {\n\n const {segments} = this\n const numSegments = segments.length\n if (numSegments === 0) return null // if segments is an empty array\n\n let bbox\n for (let i = 0; i < numSegments; i++) {\n\n const segment = segments[i]\n if (segment.isVisible) {\n const segmentBBox = segment.bbox()\n bbox = bbox ? bbox.union(segmentBBox) : segmentBBox\n }\n }\n\n if (bbox) return bbox\n\n // if the path has only invisible elements, return end point of last segment\n const lastSegment = segments[numSegments - 1]\n return new Rect(lastSegment.end.x, lastSegment.end.y, 0, 0)\n },\n\n // Returns a new path that is a clone of this path.\n clone: function() {\n\n const {segments} = this\n const numSegments = segments.length\n // works even if path has no segments\n\n const path = new Path()\n for (let i = 0; i < numSegments; i++) {\n\n const segment = segments[i].clone()\n path.appendSegment(segment)\n }\n\n return path\n },\n\n closestPoint: function(p, opt) {\n\n const t = this.closestPointT(p, opt)\n if (!t) return null\n\n return this.pointAtT(t)\n },\n\n closestPointLength: function(p, opt) {\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision\n const segmentSubdivisions = (opt.segmentSubdivisions === undefined) ? this.getSegmentSubdivisions({ precision: precision }) : opt.segmentSubdivisions\n const localOpt = { precision: precision, segmentSubdivisions: segmentSubdivisions }\n\n const t = this.closestPointT(p, localOpt)\n if (!t) return 0\n\n return this.lengthAtT(t, localOpt)\n },\n\n closestPointNormalizedLength: function(p, opt) {\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision\n const segmentSubdivisions = (opt.segmentSubdivisions === undefined) ? this.getSegmentSubdivisions({ precision: precision }) : opt.segmentSubdivisions\n const localOpt = { precision: precision, segmentSubdivisions: segmentSubdivisions }\n\n const cpLength = this.closestPointLength(p, localOpt)\n if (cpLength === 0) return 0 // shortcut\n\n const length = this.length(localOpt)\n if (length === 0) return 0 // prevents division by zero\n\n return cpLength / length\n },\n\n // Private function.\n closestPointT: function(p, opt) {\n\n const {segments} = this\n const numSegments = segments.length\n if (numSegments === 0) return null // if segments is an empty array\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision\n const segmentSubdivisions = (opt.segmentSubdivisions === undefined) ? this.getSegmentSubdivisions({ precision: precision }) : opt.segmentSubdivisions\n // not using localOpt\n\n let closestPointT\n let minSquaredDistance = Infinity\n for (let i = 0; i < numSegments; i++) {\n\n const segment = segments[i]\n const subdivisions = segmentSubdivisions[i]\n\n if (segment.isVisible) {\n const segmentClosestPointT = segment.closestPointT(p, { precision: precision, subdivisions: subdivisions })\n const segmentClosestPoint = segment.pointAtT(segmentClosestPointT)\n const squaredDistance = (new Line(segmentClosestPoint, p)).squaredLength()\n\n if (squaredDistance < minSquaredDistance) {\n closestPointT = { segmentIndex: i, value: segmentClosestPointT }\n minSquaredDistance = squaredDistance\n }\n }\n }\n\n if (closestPointT) return closestPointT\n\n // if no visible segment, return end of last segment\n return { segmentIndex: numSegments - 1, value: 1 }\n },\n\n closestPointTangent: function(p, opt) {\n\n const {segments} = this\n const numSegments = segments.length\n if (numSegments === 0) return null // if segments is an empty array\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision\n const segmentSubdivisions = (opt.segmentSubdivisions === undefined) ? this.getSegmentSubdivisions({ precision: precision }) : opt.segmentSubdivisions\n // not using localOpt\n\n let closestPointTangent\n let minSquaredDistance = Infinity\n for (let i = 0; i < numSegments; i++) {\n\n const segment = segments[i]\n const subdivisions = segmentSubdivisions[i]\n\n if (segment.isDifferentiable()) {\n const segmentClosestPointT = segment.closestPointT(p, { precision: precision, subdivisions: subdivisions })\n const segmentClosestPoint = segment.pointAtT(segmentClosestPointT)\n const squaredDistance = (new Line(segmentClosestPoint, p)).squaredLength()\n\n if (squaredDistance < minSquaredDistance) {\n closestPointTangent = segment.tangentAtT(segmentClosestPointT)\n minSquaredDistance = squaredDistance\n }\n }\n }\n\n if (closestPointTangent) return closestPointTangent\n\n // if no valid segment, return null\n return null\n },\n\n // Checks whether two paths are exactly the same.\n // If `p` is undefined or null, returns false.\n equals: function(p) {\n\n if (!p) return false\n\n const {segments} = this\n const otherSegments = p.segments\n\n const numSegments = segments.length\n if (otherSegments.length !== numSegments) return false // if the two paths have different number of segments, they cannot be equal\n\n for (let i = 0; i < numSegments; i++) {\n\n const segment = segments[i]\n const otherSegment = otherSegments[i]\n\n // as soon as an inequality is found in segments, return false\n if ((segment.type !== otherSegment.type) || (!segment.equals(otherSegment))) return false\n }\n\n // if no inequality found in segments, return true\n return true\n },\n\n // Accepts negative indices.\n // Throws an error if path has no segments.\n // Throws an error if index is out of range.\n getSegment: function(index) {\n\n const {segments} = this\n const numSegments = segments.length\n if (!numSegments === 0) throw new Error('Path has no segments.')\n\n if (index < 0) index = numSegments + index // convert negative indices to positive\n if (index >= numSegments || index < 0) throw new Error('Index out of range.')\n\n return segments[index]\n },\n\n // Returns an array of segment subdivisions, with precision better than requested `opt.precision`.\n getSegmentSubdivisions: function(opt) {\n\n const {segments} = this\n const numSegments = segments.length\n // works even if path has no segments\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision\n // not using opt.segmentSubdivisions\n // not using localOpt\n\n const segmentSubdivisions = []\n for (let i = 0; i < numSegments; i++) {\n\n const segment = segments[i]\n const subdivisions = segment.getSubdivisions({ precision: precision })\n segmentSubdivisions.push(subdivisions)\n }\n\n return segmentSubdivisions\n },\n\n // Insert `arg` at given `index`.\n // `index = 0` means insert at the beginning.\n // `index = segments.length` means insert at the end.\n // Accepts negative indices, from `-1` to `-(segments.length + 1)`.\n // Accepts one segment or an array of segments as argument.\n // Throws an error if index is out of range.\n // Throws an error if argument is not a segment or an array of segments.\n insertSegment: function(index, arg) {\n\n const {segments} = this\n const numSegments = segments.length\n // works even if path has no segments\n\n // note that these are incremented comapared to getSegments()\n // we can insert after last element (note that this changes the meaning of index -1)\n if (index < 0) index = numSegments + index + 1 // convert negative indices to positive\n if (index > numSegments || index < 0) throw new Error('Index out of range.')\n\n let currentSegment\n\n let previousSegment = null\n let nextSegment = null\n\n if (numSegments !== 0) {\n if (index >= 1) {\n previousSegment = segments[index - 1]\n nextSegment = previousSegment.nextSegment // if we are inserting at end, nextSegment is null\n\n } else { // if index === 0\n // previousSegment is null\n nextSegment = segments[0]\n }\n }\n\n if (!Array.isArray(arg)) {\n if (!arg || !arg.isSegment) throw new Error('Segment required.')\n\n currentSegment = this.prepareSegment(arg, previousSegment, nextSegment)\n segments.splice(index, 0, currentSegment)\n\n } else {\n if (!arg[0].isSegment) throw new Error('Segments required.')\n\n const n = arg.length\n for (let i = 0; i < n; i++) {\n\n const currentArg = arg[i]\n currentSegment = this.prepareSegment(currentArg, previousSegment, nextSegment)\n segments.splice((index + i), 0, currentSegment) // incrementing index to insert subsequent segments after inserted segments\n previousSegment = currentSegment\n }\n }\n },\n\n isDifferentiable: function() {\n\n const {segments} = this\n const numSegments = segments.length\n\n for (let i = 0; i < numSegments; i++) {\n\n const segment = segments[i]\n // as soon as a differentiable segment is found in segments, return true\n if (segment.isDifferentiable()) return true\n }\n\n // if no differentiable segment is found in segments, return false\n return false\n },\n\n // Checks whether current path segments are valid.\n // Note that d is allowed to be empty - should disable rendering of the path.\n isValid: function() {\n\n const {segments} = this\n const isValid = (segments.length === 0) || (segments[0].type === 'M') // either empty or first segment is a Moveto\n return isValid\n },\n\n // Returns length of the path, with precision better than requested `opt.precision`; or using `opt.segmentSubdivisions` provided.\n // If path has no segments, returns 0.\n length: function(opt) {\n\n const {segments} = this\n const numSegments = segments.length\n if (numSegments === 0) return 0 // if segments is an empty array\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision // opt.precision only used in getSegmentSubdivisions() call\n const segmentSubdivisions = (opt.segmentSubdivisions === undefined) ? this.getSegmentSubdivisions({ precision: precision }) : opt.segmentSubdivisions\n // not using localOpt\n\n let length = 0\n for (let i = 0; i < numSegments; i++) {\n\n const segment = segments[i]\n const subdivisions = segmentSubdivisions[i]\n length += segment.length({ subdivisions: subdivisions })\n }\n\n return length\n },\n\n // Private function.\n lengthAtT: function(t, opt) {\n\n const {segments} = this\n const numSegments = segments.length\n if (numSegments === 0) return 0 // if segments is an empty array\n\n let {segmentIndex} = t\n if (segmentIndex < 0) return 0 // regardless of t.value\n\n let tValue = t.value\n if (segmentIndex >= numSegments) {\n segmentIndex = numSegments - 1\n tValue = 1\n }\n else if (tValue < 0) tValue = 0\n else if (tValue > 1) tValue = 1\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision\n const segmentSubdivisions = (opt.segmentSubdivisions === undefined) ? this.getSegmentSubdivisions({ precision: precision }) : opt.segmentSubdivisions\n // not using localOpt\n\n let subdivisions\n let length = 0\n for (let i = 0; i < segmentIndex; i++) {\n\n var segment = segments[i]\n subdivisions = segmentSubdivisions[i]\n length += segment.length({ precisison: precision, subdivisions: subdivisions })\n }\n\n segment = segments[segmentIndex]\n subdivisions = segmentSubdivisions[segmentIndex]\n length += segment.lengthAtT(tValue, { precisison: precision, subdivisions: subdivisions })\n\n return length\n },\n\n // Returns point at requested `ratio` between 0 and 1, with precision better than requested `opt.precision`; optionally using `opt.segmentSubdivisions` provided.\n pointAt: function(ratio, opt) {\n\n const {segments} = this\n const numSegments = segments.length\n if (numSegments === 0) return null // if segments is an empty array\n\n if (ratio <= 0) return this.start.clone()\n if (ratio >= 1) return this.end.clone()\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision\n const segmentSubdivisions = (opt.segmentSubdivisions === undefined) ? this.getSegmentSubdivisions({ precision: precision }) : opt.segmentSubdivisions\n const localOpt = { precision: precision, segmentSubdivisions: segmentSubdivisions }\n\n const pathLength = this.length(localOpt)\n const length = pathLength * ratio\n\n return this.pointAtLength(length, localOpt)\n },\n\n // Returns point at requested `length`, with precision better than requested `opt.precision`; optionally using `opt.segmentSubdivisions` provided.\n // Accepts negative length.\n pointAtLength: function(length, opt) {\n\n const {segments} = this\n const numSegments = segments.length\n if (numSegments === 0) return null // if segments is an empty array\n\n if (length === 0) return this.start.clone()\n\n let fromStart = true\n if (length < 0) {\n fromStart = false // negative lengths mean start calculation from end point\n length = -length // absolute value\n }\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision\n const segmentSubdivisions = (opt.segmentSubdivisions === undefined) ? this.getSegmentSubdivisions({ precision: precision }) : opt.segmentSubdivisions\n // not using localOpt\n\n let lastVisibleSegment\n let l = 0 // length so far\n for (let i = (fromStart ? 0 : (numSegments - 1)); (fromStart ? (i < numSegments) : (i >= 0)); (fromStart ? (i++) : (i--))) {\n\n const segment = segments[i]\n const subdivisions = segmentSubdivisions[i]\n const d = segment.length({ precision: precision, subdivisions: subdivisions })\n\n if (segment.isVisible) {\n if (length <= (l + d)) {\n return segment.pointAtLength(((fromStart ? 1 : -1) * (length - l)), { precision: precision, subdivisions: subdivisions })\n }\n\n lastVisibleSegment = segment\n }\n\n l += d\n }\n\n // if length requested is higher than the length of the path, return last visible segment endpoint\n if (lastVisibleSegment) return (fromStart ? lastVisibleSegment.end : lastVisibleSegment.start)\n\n // if no visible segment, return last segment end point (no matter if fromStart or no)\n const lastSegment = segments[numSegments - 1]\n return lastSegment.end.clone()\n },\n\n // Private function.\n pointAtT: function(t) {\n\n const {segments} = this\n const numSegments = segments.length\n if (numSegments === 0) return null // if segments is an empty array\n\n const {segmentIndex} = t\n if (segmentIndex < 0) return segments[0].pointAtT(0)\n if (segmentIndex >= numSegments) return segments[numSegments - 1].pointAtT(1)\n\n let tValue = t.value\n if (tValue < 0) tValue = 0\n else if (tValue > 1) tValue = 1\n\n return segments[segmentIndex].pointAtT(tValue)\n },\n\n // Helper method for adding segments.\n prepareSegment: function(segment, previousSegment, nextSegment) {\n\n // insert after previous segment and before previous segment's next segment\n segment.previousSegment = previousSegment\n segment.nextSegment = nextSegment\n if (previousSegment) previousSegment.nextSegment = segment\n if (nextSegment) nextSegment.previousSegment = segment\n\n let updateSubpathStart = segment\n if (segment.isSubpathStart) {\n segment.subpathStartSegment = segment // assign self as subpath start segment\n updateSubpathStart = nextSegment // start updating from next segment\n }\n\n // assign previous segment's subpath start (or self if it is a subpath start) to subsequent segments\n if (updateSubpathStart) this.updateSubpathStartSegment(updateSubpathStart)\n\n return segment\n },\n\n // Default precision\n PRECISION: 3,\n\n // Remove the segment at `index`.\n // Accepts negative indices, from `-1` to `-segments.length`.\n // Throws an error if path has no segments.\n // Throws an error if index is out of range.\n removeSegment: function(index) {\n\n const {segments} = this\n const numSegments = segments.length\n if (numSegments === 0) throw new Error('Path has no segments.')\n\n if (index < 0) index = numSegments + index // convert negative indices to positive\n if (index >= numSegments || index < 0) throw new Error('Index out of range.')\n\n const removedSegment = segments.splice(index, 1)[0]\n const {previousSegment} = removedSegment\n const {nextSegment} = removedSegment\n\n // link the previous and next segments together (if present)\n if (previousSegment) previousSegment.nextSegment = nextSegment // may be null\n if (nextSegment) nextSegment.previousSegment = previousSegment // may be null\n\n // if removed segment used to start a subpath, update all subsequent segments until another subpath start segment is reached\n if (removedSegment.isSubpathStart && nextSegment) this.updateSubpathStartSegment(nextSegment)\n },\n\n // Replace the segment at `index` with `arg`.\n // Accepts negative indices, from `-1` to `-segments.length`.\n // Accepts one segment or an array of segments as argument.\n // Throws an error if path has no segments.\n // Throws an error if index is out of range.\n // Throws an error if argument is not a segment or an array of segments.\n replaceSegment: function(index, arg) {\n\n const {segments} = this\n const numSegments = segments.length\n if (numSegments === 0) throw new Error('Path has no segments.')\n\n if (index < 0) index = numSegments + index // convert negative indices to positive\n if (index >= numSegments || index < 0) throw new Error('Index out of range.')\n\n let currentSegment\n\n const replacedSegment = segments[index]\n let {previousSegment} = replacedSegment\n const {nextSegment} = replacedSegment\n\n let updateSubpathStart = replacedSegment.isSubpathStart // boolean: is an update of subpath starts necessary?\n\n if (!Array.isArray(arg)) {\n if (!arg || !arg.isSegment) throw new Error('Segment required.')\n\n currentSegment = this.prepareSegment(arg, previousSegment, nextSegment)\n segments.splice(index, 1, currentSegment) // directly replace\n\n if (updateSubpathStart && currentSegment.isSubpathStart) updateSubpathStart = false // already updated by `prepareSegment`\n\n } else {\n if (!arg[0].isSegment) throw new Error('Segments required.')\n\n segments.splice(index, 1)\n\n const n = arg.length\n for (let i = 0; i < n; i++) {\n\n const currentArg = arg[i]\n currentSegment = this.prepareSegment(currentArg, previousSegment, nextSegment)\n segments.splice((index + i), 0, currentSegment) // incrementing index to insert subsequent segments after inserted segments\n previousSegment = currentSegment\n\n if (updateSubpathStart && currentSegment.isSubpathStart) updateSubpathStart = false // already updated by `prepareSegment`\n }\n }\n\n // if replaced segment used to start a subpath and no new subpath start was added, update all subsequent segments until another subpath start segment is reached\n if (updateSubpathStart && nextSegment) this.updateSubpathStartSegment(nextSegment)\n },\n\n scale: function(sx, sy, origin) {\n\n const {segments} = this\n const numSegments = segments.length\n\n for (let i = 0; i < numSegments; i++) {\n\n const segment = segments[i]\n segment.scale(sx, sy, origin)\n }\n\n return this\n },\n\n segmentAt: function(ratio, opt) {\n\n const index = this.segmentIndexAt(ratio, opt)\n if (!index) return null\n\n return this.getSegment(index)\n },\n\n // Accepts negative length.\n segmentAtLength: function(length, opt) {\n\n const index = this.segmentIndexAtLength(length, opt)\n if (!index) return null\n\n return this.getSegment(index)\n },\n\n segmentIndexAt: function(ratio, opt) {\n\n const {segments} = this\n const numSegments = segments.length\n if (numSegments === 0) return null // if segments is an empty array\n\n if (ratio < 0) ratio = 0\n if (ratio > 1) ratio = 1\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision\n const segmentSubdivisions = (opt.segmentSubdivisions === undefined) ? this.getSegmentSubdivisions({ precision: precision }) : opt.segmentSubdivisions\n const localOpt = { precision: precision, segmentSubdivisions: segmentSubdivisions }\n\n const pathLength = this.length(localOpt)\n const length = pathLength * ratio\n\n return this.segmentIndexAtLength(length, localOpt)\n },\n\n toPoints: function(opt) {\n\n const {segments} = this\n const numSegments = segments.length\n if (numSegments === 0) return null // if segments is an empty array\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision\n const segmentSubdivisions = (opt.segmentSubdivisions === undefined) ? this.getSegmentSubdivisions({ precision: precision }) : opt.segmentSubdivisions\n\n const points = []\n let partialPoints = []\n for (let i = 0; i < numSegments; i++) {\n const segment = segments[i]\n if (segment.isVisible) {\n const currentSegmentSubdivisions = segmentSubdivisions[i]\n if (currentSegmentSubdivisions.length > 0) {\n const subdivisionPoints = currentSegmentSubdivisions.map(function(curve) {\n return curve.start\n })\n Array.prototype.push.apply(partialPoints, subdivisionPoints)\n } else {\n partialPoints.push(segment.start)\n }\n } else if (partialPoints.length > 0) {\n partialPoints.push(segments[i - 1].end)\n points.push(partialPoints)\n partialPoints = []\n }\n }\n\n if (partialPoints.length > 0) {\n partialPoints.push(this.end)\n points.push(partialPoints)\n }\n return points\n },\n\n toPolylines: function(opt) {\n\n const polylines = []\n const points = this.toPoints(opt)\n if (!points) return null\n for (let i = 0, n = points.length; i < n; i++) {\n polylines.push(new Polyline(points[i]))\n }\n\n return polylines\n },\n\n intersectionWithLine: function(line, opt) {\n\n let intersection = null\n const polylines = this.toPolylines(opt)\n if (!polylines) return null\n for (let i = 0, n = polylines.length; i < n; i++) {\n const polyline = polylines[i]\n const polylineIntersection = line.intersect(polyline)\n if (polylineIntersection) {\n intersection || (intersection = [])\n if (Array.isArray(polylineIntersection)) {\n Array.prototype.push.apply(intersection, polylineIntersection)\n } else {\n intersection.push(polylineIntersection)\n }\n }\n }\n\n return intersection\n },\n\n // Accepts negative length.\n segmentIndexAtLength: function(length, opt) {\n\n const {segments} = this\n const numSegments = segments.length\n if (numSegments === 0) return null // if segments is an empty array\n\n let fromStart = true\n if (length < 0) {\n fromStart = false // negative lengths mean start calculation from end point\n length = -length // absolute value\n }\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision\n const segmentSubdivisions = (opt.segmentSubdivisions === undefined) ? this.getSegmentSubdivisions({ precision: precision }) : opt.segmentSubdivisions\n // not using localOpt\n\n let lastVisibleSegmentIndex = null\n let l = 0 // length so far\n for (let i = (fromStart ? 0 : (numSegments - 1)); (fromStart ? (i < numSegments) : (i >= 0)); (fromStart ? (i++) : (i--))) {\n\n const segment = segments[i]\n const subdivisions = segmentSubdivisions[i]\n const d = segment.length({ precision: precision, subdivisions: subdivisions })\n\n if (segment.isVisible) {\n if (length <= (l + d)) return i\n lastVisibleSegmentIndex = i\n }\n\n l += d\n }\n\n // if length requested is higher than the length of the path, return last visible segment index\n // if no visible segment, return null\n return lastVisibleSegmentIndex\n },\n\n // Returns tangent line at requested `ratio` between 0 and 1, with precision better than requested `opt.precision`; optionally using `opt.segmentSubdivisions` provided.\n tangentAt: function(ratio, opt) {\n\n const {segments} = this\n const numSegments = segments.length\n if (numSegments === 0) return null // if segments is an empty array\n\n if (ratio < 0) ratio = 0\n if (ratio > 1) ratio = 1\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision\n const segmentSubdivisions = (opt.segmentSubdivisions === undefined) ? this.getSegmentSubdivisions({ precision: precision }) : opt.segmentSubdivisions\n const localOpt = { precision: precision, segmentSubdivisions: segmentSubdivisions }\n\n const pathLength = this.length(localOpt)\n const length = pathLength * ratio\n\n return this.tangentAtLength(length, localOpt)\n },\n\n // Returns tangent line at requested `length`, with precision better than requested `opt.precision`; optionally using `opt.segmentSubdivisions` provided.\n // Accepts negative length.\n tangentAtLength: function(length, opt) {\n\n const {segments} = this\n const numSegments = segments.length\n if (numSegments === 0) return null // if segments is an empty array\n\n let fromStart = true\n if (length < 0) {\n fromStart = false // negative lengths mean start calculation from end point\n length = -length // absolute value\n }\n\n opt = opt || {}\n const precision = (opt.precision === undefined) ? this.PRECISION : opt.precision\n const segmentSubdivisions = (opt.segmentSubdivisions === undefined) ? this.getSegmentSubdivisions({ precision: precision }) : opt.segmentSubdivisions\n // not using localOpt\n\n let lastValidSegment // visible AND differentiable (with a tangent)\n let l = 0 // length so far\n for (let i = (fromStart ? 0 : (numSegments - 1)); (fromStart ? (i < numSegments) : (i >= 0)); (fromStart ? (i++) : (i--))) {\n\n const segment = segments[i]\n const subdivisions = segmentSubdivisions[i]\n const d = segment.length({ precision: precision, subdivisions: subdivisions })\n\n if (segment.isDifferentiable()) {\n if (length <= (l + d)) {\n return segment.tangentAtLength(((fromStart ? 1 : -1) * (length - l)), { precision: precision, subdivisions: subdivisions })\n }\n\n lastValidSegment = segment\n }\n\n l += d\n }\n\n // if length requested is higher than the length of the path, return tangent of endpoint of last valid segment\n if (lastValidSegment) {\n const t = (fromStart ? 1 : 0)\n return lastValidSegment.tangentAtT(t)\n }\n\n // if no valid segment, return null\n return null\n },\n\n // Private function.\n tangentAtT: function(t) {\n\n const {segments} = this\n const numSegments = segments.length\n if (numSegments === 0) return null // if segments is an empty array\n\n const {segmentIndex} = t\n if (segmentIndex < 0) return segments[0].tangentAtT(0)\n if (segmentIndex >= numSegments) return segments[numSegments - 1].tangentAtT(1)\n\n let tValue = t.value\n if (tValue < 0) tValue = 0\n else if (tValue > 1) tValue = 1\n\n return segments[segmentIndex].tangentAtT(tValue)\n },\n\n translate: function(tx, ty) {\n\n const {segments} = this\n const numSegments = segments.length\n\n for (let i = 0; i < numSegments; i++) {\n\n const segment = segments[i]\n segment.translate(tx, ty)\n }\n\n return this\n },\n\n // Helper method for updating subpath start of segments, starting with the one provided.\n updateSubpathStartSegment: function(segment) {\n\n let {previousSegment} = segment // may be null\n while (segment && !segment.isSubpathStart) {\n\n // assign previous segment's subpath start segment to this segment\n if (previousSegment) segment.subpathStartSegment = previousSegment.subpathStartSegment // may be null\n else segment.subpathStartSegment = null // if segment had no previous segment, assign null - creates an invalid path!\n\n previousSegment = segment\n segment = segment.nextSegment // move on to the segment after etc.\n }\n },\n\n // Returns a string that can be used to reconstruct the path.\n // Additional error checking compared to toString (must start with M segment).\n serialize: function() {\n\n if (!this.isValid()) throw new Error('Invalid path segments.')\n\n return this.toString()\n },\n\n toString: function() {\n\n const {segments} = this\n const numSegments = segments.length\n\n let pathData = ''\n for (let i = 0; i < numSegments; i++) {\n\n const segment = segments[i]\n pathData += `${segment.serialize() } `\n }\n\n return pathData.trim()\n }\n }\n\n Object.defineProperty(Path.prototype, 'start', {\n // Getter for the first visible endpoint of the path.\n\n configurable: true,\n\n enumerable: true,\n\n get: function() {\n\n const {segments} = this\n const numSegments = segments.length\n if (numSegments === 0) return null\n\n for (let i = 0; i < numSegments; i++) {\n\n const segment = segments[i]\n if (segment.isVisible) return segment.start\n }\n\n // if no visible segment, return last segment end point\n return segments[numSegments - 1].end\n }\n })\n\n Object.defineProperty(Path.prototype, 'end', {\n // Getter for the last visible endpoint of the path.\n\n configurable: true,\n\n enumerable: true,\n\n get: function() {\n\n const {segments} = this\n const numSegments = segments.length\n if (numSegments === 0) return null\n\n for (let i = numSegments - 1; i >= 0; i--) {\n\n const segment = segments[i]\n if (segment.isVisible) return segment.end\n }\n\n // if no visible segment, return last segment end point\n return segments[numSegments - 1].end\n }\n })\n\n /*\n Point is the most basic object consisting of x/y coordinate.\n\n Possible instantiations are:\n * `Point(10, 20)`\n * `new Point(10, 20)`\n * `Point('10 20')`\n * `Point(Point(10, 20))`\n */\n var Point = g.Point = function(x, y) {\n\n if (!(this instanceof Point)) {\n return new Point(x, y)\n }\n\n if (typeof x === 'string') {\n const xy = x.split(x.indexOf('@') === -1 ? ' ' : '@')\n x = parseFloat(xy[0])\n y = parseFloat(xy[1])\n\n } else if (Object(x) === x) {\n y = x.y\n x = x.x\n }\n\n this.x = x === undefined ? 0 : x\n this.y = y === undefined ? 0 : y\n }\n\n // Alternative constructor, from polar coordinates.\n // @param {number} Distance.\n // @param {number} Angle in radians.\n // @param {point} [optional] Origin.\n Point.fromPolar = function(distance, angle, origin) {\n\n origin = (origin && new Point(origin)) || new Point(0, 0)\n let x = abs(distance * cos(angle))\n let y = abs(distance * sin(angle))\n const deg = normalizeAngle(toDeg(angle))\n\n if (deg < 90) {\n y = -y\n\n } else if (deg < 180) {\n x = -x\n y = -y\n\n } else if (deg < 270) {\n x = -x\n }\n\n return new Point(origin.x + x, origin.y + y)\n }\n\n // Create a point with random coordinates that fall into the range `[x1, x2]` and `[y1, y2]`.\n Point.random = function(x1, x2, y1, y2) {\n\n return new Point(floor(random() * (x2 - x1 + 1) + x1), floor(random() * (y2 - y1 + 1) + y1))\n }\n\n Point.prototype = {\n\n // If point lies outside rectangle `r`, return the nearest point on the boundary of rect `r`,\n // otherwise return point itself.\n // (see Squeak Smalltalk, Point>>adhereTo:)\n adhereToRect: function(r) {\n\n if (r.containsPoint(this)) {\n return this\n }\n\n this.x = min(max(this.x, r.x), r.x + r.width)\n this.y = min(max(this.y, r.y), r.y + r.height)\n return this\n },\n\n // Return the bearing between me and the given point.\n bearing: function(point) {\n\n return (new Line(this, point)).bearing()\n },\n\n // Returns change in angle from my previous position (-dx, -dy) to my new position\n // relative to ref point.\n changeInAngle: function(dx, dy, ref) {\n\n // Revert the translation and measure the change in angle around x-axis.\n return this.clone().offset(-dx, -dy).theta(ref) - this.theta(ref)\n },\n\n clone: function() {\n\n return new Point(this)\n },\n\n difference: function(dx, dy) {\n\n if ((Object(dx) === dx)) {\n dy = dx.y\n dx = dx.x\n }\n\n return new Point(this.x - (dx || 0), this.y - (dy || 0))\n },\n\n // Returns distance between me and point `p`.\n distance: function(p) {\n\n return (new Line(this, p)).length()\n },\n\n squaredDistance: function(p) {\n\n return (new Line(this, p)).squaredLength()\n },\n\n equals: function(p) {\n\n return !!p &&\n this.x === p.x &&\n this.y === p.y\n },\n\n magnitude: function() {\n\n return sqrt((this.x * this.x) + (this.y * this.y)) || 0.01\n },\n\n // Returns a manhattan (taxi-cab) distance between me and point `p`.\n manhattanDistance: function(p) {\n\n return abs(p.x - this.x) + abs(p.y - this.y)\n },\n\n // Move point on line starting from ref ending at me by\n // distance distance.\n move: function(ref, distance) {\n\n const theta = toRad((new Point(ref)).theta(this))\n const offset = this.offset(cos(theta) * distance, -sin(theta) * distance)\n return offset\n },\n\n // Scales x and y such that the distance between the point and the origin (0,0) is equal to the given length.\n normalize: function(length) {\n\n const scale = (length || 1) / this.magnitude()\n return this.scale(scale, scale)\n },\n\n // Offset me by the specified amount.\n offset: function(dx, dy) {\n\n if ((Object(dx) === dx)) {\n dy = dx.y\n dx = dx.x\n }\n\n this.x += dx || 0\n this.y += dy || 0\n return this\n },\n\n // Returns a point that is the reflection of me with\n // the center of inversion in ref point.\n reflection: function(ref) {\n\n return (new Point(ref)).move(this, this.distance(ref))\n },\n\n // Rotate point by angle around origin.\n // Angle is flipped because this is a left-handed coord system (y-axis points downward).\n rotate: function(origin, angle) {\n\n origin = origin || new g.Point(0, 0)\n\n angle = toRad(normalizeAngle(-angle))\n const cosAngle = cos(angle)\n const sinAngle = sin(angle)\n\n const x = (cosAngle * (this.x - origin.x)) - (sinAngle * (this.y - origin.y)) + origin.x\n const y = (sinAngle * (this.x - origin.x)) + (cosAngle * (this.y - origin.y)) + origin.y\n\n this.x = x\n this.y = y\n return this\n },\n\n round: function(precision) {\n\n const f = pow(10, precision || 0)\n this.x = round(this.x * f) / f\n this.y = round(this.y * f) / f\n return this\n },\n\n // Scale point with origin.\n scale: function(sx, sy, origin) {\n\n origin = (origin && new Point(origin)) || new Point(0, 0)\n this.x = origin.x + sx * (this.x - origin.x)\n this.y = origin.y + sy * (this.y - origin.y)\n return this\n },\n\n snapToGrid: function(gx, gy) {\n\n this.x = snapToGrid(this.x, gx)\n this.y = snapToGrid(this.y, gy || gx)\n return this\n },\n\n // Compute the angle between me and `p` and the x axis.\n // (cartesian-to-polar coordinates conversion)\n // Return theta angle in degrees.\n theta: function(p) {\n\n p = new Point(p)\n\n // Invert the y-axis.\n const y = -(p.y - this.y)\n const x = p.x - this.x\n let rad = atan2(y, x) // defined for all 0 corner cases\n\n // Correction for III. and IV. quadrant.\n if (rad < 0) {\n rad = 2 * PI + rad\n }\n\n return 180 * rad / PI\n },\n\n // Compute the angle between vector from me to p1 and the vector from me to p2.\n // ordering of points p1 and p2 is important!\n // theta function's angle convention:\n // returns angles between 0 and 180 when the angle is counterclockwise\n // returns angles between 180 and 360 to convert clockwise angles into counterclockwise ones\n // returns NaN if any of the points p1, p2 is coincident with this point\n angleBetween: function(p1, p2) {\n\n let angleBetween = (this.equals(p1) || this.equals(p2)) ? NaN : (this.theta(p2) - this.theta(p1))\n\n if (angleBetween < 0) {\n angleBetween += 360 // correction to keep angleBetween between 0 and 360\n }\n\n return angleBetween\n },\n\n // Compute the angle between the vector from 0,0 to me and the vector from 0,0 to p.\n // Returns NaN if p is at 0,0.\n vectorAngle: function(p) {\n\n const zero = new Point(0,0)\n return zero.angleBetween(this, p)\n },\n\n toJSON: function() {\n\n return { x: this.x, y: this.y }\n },\n\n // Converts rectangular to polar coordinates.\n // An origin can be specified, otherwise it's 0@0.\n toPolar: function(o) {\n\n o = (o && new Point(o)) || new Point(0, 0)\n const {x} = this\n const {y} = this\n this.x = sqrt((x - o.x) * (x - o.x) + (y - o.y) * (y - o.y)) // r\n this.y = toRad(o.theta(new Point(x, y)))\n return this\n },\n\n toString: function() {\n\n return `${this.x }@${ this.y}`\n },\n\n update: function(x, y) {\n\n this.x = x || 0\n this.y = y || 0\n return this\n },\n\n // Returns the dot product of this point with given other point\n dot: function(p) {\n\n return p ? (this.x * p.x + this.y * p.y) : NaN\n },\n\n // Returns the cross product of this point relative to two other points\n // this point is the common point\n // point p1 lies on the first vector, point p2 lies on the second vector\n // watch out for the ordering of points p1 and p2!\n // positive result indicates a clockwise (\"right\") turn from first to second vector\n // negative result indicates a counterclockwise (\"left\") turn from first to second vector\n // note that the above directions are reversed from the usual answer on the Internet\n // that is because we are in a left-handed coord system (because the y-axis points downward)\n cross: function(p1, p2) {\n\n return (p1 && p2) ? (((p2.x - this.x) * (p1.y - this.y)) - ((p2.y - this.y) * (p1.x - this.x))) : NaN\n },\n\n\n // Linear interpolation\n lerp: function(p, t) {\n\n const {x} = this\n const {y} = this\n return new Point((1 - t) * x + t * p.x, (1 - t) * y + t * p.y)\n }\n }\n\n Point.prototype.translate = Point.prototype.offset\n\n var Rect = g.Rect = function(x, y, w, h) {\n\n if (!(this instanceof Rect)) {\n return new Rect(x, y, w, h)\n }\n\n if ((Object(x) === x)) {\n y = x.y\n w = x.width\n h = x.height\n x = x.x\n }\n\n this.x = x === undefined ? 0 : x\n this.y = y === undefined ? 0 : y\n this.width = w === undefined ? 0 : w\n this.height = h === undefined ? 0 : h\n }\n\n Rect.fromEllipse = function(e) {\n\n e = new Ellipse(e)\n return new Rect(e.x - e.a, e.y - e.b, 2 * e.a, 2 * e.b)\n }\n\n Rect.prototype = {\n\n // Find my bounding box when I'm rotated with the center of rotation in the center of me.\n // @return r {rectangle} representing a bounding box\n bbox: function(angle) {\n\n if (!angle) return this.clone()\n\n const theta = toRad(angle || 0)\n const st = abs(sin(theta))\n const ct = abs(cos(theta))\n const w = this.width * ct + this.height * st\n const h = this.width * st + this.height * ct\n return new Rect(this.x + (this.width - w) / 2, this.y + (this.height - h) / 2, w, h)\n },\n\n bottomLeft: function() {\n\n return new Point(this.x, this.y + this.height)\n },\n\n bottomLine: function() {\n\n return new Line(this.bottomLeft(), this.bottomRight())\n },\n\n bottomMiddle: function() {\n\n return new Point(this.x + this.width / 2, this.y + this.height)\n },\n\n center: function() {\n\n return new Point(this.x + this.width / 2, this.y + this.height / 2)\n },\n\n clone: function() {\n\n return new Rect(this)\n },\n\n // @return {bool} true if point p is insight me\n containsPoint: function(p) {\n\n p = new Point(p)\n return p.x >= this.x && p.x <= this.x + this.width && p.y >= this.y && p.y <= this.y + this.height\n },\n\n // @return {bool} true if rectangle `r` is inside me.\n containsRect: function(r) {\n\n const r0 = new Rect(this).normalize()\n const r1 = new Rect(r).normalize()\n let w0 = r0.width\n let h0 = r0.height\n let w1 = r1.width\n let h1 = r1.height\n\n if (!w0 || !h0 || !w1 || !h1) {\n // At least one of the dimensions is 0\n return false\n }\n\n const x0 = r0.x\n const y0 = r0.y\n const x1 = r1.x\n const y1 = r1.y\n\n w1 += x1\n w0 += x0\n h1 += y1\n h0 += y0\n\n return x0 <= x1 && w1 <= w0 && y0 <= y1 && h1 <= h0\n },\n\n corner: function() {\n\n return new Point(this.x + this.width, this.y + this.height)\n },\n\n // @return {boolean} true if rectangles are equal.\n equals: function(r) {\n\n const mr = (new Rect(this)).normalize()\n const nr = (new Rect(r)).normalize()\n return mr.x === nr.x && mr.y === nr.y && mr.width === nr.width && mr.height === nr.height\n },\n\n // @return {rect} if rectangles intersect, {null} if not.\n intersect: function(r) {\n\n const myOrigin = this.origin()\n const myCorner = this.corner()\n const rOrigin = r.origin()\n const rCorner = r.corner()\n\n // No intersection found\n if (rCorner.x <= myOrigin.x ||\n rCorner.y <= myOrigin.y ||\n rOrigin.x >= myCorner.x ||\n rOrigin.y >= myCorner.y) return null\n\n const x = max(myOrigin.x, rOrigin.x)\n const y = max(myOrigin.y, rOrigin.y)\n\n return new Rect(x, y, min(myCorner.x, rCorner.x) - x, min(myCorner.y, rCorner.y) - y)\n },\n\n intersectionWithLine: function(line) {\n\n const r = this\n const rectLines = [ r.topLine(), r.rightLine(), r.bottomLine(), r.leftLine() ]\n const points = []\n const dedupeArr = []\n let pt; let i\n\n const n = rectLines.length\n for (i = 0; i < n; i ++) {\n\n pt = line.intersect(rectLines[i])\n if (pt !== null && dedupeArr.indexOf(pt.toString()) < 0) {\n points.push(pt)\n dedupeArr.push(pt.toString())\n }\n }\n\n return points.length > 0 ? points : null\n },\n\n // Find point on my boundary where line starting\n // from my center ending in point p intersects me.\n // @param {number} angle If angle is specified, intersection with rotated rectangle is computed.\n intersectionWithLineFromCenterToPoint: function(p, angle) {\n\n p = new Point(p)\n const center = new Point(this.x + this.width / 2, this.y + this.height / 2)\n let result\n\n if (angle) p.rotate(center, angle)\n\n // (clockwise, starting from the top side)\n const sides = [\n this.topLine(),\n this.rightLine(),\n this.bottomLine(),\n this.leftLine()\n ]\n const connector = new Line(center, p)\n\n for (let i = sides.length - 1; i >= 0; --i) {\n const intersection = sides[i].intersection(connector)\n if (intersection !== null) {\n result = intersection\n break\n }\n }\n if (result && angle) result.rotate(center, -angle)\n return result\n },\n\n leftLine: function() {\n\n return new Line(this.topLeft(), this.bottomLeft())\n },\n\n leftMiddle: function() {\n\n return new Point(this.x , this.y + this.height / 2)\n },\n\n // Move and expand me.\n // @param r {rectangle} representing deltas\n moveAndExpand: function(r) {\n\n this.x += r.x || 0\n this.y += r.y || 0\n this.width += r.width || 0\n this.height += r.height || 0\n return this\n },\n\n // Offset me by the specified amount.\n offset: function(dx, dy) {\n\n // pretend that this is a point and call offset()\n // rewrites x and y according to dx and dy\n return Point.prototype.offset.call(this, dx, dy)\n },\n\n // inflate by dx and dy, recompute origin [x, y]\n // @param dx {delta_x} representing additional size to x\n // @param dy {delta_y} representing additional size to y -\n // dy param is not required -> in that case y is sized by dx\n inflate: function(dx, dy) {\n\n if (dx === undefined) {\n dx = 0\n }\n\n if (dy === undefined) {\n dy = dx\n }\n\n this.x -= dx\n this.y -= dy\n this.width += 2 * dx\n this.height += 2 * dy\n\n return this\n },\n\n // Normalize the rectangle; i.e., make it so that it has a non-negative width and height.\n // If width < 0 the function swaps the left and right corners,\n // and it swaps the top and bottom corners if height < 0\n // like in http://qt-project.org/doc/qt-4.8/qrectf.html#normalized\n normalize: function() {\n\n let newx = this.x\n let newy = this.y\n let newwidth = this.width\n let newheight = this.height\n if (this.width < 0) {\n newx = this.x + this.width\n newwidth = -this.width\n }\n if (this.height < 0) {\n newy = this.y + this.height\n newheight = -this.height\n }\n this.x = newx\n this.y = newy\n this.width = newwidth\n this.height = newheight\n return this\n },\n\n origin: function() {\n\n return new Point(this.x, this.y)\n },\n\n // @return {point} a point on my boundary nearest to the given point.\n // @see Squeak Smalltalk, Rectangle>>pointNearestTo:\n pointNearestToPoint: function(point) {\n\n point = new Point(point)\n if (this.containsPoint(point)) {\n const side = this.sideNearestToPoint(point)\n switch (side){\n case 'right': return new Point(this.x + this.width, point.y)\n case 'left': return new Point(this.x, point.y)\n case 'bottom': return new Point(point.x, this.y + this.height)\n case 'top': return new Point(point.x, this.y)\n }\n }\n return point.adhereToRect(this)\n },\n\n rightLine: function() {\n\n return new Line(this.topRight(), this.bottomRight())\n },\n\n rightMiddle: function() {\n\n return new Point(this.x + this.width, this.y + this.height / 2)\n },\n\n round: function(precision) {\n\n const f = pow(10, precision || 0)\n this.x = round(this.x * f) / f\n this.y = round(this.y * f) / f\n this.width = round(this.width * f) / f\n this.height = round(this.height * f) / f\n return this\n },\n\n // Scale rectangle with origin.\n scale: function(sx, sy, origin) {\n\n origin = this.origin().scale(sx, sy, origin)\n this.x = origin.x\n this.y = origin.y\n this.width *= sx\n this.height *= sy\n return this\n },\n\n maxRectScaleToFit: function(rect, origin) {\n\n rect = new Rect(rect)\n origin || (origin = rect.center())\n\n let sx1; let sx2; let sx3; let sx4; let sy1; let sy2; let sy3; let sy4\n const ox = origin.x\n const oy = origin.y\n\n // Here we find the maximal possible scale for all corner points (for x and y axis) of the rectangle,\n // so when the scale is applied the point is still inside the rectangle.\n\n sx1 = sx2 = sx3 = sx4 = sy1 = sy2 = sy3 = sy4 = Infinity\n\n // Top Left\n const p1 = rect.topLeft()\n if (p1.x < ox) {\n sx1 = (this.x - ox) / (p1.x - ox)\n }\n if (p1.y < oy) {\n sy1 = (this.y - oy) / (p1.y - oy)\n }\n // Bottom Right\n const p2 = rect.bottomRight()\n if (p2.x > ox) {\n sx2 = (this.x + this.width - ox) / (p2.x - ox)\n }\n if (p2.y > oy) {\n sy2 = (this.y + this.height - oy) / (p2.y - oy)\n }\n // Top Right\n const p3 = rect.topRight()\n if (p3.x > ox) {\n sx3 = (this.x + this.width - ox) / (p3.x - ox)\n }\n if (p3.y < oy) {\n sy3 = (this.y - oy) / (p3.y - oy)\n }\n // Bottom Left\n const p4 = rect.bottomLeft()\n if (p4.x < ox) {\n sx4 = (this.x - ox) / (p4.x - ox)\n }\n if (p4.y > oy) {\n sy4 = (this.y + this.height - oy) / (p4.y - oy)\n }\n\n return {\n sx: min(sx1, sx2, sx3, sx4),\n sy: min(sy1, sy2, sy3, sy4)\n }\n },\n\n maxRectUniformScaleToFit: function(rect, origin) {\n\n const scale = this.maxRectScaleToFit(rect, origin)\n return min(scale.sx, scale.sy)\n },\n\n // @return {string} (left|right|top|bottom) side which is nearest to point\n // @see Squeak Smalltalk, Rectangle>>sideNearestTo:\n sideNearestToPoint: function(point) {\n\n point = new Point(point)\n const distToLeft = point.x - this.x\n const distToRight = (this.x + this.width) - point.x\n const distToTop = point.y - this.y\n const distToBottom = (this.y + this.height) - point.y\n let closest = distToLeft\n let side = 'left'\n\n if (distToRight < closest) {\n closest = distToRight\n side = 'right'\n }\n if (distToTop < closest) {\n closest = distToTop\n side = 'top'\n }\n if (distToBottom < closest) {\n closest = distToBottom\n side = 'bottom'\n }\n return side\n },\n\n snapToGrid: function(gx, gy) {\n\n const origin = this.origin().snapToGrid(gx, gy)\n const corner = this.corner().snapToGrid(gx, gy)\n this.x = origin.x\n this.y = origin.y\n this.width = corner.x - origin.x\n this.height = corner.y - origin.y\n return this\n },\n\n topLine: function() {\n\n return new Line(this.topLeft(), this.topRight())\n },\n\n topMiddle: function() {\n\n return new Point(this.x + this.width / 2, this.y)\n },\n\n topRight: function() {\n\n return new Point(this.x + this.width, this.y)\n },\n\n toJSON: function() {\n\n return { x: this.x, y: this.y, width: this.width, height: this.height }\n },\n\n toString: function() {\n\n return `${this.origin().toString() } ${ this.corner().toString()}`\n },\n\n // @return {rect} representing the union of both rectangles.\n union: function(rect) {\n\n rect = new Rect(rect)\n const myOrigin = this.origin()\n const myCorner = this.corner()\n const rOrigin = rect.origin()\n const rCorner = rect.corner()\n\n const originX = min(myOrigin.x, rOrigin.x)\n const originY = min(myOrigin.y, rOrigin.y)\n const cornerX = max(myCorner.x, rCorner.x)\n const cornerY = max(myCorner.y, rCorner.y)\n\n return new Rect(originX, originY, cornerX - originX, cornerY - originY)\n }\n }\n\n Rect.prototype.bottomRight = Rect.prototype.corner\n\n Rect.prototype.topLeft = Rect.prototype.origin\n\n Rect.prototype.translate = Rect.prototype.offset\n\n var Polyline = g.Polyline = function(points) {\n\n if (!(this instanceof Polyline)) {\n return new Polyline(points)\n }\n\n if (typeof points === 'string') {\n return new Polyline.parse(points)\n }\n\n this.points = (Array.isArray(points) ? points.map(Point) : [])\n }\n\n Polyline.parse = function(svgString) {\n svgString = svgString.trim()\n if (svgString === '') return new Polyline()\n\n const points = []\n\n const coords = svgString.split(/\\s*,\\s*|\\s+/)\n const n = coords.length\n for (let i = 0; i < n; i += 2) {\n points.push({ x: +coords[i], y: +coords[i + 1] })\n }\n\n return new Polyline(points)\n }\n\n Polyline.prototype = {\n\n bbox: function() {\n\n let x1 = Infinity\n let x2 = -Infinity\n let y1 = Infinity\n let y2 = -Infinity\n\n const {points} = this\n const numPoints = points.length\n if (numPoints === 0) return null // if points array is empty\n\n for (let i = 0; i < numPoints; i++) {\n\n const point = points[i]\n const {x} = point\n const {y} = point\n\n if (x < x1) x1 = x\n if (x > x2) x2 = x\n if (y < y1) y1 = y\n if (y > y2) y2 = y\n }\n\n return new Rect(x1, y1, x2 - x1, y2 - y1)\n },\n\n clone: function() {\n\n const {points} = this\n const numPoints = points.length\n if (numPoints === 0) return new Polyline() // if points array is empty\n\n const newPoints = []\n for (let i = 0; i < numPoints; i++) {\n\n const point = points[i].clone()\n newPoints.push(point)\n }\n\n return new Polyline(newPoints)\n },\n\n closestPoint: function(p) {\n\n const cpLength = this.closestPointLength(p)\n\n return this.pointAtLength(cpLength)\n },\n\n closestPointLength: function(p) {\n\n const {points} = this\n const numPoints = points.length\n if (numPoints === 0) return 0 // if points array is empty\n if (numPoints === 1) return 0 // if there is only one point\n\n let cpLength\n let minSqrDistance = Infinity\n let length = 0\n const n = numPoints - 1\n for (let i = 0; i < n; i++) {\n\n const line = new Line(points[i], points[i + 1])\n const lineLength = line.length()\n\n const cpNormalizedLength = line.closestPointNormalizedLength(p)\n const cp = line.pointAt(cpNormalizedLength)\n\n const sqrDistance = cp.squaredDistance(p)\n if (sqrDistance < minSqrDistance) {\n minSqrDistance = sqrDistance\n cpLength = length + (cpNormalizedLength * lineLength)\n }\n\n length += lineLength\n }\n\n return cpLength\n },\n\n closestPointNormalizedLength: function(p) {\n\n const cpLength = this.closestPointLength(p)\n if (cpLength === 0) return 0 // shortcut\n\n const length = this.length()\n if (length === 0) return 0 // prevents division by zero\n\n return cpLength / length\n },\n\n closestPointTangent: function(p) {\n\n const cpLength = this.closestPointLength(p)\n\n return this.tangentAtLength(cpLength)\n },\n\n // Returns a convex-hull polyline from this polyline.\n // Implements the Graham scan (https://en.wikipedia.org/wiki/Graham_scan).\n // Output polyline starts at the first element of the original polyline that is on the hull, then continues clockwise.\n // Minimal polyline is found (only vertices of the hull are reported, no collinear points).\n convexHull: function() {\n\n let i\n let n\n\n const {points} = this\n const numPoints = points.length\n if (numPoints === 0) return new Polyline() // if points array is empty\n\n // step 1: find the starting point - point with the lowest y (if equality, highest x)\n let startPoint\n for (i = 0; i < numPoints; i++) {\n if (startPoint === undefined) {\n // if this is the first point we see, set it as start point\n startPoint = points[i]\n\n } else if (points[i].y < startPoint.y) {\n // start point should have lowest y from all points\n startPoint = points[i]\n\n } else if ((points[i].y === startPoint.y) && (points[i].x > startPoint.x)) {\n // if two points have the lowest y, choose the one that has highest x\n // there are no points to the right of startPoint - no ambiguity about theta 0\n // if there are several coincident start point candidates, first one is reported\n startPoint = points[i]\n }\n }\n\n // step 2: sort the list of points\n // sorting by angle between line from startPoint to point and the x-axis (theta)\n\n // step 2a: create the point records = [point, originalIndex, angle]\n const sortedPointRecords = []\n for (i = 0; i < numPoints; i++) {\n\n let angle = startPoint.theta(points[i])\n if (angle === 0) {\n angle = 360 // give highest angle to start point\n // the start point will end up at end of sorted list\n // the start point will end up at beginning of hull points list\n }\n\n const entry = [points[i], i, angle]\n sortedPointRecords.push(entry)\n }\n\n // step 2b: sort the list in place\n sortedPointRecords.sort(function(record1, record2) {\n // returning a negative number here sorts record1 before record2\n // if first angle is smaller than second, first angle should come before second\n\n let sortOutput = record1[2] - record2[2] // negative if first angle smaller\n if (sortOutput === 0) {\n // if the two angles are equal, sort by originalIndex\n sortOutput = record2[1] - record1[1] // negative if first index larger\n // coincident points will be sorted in reverse-numerical order\n // so the coincident points with lower original index will be considered first\n }\n\n return sortOutput\n })\n\n // step 2c: duplicate start record from the top of the stack to the bottom of the stack\n if (sortedPointRecords.length > 2) {\n const startPointRecord = sortedPointRecords[sortedPointRecords.length-1]\n sortedPointRecords.unshift(startPointRecord)\n }\n\n // step 3a: go through sorted points in order and find those with right turns\n // we want to get our results in clockwise order\n const insidePoints = {} // dictionary of points with left turns - cannot be on the hull\n const hullPointRecords = [] // stack of records with right turns - hull point candidates\n\n let currentPointRecord\n let currentPoint\n let lastHullPointRecord\n let lastHullPoint\n let secondLastHullPointRecord\n let secondLastHullPoint\n while (sortedPointRecords.length !== 0) {\n\n currentPointRecord = sortedPointRecords.pop()\n currentPoint = currentPointRecord[0]\n\n // check if point has already been discarded\n // keys for insidePoints are stored in the form 'point.x@point.y@@originalIndex'\n if (insidePoints.hasOwnProperty(`${currentPointRecord[0] }@@${ currentPointRecord[1]}`)) {\n // this point had an incorrect turn at some previous iteration of this loop\n // this disqualifies it from possibly being on the hull\n continue\n }\n\n let correctTurnFound = false\n while (!correctTurnFound) {\n\n if (hullPointRecords.length < 2) {\n // not enough points for comparison, just add current point\n hullPointRecords.push(currentPointRecord)\n correctTurnFound = true\n\n } else {\n lastHullPointRecord = hullPointRecords.pop()\n lastHullPoint = lastHullPointRecord[0]\n secondLastHullPointRecord = hullPointRecords.pop()\n secondLastHullPoint = secondLastHullPointRecord[0]\n\n const crossProduct = secondLastHullPoint.cross(lastHullPoint, currentPoint)\n\n if (crossProduct < 0) {\n // found a right turn\n hullPointRecords.push(secondLastHullPointRecord)\n hullPointRecords.push(lastHullPointRecord)\n hullPointRecords.push(currentPointRecord)\n correctTurnFound = true\n\n } else if (crossProduct === 0) {\n // the three points are collinear\n // three options:\n // there may be a 180 or 0 degree angle at lastHullPoint\n // or two of the three points are coincident\n const THRESHOLD = 1e-10 // we have to take rounding errors into account\n const angleBetween = lastHullPoint.angleBetween(secondLastHullPoint, currentPoint)\n if (abs(angleBetween - 180) < THRESHOLD) { // rouding around 180 to 180\n // if the cross product is 0 because the angle is 180 degrees\n // discard last hull point (add to insidePoints)\n //insidePoints.unshift(lastHullPoint);\n insidePoints[`${lastHullPointRecord[0] }@@${ lastHullPointRecord[1]}`] = lastHullPoint\n // reenter second-to-last hull point (will be last at next iter)\n hullPointRecords.push(secondLastHullPointRecord)\n // do not do anything with current point\n // correct turn not found\n\n } else if (lastHullPoint.equals(currentPoint) || secondLastHullPoint.equals(lastHullPoint)) {\n // if the cross product is 0 because two points are the same\n // discard last hull point (add to insidePoints)\n //insidePoints.unshift(lastHullPoint);\n insidePoints[`${lastHullPointRecord[0] }@@${ lastHullPointRecord[1]}`] = lastHullPoint\n // reenter second-to-last hull point (will be last at next iter)\n hullPointRecords.push(secondLastHullPointRecord)\n // do not do anything with current point\n // correct turn not found\n\n } else if (abs(((angleBetween + 1) % 360) - 1) < THRESHOLD) { // rounding around 0 and 360 to 0\n // if the cross product is 0 because the angle is 0 degrees\n // remove last hull point from hull BUT do not discard it\n // reenter second-to-last hull point (will be last at next iter)\n hullPointRecords.push(secondLastHullPointRecord)\n // put last hull point back into the sorted point records list\n sortedPointRecords.push(lastHullPointRecord)\n // we are switching the order of the 0deg and 180deg points\n // correct turn not found\n }\n\n } else {\n // found a left turn\n // discard last hull point (add to insidePoints)\n //insidePoints.unshift(lastHullPoint);\n insidePoints[`${lastHullPointRecord[0] }@@${ lastHullPointRecord[1]}`] = lastHullPoint\n // reenter second-to-last hull point (will be last at next iter of loop)\n hullPointRecords.push(secondLastHullPointRecord)\n // do not do anything with current point\n // correct turn not found\n }\n }\n }\n }\n // at this point, hullPointRecords contains the output points in clockwise order\n // the points start with lowest-y,highest-x startPoint, and end at the same point\n\n // step 3b: remove duplicated startPointRecord from the end of the array\n if (hullPointRecords.length > 2) {\n hullPointRecords.pop()\n }\n\n // step 4: find the lowest originalIndex record and put it at the beginning of hull\n let lowestHullIndex // the lowest originalIndex on the hull\n let indexOfLowestHullIndexRecord = -1 // the index of the record with lowestHullIndex\n n = hullPointRecords.length\n for (i = 0; i < n; i++) {\n\n const currentHullIndex = hullPointRecords[i][1]\n\n if (lowestHullIndex === undefined || currentHullIndex < lowestHullIndex) {\n lowestHullIndex = currentHullIndex\n indexOfLowestHullIndexRecord = i\n }\n }\n\n let hullPointRecordsReordered = []\n if (indexOfLowestHullIndexRecord > 0) {\n const newFirstChunk = hullPointRecords.slice(indexOfLowestHullIndexRecord)\n const newSecondChunk = hullPointRecords.slice(0, indexOfLowestHullIndexRecord)\n hullPointRecordsReordered = newFirstChunk.concat(newSecondChunk)\n\n } else {\n hullPointRecordsReordered = hullPointRecords\n }\n\n const hullPoints = []\n n = hullPointRecordsReordered.length\n for (i = 0; i < n; i++) {\n hullPoints.push(hullPointRecordsReordered[i][0])\n }\n\n return new Polyline(hullPoints)\n },\n\n // Checks whether two polylines are exactly the same.\n // If `p` is undefined or null, returns false.\n equals: function(p) {\n\n if (!p) return false\n\n const {points} = this\n const otherPoints = p.points\n\n const numPoints = points.length\n if (otherPoints.length !== numPoints) return false // if the two polylines have different number of points, they cannot be equal\n\n for (let i = 0; i < numPoints; i++) {\n\n const point = points[i]\n const otherPoint = p.points[i]\n\n // as soon as an inequality is found in points, return false\n if (!point.equals(otherPoint)) return false\n }\n\n // if no inequality found in points, return true\n return true\n },\n\n isDifferentiable: function() {\n\n const {points} = this\n const numPoints = points.length\n if (numPoints === 0) return false\n\n const n = numPoints - 1\n for (let i = 0; i < n; i++) {\n\n const a = points[i]\n const b = points[i + 1]\n const line = new Line(a, b)\n\n // as soon as a differentiable line is found between two points, return true\n if (line.isDifferentiable()) return true\n }\n\n // if no differentiable line is found between pairs of points, return false\n return false\n },\n\n length: function() {\n\n const {points} = this\n const numPoints = points.length\n if (numPoints === 0) return 0 // if points array is empty\n\n let length = 0\n const n = numPoints - 1\n for (let i = 0; i < n; i++) {\n length += points[i].distance(points[i + 1])\n }\n\n return length\n },\n\n pointAt: function(ratio) {\n\n const {points} = this\n const numPoints = points.length\n if (numPoints === 0) return null // if points array is empty\n if (numPoints === 1) return points[0].clone() // if there is only one point\n\n if (ratio <= 0) return points[0].clone()\n if (ratio >= 1) return points[numPoints - 1].clone()\n\n const polylineLength = this.length()\n const length = polylineLength * ratio\n\n return this.pointAtLength(length)\n },\n\n pointAtLength: function(length) {\n\n const {points} = this\n const numPoints = points.length\n if (numPoints === 0) return null // if points array is empty\n if (numPoints === 1) return points[0].clone() // if there is only one point\n\n let fromStart = true\n if (length < 0) {\n fromStart = false // negative lengths mean start calculation from end point\n length = -length // absolute value\n }\n\n let l = 0\n const n = numPoints - 1\n for (let i = (fromStart ? 0 : (n - 1)); (fromStart ? (i < n) : (i >= 0)); (fromStart ? (i++) : (i--))) {\n\n const a = points[i]\n const b = points[i + 1]\n const line = new Line(a, b)\n const d = a.distance(b)\n\n if (length <= (l + d)) {\n return line.pointAtLength((fromStart ? 1 : -1) * (length - l))\n }\n\n l += d\n }\n\n // if length requested is higher than the length of the polyline, return last endpoint\n const lastPoint = (fromStart ? points[numPoints - 1] : points[0])\n return lastPoint.clone()\n },\n\n scale: function(sx, sy, origin) {\n\n const {points} = this\n const numPoints = points.length\n if (numPoints === 0) return this // if points array is empty\n\n for (let i = 0; i < numPoints; i++) {\n points[i].scale(sx, sy, origin)\n }\n\n return this\n },\n\n tangentAt: function(ratio) {\n\n const {points} = this\n const numPoints = points.length\n if (numPoints === 0) return null // if points array is empty\n if (numPoints === 1) return null // if there is only one point\n\n if (ratio < 0) ratio = 0\n if (ratio > 1) ratio = 1\n\n const polylineLength = this.length()\n const length = polylineLength * ratio\n\n return this.tangentAtLength(length)\n },\n\n tangentAtLength: function(length) {\n\n const {points} = this\n const numPoints = points.length\n if (numPoints === 0) return null // if points array is empty\n if (numPoints === 1) return null // if there is only one point\n\n let fromStart = true\n if (length < 0) {\n fromStart = false // negative lengths mean start calculation from end point\n length = -length // absolute value\n }\n\n let lastValidLine // differentiable (with a tangent)\n let l = 0 // length so far\n const n = numPoints - 1\n for (let i = (fromStart ? (0) : (n - 1)); (fromStart ? (i < n) : (i >= 0)); (fromStart ? (i++) : (i--))) {\n\n const a = points[i]\n const b = points[i + 1]\n const line = new Line(a, b)\n const d = a.distance(b)\n\n if (line.isDifferentiable()) { // has a tangent line (line length is not 0)\n if (length <= (l + d)) {\n return line.tangentAtLength((fromStart ? 1 : -1) * (length - l))\n }\n\n lastValidLine = line\n }\n\n l += d\n }\n\n // if length requested is higher than the length of the polyline, return last valid endpoint\n if (lastValidLine) {\n const ratio = (fromStart ? 1 : 0)\n return lastValidLine.tangentAt(ratio)\n }\n\n // if no valid line, return null\n return null\n },\n\n intersectionWithLine: function(l) {\n const line = new Line(l)\n const intersections = []\n const {points} = this\n for (let i = 0, n = points.length - 1; i < n; i++) {\n const a = points[i]\n const b = points[i+1]\n const l2 = new Line(a, b)\n const int = line.intersectionWithLine(l2)\n if (int) intersections.push(int[0])\n }\n return (intersections.length > 0) ? intersections : null\n },\n\n translate: function(tx, ty) {\n\n const {points} = this\n const numPoints = points.length\n if (numPoints === 0) return this // if points array is empty\n\n for (let i = 0; i < numPoints; i++) {\n points[i].translate(tx, ty)\n }\n\n return this\n },\n\n // Return svgString that can be used to recreate this line.\n serialize: function() {\n\n const {points} = this\n const numPoints = points.length\n if (numPoints === 0) return '' // if points array is empty\n\n let output = ''\n for (let i = 0; i < numPoints; i++) {\n\n const point = points[i]\n output += `${point.x },${ point.y } `\n }\n\n return output.trim()\n },\n\n toString: function() {\n\n return `${this.points }`\n }\n }\n\n Object.defineProperty(Polyline.prototype, 'start', {\n // Getter for the first point of the polyline.\n\n configurable: true,\n\n enumerable: true,\n\n get: function() {\n\n const {points} = this\n const numPoints = points.length\n if (numPoints === 0) return null // if points array is empty\n\n return this.points[0]\n },\n })\n\n Object.defineProperty(Polyline.prototype, 'end', {\n // Getter for the last point of the polyline.\n\n configurable: true,\n\n enumerable: true,\n\n get: function() {\n\n const {points} = this\n const numPoints = points.length\n if (numPoints === 0) return null // if points array is empty\n\n return this.points[numPoints - 1]\n },\n })\n\n g.scale = {\n\n // Return the `value` from the `domain` interval scaled to the `range` interval.\n linear: function(domain, range, value) {\n\n const domainSpan = domain[1] - domain[0]\n const rangeSpan = range[1] - range[0]\n return (((value - domain[0]) / domainSpan) * rangeSpan + range[0]) || 0\n }\n }\n\n var normalizeAngle = g.normalizeAngle = function(angle) {\n\n return (angle % 360) + (angle < 0 ? 360 : 0)\n }\n\n var snapToGrid = g.snapToGrid = function(value, gridSize) {\n\n return gridSize * round(value / gridSize)\n }\n\n var toDeg = g.toDeg = function(rad) {\n\n return (180 * rad / PI) % 360\n }\n\n var toRad = g.toRad = function(deg, over360) {\n\n over360 = over360 || false\n deg = over360 ? deg : (deg % 360)\n return deg * PI / 180\n }\n\n // For backwards compatibility:\n g.ellipse = g.Ellipse\n g.line = g.Line\n g.point = g.Point\n g.rect = g.Rect\n\n // Local helper function.\n // Use an array of arguments to call a constructor (function called with `new`).\n // Adapted from https://stackoverflow.com/a/8843181/2263595\n // It is not necessary to use this function if the arguments can be passed separately (i.e. if the number of arguments is limited).\n // - If that is the case, use `new constructor(arg1, arg2)`, for example.\n // It is not necessary to use this function if the function that needs an array of arguments is not supposed to be used as a constructor.\n // - If that is the case, use `f.apply(thisArg, [arg1, arg2...])`, for example.\n function applyToNew(constructor, argsArray) {\n // The `new` keyword can only be applied to functions that take a limited number of arguments.\n // - We can fake that with .bind().\n // - It calls a function (`constructor`, here) with the arguments that were provided to it - effectively transforming an unlimited number of arguments into limited.\n // - So `new (constructor.bind(thisArg, arg1, arg2...))`\n // - `thisArg` can be anything (e.g. null) because `new` keyword resets context to the constructor object.\n // We need to pass in a variable number of arguments to the bind() call.\n // - We can use .apply().\n // - So `new (constructor.bind.apply(constructor, [thisArg, arg1, arg2...]))`\n // - `thisArg` can still be anything because `new` overwrites it.\n // Finally, to make sure that constructor.bind overwriting is not a problem, we switch to `Function.prototype.bind`.\n // - So, the final version is `new (Function.prototype.bind.apply(constructor, [thisArg, arg1, arg2...]))`\n\n // The function expects `argsArray[0]` to be `thisArg`.\n // - This means that whatever is sent as the first element will be ignored.\n // - The constructor will only see arguments starting from argsArray[1].\n // - So, a new dummy element is inserted at the start of the array.\n argsArray.unshift(null)\n\n return new (Function.prototype.bind.apply(constructor, argsArray))\n }\n\n // Local helper function.\n // Add properties from arguments on top of properties from `obj`.\n // This allows for rudimentary inheritance.\n // - The `obj` argument acts as parent.\n // - This function creates a new object that inherits all `obj` properties and adds/replaces those that are present in arguments.\n // - A high-level example: calling `extend(Vehicle, Car)` would be akin to declaring `class Car extends Vehicle`.\n function extend(obj) {\n // In JavaScript, the combination of a constructor function (e.g. `g.Line = function(...) {...}`) and prototype (e.g. `g.Line.prototype = {...}) is akin to a C++ class.\n // - When inheritance is not necessary, we can leave it at that. (This would be akin to calling extend with only `obj`.)\n // - But, what if we wanted the `g.Line` quasiclass to inherit from another quasiclass (let's call it `g.GeometryObject`) in JavaScript?\n // - First, realize that both of those quasiclasses would still have their own separate constructor function.\n // - So what we are actually saying is that we want the `g.Line` prototype to inherit from `g.GeometryObject` prototype.\n // - This method provides a way to do exactly that.\n // - It copies parent prototype's properties, then adds extra ones from child prototype/overrides parent prototype properties with child prototype properties.\n // - Therefore, to continue with the example above:\n // - `g.Line.prototype = extend(g.GeometryObject.prototype, linePrototype)`\n // - Where `linePrototype` is a properties object that looks just like `g.Line.prototype` does right now.\n // - Then, `g.Line` would allow the programmer to access to all methods currently in `g.Line.Prototype`, plus any non-overriden methods from `g.GeometryObject.prototype`.\n // - In that aspect, `g.GeometryObject` would then act like the parent of `g.Line`.\n // - Multiple inheritance is also possible, if multiple arguments are provided.\n // - What if we wanted to add another level of abstraction between `g.GeometryObject` and `g.Line` (let's call it `g.LinearObject`)?\n // - `g.Line.prototype = extend(g.GeometryObject.prototype, g.LinearObject.prototype, linePrototype)`\n // - The ancestors are applied in order of appearance.\n // - That means that `g.Line` would have inherited from `g.LinearObject` that would have inherited from `g.GeometryObject`.\n // - Any number of ancestors may be provided.\n // - Note that neither `obj` nor any of the arguments need to actually be prototypes of any JavaScript quasiclass, that was just a simplified explanation.\n // - We can create a new object composed from the properties of any number of other objects (since they do not have a constructor, we can think of those as interfaces).\n // - `extend({ a: 1, b: 2 }, { b: 10, c: 20 }, { c: 100, d: 200 })` gives `{ a: 1, b: 10, c: 100, d: 200 }`.\n // - Basically, with this function, we can emulate the `extends` keyword as well as the `implements` keyword.\n // - Therefore, both of the following are valid:\n // - `Lineto.prototype = extend(Line.prototype, segmentPrototype, linetoPrototype)`\n // - `Moveto.prototype = extend(segmentPrototype, movetoPrototype)`\n\n let i\n let n\n\n const args = []\n n = arguments.length\n for (i = 1; i < n; i++) { // skip over obj\n args.push(arguments[i])\n }\n\n if (!obj) throw new Error('Missing a parent object.')\n const child = Object.create(obj)\n\n n = args.length\n for (i = 0; i < n; i++) {\n\n const src = args[i]\n\n var inheritedProperty\n var key\n for (key in src) {\n\n if (src.hasOwnProperty(key)) {\n delete child[key] // delete property inherited from parent\n inheritedProperty = Object.getOwnPropertyDescriptor(src, key) // get new definition of property from src\n Object.defineProperty(child, key, inheritedProperty) // re-add property with new definition (includes getter/setter methods)\n }\n }\n }\n\n return child\n }\n\n // Path segment interface:\n const segmentPrototype = {\n\n // Redirect calls to closestPointNormalizedLength() function if closestPointT() is not defined for segment.\n closestPointT: function(p) {\n\n if (this.closestPointNormalizedLength) return this.closestPointNormalizedLength(p)\n\n throw new Error('Neither closestPointT() nor closestPointNormalizedLength() function is implemented.')\n },\n\n isSegment: true,\n\n isSubpathStart: false, // true for Moveto segments\n\n isVisible: true, // false for Moveto segments\n\n nextSegment: null, // needed for subpath start segment updating\n\n // Return a fraction of result of length() function if lengthAtT() is not defined for segment.\n lengthAtT: function(t) {\n\n if (t <= 0) return 0\n\n const length = this.length()\n\n if (t >= 1) return length\n\n return length * t\n },\n\n // Redirect calls to pointAt() function if pointAtT() is not defined for segment.\n pointAtT: function(t) {\n\n if (this.pointAt) return this.pointAt(t)\n\n throw new Error('Neither pointAtT() nor pointAt() function is implemented.')\n },\n\n previousSegment: null, // needed to get segment start property\n\n subpathStartSegment: null, // needed to get closepath segment end property\n\n // Redirect calls to tangentAt() function if tangentAtT() is not defined for segment.\n tangentAtT: function(t) {\n\n if (this.tangentAt) return this.tangentAt(t)\n\n throw new Error('Neither tangentAtT() nor tangentAt() function is implemented.')\n },\n\n // VIRTUAL PROPERTIES (must be overriden by actual Segment implementations):\n\n // type\n\n // start // getter, always throws error for Moveto\n\n // end // usually directly assigned, getter for Closepath\n\n bbox: function() {\n\n throw new Error('Declaration missing for virtual function.')\n },\n\n clone: function() {\n\n throw new Error('Declaration missing for virtual function.')\n },\n\n closestPoint: function() {\n\n throw new Error('Declaration missing for virtual function.')\n },\n\n closestPointLength: function() {\n\n throw new Error('Declaration missing for virtual function.')\n },\n\n closestPointNormalizedLength: function() {\n\n throw new Error('Declaration missing for virtual function.')\n },\n\n closestPointTangent: function() {\n\n throw new Error('Declaration missing for virtual function.')\n },\n\n equals: function() {\n\n throw new Error('Declaration missing for virtual function.')\n },\n\n getSubdivisions: function() {\n\n throw new Error('Declaration missing for virtual function.')\n },\n\n isDifferentiable: function() {\n\n throw new Error('Declaration missing for virtual function.')\n },\n\n length: function() {\n\n throw new Error('Declaration missing for virtual function.')\n },\n\n pointAt: function() {\n\n throw new Error('Declaration missing for virtual function.')\n },\n\n pointAtLength: function() {\n\n throw new Error('Declaration missing for virtual function.')\n },\n\n scale: function() {\n\n throw new Error('Declaration missing for virtual function.')\n },\n\n tangentAt: function() {\n\n throw new Error('Declaration missing for virtual function.')\n },\n\n tangentAtLength: function() {\n\n throw new Error('Declaration missing for virtual function.')\n },\n\n translate: function() {\n\n throw new Error('Declaration missing for virtual function.')\n },\n\n serialize: function() {\n\n throw new Error('Declaration missing for virtual function.')\n },\n\n toString: function() {\n\n throw new Error('Declaration missing for virtual function.')\n }\n }\n\n // Path segment implementations:\n var Lineto = function() {\n\n const args = []\n const n = arguments.length\n for (var i = 0; i < n; i++) {\n args.push(arguments[i])\n }\n\n if (!(this instanceof Lineto)) { // switching context of `this` to Lineto when called without `new`\n return applyToNew(Lineto, args)\n }\n\n if (n === 0) {\n throw new Error('Lineto constructor expects 1 point or 2 coordinates (none provided).')\n }\n\n let outputArray\n\n if (typeof args[0] === 'string' || typeof args[0] === 'number') { // coordinates provided\n if (n === 2) {\n this.end = new Point(+args[0], +args[1])\n return this\n\n } else if (n < 2) {\n throw new Error(`Lineto constructor expects 1 point or 2 coordinates (${ n } coordinates provided).`)\n\n } else { // this is a poly-line segment\n let segmentCoords\n outputArray = []\n for (i = 0; i < n; i += 2) { // coords come in groups of two\n\n segmentCoords = args.slice(i, i + 2) // will send one coord if args.length not divisible by 2\n outputArray.push(applyToNew(Lineto, segmentCoords))\n }\n return outputArray\n }\n\n } else { // points provided\n if (n === 1) {\n this.end = new Point(args[0])\n return this\n\n } else { // this is a poly-line segment\n let segmentPoint\n outputArray = []\n for (i = 0; i < n; i += 1) {\n\n segmentPoint = args[i]\n outputArray.push(new Lineto(segmentPoint))\n }\n return outputArray\n }\n }\n }\n\n const linetoPrototype = {\n\n clone: function() {\n\n return new Lineto(this.end)\n },\n\n getSubdivisions: function() {\n\n return []\n },\n\n isDifferentiable: function() {\n\n if (!this.previousSegment) return false\n\n return !this.start.equals(this.end)\n },\n\n scale: function(sx, sy, origin) {\n\n this.end.scale(sx, sy, origin)\n return this\n },\n\n translate: function(tx, ty) {\n\n this.end.translate(tx, ty)\n return this\n },\n\n type: 'L',\n\n serialize: function() {\n\n const {end} = this\n return `${this.type } ${ end.x } ${ end.y}`\n },\n\n toString: function() {\n\n return `${this.type } ${ this.start } ${ this.end}`\n }\n }\n\n Object.defineProperty(linetoPrototype, 'start', {\n // get a reference to the end point of previous segment\n\n configurable: true,\n\n enumerable: true,\n\n get: function() {\n\n if (!this.previousSegment) throw new Error('Missing previous segment. (This segment cannot be the first segment of a path; OR segment has not yet been added to a path.)')\n\n return this.previousSegment.end\n }\n })\n\n Lineto.prototype = extend(segmentPrototype, Line.prototype, linetoPrototype)\n\n var Curveto = function() {\n\n const args = []\n const n = arguments.length\n for (var i = 0; i < n; i++) {\n args.push(arguments[i])\n }\n\n if (!(this instanceof Curveto)) { // switching context of `this` to Curveto when called without `new`\n return applyToNew(Curveto, args)\n }\n\n if (n === 0) {\n throw new Error('Curveto constructor expects 3 points or 6 coordinates (none provided).')\n }\n\n let outputArray\n\n if (typeof args[0] === 'string' || typeof args[0] === 'number') { // coordinates provided\n if (n === 6) {\n this.controlPoint1 = new Point(+args[0], +args[1])\n this.controlPoint2 = new Point(+args[2], +args[3])\n this.end = new Point(+args[4], +args[5])\n return this\n\n } else if (n < 6) {\n throw new Error(`Curveto constructor expects 3 points or 6 coordinates (${ n } coordinates provided).`)\n\n } else { // this is a poly-bezier segment\n let segmentCoords\n outputArray = []\n for (i = 0; i < n; i += 6) { // coords come in groups of six\n\n segmentCoords = args.slice(i, i + 6) // will send fewer than six coords if args.length not divisible by 6\n outputArray.push(applyToNew(Curveto, segmentCoords))\n }\n return outputArray\n }\n\n } else { // points provided\n if (n === 3) {\n this.controlPoint1 = new Point(args[0])\n this.controlPoint2 = new Point(args[1])\n this.end = new Point(args[2])\n return this\n\n } else if (n < 3) {\n throw new Error(`Curveto constructor expects 3 points or 6 coordinates (${ n } points provided).`)\n\n } else { // this is a poly-bezier segment\n let segmentPoints\n outputArray = []\n for (i = 0; i < n; i += 3) { // points come in groups of three\n\n segmentPoints = args.slice(i, i + 3) // will send fewer than three points if args.length is not divisible by 3\n outputArray.push(applyToNew(Curveto, segmentPoints))\n }\n return outputArray\n }\n }\n }\n\n const curvetoPrototype = {\n\n clone: function() {\n\n return new Curveto(this.controlPoint1, this.controlPoint2, this.end)\n },\n\n isDifferentiable: function() {\n\n if (!this.previousSegment) return false\n\n const {start} = this\n const control1 = this.controlPoint1\n const control2 = this.controlPoint2\n const {end} = this\n\n return !(start.equals(control1) && control1.equals(control2) && control2.equals(end))\n },\n\n scale: function(sx, sy, origin) {\n\n this.controlPoint1.scale(sx, sy, origin)\n this.controlPoint2.scale(sx, sy, origin)\n this.end.scale(sx, sy, origin)\n return this\n },\n\n translate: function(tx, ty) {\n\n this.controlPoint1.translate(tx, ty)\n this.controlPoint2.translate(tx, ty)\n this.end.translate(tx, ty)\n return this\n },\n\n type: 'C',\n\n serialize: function() {\n\n const c1 = this.controlPoint1\n const c2 = this.controlPoint2\n const {end} = this\n return `${this.type } ${ c1.x } ${ c1.y } ${ c2.x } ${ c2.y } ${ end.x } ${ end.y}`\n },\n\n toString: function() {\n\n return `${this.type } ${ this.start } ${ this.controlPoint1 } ${ this.controlPoint2 } ${ this.end}`\n }\n }\n\n Object.defineProperty(curvetoPrototype, 'start', {\n // get a reference to the end point of previous segment\n\n configurable: true,\n\n enumerable: true,\n\n get: function() {\n\n if (!this.previousSegment) throw new Error('Missing previous segment. (This segment cannot be the first segment of a path; OR segment has not yet been added to a path.)')\n\n return this.previousSegment.end\n }\n })\n\n Curveto.prototype = extend(segmentPrototype, Curve.prototype, curvetoPrototype)\n\n var Moveto = function() {\n\n const args = []\n const n = arguments.length\n for (var i = 0; i < n; i++) {\n args.push(arguments[i])\n }\n\n if (!(this instanceof Moveto)) { // switching context of `this` to Moveto when called without `new`\n return applyToNew(Moveto, args)\n }\n\n if (n === 0) {\n throw new Error('Moveto constructor expects 1 point or 2 coordinates (none provided).')\n }\n\n let outputArray\n\n if (typeof args[0] === 'string' || typeof args[0] === 'number') { // coordinates provided\n if (n === 2) {\n this.end = new Point(+args[0], +args[1])\n return this\n\n } else if (n < 2) {\n throw new Error(`Moveto constructor expects 1 point or 2 coordinates (${ n } coordinates provided).`)\n\n } else { // this is a moveto-with-subsequent-poly-line segment\n let segmentCoords\n outputArray = []\n for (i = 0; i < n; i += 2) { // coords come in groups of two\n\n segmentCoords = args.slice(i, i + 2) // will send one coord if args.length not divisible by 2\n if (i === 0) outputArray.push(applyToNew(Moveto, segmentCoords))\n else outputArray.push(applyToNew(Lineto, segmentCoords))\n }\n return outputArray\n }\n\n } else { // points provided\n if (n === 1) {\n this.end = new Point(args[0])\n return this\n\n } else { // this is a moveto-with-subsequent-poly-line segment\n let segmentPoint\n outputArray = []\n for (i = 0; i < n; i += 1) { // points come one by one\n\n segmentPoint = args[i]\n if (i === 0) outputArray.push(new Moveto(segmentPoint))\n else outputArray.push(new Lineto(segmentPoint))\n }\n return outputArray\n }\n }\n }\n\n const movetoPrototype = {\n\n bbox: function() {\n\n return null\n },\n\n clone: function() {\n\n return new Moveto(this.end)\n },\n\n closestPoint: function() {\n\n return this.end.clone()\n },\n\n closestPointNormalizedLength: function() {\n\n return 0\n },\n\n closestPointLength: function() {\n\n return 0\n },\n\n closestPointT: function() {\n\n return 1\n },\n\n closestPointTangent: function() {\n\n return null\n },\n\n equals: function(m) {\n\n return this.end.equals(m.end)\n },\n\n getSubdivisions: function() {\n\n return []\n },\n\n isDifferentiable: function() {\n\n return false\n },\n\n isSubpathStart: true,\n\n isVisible: false,\n\n length: function() {\n\n return 0\n },\n\n lengthAtT: function() {\n\n return 0\n },\n\n pointAt: function() {\n\n return this.end.clone()\n },\n\n pointAtLength: function() {\n\n return this.end.clone()\n },\n\n pointAtT: function() {\n\n return this.end.clone()\n },\n\n scale: function(sx, sy, origin) {\n\n this.end.scale(sx, sy, origin)\n return this\n },\n\n tangentAt: function() {\n\n return null\n },\n\n tangentAtLength: function() {\n\n return null\n },\n\n tangentAtT: function() {\n\n return null\n },\n\n translate: function(tx, ty) {\n\n this.end.translate(tx, ty)\n return this\n },\n\n type: 'M',\n\n serialize: function() {\n\n const {end} = this\n return `${this.type } ${ end.x } ${ end.y}`\n },\n\n toString: function() {\n\n return `${this.type } ${ this.end}`\n }\n }\n\n Object.defineProperty(movetoPrototype, 'start', {\n\n configurable: true,\n\n enumerable: true,\n\n get: function() {\n\n throw new Error('Illegal access. Moveto segments should not need a start property.')\n }\n })\n\n Moveto.prototype = extend(segmentPrototype, movetoPrototype) // does not inherit from any other geometry object\n\n var Closepath = function() {\n\n const args = []\n const n = arguments.length\n for (let i = 0; i < n; i++) {\n args.push(arguments[i])\n }\n\n if (!(this instanceof Closepath)) { // switching context of `this` to Closepath when called without `new`\n return applyToNew(Closepath, args)\n }\n\n if (n > 0) {\n throw new Error('Closepath constructor expects no arguments.')\n }\n\n return this\n }\n\n const closepathPrototype = {\n\n clone: function() {\n\n return new Closepath()\n },\n\n getSubdivisions: function() {\n\n return []\n },\n\n isDifferentiable: function() {\n\n if (!this.previousSegment || !this.subpathStartSegment) return false\n\n return !this.start.equals(this.end)\n },\n\n scale: function() {\n\n return this\n },\n\n translate: function() {\n\n return this\n },\n\n type: 'Z',\n\n serialize: function() {\n\n return this.type\n },\n\n toString: function() {\n\n return `${this.type } ${ this.start } ${ this.end}`\n }\n }\n\n Object.defineProperty(closepathPrototype, 'start', {\n // get a reference to the end point of previous segment\n\n configurable: true,\n\n enumerable: true,\n\n get: function() {\n\n if (!this.previousSegment) throw new Error('Missing previous segment. (This segment cannot be the first segment of a path; OR segment has not yet been added to a path.)')\n\n return this.previousSegment.end\n }\n })\n\n Object.defineProperty(closepathPrototype, 'end', {\n // get a reference to the end point of subpath start segment\n\n configurable: true,\n\n enumerable: true,\n\n get: function() {\n\n if (!this.subpathStartSegment) throw new Error('Missing subpath start segment. (This segment needs a subpath start segment (e.g. Moveto); OR segment has not yet been added to a path.)')\n\n return this.subpathStartSegment.end\n }\n })\n\n Closepath.prototype = extend(segmentPrototype, Line.prototype, closepathPrototype)\n\n const segmentTypes = Path.segmentTypes = {\n L: Lineto,\n C: Curveto,\n M: Moveto,\n Z: Closepath,\n z: Closepath\n }\n\n Path.regexSupportedData = new RegExp(`^[\\\\s\\\\d${ Object.keys(segmentTypes).join('') },.]*$`)\n\n Path.isDataSupported = function(d) {\n if (typeof d !== 'string') return false\n return this.regexSupportedData.test(d)\n }\n\n})(g)\n\n// Vectorizer.\n// -----------\n\n// A tiny library for making your life easier when dealing with SVG.\n// The only Vectorizer dependency is the Geometry library.\n\nlet V // eslint-disable-line no-unused-vars\nlet Vectorizer // eslint-disable-line no-unused-vars\n\nV = Vectorizer = (function() {\n\n \n\n const hasSvg = typeof window === 'object' &&\n !!(\n window.SVGAngle ||\n document.implementation.hasFeature('http://www.w3.org/TR/SVG11/feature#BasicStructure', '1.1')\n )\n\n // SVG support is required.\n if (!hasSvg) {\n\n // Return a function that throws an error when it is used.\n return function() {\n throw new Error('SVG is required to use Vectorizer.')\n }\n }\n\n // XML namespaces.\n const ns = {\n xmlns: 'http://www.w3.org/2000/svg',\n xml: 'http://www.w3.org/XML/1998/namespace',\n xlink: 'http://www.w3.org/1999/xlink',\n xhtml: 'http://www.w3.org/1999/xhtml'\n }\n\n const SVGversion = '1.1'\n\n // Declare shorthands to the most used math functions.\n const math = Math\n const {PI} = math\n const {atan2} = math\n const {sqrt} = math\n const {min} = math\n const {max} = math\n const {cos} = math\n const {sin} = math\n\n var V = function(el, attrs, children) {\n\n // This allows using V() without the new keyword.\n if (!(this instanceof V)) {\n return V.apply(Object.create(V.prototype), arguments)\n }\n\n if (!el) return\n\n if (V.isV(el)) {\n el = el.node\n }\n\n attrs = attrs || {}\n\n if (V.isString(el)) {\n\n if (el.toLowerCase() === 'svg') {\n\n // Create a new SVG canvas.\n el = V.createSvgDocument()\n\n } else if (el[0] === '<') {\n\n // Create element from an SVG string.\n // Allows constructs of type: `document.appendChild(V('').node)`.\n\n const svgDoc = V.createSvgDocument(el)\n\n // Note that `V()` might also return an array should the SVG string passed as\n // the first argument contain more than one root element.\n if (svgDoc.childNodes.length > 1) {\n\n // Map child nodes to `V`s.\n const arrayOfVels = []\n let i; let len\n\n for (i = 0, len = svgDoc.childNodes.length; i < len; i++) {\n\n const childNode = svgDoc.childNodes[i]\n arrayOfVels.push(new V(document.importNode(childNode, true)))\n }\n\n return arrayOfVels\n }\n\n el = document.importNode(svgDoc.firstChild, true)\n\n } else {\n\n el = document.createElementNS(ns.xmlns, el)\n }\n\n V.ensureId(el)\n }\n\n this.node = el\n\n this.setAttributes(attrs)\n\n if (children) {\n this.append(children)\n }\n\n return this\n }\n\n const VPrototype = V.prototype\n\n Object.defineProperty(VPrototype, 'id', {\n enumerable: true,\n get: function() {\n return this.node.id\n },\n set: function(id) {\n this.node.id = id\n }\n })\n\n /**\n * @param {SVGGElement} toElem\n * @returns {SVGMatrix}\n */\n VPrototype.getTransformToElement = function(toElem) {\n toElem = V.toNode(toElem)\n return toElem.getScreenCTM().inverse().multiply(this.node.getScreenCTM())\n }\n\n /**\n * @param {SVGMatrix} matrix\n * @param {Object=} opt\n * @returns {Vectorizer|SVGMatrix} Setter / Getter\n */\n VPrototype.transform = function(matrix, opt) {\n\n const {node} = this\n if (V.isUndefined(matrix)) {\n return V.transformStringToMatrix(this.attr('transform'))\n }\n\n if (opt && opt.absolute) {\n return this.attr('transform', V.matrixToTransformString(matrix))\n }\n\n const svgTransform = V.createSVGTransform(matrix)\n node.transform.baseVal.appendItem(svgTransform)\n return this\n }\n\n VPrototype.translate = function(tx, ty, opt) {\n\n opt = opt || {}\n ty = ty || 0\n\n let transformAttr = this.attr('transform') || ''\n const transform = V.parseTransformString(transformAttr)\n transformAttr = transform.value\n // Is it a getter?\n if (V.isUndefined(tx)) {\n return transform.translate\n }\n\n transformAttr = transformAttr.replace(/translate\\([^)]*\\)/g, '').trim()\n\n const newTx = opt.absolute ? tx : transform.translate.tx + tx\n const newTy = opt.absolute ? ty : transform.translate.ty + ty\n const newTranslate = `translate(${ newTx },${ newTy })`\n\n // Note that `translate()` is always the first transformation. This is\n // usually the desired case.\n this.attr('transform', (`${newTranslate } ${ transformAttr}`).trim())\n return this\n }\n\n VPrototype.rotate = function(angle, cx, cy, opt) {\n\n opt = opt || {}\n\n let transformAttr = this.attr('transform') || ''\n const transform = V.parseTransformString(transformAttr)\n transformAttr = transform.value\n\n // Is it a getter?\n if (V.isUndefined(angle)) {\n return transform.rotate\n }\n\n transformAttr = transformAttr.replace(/rotate\\([^)]*\\)/g, '').trim()\n\n angle %= 360\n\n const newAngle = opt.absolute ? angle : transform.rotate.angle + angle\n const newOrigin = (cx !== undefined && cy !== undefined) ? `,${ cx },${ cy}` : ''\n const newRotate = `rotate(${ newAngle }${newOrigin })`\n\n this.attr('transform', (`${transformAttr } ${ newRotate}`).trim())\n return this\n }\n\n // Note that `scale` as the only transformation does not combine with previous values.\n VPrototype.scale = function(sx, sy) {\n\n sy = V.isUndefined(sy) ? sx : sy\n\n let transformAttr = this.attr('transform') || ''\n const transform = V.parseTransformString(transformAttr)\n transformAttr = transform.value\n\n // Is it a getter?\n if (V.isUndefined(sx)) {\n return transform.scale\n }\n\n transformAttr = transformAttr.replace(/scale\\([^)]*\\)/g, '').trim()\n\n const newScale = `scale(${ sx },${ sy })`\n\n this.attr('transform', (`${transformAttr } ${ newScale}`).trim())\n return this\n }\n\n // Get SVGRect that contains coordinates and dimension of the real bounding box,\n // i.e. after transformations are applied.\n // If `target` is specified, bounding box will be computed relatively to `target` element.\n VPrototype.bbox = function(withoutTransformations, target) {\n\n let box\n const {node} = this\n const {ownerSVGElement} = node\n\n // If the element is not in the live DOM, it does not have a bounding box defined and\n // so fall back to 'zero' dimension element.\n if (!ownerSVGElement) {\n return new g.Rect(0, 0, 0, 0)\n }\n\n try {\n\n box = node.getBBox()\n\n } catch (e) {\n\n // Fallback for IE.\n box = {\n x: node.clientLeft,\n y: node.clientTop,\n width: node.clientWidth,\n height: node.clientHeight\n }\n }\n\n if (withoutTransformations) {\n return new g.Rect(box)\n }\n\n const matrix = this.getTransformToElement(target || ownerSVGElement)\n\n return V.transformRect(box, matrix)\n }\n\n // Returns an SVGRect that contains coordinates and dimensions of the real bounding box,\n // i.e. after transformations are applied.\n // Fixes a browser implementation bug that returns incorrect bounding boxes for groups of svg elements.\n // Takes an (Object) `opt` argument (optional) with the following attributes:\n // (Object) `target` (optional): if not undefined, transform bounding boxes relative to `target`; if undefined, transform relative to this\n // (Boolean) `recursive` (optional): if true, recursively enter all groups and get a union of element bounding boxes (svg bbox fix); if false or undefined, return result of native function this.node.getBBox();\n VPrototype.getBBox = function(opt) {\n\n const options = {}\n\n let outputBBox\n const {node} = this\n const {ownerSVGElement} = node\n\n // If the element is not in the live DOM, it does not have a bounding box defined and\n // so fall back to 'zero' dimension element.\n if (!ownerSVGElement) {\n return new g.Rect(0, 0, 0, 0)\n }\n\n if (opt) {\n if (opt.target) { // check if target exists\n options.target = V.toNode(opt.target) // works for V objects, jquery objects, and node objects\n }\n if (opt.recursive) {\n options.recursive = opt.recursive\n }\n }\n\n if (!options.recursive) {\n try {\n outputBBox = node.getBBox()\n } catch (e) {\n // Fallback for IE.\n outputBBox = {\n x: node.clientLeft,\n y: node.clientTop,\n width: node.clientWidth,\n height: node.clientHeight\n }\n }\n\n if (!options.target) {\n // transform like this (that is, not at all)\n return new g.Rect(outputBBox)\n } else {\n // transform like target\n const matrix = this.getTransformToElement(options.target)\n return V.transformRect(outputBBox, matrix)\n }\n } else { // if we want to calculate the bbox recursively\n // browsers report correct bbox around svg elements (one that envelops the path lines tightly)\n // but some browsers fail to report the same bbox when the elements are in a group (returning a looser bbox that also includes control points, like node.getClientRect())\n // this happens even if we wrap a single svg element into a group!\n // this option setting makes the function recursively enter all the groups from this and deeper, get bboxes of the elements inside, then return a union of those bboxes\n\n const children = this.children()\n const n = children.length\n\n if (n === 0) {\n return this.getBBox({ target: options.target, recursive: false })\n }\n\n // recursion's initial pass-through setting:\n // recursive passes-through just keep the target as whatever was set up here during the initial pass-through\n if (!options.target) {\n // transform children/descendants like this (their parent/ancestor)\n options.target = this\n } // else transform children/descendants like target\n\n for (let i = 0; i < n; i++) {\n const currentChild = children[i]\n\n var childBBox\n\n // if currentChild is not a group element, get its bbox with a nonrecursive call\n if (currentChild.children().length === 0) {\n childBBox = currentChild.getBBox({ target: options.target, recursive: false })\n }\n else {\n // if currentChild is a group element (determined by checking the number of children), enter it with a recursive call\n childBBox = currentChild.getBBox({ target: options.target, recursive: true })\n }\n\n if (!outputBBox) {\n // if this is the first iteration\n outputBBox = childBBox\n } else {\n // make a new bounding box rectangle that contains this child's bounding box and previous bounding box\n outputBBox = outputBBox.union(childBBox)\n }\n }\n\n return outputBBox\n }\n }\n\n // Text() helpers\n\n function createTextPathNode(attrs, vel) {\n attrs || (attrs = {})\n const textPathElement = V('textPath')\n const {d} = attrs\n if (d && attrs['xlink:href'] === undefined) {\n // If `opt.attrs` is a plain string, consider it to be directly the\n // SVG path data for the text to go along (this is a shortcut).\n // Otherwise if it is an object and contains the `d` property, then this is our path.\n // Wrap the text in the SVG element that points\n // to a path defined by `opt.attrs` inside the `` element.\n const linkedPath = V('path').attr('d', d).appendTo(vel.defs())\n textPathElement.attr('xlink:href', `#${ linkedPath.id}`)\n }\n if (V.isObject(attrs)) {\n // Set attributes on the ``. The most important one\n // is the `xlink:href` that points to our newly created `` element in ``.\n // Note that we also allow the following construct:\n // `t.text('my text', { textPath: { 'xlink:href': '#my-other-path' } })`.\n // In other words, one can completely skip the auto-creation of the path\n // and use any other arbitrary path that is in the document.\n textPathElement.attr(attrs)\n }\n return textPathElement.node\n }\n\n function annotateTextLine(lineNode, lineAnnotations, opt) {\n opt || (opt = {})\n const {includeAnnotationIndices} = opt\n const {eol} = opt\n const {lineHeight} = opt\n const {baseSize} = opt\n let maxFontSize = 0\n const fontMetrics = {}\n const lastJ = lineAnnotations.length - 1\n for (let j = 0; j <= lastJ; j++) {\n let annotation = lineAnnotations[j]\n let fontSize = null\n if (V.isObject(annotation)) {\n const annotationAttrs = annotation.attrs\n const vTSpan = V('tspan', annotationAttrs)\n var tspanNode = vTSpan.node\n let {t} = annotation\n if (eol && j === lastJ) t += eol\n tspanNode.textContent = t\n // Per annotation className\n const annotationClass = annotationAttrs.class\n if (annotationClass) vTSpan.addClass(annotationClass)\n // If `opt.includeAnnotationIndices` is `true`,\n // set the list of indices of all the applied annotations\n // in the `annotations` attribute. This list is a comma\n // separated list of indices.\n if (includeAnnotationIndices) vTSpan.attr('annotations', annotation.annotations)\n // Check for max font size\n fontSize = parseFloat(annotationAttrs['font-size'])\n if (fontSize === undefined) fontSize = baseSize\n if (fontSize && fontSize > maxFontSize) maxFontSize = fontSize\n } else {\n if (eol && j === lastJ) annotation += eol\n tspanNode = document.createTextNode(annotation || ' ')\n if (baseSize && baseSize > maxFontSize) maxFontSize = baseSize\n }\n lineNode.appendChild(tspanNode)\n }\n\n if (maxFontSize) fontMetrics.maxFontSize = maxFontSize\n if (lineHeight) {\n fontMetrics.lineHeight = lineHeight\n } else if (maxFontSize) {\n fontMetrics.lineHeight = (maxFontSize * 1.2)\n }\n return fontMetrics\n }\n\n const emRegex = /em$/\n\n function convertEmToPx(em, fontSize) {\n const numerical = parseFloat(em)\n if (emRegex.test(em)) return numerical * fontSize\n return numerical\n }\n\n function calculateDY(alignment, linesMetrics, baseSizePx, lineHeight) {\n if (!Array.isArray(linesMetrics)) return 0\n const n = linesMetrics.length\n if (!n) return 0\n let lineMetrics = linesMetrics[0]\n const flMaxFont = convertEmToPx(lineMetrics.maxFontSize, baseSizePx) || baseSizePx\n let rLineHeights = 0\n const lineHeightPx = convertEmToPx(lineHeight, baseSizePx)\n for (let i = 1; i < n; i++) {\n lineMetrics = linesMetrics[i]\n const iLineHeight = convertEmToPx(lineMetrics.lineHeight, baseSizePx) || lineHeightPx\n rLineHeights += iLineHeight\n }\n const llMaxFont = convertEmToPx(lineMetrics.maxFontSize, baseSizePx) || baseSizePx\n let dy\n switch (alignment) {\n case 'middle':\n dy = (flMaxFont / 2) - (0.15 * llMaxFont) - (rLineHeights / 2)\n break\n case 'bottom':\n dy = -(0.25 * llMaxFont) - rLineHeights\n break\n default:\n case 'top':\n dy = (0.8 * flMaxFont)\n break\n }\n return dy\n }\n\n VPrototype.text = function(content, opt) {\n\n if (content && typeof content !== 'string') throw new Error('Vectorizer: text() expects the first argument to be a string.')\n\n // Replace all spaces with the Unicode No-break space (http://www.fileformat.info/info/unicode/char/a0/index.htm).\n // IE would otherwise collapse all spaces into one.\n content = V.sanitizeText(content)\n opt || (opt = {})\n\n // End of Line character\n const {eol} = opt\n // Text along path\n let {textPath} = opt\n // Vertical shift\n const verticalAnchor = opt.textVerticalAnchor\n const namedVerticalAnchor = (verticalAnchor === 'middle' || verticalAnchor === 'bottom' || verticalAnchor === 'top')\n // Horizontal shift applied to all the lines but the first.\n let {x} = opt\n if (x === undefined) x = this.attr('x') || 0\n // Annotations\n const iai = opt.includeAnnotationIndices\n let {annotations} = opt\n if (annotations && !V.isArray(annotations)) annotations = [annotations]\n // Shift all the but first by one line (`1em`)\n const defaultLineHeight = opt.lineHeight\n const autoLineHeight = (defaultLineHeight === 'auto')\n const lineHeight = (autoLineHeight) ? '1.5em' : (defaultLineHeight || '1em')\n // Clearing the element\n this.empty()\n this.attr({\n // Preserve spaces. In other words, we do not want consecutive spaces to get collapsed to one.\n 'xml:space': 'preserve',\n // An empty text gets rendered into the DOM in webkit-based browsers.\n // In order to unify this behaviour across all browsers\n // we rather hide the text element when it's empty.\n display: (content) ? null : 'none'\n })\n // Set default font-size if none\n let fontSize = parseFloat(this.attr('font-size'))\n if (!fontSize) {\n fontSize = 16\n if (namedVerticalAnchor || annotations) this.attr('font-size', fontSize)\n }\n\n const doc = document\n let containerNode\n if (textPath) {\n // Now all the ``s will be inside the ``.\n if (typeof textPath === 'string') textPath = { d: textPath }\n containerNode = createTextPathNode(textPath, this)\n } else {\n containerNode = doc.createDocumentFragment()\n }\n let offset = 0\n const lines = content.split('\\n')\n const linesMetrics = []\n let annotatedY\n for (var i = 0, lastI = lines.length - 1; i <= lastI; i++) {\n var dy = lineHeight\n let lineClassName = 'v-line'\n const lineNode = doc.createElementNS(V.namespace.xmlns, 'tspan')\n let line = lines[i]\n var lineMetrics\n if (line) {\n if (annotations) {\n // Find the *compacted* annotations for this line.\n const lineAnnotations = V.annotateString(line, annotations, {\n offset: -offset,\n includeAnnotationIndices: iai\n })\n lineMetrics = annotateTextLine(lineNode, lineAnnotations, {\n includeAnnotationIndices: iai,\n eol: (i !== lastI && eol),\n lineHeight: (autoLineHeight) ? null : lineHeight,\n baseSize: fontSize\n })\n // Get the line height based on the biggest font size in the annotations for this line.\n const iLineHeight = lineMetrics.lineHeight\n if (iLineHeight && autoLineHeight && i !== 0) dy = iLineHeight\n if (i === 0) annotatedY = lineMetrics.maxFontSize * 0.8\n } else {\n if (eol && i !== lastI) line += eol\n lineNode.textContent = line\n }\n } else {\n // Make sure the textContent is never empty. If it is, add a dummy\n // character and make it invisible, making the following lines correctly\n // relatively positioned. `dy=1em` won't work with empty lines otherwise.\n lineNode.textContent = '-'\n lineClassName += ' v-empty-line'\n // 'opacity' needs to be specified with fill, stroke. Opacity without specification\n // is not applied in Firefox\n const lineNodeStyle = lineNode.style\n lineNodeStyle.fillOpacity = 0\n lineNodeStyle.strokeOpacity = 0\n if (annotations) lineMetrics = {}\n }\n if (lineMetrics) linesMetrics.push(lineMetrics)\n if (i > 0) lineNode.setAttribute('dy', dy)\n // Firefox requires 'x' to be set on the first line when inside a text path\n if (i > 0 || textPath) lineNode.setAttribute('x', x)\n lineNode.className.baseVal = lineClassName\n containerNode.appendChild(lineNode)\n offset += line.length + 1 // + 1 = newline character.\n }\n // Y Alignment calculation\n if (namedVerticalAnchor) {\n if (annotations) {\n dy = calculateDY(verticalAnchor, linesMetrics, fontSize, lineHeight)\n } else if (verticalAnchor === 'top') {\n // A shortcut for top alignment. It does not depend on font-size nor line-height\n dy = '0.8em'\n } else {\n let rh // remaining height\n if (lastI > 0) {\n rh = parseFloat(lineHeight) || 1\n rh *= lastI\n if (!emRegex.test(lineHeight)) rh /= fontSize\n } else {\n // Single-line text\n rh = 0\n }\n switch (verticalAnchor) {\n case 'middle':\n dy = `${0.3 - (rh / 2) }em`\n break\n case 'bottom':\n dy = `${-rh - 0.3 }em`\n break\n }\n }\n } else if (verticalAnchor === 0) {\n dy = '0em'\n } else if (verticalAnchor) {\n dy = verticalAnchor\n } else {\n // No vertical anchor is defined\n dy = 0\n // Backwards compatibility - we change the `y` attribute instead of `dy`.\n if (this.attr('y') === null) this.attr('y', annotatedY || '0.8em')\n }\n containerNode.firstChild.setAttribute('dy', dy)\n // Appending lines to the element.\n this.append(containerNode)\n return this\n }\n\n /**\n * @public\n * @param {string} name\n * @returns {Vectorizer}\n */\n VPrototype.removeAttr = function(name) {\n\n const qualifiedName = V.qualifyAttr(name)\n const el = this.node\n\n if (qualifiedName.ns) {\n if (el.hasAttributeNS(qualifiedName.ns, qualifiedName.local)) {\n el.removeAttributeNS(qualifiedName.ns, qualifiedName.local)\n }\n } else if (el.hasAttribute(name)) {\n el.removeAttribute(name)\n }\n return this\n }\n\n VPrototype.attr = function(name, value) {\n\n if (V.isUndefined(name)) {\n\n // Return all attributes.\n const {attributes} = this.node\n const attrs = {}\n\n for (let i = 0; i < attributes.length; i++) {\n attrs[attributes[i].name] = attributes[i].value\n }\n\n return attrs\n }\n\n if (V.isString(name) && V.isUndefined(value)) {\n return this.node.getAttribute(name)\n }\n\n if (typeof name === 'object') {\n\n for (const attrName in name) {\n if (name.hasOwnProperty(attrName)) {\n this.setAttribute(attrName, name[attrName])\n }\n }\n\n } else {\n\n this.setAttribute(name, value)\n }\n\n return this\n }\n\n VPrototype.normalizePath = function() {\n\n const tagName = this.tagName()\n if (tagName === 'PATH') {\n this.attr('d', V.normalizePathData(this.attr('d')))\n }\n\n return this\n }\n\n VPrototype.remove = function() {\n\n if (this.node.parentNode) {\n this.node.parentNode.removeChild(this.node)\n }\n\n return this\n }\n\n VPrototype.empty = function() {\n\n while (this.node.firstChild) {\n this.node.removeChild(this.node.firstChild)\n }\n\n return this\n }\n\n /**\n * @private\n * @param {object} attrs\n * @returns {Vectorizer}\n */\n VPrototype.setAttributes = function(attrs) {\n\n for (const key in attrs) {\n if (attrs.hasOwnProperty(key)) {\n this.setAttribute(key, attrs[key])\n }\n }\n\n return this\n }\n\n VPrototype.append = function(els) {\n\n if (!V.isArray(els)) {\n els = [els]\n }\n\n for (let i = 0, len = els.length; i < len; i++) {\n this.node.appendChild(V.toNode(els[i]))\n }\n\n return this\n }\n\n VPrototype.prepend = function(els) {\n\n const child = this.node.firstChild\n return child ? V(child).before(els) : this.append(els)\n }\n\n VPrototype.before = function(els) {\n\n const {node} = this\n const parent = node.parentNode\n\n if (parent) {\n\n if (!V.isArray(els)) {\n els = [els]\n }\n\n for (let i = 0, len = els.length; i < len; i++) {\n parent.insertBefore(V.toNode(els[i]), node)\n }\n }\n\n return this\n }\n\n VPrototype.appendTo = function(node) {\n V.toNode(node).appendChild(this.node)\n return this\n },\n\n VPrototype.svg = function() {\n\n return this.node instanceof window.SVGSVGElement ? this : V(this.node.ownerSVGElement)\n }\n\n VPrototype.tagName = function() {\n\n return this.node.tagName.toUpperCase()\n }\n\n VPrototype.defs = function() {\n const context = this.svg() || this\n const defsNode = context.node.getElementsByTagName('defs')[0]\n if (defsNode) return V(defsNode)\n return V('defs').appendTo(context)\n }\n\n VPrototype.clone = function() {\n\n const clone = V(this.node.cloneNode(true/* deep */))\n // Note that clone inherits also ID. Therefore, we need to change it here.\n clone.node.id = V.uniqueId()\n return clone\n }\n\n VPrototype.findOne = function(selector) {\n\n const found = this.node.querySelector(selector)\n return found ? V(found) : undefined\n }\n\n VPrototype.find = function(selector) {\n\n const vels = []\n const nodes = this.node.querySelectorAll(selector)\n\n if (nodes) {\n\n // Map DOM elements to `V`s.\n for (let i = 0; i < nodes.length; i++) {\n vels.push(V(nodes[i]))\n }\n }\n\n return vels\n }\n\n // Returns an array of V elements made from children of this.node.\n VPrototype.children = function() {\n\n const children = this.node.childNodes\n\n const outputArray = []\n for (let i = 0; i < children.length; i++) {\n const currentChild = children[i]\n if (currentChild.nodeType === 1) {\n outputArray.push(V(children[i]))\n }\n }\n return outputArray\n }\n\n // Find an index of an element inside its container.\n VPrototype.index = function() {\n\n let index = 0\n let node = this.node.previousSibling\n\n while (node) {\n // nodeType 1 for ELEMENT_NODE\n if (node.nodeType === 1) index++\n node = node.previousSibling\n }\n\n return index\n }\n\n VPrototype.findParentByClass = function(className, terminator) {\n\n const {ownerSVGElement} = this.node\n let node = this.node.parentNode\n\n while (node && node !== terminator && node !== ownerSVGElement) {\n\n const vel = V(node)\n if (vel.hasClass(className)) {\n return vel\n }\n\n node = node.parentNode\n }\n\n return null\n }\n\n // https://jsperf.com/get-common-parent\n VPrototype.contains = function(el) {\n\n const a = this.node\n const b = V.toNode(el)\n const bup = b && b.parentNode\n\n return (a === bup) || !!(bup && bup.nodeType === 1 && (a.compareDocumentPosition(bup) & 16))\n }\n\n // Convert global point into the coordinate space of this element.\n VPrototype.toLocalPoint = function(x, y) {\n\n const svg = this.svg().node\n\n const p = svg.createSVGPoint()\n p.x = x\n p.y = y\n\n try {\n\n var globalPoint = p.matrixTransform(svg.getScreenCTM().inverse())\n var globalToLocalMatrix = this.getTransformToElement(svg).inverse()\n\n } catch (e) {\n // IE9 throws an exception in odd cases. (`Unexpected call to method or property access`)\n // We have to make do with the original coordianates.\n return p\n }\n\n return globalPoint.matrixTransform(globalToLocalMatrix)\n }\n\n VPrototype.translateCenterToPoint = function(p) {\n\n const bbox = this.getBBox({ target: this.svg() })\n const center = bbox.center()\n\n this.translate(p.x - center.x, p.y - center.y)\n return this\n }\n\n // Efficiently auto-orient an element. This basically implements the orient=auto attribute\n // of markers. The easiest way of understanding on what this does is to imagine the element is an\n // arrowhead. Calling this method on the arrowhead makes it point to the `position` point while\n // being auto-oriented (properly rotated) towards the `reference` point.\n // `target` is the element relative to which the transformations are applied. Usually a viewport.\n VPrototype.translateAndAutoOrient = function(position, reference, target) {\n\n // Clean-up previously set transformations except the scale. If we didn't clean up the\n // previous transformations then they'd add up with the old ones. Scale is an exception as\n // it doesn't add up, consider: `this.scale(2).scale(2).scale(2)`. The result is that the\n // element is scaled by the factor 2, not 8.\n\n const s = this.scale()\n this.attr('transform', '')\n this.scale(s.sx, s.sy)\n\n const svg = this.svg().node\n const bbox = this.getBBox({ target: target || svg })\n\n // 1. Translate to origin.\n const translateToOrigin = svg.createSVGTransform()\n translateToOrigin.setTranslate(-bbox.x - bbox.width / 2, -bbox.y - bbox.height / 2)\n\n // 2. Rotate around origin.\n const rotateAroundOrigin = svg.createSVGTransform()\n const angle = (new g.Point(position)).changeInAngle(position.x - reference.x, position.y - reference.y, reference)\n rotateAroundOrigin.setRotate(angle, 0, 0)\n\n // 3. Translate to the `position` + the offset (half my width) towards the `reference` point.\n const translateFinal = svg.createSVGTransform()\n const finalPosition = (new g.Point(position)).move(reference, bbox.width / 2)\n translateFinal.setTranslate(position.x + (position.x - finalPosition.x), position.y + (position.y - finalPosition.y))\n\n // 4. Apply transformations.\n const ctm = this.getTransformToElement(target || svg)\n const transform = svg.createSVGTransform()\n transform.setMatrix(\n translateFinal.matrix.multiply(\n rotateAroundOrigin.matrix.multiply(\n translateToOrigin.matrix.multiply(\n ctm)))\n )\n\n // Instead of directly setting the `matrix()` transform on the element, first, decompose\n // the matrix into separate transforms. This allows us to use normal Vectorizer methods\n // as they don't work on matrices. An example of this is to retrieve a scale of an element.\n // this.node.transform.baseVal.initialize(transform);\n\n const decomposition = V.decomposeMatrix(transform.matrix)\n\n this.translate(decomposition.translateX, decomposition.translateY)\n this.rotate(decomposition.rotation)\n // Note that scale has been already applied, hence the following line stays commented. (it's here just for reference).\n //this.scale(decomposition.scaleX, decomposition.scaleY);\n\n return this\n }\n\n VPrototype.animateAlongPath = function(attrs, path) {\n\n path = V.toNode(path)\n\n const id = V.ensureId(path)\n const animateMotion = V('animateMotion', attrs)\n const mpath = V('mpath', { 'xlink:href': `#${ id}` })\n\n animateMotion.append(mpath)\n\n this.append(animateMotion)\n try {\n animateMotion.node.beginElement()\n } catch (e) {\n // Fallback for IE 9.\n // Run the animation programatically if FakeSmile (`http://leunen.me/fakesmile/`) present\n if (document.documentElement.getAttribute('smiling') === 'fake') {\n /* global getTargets:true, Animator:true, animators:true id2anim:true */\n // Register the animation. (See `https://answers.launchpad.net/smil/+question/203333`)\n const animation = animateMotion.node\n animation.animators = []\n\n const animationID = animation.getAttribute('id')\n if (animationID) id2anim[animationID] = animation\n\n const targets = getTargets(animation)\n for (let i = 0, len = targets.length; i < len; i++) {\n const target = targets[i]\n const animator = new Animator(animation, target, i)\n animators.push(animator)\n animation.animators[i] = animator\n animator.register()\n }\n }\n }\n return this\n }\n\n VPrototype.hasClass = function(className) {\n\n return new RegExp(`(\\\\s|^)${ className }(\\\\s|$)`).test(this.node.getAttribute('class'))\n }\n\n VPrototype.addClass = function(className) {\n\n if (!this.hasClass(className)) {\n const prevClasses = this.node.getAttribute('class') || ''\n this.node.setAttribute('class', (`${prevClasses } ${ className}`).trim())\n }\n\n return this\n }\n\n VPrototype.removeClass = function(className) {\n\n if (this.hasClass(className)) {\n const newClasses = this.node.getAttribute('class').replace(new RegExp(`(\\\\s|^)${ className }(\\\\s|$)`, 'g'), '$2')\n this.node.setAttribute('class', newClasses)\n }\n\n return this\n }\n\n VPrototype.toggleClass = function(className, toAdd) {\n\n const toRemove = V.isUndefined(toAdd) ? this.hasClass(className) : !toAdd\n\n if (toRemove) {\n this.removeClass(className)\n } else {\n this.addClass(className)\n }\n\n return this\n }\n\n // Interpolate path by discrete points. The precision of the sampling\n // is controlled by `interval`. In other words, `sample()` will generate\n // a point on the path starting at the beginning of the path going to the end\n // every `interval` pixels.\n // The sampler can be very useful for e.g. finding intersection between two\n // paths (finding the two closest points from two samples).\n VPrototype.sample = function(interval) {\n\n interval = interval || 1\n const {node} = this\n const length = node.getTotalLength()\n const samples = []\n let distance = 0\n let sample\n while (distance < length) {\n sample = node.getPointAtLength(distance)\n samples.push({ x: sample.x, y: sample.y, distance: distance })\n distance += interval\n }\n return samples\n }\n\n VPrototype.convertToPath = function() {\n\n const path = V('path')\n path.attr(this.attr())\n const d = this.convertToPathData()\n if (d) {\n path.attr('d', d)\n }\n return path\n }\n\n VPrototype.convertToPathData = function() {\n\n const tagName = this.tagName()\n\n switch (tagName) {\n case 'PATH':\n return this.attr('d')\n case 'LINE':\n return V.convertLineToPathData(this.node)\n case 'POLYGON':\n return V.convertPolygonToPathData(this.node)\n case 'POLYLINE':\n return V.convertPolylineToPathData(this.node)\n case 'ELLIPSE':\n return V.convertEllipseToPathData(this.node)\n case 'CIRCLE':\n return V.convertCircleToPathData(this.node)\n case 'RECT':\n return V.convertRectToPathData(this.node)\n }\n\n throw new Error(`${tagName } cannot be converted to PATH.`)\n }\n\n V.prototype.toGeometryShape = function() {\n let x; let y; let width; let height; let cx; let cy; let r; let rx; let ry; let points; let d; let x1; let x2; let y1; let y2\n switch (this.tagName()) {\n\n case 'RECT':\n x = parseFloat(this.attr('x')) || 0\n y = parseFloat(this.attr('y')) || 0\n width = parseFloat(this.attr('width')) || 0\n height = parseFloat(this.attr('height')) || 0\n return new g.Rect(x, y, width, height)\n\n case 'CIRCLE':\n cx = parseFloat(this.attr('cx')) || 0\n cy = parseFloat(this.attr('cy')) || 0\n r = parseFloat(this.attr('r')) || 0\n return new g.Ellipse({ x: cx, y: cy }, r, r)\n\n case 'ELLIPSE':\n cx = parseFloat(this.attr('cx')) || 0\n cy = parseFloat(this.attr('cy')) || 0\n rx = parseFloat(this.attr('rx')) || 0\n ry = parseFloat(this.attr('ry')) || 0\n return new g.Ellipse({ x: cx, y: cy }, rx, ry)\n\n case 'POLYLINE':\n points = V.getPointsFromSvgNode(this)\n return new g.Polyline(points)\n\n case 'POLYGON':\n points = V.getPointsFromSvgNode(this)\n if (points.length > 1) points.push(points[0])\n return new g.Polyline(points)\n\n case 'PATH':\n d = this.attr('d')\n if (!g.Path.isDataSupported(d)) d = V.normalizePathData(d)\n return new g.Path(d)\n\n case 'LINE':\n x1 = parseFloat(this.attr('x1')) || 0\n y1 = parseFloat(this.attr('y1')) || 0\n x2 = parseFloat(this.attr('x2')) || 0\n y2 = parseFloat(this.attr('y2')) || 0\n return new g.Line({ x: x1, y: y1 }, { x: x2, y: y2 })\n }\n\n // Anything else is a rectangle\n return this.getBBox()\n },\n\n // Find the intersection of a line starting in the center\n // of the SVG `node` ending in the point `ref`.\n // `target` is an SVG element to which `node`s transformations are relative to.\n // In JointJS, `target` is the `paper.viewport` SVG group element.\n // Note that `ref` point must be in the coordinate system of the `target` for this function to work properly.\n // Returns a point in the `target` coordinte system (the same system as `ref` is in) if\n // an intersection is found. Returns `undefined` otherwise.\n VPrototype.findIntersection = function(ref, target) {\n\n const svg = this.svg().node\n target = target || svg\n const bbox = this.getBBox({ target: target })\n const center = bbox.center()\n\n if (!bbox.intersectionWithLineFromCenterToPoint(ref)) return undefined\n\n let spot\n const tagName = this.tagName()\n\n // Little speed up optimalization for `` element. We do not do conversion\n // to path element and sampling but directly calculate the intersection through\n // a transformed geometrical rectangle.\n if (tagName === 'RECT') {\n\n const gRect = new g.Rect(\n parseFloat(this.attr('x') || 0),\n parseFloat(this.attr('y') || 0),\n parseFloat(this.attr('width')),\n parseFloat(this.attr('height'))\n )\n // Get the rect transformation matrix with regards to the SVG document.\n const rectMatrix = this.getTransformToElement(target)\n // Decompose the matrix to find the rotation angle.\n const rectMatrixComponents = V.decomposeMatrix(rectMatrix)\n // Now we want to rotate the rectangle back so that we\n // can use `intersectionWithLineFromCenterToPoint()` passing the angle as the second argument.\n const resetRotation = svg.createSVGTransform()\n resetRotation.setRotate(-rectMatrixComponents.rotation, center.x, center.y)\n const rect = V.transformRect(gRect, resetRotation.matrix.multiply(rectMatrix))\n spot = (new g.Rect(rect)).intersectionWithLineFromCenterToPoint(ref, rectMatrixComponents.rotation)\n\n } else if (tagName === 'PATH' || tagName === 'POLYGON' || tagName === 'POLYLINE' || tagName === 'CIRCLE' || tagName === 'ELLIPSE') {\n\n const pathNode = (tagName === 'PATH') ? this : this.convertToPath()\n const samples = pathNode.sample()\n let minDistance = Infinity\n let closestSamples = []\n\n let i; let sample; let gp; let centerDistance; let refDistance; let distance\n\n for (i = 0; i < samples.length; i++) {\n\n sample = samples[i]\n // Convert the sample point in the local coordinate system to the global coordinate system.\n gp = V.createSVGPoint(sample.x, sample.y)\n gp = gp.matrixTransform(this.getTransformToElement(target))\n sample = new g.Point(gp)\n centerDistance = sample.distance(center)\n // Penalize a higher distance to the reference point by 10%.\n // This gives better results. This is due to\n // inaccuracies introduced by rounding errors and getPointAtLength() returns.\n refDistance = sample.distance(ref) * 1.1\n distance = centerDistance + refDistance\n\n if (distance < minDistance) {\n minDistance = distance\n closestSamples = [{ sample: sample, refDistance: refDistance }]\n } else if (distance < minDistance + 1) {\n closestSamples.push({ sample: sample, refDistance: refDistance })\n }\n }\n\n closestSamples.sort(function(a, b) {\n return a.refDistance - b.refDistance\n })\n\n if (closestSamples[0]) {\n spot = closestSamples[0].sample\n }\n }\n\n return spot\n }\n\n /**\n * @private\n * @param {string} name\n * @param {string} value\n * @returns {Vectorizer}\n */\n VPrototype.setAttribute = function(name, value) {\n\n const el = this.node\n\n if (value === null) {\n this.removeAttr(name)\n return this\n }\n\n const qualifiedName = V.qualifyAttr(name)\n\n if (qualifiedName.ns) {\n // Attribute names can be namespaced. E.g. `image` elements\n // have a `xlink:href` attribute to set the source of the image.\n el.setAttributeNS(qualifiedName.ns, name, value)\n } else if (name === 'id') {\n el.id = value\n } else {\n el.setAttribute(name, value)\n }\n\n return this\n }\n\n // Create an SVG document element.\n // If `content` is passed, it will be used as the SVG content of the `` root element.\n V.createSvgDocument = function(content) {\n\n const svg = `${ content || '' }`\n const xml = V.parseXML(svg, { async: false })\n return xml.documentElement\n }\n\n V.idCounter = 0\n\n // A function returning a unique identifier for this client session with every call.\n V.uniqueId = function() {\n\n return `v-${ ++V.idCounter}`\n }\n\n V.toNode = function(el) {\n\n return V.isV(el) ? el.node : (el.nodeName && el || el[0])\n }\n\n V.ensureId = function(node) {\n\n node = V.toNode(node)\n return node.id || (node.id = V.uniqueId())\n }\n\n // Replace all spaces with the Unicode No-break space (http://www.fileformat.info/info/unicode/char/a0/index.htm).\n // IE would otherwise collapse all spaces into one. This is used in the text() method but it is\n // also exposed so that the programmer can use it in case he needs to. This is useful e.g. in tests\n // when you want to compare the actual DOM text content without having to add the unicode character in\n // the place of all spaces.\n V.sanitizeText = function(text) {\n\n return (text || '').replace(/ /g, '\\u00A0')\n }\n\n V.isUndefined = function(value) {\n\n return typeof value === 'undefined'\n }\n\n V.isString = function(value) {\n\n return typeof value === 'string'\n }\n\n V.isObject = function(value) {\n\n return value && (typeof value === 'object')\n }\n\n V.isArray = Array.isArray\n\n V.parseXML = function(data, opt) {\n\n opt = opt || {}\n\n let xml\n\n try {\n const parser = new DOMParser()\n\n if (!V.isUndefined(opt.async)) {\n parser.async = opt.async\n }\n\n xml = parser.parseFromString(data, 'text/xml')\n } catch (error) {\n xml = undefined\n }\n\n if (!xml || xml.getElementsByTagName('parsererror').length) {\n throw new Error(`Invalid XML: ${ data}`)\n }\n\n return xml\n }\n\n /**\n * @param {string} name\n * @returns {{ns: string|null, local: string}} namespace and attribute name\n */\n V.qualifyAttr = function(name) {\n\n if (name.indexOf(':') !== -1) {\n const combinedKey = name.split(':')\n return {\n ns: ns[combinedKey[0]],\n local: combinedKey[1]\n }\n }\n\n return {\n ns: null,\n local: name\n }\n }\n\n V.transformRegex = /(\\w+)\\(([^,)]+),?([^)]+)?\\)/gi\n V.transformSeparatorRegex = /[ ,]+/\n V.transformationListRegex = /^(\\w+)\\((.*)\\)/\n\n V.transformStringToMatrix = function(transform) {\n\n let transformationMatrix = V.createSVGMatrix()\n const matches = transform && transform.match(V.transformRegex)\n if (!matches) {\n return transformationMatrix\n }\n\n for (let i = 0, n = matches.length; i < n; i++) {\n const transformationString = matches[i]\n\n const transformationMatch = transformationString.match(V.transformationListRegex)\n if (transformationMatch) {\n var sx; var sy; var tx; var ty; var angle\n let ctm = V.createSVGMatrix()\n const args = transformationMatch[2].split(V.transformSeparatorRegex)\n switch (transformationMatch[1].toLowerCase()) {\n case 'scale':\n sx = parseFloat(args[0])\n sy = (args[1] === undefined) ? sx : parseFloat(args[1])\n ctm = ctm.scaleNonUniform(sx, sy)\n break\n case 'translate':\n tx = parseFloat(args[0])\n ty = parseFloat(args[1])\n ctm = ctm.translate(tx, ty)\n break\n case 'rotate':\n angle = parseFloat(args[0])\n tx = parseFloat(args[1]) || 0\n ty = parseFloat(args[2]) || 0\n if (tx !== 0 || ty !== 0) {\n ctm = ctm.translate(tx, ty).rotate(angle).translate(-tx, -ty)\n } else {\n ctm = ctm.rotate(angle)\n }\n break\n case 'skewx':\n angle = parseFloat(args[0])\n ctm = ctm.skewX(angle)\n break\n case 'skewy':\n angle = parseFloat(args[0])\n ctm = ctm.skewY(angle)\n break\n case 'matrix':\n ctm.a = parseFloat(args[0])\n ctm.b = parseFloat(args[1])\n ctm.c = parseFloat(args[2])\n ctm.d = parseFloat(args[3])\n ctm.e = parseFloat(args[4])\n ctm.f = parseFloat(args[5])\n break\n default:\n continue\n }\n\n transformationMatrix = transformationMatrix.multiply(ctm)\n }\n\n }\n return transformationMatrix\n }\n\n V.matrixToTransformString = function(matrix) {\n matrix || (matrix = true)\n\n return `matrix(${ \n matrix.a !== undefined ? matrix.a : 1 },${ \n matrix.b !== undefined ? matrix.b : 0 },${ \n matrix.c !== undefined ? matrix.c : 0 },${ \n matrix.d !== undefined ? matrix.d : 1 },${ \n matrix.e !== undefined ? matrix.e : 0 },${ \n matrix.f !== undefined ? matrix.f : 0 \n })`\n }\n\n V.parseTransformString = function(transform) {\n\n let translate; let rotate; let scale\n\n if (transform) {\n\n const separator = V.transformSeparatorRegex\n\n // Allow reading transform string with a single matrix\n if (transform.trim().indexOf('matrix') >= 0) {\n\n const matrix = V.transformStringToMatrix(transform)\n const decomposedMatrix = V.decomposeMatrix(matrix)\n\n translate = [decomposedMatrix.translateX, decomposedMatrix.translateY]\n scale = [decomposedMatrix.scaleX, decomposedMatrix.scaleY]\n rotate = [decomposedMatrix.rotation]\n\n const transformations = []\n if (translate[0] !== 0 || translate[0] !== 0) {\n transformations.push(`translate(${ translate })`)\n }\n if (scale[0] !== 1 || scale[1] !== 1) {\n transformations.push(`scale(${ scale })`)\n }\n if (rotate[0] !== 0) {\n transformations.push(`rotate(${ rotate })`)\n }\n transform = transformations.join(' ')\n\n } else {\n\n const translateMatch = transform.match(/translate\\((.*?)\\)/)\n if (translateMatch) {\n translate = translateMatch[1].split(separator)\n }\n const rotateMatch = transform.match(/rotate\\((.*?)\\)/)\n if (rotateMatch) {\n rotate = rotateMatch[1].split(separator)\n }\n const scaleMatch = transform.match(/scale\\((.*?)\\)/)\n if (scaleMatch) {\n scale = scaleMatch[1].split(separator)\n }\n }\n }\n\n const sx = (scale && scale[0]) ? parseFloat(scale[0]) : 1\n\n return {\n value: transform,\n translate: {\n tx: (translate && translate[0]) ? parseInt(translate[0], 10) : 0,\n ty: (translate && translate[1]) ? parseInt(translate[1], 10) : 0\n },\n rotate: {\n angle: (rotate && rotate[0]) ? parseInt(rotate[0], 10) : 0,\n cx: (rotate && rotate[1]) ? parseInt(rotate[1], 10) : undefined,\n cy: (rotate && rotate[2]) ? parseInt(rotate[2], 10) : undefined\n },\n scale: {\n sx: sx,\n sy: (scale && scale[1]) ? parseFloat(scale[1]) : sx\n }\n }\n }\n\n V.deltaTransformPoint = function(matrix, point) {\n\n const dx = point.x * matrix.a + point.y * matrix.c + 0\n const dy = point.x * matrix.b + point.y * matrix.d + 0\n return { x: dx, y: dy }\n }\n\n V.decomposeMatrix = function(matrix) {\n\n // @see https://gist.github.com/2052247\n\n // calculate delta transform point\n const px = V.deltaTransformPoint(matrix, { x: 0, y: 1 })\n const py = V.deltaTransformPoint(matrix, { x: 1, y: 0 })\n\n // calculate skew\n const skewX = ((180 / PI) * atan2(px.y, px.x) - 90)\n const skewY = ((180 / PI) * atan2(py.y, py.x))\n\n return {\n\n translateX: matrix.e,\n translateY: matrix.f,\n scaleX: sqrt(matrix.a * matrix.a + matrix.b * matrix.b),\n scaleY: sqrt(matrix.c * matrix.c + matrix.d * matrix.d),\n skewX: skewX,\n skewY: skewY,\n rotation: skewX // rotation is the same as skew x\n }\n }\n\n // Return the `scale` transformation from the following equation:\n // `translate(tx, ty) . rotate(angle) . scale(sx, sy) === matrix(a,b,c,d,e,f)`\n V.matrixToScale = function(matrix) {\n\n let a; let b; let c; let d\n if (matrix) {\n a = V.isUndefined(matrix.a) ? 1 : matrix.a\n d = V.isUndefined(matrix.d) ? 1 : matrix.d\n b = matrix.b\n c = matrix.c\n } else {\n a = d = 1\n }\n return {\n sx: b ? sqrt(a * a + b * b) : a,\n sy: c ? sqrt(c * c + d * d) : d\n }\n },\n\n // Return the `rotate` transformation from the following equation:\n // `translate(tx, ty) . rotate(angle) . scale(sx, sy) === matrix(a,b,c,d,e,f)`\n V.matrixToRotate = function(matrix) {\n\n let p = { x: 0, y: 1 }\n if (matrix) {\n p = V.deltaTransformPoint(matrix, p)\n }\n\n return {\n angle: g.normalizeAngle(g.toDeg(atan2(p.y, p.x)) - 90)\n }\n },\n\n // Return the `translate` transformation from the following equation:\n // `translate(tx, ty) . rotate(angle) . scale(sx, sy) === matrix(a,b,c,d,e,f)`\n V.matrixToTranslate = function(matrix) {\n\n return {\n tx: (matrix && matrix.e) || 0,\n ty: (matrix && matrix.f) || 0\n }\n },\n\n V.isV = function(object) {\n\n return object instanceof V\n }\n\n // For backwards compatibility:\n V.isVElement = V.isV\n\n const svgDocument = V('svg').node\n\n V.createSVGMatrix = function(matrix) {\n\n const svgMatrix = svgDocument.createSVGMatrix()\n for (const component in matrix) {\n svgMatrix[component] = matrix[component]\n }\n\n return svgMatrix\n }\n\n V.createSVGTransform = function(matrix) {\n\n if (!V.isUndefined(matrix)) {\n\n if (!(matrix instanceof SVGMatrix)) {\n matrix = V.createSVGMatrix(matrix)\n }\n\n return svgDocument.createSVGTransformFromMatrix(matrix)\n }\n\n return svgDocument.createSVGTransform()\n }\n\n V.createSVGPoint = function(x, y) {\n\n const p = svgDocument.createSVGPoint()\n p.x = x\n p.y = y\n return p\n }\n\n V.transformRect = function(r, matrix) {\n\n const p = svgDocument.createSVGPoint()\n\n p.x = r.x\n p.y = r.y\n const corner1 = p.matrixTransform(matrix)\n\n p.x = r.x + r.width\n p.y = r.y\n const corner2 = p.matrixTransform(matrix)\n\n p.x = r.x + r.width\n p.y = r.y + r.height\n const corner3 = p.matrixTransform(matrix)\n\n p.x = r.x\n p.y = r.y + r.height\n const corner4 = p.matrixTransform(matrix)\n\n const minX = min(corner1.x, corner2.x, corner3.x, corner4.x)\n const maxX = max(corner1.x, corner2.x, corner3.x, corner4.x)\n const minY = min(corner1.y, corner2.y, corner3.y, corner4.y)\n const maxY = max(corner1.y, corner2.y, corner3.y, corner4.y)\n\n return new g.Rect(minX, minY, maxX - minX, maxY - minY)\n }\n\n V.transformPoint = function(p, matrix) {\n\n return new g.Point(V.createSVGPoint(p.x, p.y).matrixTransform(matrix))\n }\n\n V.transformLine = function(l, matrix) {\n\n return new g.Line(\n V.transformPoint(l.start, matrix),\n V.transformPoint(l.end, matrix)\n )\n }\n\n V.transformPolyline = function(p, matrix) {\n\n let inPoints = (p instanceof g.Polyline) ? p.points : p\n if (!V.isArray(inPoints)) inPoints = []\n const outPoints = []\n for (let i = 0, n = inPoints.length; i < n; i++) outPoints[i] = V.transformPoint(inPoints[i], matrix)\n return new g.Polyline(outPoints)\n },\n\n // Convert a style represented as string (e.g. `'fill=\"blue\"; stroke=\"red\"'`) to\n // an object (`{ fill: 'blue', stroke: 'red' }`).\n V.styleToObject = function(styleString) {\n const ret = {}\n const styles = styleString.split(';')\n for (let i = 0; i < styles.length; i++) {\n const style = styles[i]\n const pair = style.split('=')\n ret[pair[0].trim()] = pair[1].trim()\n }\n return ret\n }\n\n // Inspired by d3.js https://github.com/mbostock/d3/blob/master/src/svg/arc.js\n V.createSlicePathData = function(innerRadius, outerRadius, startAngle, endAngle) {\n\n const svgArcMax = 2 * PI - 1e-6\n const r0 = innerRadius\n const r1 = outerRadius\n let a0 = startAngle\n let a1 = endAngle\n var da = (a1 < a0 && (da = a0, a0 = a1, a1 = da), a1 - a0)\n const df = da < PI ? '0' : '1'\n const c0 = cos(a0)\n const s0 = sin(a0)\n const c1 = cos(a1)\n const s1 = sin(a1)\n\n return (da >= svgArcMax)\n ? (r0\n ? `M0,${ r1\n }A${ r1 },${ r1 } 0 1,1 0,${ -r1\n }A${ r1 },${ r1 } 0 1,1 0,${ r1\n }M0,${ r0\n }A${ r0 },${ r0 } 0 1,0 0,${ -r0\n }A${ r0 },${ r0 } 0 1,0 0,${ r0\n }Z`\n : `M0,${ r1\n }A${ r1 },${ r1 } 0 1,1 0,${ -r1\n }A${ r1 },${ r1 } 0 1,1 0,${ r1\n }Z`)\n : (r0\n ? `M${ r1 * c0 },${ r1 * s0\n }A${ r1 },${ r1 } 0 ${ df },1 ${ r1 * c1 },${ r1 * s1\n }L${ r0 * c1 },${ r0 * s1\n }A${ r0 },${ r0 } 0 ${ df },0 ${ r0 * c0 },${ r0 * s0\n }Z`\n : `M${ r1 * c0 },${ r1 * s0\n }A${ r1 },${ r1 } 0 ${ df },1 ${ r1 * c1 },${ r1 * s1\n }L0,0`\n + `Z`)\n }\n\n // Merge attributes from object `b` with attributes in object `a`.\n // Note that this modifies the object `a`.\n // Also important to note that attributes are merged but CSS classes are concatenated.\n V.mergeAttrs = function(a, b) {\n\n for (const attr in b) {\n\n if (attr === 'class') {\n // Concatenate classes.\n a[attr] = a[attr] ? `${a[attr] } ${ b[attr]}` : b[attr]\n } else if (attr === 'style') {\n // `style` attribute can be an object.\n if (V.isObject(a[attr]) && V.isObject(b[attr])) {\n // `style` stored in `a` is an object.\n a[attr] = V.mergeAttrs(a[attr], b[attr])\n } else if (V.isObject(a[attr])) {\n // `style` in `a` is an object but it's a string in `b`.\n // Convert the style represented as a string to an object in `b`.\n a[attr] = V.mergeAttrs(a[attr], V.styleToObject(b[attr]))\n } else if (V.isObject(b[attr])) {\n // `style` in `a` is a string, in `b` it's an object.\n a[attr] = V.mergeAttrs(V.styleToObject(a[attr]), b[attr])\n } else {\n // Both styles are strings.\n a[attr] = V.mergeAttrs(V.styleToObject(a[attr]), V.styleToObject(b[attr]))\n }\n } else {\n a[attr] = b[attr]\n }\n }\n\n return a\n }\n\n V.annotateString = function(t, annotations, opt) {\n\n annotations = annotations || []\n opt = opt || {}\n\n const offset = opt.offset || 0\n const compacted = []\n let batch\n const ret = []\n let item\n let prev\n\n for (let i = 0; i < t.length; i++) {\n\n item = ret[i] = t[i]\n\n for (let j = 0; j < annotations.length; j++) {\n\n const annotation = annotations[j]\n const start = annotation.start + offset\n const end = annotation.end + offset\n\n if (i >= start && i < end) {\n // Annotation applies.\n if (V.isObject(item)) {\n // There is more than one annotation to be applied => Merge attributes.\n item.attrs = V.mergeAttrs(V.mergeAttrs({}, item.attrs), annotation.attrs)\n } else {\n item = ret[i] = { t: t[i], attrs: annotation.attrs }\n }\n if (opt.includeAnnotationIndices) {\n (item.annotations || (item.annotations = [])).push(j)\n }\n }\n }\n\n prev = ret[i - 1]\n\n if (!prev) {\n\n batch = item\n\n } else if (V.isObject(item) && V.isObject(prev)) {\n // Both previous item and the current one are annotations. If the attributes\n // didn't change, merge the text.\n if (JSON.stringify(item.attrs) === JSON.stringify(prev.attrs)) {\n batch.t += item.t\n } else {\n compacted.push(batch)\n batch = item\n }\n\n } else if (V.isObject(item)) {\n // Previous item was a string, current item is an annotation.\n compacted.push(batch)\n batch = item\n\n } else if (V.isObject(prev)) {\n // Previous item was an annotation, current item is a string.\n compacted.push(batch)\n batch = item\n\n } else {\n // Both previous and current item are strings.\n batch = (batch || '') + item\n }\n }\n\n if (batch) {\n compacted.push(batch)\n }\n\n return compacted\n }\n\n V.findAnnotationsAtIndex = function(annotations, index) {\n\n const found = []\n\n if (annotations) {\n\n annotations.forEach(function(annotation) {\n\n if (annotation.start < index && index <= annotation.end) {\n found.push(annotation)\n }\n })\n }\n\n return found\n }\n\n V.findAnnotationsBetweenIndexes = function(annotations, start, end) {\n\n const found = []\n\n if (annotations) {\n\n annotations.forEach(function(annotation) {\n\n if ((start >= annotation.start && start < annotation.end) || (end > annotation.start && end <= annotation.end) || (annotation.start >= start && annotation.end < end)) {\n found.push(annotation)\n }\n })\n }\n\n return found\n }\n\n // Shift all the text annotations after character `index` by `offset` positions.\n V.shiftAnnotations = function(annotations, index, offset) {\n\n if (annotations) {\n\n annotations.forEach(function(annotation) {\n\n if (annotation.start < index && annotation.end >= index) {\n annotation.end += offset\n } else if (annotation.start >= index) {\n annotation.start += offset\n annotation.end += offset\n }\n })\n }\n\n return annotations\n }\n\n V.convertLineToPathData = function(line) {\n\n line = V(line)\n const d = [\n 'M', line.attr('x1'), line.attr('y1'),\n 'L', line.attr('x2'), line.attr('y2')\n ].join(' ')\n return d\n }\n\n V.convertPolygonToPathData = function(polygon) {\n\n const points = V.getPointsFromSvgNode(polygon)\n if (points.length === 0) return null\n\n return `${V.svgPointsToPath(points) } Z`\n }\n\n V.convertPolylineToPathData = function(polyline) {\n\n const points = V.getPointsFromSvgNode(polyline)\n if (points.length === 0) return null\n\n return V.svgPointsToPath(points)\n }\n\n V.svgPointsToPath = function(points) {\n\n for (let i = 0, n = points.length; i < n; i++) {\n points[i] = `${points[i].x } ${ points[i].y}`\n }\n\n return `M ${ points.join(' L')}`\n }\n\n V.getPointsFromSvgNode = function(node) {\n\n node = V.toNode(node)\n const points = []\n const nodePoints = node.points\n if (nodePoints) {\n for (let i = 0, n = nodePoints.numberOfItems; i < n; i++) {\n points.push(nodePoints.getItem(i))\n }\n }\n\n return points\n }\n\n V.KAPPA = 0.551784\n\n V.convertCircleToPathData = function(circle) {\n\n circle = V(circle)\n const cx = parseFloat(circle.attr('cx')) || 0\n const cy = parseFloat(circle.attr('cy')) || 0\n const r = parseFloat(circle.attr('r'))\n const cd = r * V.KAPPA // Control distance.\n\n const d = [\n 'M', cx, cy - r, // Move to the first point.\n 'C', cx + cd, cy - r, cx + r, cy - cd, cx + r, cy, // I. Quadrant.\n 'C', cx + r, cy + cd, cx + cd, cy + r, cx, cy + r, // II. Quadrant.\n 'C', cx - cd, cy + r, cx - r, cy + cd, cx - r, cy, // III. Quadrant.\n 'C', cx - r, cy - cd, cx - cd, cy - r, cx, cy - r, // IV. Quadrant.\n 'Z'\n ].join(' ')\n return d\n }\n\n V.convertEllipseToPathData = function(ellipse) {\n\n ellipse = V(ellipse)\n const cx = parseFloat(ellipse.attr('cx')) || 0\n const cy = parseFloat(ellipse.attr('cy')) || 0\n const rx = parseFloat(ellipse.attr('rx'))\n const ry = parseFloat(ellipse.attr('ry')) || rx\n const cdx = rx * V.KAPPA // Control distance x.\n const cdy = ry * V.KAPPA // Control distance y.\n\n const d = [\n 'M', cx, cy - ry, // Move to the first point.\n 'C', cx + cdx, cy - ry, cx + rx, cy - cdy, cx + rx, cy, // I. Quadrant.\n 'C', cx + rx, cy + cdy, cx + cdx, cy + ry, cx, cy + ry, // II. Quadrant.\n 'C', cx - cdx, cy + ry, cx - rx, cy + cdy, cx - rx, cy, // III. Quadrant.\n 'C', cx - rx, cy - cdy, cx - cdx, cy - ry, cx, cy - ry, // IV. Quadrant.\n 'Z'\n ].join(' ')\n return d\n }\n\n V.convertRectToPathData = function(rect) {\n\n rect = V(rect)\n\n return V.rectToPath({\n x: parseFloat(rect.attr('x')) || 0,\n y: parseFloat(rect.attr('y')) || 0,\n width: parseFloat(rect.attr('width')) || 0,\n height: parseFloat(rect.attr('height')) || 0,\n rx: parseFloat(rect.attr('rx')) || 0,\n ry: parseFloat(rect.attr('ry')) || 0\n })\n }\n\n // Convert a rectangle to SVG path commands. `r` is an object of the form:\n // `{ x: [number], y: [number], width: [number], height: [number], top-ry: [number], top-ry: [number], bottom-rx: [number], bottom-ry: [number] }`,\n // where `x, y, width, height` are the usual rectangle attributes and [top-/bottom-]rx/ry allows for\n // specifying radius of the rectangle for all its sides (as opposed to the built-in SVG rectangle\n // that has only `rx` and `ry` attributes).\n V.rectToPath = function(r) {\n\n let d\n const {x} = r\n const {y} = r\n const {width} = r\n const {height} = r\n const topRx = min(r.rx || r['top-rx'] || 0, width / 2)\n const bottomRx = min(r.rx || r['bottom-rx'] || 0, width / 2)\n const topRy = min(r.ry || r['top-ry'] || 0, height / 2)\n const bottomRy = min(r.ry || r['bottom-ry'] || 0, height / 2)\n\n if (topRx || bottomRx || topRy || bottomRy) {\n d = [\n 'M', x, y + topRy,\n 'v', height - topRy - bottomRy,\n 'a', bottomRx, bottomRy, 0, 0, 0, bottomRx, bottomRy,\n 'h', width - 2 * bottomRx,\n 'a', bottomRx, bottomRy, 0, 0, 0, bottomRx, -bottomRy,\n 'v', -(height - bottomRy - topRy),\n 'a', topRx, topRy, 0, 0, 0, -topRx, -topRy,\n 'h', -(width - 2 * topRx),\n 'a', topRx, topRy, 0, 0, 0, -topRx, topRy,\n 'Z'\n ]\n } else {\n d = [\n 'M', x, y,\n 'H', x + width,\n 'V', y + height,\n 'H', x,\n 'V', y,\n 'Z'\n ]\n }\n\n return d.join(' ')\n }\n\n // Take a path data string\n // Return a normalized path data string\n // If data cannot be parsed, return 'M 0 0'\n // Adapted from Rappid normalizePath polyfill\n // Highly inspired by Raphael Library (www.raphael.com)\n V.normalizePathData = (function() {\n\n const spaces = '\\x09\\x0a\\x0b\\x0c\\x0d\\x20\\xa0\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\\u2029'\n const pathCommand = new RegExp(`([a-z])[${ spaces },]*((-?\\\\d*\\\\.?\\\\d*(?:e[\\\\-+]?\\\\d+)?[${ spaces }]*,?[${ spaces }]*)+)`, 'ig')\n const pathValues = new RegExp(`(-?\\\\d*\\\\.?\\\\d*(?:e[\\\\-+]?\\\\d+)?)[${ spaces }]*,?[${ spaces }]*`, 'ig')\n\n const math = Math\n const {PI} = math\n const {sin} = math\n const {cos} = math\n const {tan} = math\n const {asin} = math\n const {sqrt} = math\n const {abs} = math\n\n function q2c(x1, y1, ax, ay, x2, y2) {\n\n const _13 = 1 / 3\n const _23 = 2 / 3\n return [(_13 * x1) + (_23 * ax), (_13 * y1) + (_23 * ay), (_13 * x2) + (_23 * ax), (_13 * y2) + (_23 * ay), x2, y2]\n }\n\n function a2c(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {\n // for more information of where this math came from visit:\n // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes\n\n const _120 = (PI * 120) / 180\n const rad = (PI / 180) * (+angle || 0)\n let res = []\n let xy\n\n const rotate = function(x, y, rad) {\n\n const X = (x * cos(rad)) - (y * sin(rad))\n const Y = (x * sin(rad)) + (y * cos(rad))\n return { x: X, y: Y }\n }\n\n if (!recursive) {\n xy = rotate(x1, y1, -rad)\n x1 = xy.x\n y1 = xy.y\n\n xy = rotate(x2, y2, -rad)\n x2 = xy.x\n y2 = xy.y\n\n const x = (x1 - x2) / 2\n const y = (y1 - y2) / 2\n let h = ((x * x) / (rx * rx)) + ((y * y) / (ry * ry))\n\n if (h > 1) {\n h = sqrt(h)\n rx = h * rx\n ry = h * ry\n }\n\n const rx2 = rx * rx\n const ry2 = ry * ry\n\n const k = ((large_arc_flag == sweep_flag) ? -1 : 1) * sqrt(abs(((rx2 * ry2) - (rx2 * y * y) - (ry2 * x * x)) / ((rx2 * y * y) + (ry2 * x * x))))\n\n var cx = ((k * rx * y) / ry) + ((x1 + x2) / 2)\n var cy = ((k * -ry * x) / rx) + ((y1 + y2) / 2)\n\n var f1 = asin(((y1 - cy) / ry).toFixed(9))\n var f2 = asin(((y2 - cy) / ry).toFixed(9))\n\n f1 = ((x1 < cx) ? (PI - f1) : f1)\n f2 = ((x2 < cx) ? (PI - f2) : f2)\n\n if (f1 < 0) f1 = (PI * 2) + f1\n if (f2 < 0) f2 = (PI * 2) + f2\n\n if ((sweep_flag && f1) > f2) f1 -= (PI * 2)\n if ((!sweep_flag && f2) > f1) f2 -= (PI * 2)\n\n } else {\n f1 = recursive[0]\n f2 = recursive[1]\n cx = recursive[2]\n cy = recursive[3]\n }\n\n let df = f2 - f1\n\n if (abs(df) > _120) {\n const f2old = f2\n const x2old = x2\n const y2old = y2\n\n f2 = f1 + (_120 * (((sweep_flag && f2) > f1) ? 1 : -1))\n x2 = cx + (rx * cos(f2))\n y2 = cy + (ry * sin(f2))\n\n res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy])\n }\n\n df = f2 - f1\n\n const c1 = cos(f1)\n const s1 = sin(f1)\n const c2 = cos(f2)\n const s2 = sin(f2)\n\n const t = tan(df / 4)\n\n const hx = (4 / 3) * (rx * t)\n const hy = (4 / 3) * (ry * t)\n\n const m1 = [x1, y1]\n const m2 = [x1 + (hx * s1), y1 - (hy * c1)]\n const m3 = [x2 + (hx * s2), y2 - (hy * c2)]\n const m4 = [x2, y2]\n\n m2[0] = (2 * m1[0]) - m2[0]\n m2[1] = (2 * m1[1]) - m2[1]\n\n if (recursive) {\n return [m2, m3, m4].concat(res)\n\n } else {\n res = [m2, m3, m4].concat(res).join().split(',')\n\n const newres = []\n const ii = res.length\n for (let i = 0; i < ii; i++) {\n newres[i] = (i % 2) ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x\n }\n\n return newres\n }\n }\n\n function parsePathString(pathString) {\n\n if (!pathString) return null\n\n const paramCounts = { a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0 }\n const data = []\n\n String(pathString).replace(pathCommand, function(a, b, c) {\n\n const params = []\n let name = b.toLowerCase()\n c.replace(pathValues, function(a, b) {\n if (b) params.push(+b)\n })\n\n if ((name === 'm') && (params.length > 2)) {\n data.push([b].concat(params.splice(0, 2)))\n name = 'l'\n b = ((b === 'm') ? 'l' : 'L')\n }\n\n while (params.length >= paramCounts[name]) {\n data.push([b].concat(params.splice(0, paramCounts[name])))\n if (!paramCounts[name]) break\n }\n })\n\n return data\n }\n\n function pathToAbsolute(pathArray) {\n\n if (!Array.isArray(pathArray) || !Array.isArray(pathArray && pathArray[0])) { // rough assumption\n pathArray = parsePathString(pathArray)\n }\n\n // if invalid string, return 'M 0 0'\n if (!pathArray || !pathArray.length) return [['M', 0, 0]]\n\n const res = []\n let x = 0\n let y = 0\n let mx = 0\n let my = 0\n const start = 0\n let pa0\n\n const ii = pathArray.length\n for (let i = start; i < ii; i++) {\n\n const r = []\n res.push(r)\n\n const pa = pathArray[i]\n pa0 = pa[0]\n\n if (pa0 != pa0.toUpperCase()) {\n r[0] = pa0.toUpperCase()\n\n var jj\n var j\n switch (r[0]) {\n case 'A':\n r[1] = pa[1]\n r[2] = pa[2]\n r[3] = pa[3]\n r[4] = pa[4]\n r[5] = pa[5]\n r[6] = +pa[6] + x\n r[7] = +pa[7] + y\n break\n\n case 'V':\n r[1] = +pa[1] + y\n break\n\n case 'H':\n r[1] = +pa[1] + x\n break\n\n case 'M':\n mx = +pa[1] + x\n my = +pa[2] + y\n\n jj = pa.length\n for (j = 1; j < jj; j++) {\n r[j] = +pa[j] + ((j % 2) ? x : y)\n }\n break\n\n default:\n jj = pa.length\n for (j = 1; j < jj; j++) {\n r[j] = +pa[j] + ((j % 2) ? x : y)\n }\n break\n }\n } else {\n const kk = pa.length\n for (let k = 0; k < kk; k++) {\n r[k] = pa[k]\n }\n }\n\n switch (r[0]) {\n case 'Z':\n x = +mx\n y = +my\n break\n\n case 'H':\n x = r[1]\n break\n\n case 'V':\n y = r[1]\n break\n\n case 'M':\n mx = r[r.length - 2]\n my = r[r.length - 1]\n x = r[r.length - 2]\n y = r[r.length - 1]\n break\n\n default:\n x = r[r.length - 2]\n y = r[r.length - 1]\n break\n }\n }\n\n return res\n }\n\n function normalize(path) {\n\n const p = pathToAbsolute(path)\n const attrs = { x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null }\n\n function processPath(path, d, pcom) {\n\n let nx; let ny\n\n if (!path) return ['C', d.x, d.y, d.x, d.y, d.x, d.y]\n\n if (!(path[0] in { T: 1, Q: 1 })) {\n d.qx = null\n d.qy = null\n }\n\n switch (path[0]) {\n case 'M':\n d.X = path[1]\n d.Y = path[2]\n break\n\n case 'A':\n path = ['C'].concat(a2c.apply(0, [d.x, d.y].concat(path.slice(1))))\n break\n\n case 'S':\n if (pcom === 'C' || pcom === 'S') { // In 'S' case we have to take into account, if the previous command is C/S.\n nx = (d.x * 2) - d.bx // And reflect the previous\n ny = (d.y * 2) - d.by // command's control point relative to the current point.\n }\n else { // or some else or nothing\n nx = d.x\n ny = d.y\n }\n path = ['C', nx, ny].concat(path.slice(1))\n break\n\n case 'T':\n if (pcom === 'Q' || pcom === 'T') { // In 'T' case we have to take into account, if the previous command is Q/T.\n d.qx = (d.x * 2) - d.qx // And make a reflection similar\n d.qy = (d.y * 2) - d.qy // to case 'S'.\n }\n else { // or something else or nothing\n d.qx = d.x\n d.qy = d.y\n }\n path = ['C'].concat(q2c(d.x, d.y, d.qx, d.qy, path[1], path[2]))\n break\n\n case 'Q':\n d.qx = path[1]\n d.qy = path[2]\n path = ['C'].concat(q2c(d.x, d.y, path[1], path[2], path[3], path[4]))\n break\n\n case 'H':\n path = ['L'].concat(path[1], d.y)\n break\n\n case 'V':\n path = ['L'].concat(d.x, path[1])\n break\n\n // leave 'L' & 'Z' commands as they were:\n\n case 'L':\n break\n\n case 'Z':\n break\n }\n\n return path\n }\n\n function fixArc(pp, i) {\n\n if (pp[i].length > 7) {\n\n pp[i].shift()\n const pi = pp[i]\n\n while (pi.length) {\n pcoms[i] = 'A' // if created multiple 'C's, their original seg is saved\n pp.splice(i++, 0, ['C'].concat(pi.splice(0, 6)))\n }\n\n pp.splice(i, 1)\n ii = p.length\n }\n }\n\n var pcoms = [] // path commands of original path p\n let pfirst = '' // temporary holder for original path command\n let pcom = '' // holder for previous path command of original path\n\n var ii = p.length\n for (let i = 0; i < ii; i++) {\n if (p[i]) pfirst = p[i][0] // save current path command\n\n if (pfirst !== 'C') { // C is not saved yet, because it may be result of conversion\n pcoms[i] = pfirst // Save current path command\n if (i > 0) pcom = pcoms[i - 1] // Get previous path command pcom\n }\n\n p[i] = processPath(p[i], attrs, pcom) // Previous path command is inputted to processPath\n\n if (pcoms[i] !== 'A' && pfirst === 'C') pcoms[i] = 'C' // 'A' is the only command\n // which may produce multiple 'C's\n // so we have to make sure that 'C' is also 'C' in original path\n\n fixArc(p, i) // fixArc adds also the right amount of 'A's to pcoms\n\n const seg = p[i]\n const seglen = seg.length\n\n attrs.x = seg[seglen - 2]\n attrs.y = seg[seglen - 1]\n\n attrs.bx = parseFloat(seg[seglen - 4]) || attrs.x\n attrs.by = parseFloat(seg[seglen - 3]) || attrs.y\n }\n\n // make sure normalized path data string starts with an M segment\n if (!p[0][0] || p[0][0] !== 'M') {\n p.unshift(['M', 0, 0])\n }\n\n return p\n }\n\n return function(pathData) {\n return normalize(pathData).join(',').split(',').join(' ')\n }\n })()\n\n V.namespace = ns\n\n return V\n\n})()\n\n// Global namespace.\n\nvar joint = {\n\n version: '2.1.4',\n\n config: {\n // The class name prefix config is for advanced use only.\n // Be aware that if you change the prefix, the JointJS CSS will no longer function properly.\n classNamePrefix: 'joint-',\n defaultTheme: 'default'\n },\n\n // `joint.dia` namespace.\n dia: {},\n\n // `joint.ui` namespace.\n ui: {},\n\n // `joint.layout` namespace.\n layout: {},\n\n // `joint.shapes` namespace.\n shapes: {},\n\n // `joint.format` namespace.\n format: {},\n\n // `joint.connectors` namespace.\n connectors: {},\n\n // `joint.highlighters` namespace.\n highlighters: {},\n\n // `joint.routers` namespace.\n routers: {},\n\n // `joint.anchors` namespace.\n anchors: {},\n\n // `joint.connectionPoints` namespace.\n connectionPoints: {},\n\n // `joint.connectionStrategies` namespace.\n connectionStrategies: {},\n\n // `joint.linkTools` namespace.\n linkTools: {},\n\n // `joint.mvc` namespace.\n mvc: {\n views: {}\n },\n\n setTheme: function(theme, opt) {\n\n opt = opt || {}\n\n joint.util.invoke(joint.mvc.views, 'setTheme', theme, opt)\n\n // Update the default theme on the view prototype.\n joint.mvc.View.prototype.defaultTheme = theme\n },\n\n // `joint.env` namespace.\n env: {\n\n _results: {},\n\n _tests: {\n\n svgforeignobject: function() {\n return !!document.createElementNS &&\n /SVGForeignObject/.test(({}).toString.call(document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject')))\n }\n },\n\n addTest: function(name, fn) {\n\n return joint.env._tests[name] = fn\n },\n\n test: function(name) {\n\n const fn = joint.env._tests[name]\n\n if (!fn) {\n throw new Error(`Test not defined (\"${ name }\"). Use \\`joint.env.addTest(name, fn) to add a new test.\\``)\n }\n\n let result = joint.env._results[name]\n\n if (typeof result !== 'undefined') {\n return result\n }\n\n try {\n result = fn()\n } catch (error) {\n result = false\n }\n\n // Cache the test result.\n joint.env._results[name] = result\n\n return result\n }\n },\n\n util: {\n\n // Return a simple hash code from a string. See http://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/.\n hashCode: function(str) {\n\n let hash = 0\n if (str.length == 0) return hash\n for (let i = 0; i < str.length; i++) {\n const c = str.charCodeAt(i)\n hash = ((hash << 5) - hash) + c\n hash &= hash // Convert to 32bit integer\n }\n return hash\n },\n\n getByPath: function(obj, path, delim) {\n\n const keys = Array.isArray(path) ? path.slice() : path.split(delim || '/')\n let key\n\n while (keys.length) {\n key = keys.shift()\n if (Object(obj) === obj && key in obj) {\n obj = obj[key]\n } else {\n return undefined\n }\n }\n return obj\n },\n\n setByPath: function(obj, path, value, delim) {\n\n const keys = Array.isArray(path) ? path : path.split(delim || '/')\n\n let diver = obj\n let i = 0\n\n for (var len = keys.length; i < len - 1; i++) {\n // diver creates an empty object if there is no nested object under such a key.\n // This means that one can populate an empty nested object with setByPath().\n diver = diver[keys[i]] || (diver[keys[i]] = {})\n }\n diver[keys[len - 1]] = value\n\n return obj\n },\n\n unsetByPath: function(obj, path, delim) {\n\n delim = delim || '/'\n\n const pathArray = Array.isArray(path) ? path.slice() : path.split(delim)\n\n const propertyToRemove = pathArray.pop()\n if (pathArray.length > 0) {\n\n // unsetting a nested attribute\n const parent = joint.util.getByPath(obj, pathArray, delim)\n\n if (parent) {\n delete parent[propertyToRemove]\n }\n\n } else {\n\n // unsetting a primitive attribute\n delete obj[propertyToRemove]\n }\n\n return obj\n },\n\n flattenObject: function(obj, delim, stop) {\n\n delim = delim || '/'\n const ret = {}\n\n for (const key in obj) {\n\n if (!obj.hasOwnProperty(key)) continue\n\n let shouldGoDeeper = typeof obj[key] === 'object'\n if (shouldGoDeeper && stop && stop(obj[key])) {\n shouldGoDeeper = false\n }\n\n if (shouldGoDeeper) {\n\n const flatObject = this.flattenObject(obj[key], delim, stop)\n\n for (const flatKey in flatObject) {\n if (!flatObject.hasOwnProperty(flatKey)) continue\n ret[key + delim + flatKey] = flatObject[flatKey]\n }\n\n } else {\n\n ret[key] = obj[key]\n }\n }\n\n return ret\n },\n\n uuid: function() {\n\n // credit: http://stackoverflow.com/posts/2117523/revisions\n\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n const r = Math.random() * 16|0\n const v = c == 'x' ? r : (r&0x3|0x8)\n return v.toString(16)\n })\n },\n\n // Generate global unique id for obj and store it as a property of the object.\n guid: function(obj) {\n\n this.guid.id = this.guid.id || 1\n obj.id = (obj.id === undefined ? `j_${ this.guid.id++}` : obj.id)\n return obj.id\n },\n\n toKebabCase: function(string) {\n\n return string.replace(/[A-Z]/g, '-$&').toLowerCase()\n },\n\n normalizeEvent: function(evt) {\n\n const touchEvt = evt.originalEvent && evt.originalEvent.changedTouches && evt.originalEvent.changedTouches[0]\n if (touchEvt) {\n for (const property in evt) {\n // copy all the properties from the input event that are not\n // defined on the touch event (functions included).\n if (touchEvt[property] === undefined) {\n touchEvt[property] = evt[property]\n }\n }\n return touchEvt\n }\n\n return evt\n },\n\n nextFrame: (function() {\n\n let raf\n\n if (typeof window !== 'undefined') {\n\n raf = window.requestAnimationFrame ||\n window.webkitRequestAnimationFrame ||\n window.mozRequestAnimationFrame ||\n window.oRequestAnimationFrame ||\n window.msRequestAnimationFrame\n }\n\n if (!raf) {\n\n let lastTime = 0\n\n raf = function(callback) {\n\n const currTime = new Date().getTime()\n const timeToCall = Math.max(0, 16 - (currTime - lastTime))\n const id = setTimeout(function() { callback(currTime + timeToCall) }, timeToCall)\n\n lastTime = currTime + timeToCall\n\n return id\n }\n }\n\n return function(callback, context) {\n return context\n ? raf(callback.bind(context))\n : raf(callback)\n }\n\n })(),\n\n cancelFrame: (function() {\n\n let caf\n const client = typeof window !== 'undefined'\n\n if (client) {\n\n caf = window.cancelAnimationFrame ||\n window.webkitCancelAnimationFrame ||\n window.webkitCancelRequestAnimationFrame ||\n window.msCancelAnimationFrame ||\n window.msCancelRequestAnimationFrame ||\n window.oCancelAnimationFrame ||\n window.oCancelRequestAnimationFrame ||\n window.mozCancelAnimationFrame ||\n window.mozCancelRequestAnimationFrame\n }\n\n caf = caf || clearTimeout\n\n return client ? caf.bind(window) : caf\n\n })(),\n\n // ** Deprecated **\n shapePerimeterConnectionPoint: function(linkView, view, magnet, reference) {\n\n let bbox\n let spot\n\n if (!magnet) {\n\n // There is no magnet, try to make the best guess what is the\n // wrapping SVG element. This is because we want this \"smart\"\n // connection points to work out of the box without the\n // programmer to put magnet marks to any of the subelements.\n // For example, we want the functoin to work on basic.Path elements\n // without any special treatment of such elements.\n // The code below guesses the wrapping element based on\n // one simple assumption. The wrapping elemnet is the\n // first child of the scalable group if such a group exists\n // or the first child of the rotatable group if not.\n // This makese sense because usually the wrapping element\n // is below any other sub element in the shapes.\n const scalable = view.$('.scalable')[0]\n const rotatable = view.$('.rotatable')[0]\n\n if (scalable && scalable.firstChild) {\n\n magnet = scalable.firstChild\n\n } else if (rotatable && rotatable.firstChild) {\n\n magnet = rotatable.firstChild\n }\n }\n\n if (magnet) {\n\n spot = V(magnet).findIntersection(reference, linkView.paper.viewport)\n if (!spot) {\n bbox = V(magnet).getBBox({ target: linkView.paper.viewport })\n }\n\n } else {\n\n bbox = view.model.getBBox()\n spot = bbox.intersectionWithLineFromCenterToPoint(reference)\n }\n return spot || bbox.center()\n },\n\n isPercentage: function(val) {\n\n return joint.util.isString(val) && val.slice(-1) === '%'\n },\n\n parseCssNumeric: function(strValue, restrictUnits) {\n\n restrictUnits = restrictUnits || []\n const cssNumeric = { value: parseFloat(strValue) }\n\n if (Number.isNaN(cssNumeric.value)) {\n return null\n }\n\n const validUnitsExp = restrictUnits.join('|')\n\n if (joint.util.isString(strValue)) {\n const matches = new RegExp(`(\\\\d+)(${ validUnitsExp })$`).exec(strValue)\n if (!matches) {\n return null\n }\n if (matches[2]) {\n cssNumeric.unit = matches[2]\n }\n }\n return cssNumeric\n },\n\n breakText: function(text, size, styles, opt) {\n\n opt = opt || {}\n styles = styles || {}\n\n const {width} = size\n const {height} = size\n\n const svgDocument = opt.svgDocument || V('svg').node\n const textSpan = V('tspan').node\n const textElement = V('text').attr(styles).append(textSpan).node\n const textNode = document.createTextNode('')\n\n // Prevent flickering\n textElement.style.opacity = 0\n // Prevent FF from throwing an uncaught exception when `getBBox()`\n // called on element that is not in the render tree (is not measurable).\n // .getComputedTextLength() returns always 0 in this case.\n // Note that the `textElement` resp. `textSpan` can become hidden\n // when it's appended to the DOM and a `display: none` CSS stylesheet\n // rule gets applied.\n textElement.style.display = 'block'\n textSpan.style.display = 'block'\n\n textSpan.appendChild(textNode)\n svgDocument.appendChild(textElement)\n\n if (!opt.svgDocument) {\n\n document.body.appendChild(svgDocument)\n }\n\n const separator = opt.separator || ' '\n const eol = opt.eol || '\\n'\n\n const words = text.split(separator)\n const full = []\n let lines = []\n let p\n let lineHeight\n\n for (let i = 0, l = 0, len = words.length; i < len; i++) {\n\n const word = words[i]\n\n if (!word) continue\n\n if (eol && word.indexOf(eol) >= 0) {\n // word cotains end-of-line character\n if (word.length > 1) {\n // separate word and continue cycle\n const eolWords = word.split(eol)\n for (let j = 0, jl = eolWords.length - 1; j < jl; j++) {\n eolWords.splice(2 * j + 1, 0, eol)\n }\n Array.prototype.splice.apply(words, [i, 1].concat(eolWords))\n i--\n len += eolWords.length - 1\n } else {\n // creates new line\n l++\n }\n continue\n }\n\n\n textNode.data = lines[l] ? `${lines[l] } ${ word}` : word\n\n if (textSpan.getComputedTextLength() <= width) {\n\n // the current line fits\n lines[l] = textNode.data\n\n if (p) {\n // We were partitioning. Put rest of the word onto next line\n full[l++] = true\n\n // cancel partitioning\n p = 0\n }\n\n } else {\n\n if (!lines[l] || p) {\n\n const partition = !!p\n\n p = word.length - 1\n\n if (partition || !p) {\n\n // word has only one character.\n if (!p) {\n\n if (!lines[l]) {\n\n // we won't fit this text within our rect\n lines = []\n\n break\n }\n\n // partitioning didn't help on the non-empty line\n // try again, but this time start with a new line\n\n // cancel partitions created\n words.splice(i, 2, word + words[i + 1])\n\n // adjust word length\n len--\n\n full[l++] = true\n i--\n\n continue\n }\n\n // move last letter to the beginning of the next word\n words[i] = word.substring(0, p)\n words[i + 1] = word.substring(p) + words[i + 1]\n\n } else {\n\n // We initiate partitioning\n // split the long word into two words\n words.splice(i, 1, word.substring(0, p), word.substring(p))\n\n // adjust words length\n len++\n\n if (l && !full[l - 1]) {\n // if the previous line is not full, try to fit max part of\n // the current word there\n l--\n }\n }\n\n i--\n\n continue\n }\n\n l++\n i--\n }\n\n // if size.height is defined we have to check whether the height of the entire\n // text exceeds the rect height\n if (height !== undefined) {\n\n if (lineHeight === undefined) {\n\n var heightValue\n\n // use the same defaults as in V.prototype.text\n if (styles.lineHeight === 'auto') {\n heightValue = { value: 1.5, unit: 'em' }\n } else {\n heightValue = joint.util.parseCssNumeric(styles.lineHeight, ['em']) || { value: 1, unit: 'em' }\n }\n\n lineHeight = heightValue.value\n if (heightValue.unit === 'em' ) {\n lineHeight *= textElement.getBBox().height\n }\n }\n\n if (lineHeight * lines.length > height) {\n\n // remove overflowing lines\n lines.splice(Math.floor(height / lineHeight))\n\n break\n }\n }\n }\n\n if (opt.svgDocument) {\n\n // svg document was provided, remove the text element only\n svgDocument.removeChild(textElement)\n\n } else {\n\n // clean svg document\n document.body.removeChild(svgDocument)\n }\n\n return lines.join(eol)\n },\n\n // Sanitize HTML\n // Based on https://gist.github.com/ufologist/5a0da51b2b9ef1b861c30254172ac3c9\n // Parses a string into an array of DOM nodes.\n // Then outputs it back as a string.\n sanitizeHTML: function(html) {\n\n // Ignores tags that are invalid inside a
tag (e.g. , )\n\n // If documentContext (second parameter) is not specified or given as `null` or `undefined`, a new document is used.\n // Inline events will not execute when the HTML is parsed; this includes, for example, sending GET requests for images.\n\n // If keepScripts (last parameter) is `false`, scripts are not executed.\n const output = $($.parseHTML(`
${ html }
`, null, false))\n\n output.find('*').each(function() { // for all nodes\n const currentNode = this\n\n $.each(currentNode.attributes, function() { // for all attributes in each node\n const currentAttribute = this\n\n const attrName = currentAttribute.name\n const attrValue = currentAttribute.value\n\n // Remove attribute names that start with \"on\" (e.g. onload, onerror...).\n // Remove attribute values that start with \"javascript:\" pseudo protocol (e.g. `href=\"javascript:alert(1)\"`).\n if (attrName.indexOf('on') === 0 || attrValue.indexOf('javascript:') === 0) {\n $(currentNode).removeAttr(attrName)\n }\n })\n })\n\n return output.html()\n },\n\n // Download `blob` as file with `fileName`.\n // Does not work in IE9.\n downloadBlob: function(blob, fileName) {\n\n if (window.navigator.msSaveBlob) { // requires IE 10+\n // pulls up a save dialog\n window.navigator.msSaveBlob(blob, fileName)\n\n } else { // other browsers\n // downloads directly in Chrome and Safari\n\n // presents a save/open dialog in Firefox\n // Firefox bug: `from` field in save dialog always shows `from:blob:`\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1053327\n\n const url = window.URL.createObjectURL(blob)\n const link = document.createElement('a')\n\n link.href = url\n link.download = fileName\n document.body.appendChild(link)\n\n link.click()\n\n document.body.removeChild(link)\n window.URL.revokeObjectURL(url) // mark the url for garbage collection\n }\n },\n\n // Download `dataUri` as file with `fileName`.\n // Does not work in IE9.\n downloadDataUri: function(dataUri, fileName) {\n\n const blob = joint.util.dataUriToBlob(dataUri)\n joint.util.downloadBlob(blob, fileName)\n },\n\n // Convert an uri-encoded data component (possibly also base64-encoded) to a blob.\n dataUriToBlob: function(dataUri) {\n\n // first, make sure there are no newlines in the data uri\n dataUri = dataUri.replace(/\\s/g, '')\n dataUri = decodeURIComponent(dataUri)\n\n const firstCommaIndex = dataUri.indexOf(',') // split dataUri as `dataTypeString`,`data`\n\n const dataTypeString = dataUri.slice(0, firstCommaIndex) // e.g. 'data:image/jpeg;base64'\n const mimeString = dataTypeString.split(':')[1].split(';')[0] // e.g. 'image/jpeg'\n\n const data = dataUri.slice(firstCommaIndex + 1)\n let decodedString\n if (dataTypeString.indexOf('base64') >= 0) { // data may be encoded in base64\n decodedString = atob(data) // decode data\n } else {\n // convert the decoded string to UTF-8\n decodedString = unescape(encodeURIComponent(data))\n }\n // write the bytes of the string to a typed array\n const ia = new Uint8Array(decodedString.length)\n for (let i = 0; i < decodedString.length; i++) {\n ia[i] = decodedString.charCodeAt(i)\n }\n\n return new Blob([ia], { type: mimeString }) // return the typed array as Blob\n },\n\n // Read an image at `url` and return it as base64-encoded data uri.\n // The mime type of the image is inferred from the `url` file extension.\n // If data uri is provided as `url`, it is returned back unchanged.\n // `callback` is a method with `err` as first argument and `dataUri` as second argument.\n // Works with IE9.\n imageToDataUri: function(url, callback) {\n\n if (!url || url.substr(0, 'data:'.length) === 'data:') {\n // No need to convert to data uri if it is already in data uri.\n\n // This not only convenient but desired. For example,\n // IE throws a security error if data:image/svg+xml is used to render\n // an image to the canvas and an attempt is made to read out data uri.\n // Now if our image is already in data uri, there is no need to render it to the canvas\n // and so we can bypass this error.\n\n // Keep the async nature of the function.\n return setTimeout(function() {\n callback(null, url)\n }, 0)\n }\n\n // chrome, IE10+\n const modernHandler = function(xhr, callback) {\n\n if (xhr.status === 200) {\n\n const reader = new FileReader()\n\n reader.onload = function(evt) {\n const dataUri = evt.target.result\n callback(null, dataUri)\n }\n\n reader.onerror = function() {\n callback(new Error(`Failed to load image ${ url}`))\n }\n\n reader.readAsDataURL(xhr.response)\n } else {\n callback(new Error(`Failed to load image ${ url}`))\n }\n }\n\n const legacyHandler = function(xhr, callback) {\n\n const Uint8ToString = function(u8a) {\n const CHUNK_SZ = 0x8000\n const c = []\n for (let i = 0; i < u8a.length; i += CHUNK_SZ) {\n c.push(String.fromCharCode.apply(null, u8a.subarray(i, i + CHUNK_SZ)))\n }\n return c.join('')\n }\n\n if (xhr.status === 200) {\n\n const bytes = new Uint8Array(xhr.response)\n\n const suffix = (url.split('.').pop()) || 'png'\n const map = {\n svg: 'svg+xml'\n }\n const meta = `data:image/${ map[suffix] || suffix };base64,`\n const b64encoded = meta + btoa(Uint8ToString(bytes))\n callback(null, b64encoded)\n } else {\n callback(new Error(`Failed to load image ${ url}`))\n }\n }\n\n const xhr = new XMLHttpRequest()\n\n xhr.open('GET', url, true)\n xhr.addEventListener('error', function() {\n callback(new Error(`Failed to load image ${ url}`))\n })\n\n xhr.responseType = window.FileReader ? 'blob' : 'arraybuffer'\n\n xhr.addEventListener('load', function() {\n if (window.FileReader) {\n modernHandler(xhr, callback)\n } else {\n legacyHandler(xhr, callback)\n }\n })\n\n xhr.send()\n },\n\n getElementBBox: function(el) {\n\n const $el = $(el)\n if ($el.length === 0) {\n throw new Error('Element not found')\n }\n\n const element = $el[0]\n const doc = element.ownerDocument\n const clientBBox = element.getBoundingClientRect()\n\n let strokeWidthX = 0\n let strokeWidthY = 0\n\n // Firefox correction\n if (element.ownerSVGElement) {\n\n const vel = V(element)\n const bbox = vel.getBBox({ target: vel.svg() })\n\n // if FF getBoundingClientRect includes stroke-width, getBBox doesn't.\n // To unify this across all browsers we need to adjust the final bBox with `stroke-width` value.\n strokeWidthX = (clientBBox.width - bbox.width)\n strokeWidthY = (clientBBox.height - bbox.height)\n }\n\n return {\n x: clientBBox.left + window.pageXOffset - doc.documentElement.offsetLeft + strokeWidthX / 2,\n y: clientBBox.top + window.pageYOffset - doc.documentElement.offsetTop + strokeWidthY / 2,\n width: clientBBox.width - strokeWidthX,\n height: clientBBox.height - strokeWidthY\n }\n },\n\n\n // Highly inspired by the jquery.sortElements plugin by Padolsey.\n // See http://james.padolsey.com/javascript/sorting-elements-with-jquery/.\n sortElements: function(elements, comparator) {\n\n const $elements = $(elements)\n const placements = $elements.map(function() {\n\n const sortElement = this\n const {parentNode} = sortElement\n // Since the element itself will change position, we have\n // to have some way of storing it's original position in\n // the DOM. The easiest way is to have a 'flag' node:\n const nextSibling = parentNode.insertBefore(document.createTextNode(''), sortElement.nextSibling)\n\n return function() {\n\n if (parentNode === this) {\n throw new Error('You can\\'t sort elements if any one is a descendant of another.')\n }\n\n // Insert before flag:\n parentNode.insertBefore(this, nextSibling)\n // Remove flag:\n parentNode.removeChild(nextSibling)\n }\n })\n\n return Array.prototype.sort.call($elements, comparator).each(function(i) {\n placements[i].call(this)\n })\n },\n\n // Sets attributes on the given element and its descendants based on the selector.\n // `attrs` object: { [SELECTOR1]: { attrs1 }, [SELECTOR2]: { attrs2}, ... } e.g. { 'input': { color : 'red' }}\n setAttributesBySelector: function(element, attrs) {\n\n const $element = $(element)\n\n joint.util.forIn(attrs, function(attrs, selector) {\n const $elements = $element.find(selector).addBack().filter(selector)\n // Make a special case for setting classes.\n // We do not want to overwrite any existing class.\n if (joint.util.has(attrs, 'class')) {\n $elements.addClass(attrs.class)\n attrs = joint.util.omit(attrs, 'class')\n }\n $elements.attr(attrs)\n })\n },\n\n // Return a new object with all four sides (top, right, bottom, left) in it.\n // Value of each side is taken from the given argument (either number or object).\n // Default value for a side is 0.\n // Examples:\n // joint.util.normalizeSides(5) --> { top: 5, right: 5, bottom: 5, left: 5 }\n // joint.util.normalizeSides({ horizontal: 5 }) --> { top: 0, right: 5, bottom: 0, left: 5 }\n // joint.util.normalizeSides({ left: 5 }) --> { top: 0, right: 0, bottom: 0, left: 5 }\n // joint.util.normalizeSides({ horizontal: 10, left: 5 }) --> { top: 0, right: 10, bottom: 0, left: 5 }\n // joint.util.normalizeSides({ horizontal: 0, left: 5 }) --> { top: 0, right: 0, bottom: 0, left: 5 }\n normalizeSides: function(box) {\n\n if (Object(box) !== box) { // `box` is not an object\n let val = 0 // `val` left as 0 if `box` cannot be understood as finite number\n if (isFinite(box)) val = +box // actually also accepts string numbers (e.g. '100')\n\n return { top: val, right: val, bottom: val, left: val }\n }\n\n // `box` is an object\n let top; let right; let bottom; let left\n top = right = bottom = left = 0\n\n if (isFinite(box.vertical)) top = bottom = +box.vertical\n if (isFinite(box.horizontal)) right = left = +box.horizontal\n\n if (isFinite(box.top)) top = +box.top // overwrite vertical\n if (isFinite(box.right)) right = +box.right // overwrite horizontal\n if (isFinite(box.bottom)) bottom = +box.bottom // overwrite vertical\n if (isFinite(box.left)) left = +box.left // overwrite horizontal\n\n return { top: top, right: right, bottom: bottom, left: left }\n },\n\n timing: {\n\n linear: function(t) {\n return t\n },\n\n quad: function(t) {\n return t * t\n },\n\n cubic: function(t) {\n return t * t * t\n },\n\n inout: function(t) {\n if (t <= 0) return 0\n if (t >= 1) return 1\n const t2 = t * t\n const t3 = t2 * t\n return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75)\n },\n\n exponential: function(t) {\n return Math.pow(2, 10 * (t - 1))\n },\n\n bounce: function(t) {\n for (let a = 0, b = 1; 1; a += b, b /= 2) {\n if (t >= (7 - 4 * a) / 11) {\n const q = (11 - 6 * a - 11 * t) / 4\n return -q * q + b * b\n }\n }\n },\n\n reverse: function(f) {\n return function(t) {\n return 1 - f(1 - t)\n }\n },\n\n reflect: function(f) {\n return function(t) {\n return .5 * (t < .5 ? f(2 * t) : (2 - f(2 - 2 * t)))\n }\n },\n\n clamp: function(f, n, x) {\n n = n || 0\n x = x || 1\n return function(t) {\n const r = f(t)\n return r < n ? n : r > x ? x : r\n }\n },\n\n back: function(s) {\n if (!s) s = 1.70158\n return function(t) {\n return t * t * ((s + 1) * t - s)\n }\n },\n\n elastic: function(x) {\n if (!x) x = 1.5\n return function(t) {\n return Math.pow(2, 10 * (t - 1)) * Math.cos(20 * Math.PI * x / 3 * t)\n }\n }\n },\n\n interpolate: {\n\n number: function(a, b) {\n const d = b - a\n return function(t) { return a + d * t }\n },\n\n object: function(a, b) {\n const s = Object.keys(a)\n return function(t) {\n let i; let p\n const r = {}\n for (i = s.length - 1; i != -1; i--) {\n p = s[i]\n r[p] = a[p] + (b[p] - a[p]) * t\n }\n return r\n }\n },\n\n hexColor: function(a, b) {\n\n const ca = parseInt(a.slice(1), 16)\n const cb = parseInt(b.slice(1), 16)\n const ra = ca & 0x0000ff\n const rd = (cb & 0x0000ff) - ra\n const ga = ca & 0x00ff00\n const gd = (cb & 0x00ff00) - ga\n const ba = ca & 0xff0000\n const bd = (cb & 0xff0000) - ba\n\n return function(t) {\n\n const r = (ra + rd * t) & 0x000000ff\n const g = (ga + gd * t) & 0x0000ff00\n const b = (ba + bd * t) & 0x00ff0000\n\n return `#${ (1 << 24 | r | g | b ).toString(16).slice(1)}`\n }\n },\n\n unit: function(a, b) {\n\n const r = /(-?[0-9]*.[0-9]*)(px|em|cm|mm|in|pt|pc|%)/\n const ma = r.exec(a)\n const mb = r.exec(b)\n const p = mb[1].indexOf('.')\n const f = p > 0 ? mb[1].length - p - 1 : 0\n a = +ma[1]\n const d = +mb[1] - a\n const u = ma[2]\n\n return function(t) {\n return (a + d * t).toFixed(f) + u\n }\n }\n },\n\n // SVG filters.\n // (values in parentheses are default values)\n filter: {\n\n // `color` ... outline color ('blue')\n // `width`... outline width (1)\n // `opacity` ... outline opacity (1)\n // `margin` ... gap between outline and the element (2)\n outline: function(args) {\n\n const tpl = ''\n\n const margin = Number.isFinite(args.margin) ? args.margin : 2\n const width = Number.isFinite(args.width) ? args.width : 1\n\n return joint.util.template(tpl)({\n color: args.color || 'blue',\n opacity: Number.isFinite(args.opacity) ? args.opacity : 1,\n outerRadius: margin + width,\n innerRadius: margin\n })\n },\n\n // `color` ... color ('red')\n // `width`... width (1)\n // `blur` ... blur (0)\n // `opacity` ... opacity (1)\n highlight: function(args) {\n\n const tpl = ''\n\n return joint.util.template(tpl)({\n color: args.color || 'red',\n width: Number.isFinite(args.width) ? args.width : 1,\n blur: Number.isFinite(args.blur) ? args.blur : 0,\n opacity: Number.isFinite(args.opacity) ? args.opacity : 1\n })\n },\n\n // `x` ... horizontal blur (2)\n // `y` ... vertical blur (optional)\n blur: function(args) {\n\n const x = Number.isFinite(args.x) ? args.x : 2\n\n return joint.util.template('')({\n stdDeviation: Number.isFinite(args.y) ? [x, args.y] : x\n })\n },\n\n // `dx` ... horizontal shift (0)\n // `dy` ... vertical shift (0)\n // `blur` ... blur (4)\n // `color` ... color ('black')\n // `opacity` ... opacity (1)\n dropShadow: function(args) {\n\n const tpl = 'SVGFEDropShadowElement' in window\n ? ''\n : ''\n\n return joint.util.template(tpl)({\n dx: args.dx || 0,\n dy: args.dy || 0,\n opacity: Number.isFinite(args.opacity) ? args.opacity : 1,\n color: args.color || 'black',\n blur: Number.isFinite(args.blur) ? args.blur : 4\n })\n },\n\n // `amount` ... the proportion of the conversion (1). A value of 1 (default) is completely grayscale. A value of 0 leaves the input unchanged.\n grayscale: function(args) {\n\n const amount = Number.isFinite(args.amount) ? args.amount : 1\n\n return joint.util.template('')({\n a: 0.2126 + 0.7874 * (1 - amount),\n b: 0.7152 - 0.7152 * (1 - amount),\n c: 0.0722 - 0.0722 * (1 - amount),\n d: 0.2126 - 0.2126 * (1 - amount),\n e: 0.7152 + 0.2848 * (1 - amount),\n f: 0.0722 - 0.0722 * (1 - amount),\n g: 0.2126 - 0.2126 * (1 - amount),\n h: 0.0722 + 0.9278 * (1 - amount)\n })\n },\n\n // `amount` ... the proportion of the conversion (1). A value of 1 (default) is completely sepia. A value of 0 leaves the input unchanged.\n sepia: function(args) {\n\n const amount = Number.isFinite(args.amount) ? args.amount : 1\n\n return joint.util.template('')({\n a: 0.393 + 0.607 * (1 - amount),\n b: 0.769 - 0.769 * (1 - amount),\n c: 0.189 - 0.189 * (1 - amount),\n d: 0.349 - 0.349 * (1 - amount),\n e: 0.686 + 0.314 * (1 - amount),\n f: 0.168 - 0.168 * (1 - amount),\n g: 0.272 - 0.272 * (1 - amount),\n h: 0.534 - 0.534 * (1 - amount),\n i: 0.131 + 0.869 * (1 - amount)\n })\n },\n\n // `amount` ... the proportion of the conversion (1). A value of 0 is completely un-saturated. A value of 1 (default) leaves the input unchanged.\n saturate: function(args) {\n\n const amount = Number.isFinite(args.amount) ? args.amount : 1\n\n return joint.util.template('')({\n amount: 1 - amount\n })\n },\n\n // `angle` ... the number of degrees around the color circle the input samples will be adjusted (0).\n hueRotate: function(args) {\n\n return joint.util.template('')({\n angle: args.angle || 0\n })\n },\n\n // `amount` ... the proportion of the conversion (1). A value of 1 (default) is completely inverted. A value of 0 leaves the input unchanged.\n invert: function(args) {\n\n const amount = Number.isFinite(args.amount) ? args.amount : 1\n\n return joint.util.template('')({\n amount: amount,\n amount2: 1 - amount\n })\n },\n\n // `amount` ... proportion of the conversion (1). A value of 0 will create an image that is completely black. A value of 1 (default) leaves the input unchanged.\n brightness: function(args) {\n\n return joint.util.template('')({\n amount: Number.isFinite(args.amount) ? args.amount : 1\n })\n },\n\n // `amount` ... proportion of the conversion (1). A value of 0 will create an image that is completely black. A value of 1 (default) leaves the input unchanged.\n contrast: function(args) {\n\n const amount = Number.isFinite(args.amount) ? args.amount : 1\n\n return joint.util.template('')({\n amount: amount,\n amount2: .5 - amount / 2\n })\n }\n },\n\n format: {\n\n // Formatting numbers via the Python Format Specification Mini-language.\n // See http://docs.python.org/release/3.1.3/library/string.html#format-specification-mini-language.\n // Heavilly inspired by the D3.js library implementation.\n number: function(specifier, value, locale) {\n\n locale = locale || {\n\n currency: ['$', ''],\n decimal: '.',\n thousands: ',',\n grouping: [3]\n }\n\n // See Python format specification mini-language: http://docs.python.org/release/3.1.3/library/string.html#format-specification-mini-language.\n // [[fill]align][sign][symbol][0][width][,][.precision][type]\n const re = /(?:([^{])?([<>=^]))?([+\\- ])?([$#])?(0)?(\\d+)?(,)?(\\.-?\\d+)?([a-z%])?/i\n\n const match = re.exec(specifier)\n let fill = match[1] || ' '\n let align = match[2] || '>'\n const sign = match[3] || ''\n const symbol = match[4] || ''\n let zfill = match[5]\n let width = +match[6]\n let comma = match[7]\n let precision = match[8]\n let type = match[9]\n let scale = 1\n let prefix = ''\n let suffix = ''\n let integer = false\n\n if (precision) precision = +precision.substring(1)\n\n if (zfill || fill === '0' && align === '=') {\n zfill = fill = '0'\n align = '='\n if (comma) width -= Math.floor((width - 1) / 4)\n }\n\n switch (type) {\n case 'n':\n comma = true; type = 'g'\n break\n case '%':\n scale = 100; suffix = '%'; type = 'f'\n break\n case 'p':\n scale = 100; suffix = '%'; type = 'r'\n break\n case 'b':\n case 'o':\n case 'x':\n case 'X':\n if (symbol === '#') prefix = `0${ type.toLowerCase()}`\n break\n case 'c':\n case 'd':\n integer = true; precision = 0\n break\n case 's':\n scale = -1; type = 'r'\n break\n }\n\n if (symbol === '$') {\n prefix = locale.currency[0]\n suffix = locale.currency[1]\n }\n\n // If no precision is specified for `'r'`, fallback to general notation.\n if (type == 'r' && !precision) type = 'g'\n\n // Ensure that the requested precision is in the supported range.\n if (precision != null) {\n if (type == 'g') precision = Math.max(1, Math.min(21, precision))\n else if (type == 'e' || type == 'f') precision = Math.max(0, Math.min(20, precision))\n }\n\n const zcomma = zfill && comma\n\n // Return the empty string for floats formatted as ints.\n if (integer && (value % 1)) return ''\n\n // Convert negative to positive, and record the sign prefix.\n let negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, '-') : sign\n\n let fullSuffix = suffix\n\n // Apply the scale, computing it from the value's exponent for si format.\n // Preserve the existing suffix, if any, such as the currency symbol.\n if (scale < 0) {\n const unit = this.prefix(value, precision)\n value = unit.scale(value)\n fullSuffix = unit.symbol + suffix\n } else {\n value *= scale\n }\n\n // Convert to the desired precision.\n value = this.convert(type, value, precision)\n\n // Break the value into the integer part (before) and decimal part (after).\n const i = value.lastIndexOf('.')\n let before = i < 0 ? value : value.substring(0, i)\n const after = i < 0 ? '' : locale.decimal + value.substring(i + 1)\n\n function formatGroup(value) {\n\n let i = value.length\n const t = []\n let j = 0\n let g = locale.grouping[0]\n while (i > 0 && g > 0) {\n t.push(value.substring(i -= g, i + g))\n g = locale.grouping[j = (j + 1) % locale.grouping.length]\n }\n return t.reverse().join(locale.thousands)\n }\n\n // If the fill character is not `'0'`, grouping is applied before padding.\n if (!zfill && comma && locale.grouping) {\n\n before = formatGroup(before)\n }\n\n let length = prefix.length + before.length + after.length + (zcomma ? 0 : negative.length)\n const padding = length < width ? new Array(length = width - length + 1).join(fill) : ''\n\n // If the fill character is `'0'`, grouping is applied after padding.\n if (zcomma) before = formatGroup(padding + before)\n\n // Apply prefix.\n negative += prefix\n\n // Rejoin integer and decimal parts.\n value = before + after\n\n return (align === '<' ? negative + value + padding\n : align === '>' ? padding + negative + value\n : align === '^' ? padding.substring(0, length >>= 1) + negative + value + padding.substring(length)\n : negative + (zcomma ? value : padding + value)) + fullSuffix\n },\n\n // Formatting string via the Python Format string.\n // See https://docs.python.org/2/library/string.html#format-string-syntax)\n string: function(formatString, value) {\n\n let fieldDelimiterIndex\n let fieldDelimiter = '{'\n let endPlaceholder = false\n const formattedStringArray = []\n\n while ((fieldDelimiterIndex = formatString.indexOf(fieldDelimiter)) !== -1) {\n\n var pieceFormatedString; var formatSpec; var fieldName\n\n pieceFormatedString = formatString.slice(0, fieldDelimiterIndex)\n\n if (endPlaceholder) {\n formatSpec = pieceFormatedString.split(':')\n fieldName = formatSpec.shift().split('.')\n pieceFormatedString = value\n\n for (let i = 0; i < fieldName.length; i++)\n pieceFormatedString = pieceFormatedString[fieldName[i]]\n\n if (formatSpec.length)\n pieceFormatedString = this.number(formatSpec, pieceFormatedString)\n }\n\n formattedStringArray.push(pieceFormatedString)\n\n formatString = formatString.slice(fieldDelimiterIndex + 1)\n endPlaceholder = !endPlaceholder\n fieldDelimiter = (endPlaceholder) ? '}' : '{'\n }\n formattedStringArray.push(formatString)\n\n return formattedStringArray.join('')\n },\n\n convert: function(type, value, precision) {\n\n switch (type) {\n case 'b': return value.toString(2)\n case 'c': return String.fromCharCode(value)\n case 'o': return value.toString(8)\n case 'x': return value.toString(16)\n case 'X': return value.toString(16).toUpperCase()\n case 'g': return value.toPrecision(precision)\n case 'e': return value.toExponential(precision)\n case 'f': return value.toFixed(precision)\n case 'r': return (value = this.round(value, this.precision(value, precision))).toFixed(Math.max(0, Math.min(20, this.precision(value * (1 + 1e-15), precision))))\n default: return `${value }`\n }\n },\n\n round: function(value, precision) {\n\n return precision\n ? Math.round(value * (precision = Math.pow(10, precision))) / precision\n : Math.round(value)\n },\n\n precision: function(value, precision) {\n\n return precision - (value ? Math.ceil(Math.log(value) / Math.LN10) : 1)\n },\n\n prefix: function(value, precision) {\n\n const prefixes = ['y', 'z', 'a', 'f', 'p', 'n', 'µ', 'm', '', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'].map(function(d, i) {\n const k = Math.pow(10, Math.abs(8 - i) * 3)\n return {\n scale: i > 8 ? function(d) { return d / k } : function(d) { return d * k },\n symbol: d\n }\n })\n\n let i = 0\n if (value) {\n if (value < 0) value *= -1\n if (precision) value = this.round(value, this.precision(value, precision))\n i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10)\n i = Math.max(-24, Math.min(24, Math.floor((i <= 0 ? i + 1 : i - 1) / 3) * 3))\n }\n return prefixes[8 + i / 3]\n }\n },\n\n /*\n Pre-compile the HTML to be used as a template.\n */\n template: function(html) {\n\n /*\n Must support the variation in templating syntax found here:\n https://lodash.com/docs#template\n */\n const regex = /<%= ([^ ]+) %>|\\$\\{ ?([^{} ]+) ?\\}|\\{\\{([^{} ]+)\\}\\}/g\n\n return function(data) {\n\n data = data || {}\n\n return html.replace(regex, function(match) {\n\n const args = Array.from(arguments)\n const attr = args.slice(1, 4).find(function(_attr) {\n return !!_attr\n })\n\n const attrArray = attr.split('.')\n let value = data[attrArray.shift()]\n\n while (value !== undefined && attrArray.length) {\n value = value[attrArray.shift()]\n }\n\n return value !== undefined ? value : ''\n })\n }\n },\n\n /**\n * @param {Element} el Element, which content is intent to display in full-screen mode, 'window.top.document.body' is default.\n */\n toggleFullScreen: function(el) {\n\n const topDocument = window.top.document\n el = el || topDocument.body\n\n function prefixedResult(el, prop) {\n\n const prefixes = ['webkit', 'moz', 'ms', 'o', '']\n for (let i = 0; i < prefixes.length; i++) {\n const prefix = prefixes[i]\n const propName = prefix ? (prefix + prop) : (prop.substr(0, 1).toLowerCase() + prop.substr(1))\n if (el[propName] !== undefined) {\n return joint.util.isFunction(el[propName]) ? el[propName]() : el[propName]\n }\n }\n }\n\n if (prefixedResult(topDocument, 'FullscreenElement') || prefixedResult(topDocument, 'FullScreenElement')) {\n prefixedResult(topDocument, 'ExitFullscreen') || // Spec.\n prefixedResult(topDocument, 'CancelFullScreen') // Firefox\n } else {\n prefixedResult(el, 'RequestFullscreen') || // Spec.\n prefixedResult(el, 'RequestFullScreen') // Firefox\n }\n },\n\n addClassNamePrefix: function(className) {\n\n if (!className) return className\n\n return className.toString().split(' ').map(function(_className) {\n\n if (_className.substr(0, joint.config.classNamePrefix.length) !== joint.config.classNamePrefix) {\n _className = joint.config.classNamePrefix + _className\n }\n\n return _className\n\n }).join(' ')\n },\n\n removeClassNamePrefix: function(className) {\n\n if (!className) return className\n\n return className.toString().split(' ').map(function(_className) {\n\n if (_className.substr(0, joint.config.classNamePrefix.length) === joint.config.classNamePrefix) {\n _className = _className.substr(joint.config.classNamePrefix.length)\n }\n\n return _className\n\n }).join(' ')\n },\n\n wrapWith: function(object, methods, wrapper) {\n\n if (joint.util.isString(wrapper)) {\n\n if (!joint.util.wrappers[wrapper]) {\n throw new Error(`Unknown wrapper: \"${ wrapper }\"`)\n }\n\n wrapper = joint.util.wrappers[wrapper]\n }\n\n if (!joint.util.isFunction(wrapper)) {\n throw new Error('Wrapper must be a function.')\n }\n\n this.toArray(methods).forEach(function(method) {\n object[method] = wrapper(object[method])\n })\n },\n\n wrappers: {\n\n /*\n Prepares a function with the following usage:\n\n fn([cell, cell, cell], opt);\n fn([cell, cell, cell]);\n fn(cell, cell, cell, opt);\n fn(cell, cell, cell);\n fn(cell);\n */\n cells: function(fn) {\n\n return function() {\n\n const args = Array.from(arguments)\n const n = args.length\n let cells = n > 0 && args[0] || []\n let opt = n > 1 && args[n - 1] || {}\n\n if (!Array.isArray(cells)) {\n\n if (opt instanceof joint.dia.Cell) {\n cells = args\n } else if (cells instanceof joint.dia.Cell) {\n if (args.length > 1) {\n args.pop()\n }\n cells = args\n }\n }\n\n if (opt instanceof joint.dia.Cell) {\n opt = {}\n }\n\n return fn.call(this, cells, opt)\n }\n }\n },\n\n parseDOMJSON: function(json, namespace) {\n\n const selectors = {}\n const svgNamespace = V.namespace.xmlns\n let ns = namespace || svgNamespace\n const fragment = document.createDocumentFragment()\n const queue = [json, fragment, ns]\n while (queue.length > 0) {\n ns = queue.pop()\n const parentNode = queue.pop()\n const siblingsDef = queue.pop()\n for (let i = 0, n = siblingsDef.length; i < n; i++) {\n const nodeDef = siblingsDef[i]\n // TagName\n if (!nodeDef.hasOwnProperty('tagName')) throw new Error('json-dom-parser: missing tagName')\n const {tagName} = nodeDef\n // Namespace URI\n if (nodeDef.hasOwnProperty('namespaceURI')) ns = nodeDef.namespaceURI\n const node = document.createElementNS(ns, tagName)\n const svg = (ns === svgNamespace)\n const wrapper = (svg) ? V : $\n // Attributes\n const {attributes} = nodeDef\n if (attributes) wrapper(node).attr(attributes)\n // Style\n const {style} = nodeDef\n if (style) $(node).css(style)\n // ClassName\n if (nodeDef.hasOwnProperty('className')) {\n const {className} = nodeDef\n if (svg) {\n node.className.baseVal = className\n } else {\n node.className = className\n }\n }\n // Selector\n if (nodeDef.hasOwnProperty('selector')) {\n const nodeSelector = nodeDef.selector\n if (selectors[nodeSelector]) throw new Error('json-dom-parser: selector must be unique')\n selectors[nodeSelector] = node\n wrapper(node).attr('joint-selector', nodeSelector)\n }\n parentNode.appendChild(node)\n // Children\n const childrenDef = nodeDef.children\n if (Array.isArray(childrenDef)) queue.push(childrenDef, node, ns)\n }\n }\n return {\n fragment: fragment,\n selectors: selectors\n }\n },\n\n /* global _:true */\n\n // Copy all the properties to the first argument from the following arguments.\n // All the properties will be overwritten by the properties from the following\n // arguments. Inherited properties are ignored.\n mixin: _.assign,\n\n // Copy all properties to the first argument from the following\n // arguments only in case if they don't exists in the first argument.\n // All the function propererties in the first argument will get\n // additional property base pointing to the extenders same named\n // property function's call method.\n supplement: _.defaults,\n\n // Same as `mixin()` but deep version.\n deepMixin: _.mixin,\n\n // Same as `supplement()` but deep version.\n deepSupplement: _.defaultsDeep,\n\n // lodash 3 vs 4 incompatible\n sortedIndex: _.sortedIndexBy || _.sortedIndex,\n uniq: _.uniqBy || _.uniq,\n uniqueId: _.uniqueId,\n sortBy: _.sortBy,\n isFunction: _.isFunction,\n result: _.result,\n union: _.union,\n invoke: _.invokeMap || _.invoke,\n difference: _.difference,\n intersection: _.intersection,\n omit: _.omit,\n pick: _.pick,\n has: _.has,\n bindAll: _.bindAll,\n assign: _.assign,\n defaults: _.defaults,\n defaultsDeep: _.defaultsDeep,\n isPlainObject: _.isPlainObject,\n isEmpty: _.isEmpty,\n isEqual: _.isEqual,\n cloneDeep: _.cloneDeep,\n toArray: _.toArray,\n flattenDeep: _.flattenDeep,\n camelCase: _.camelCase,\n groupBy: _.groupBy,\n forIn: _.forIn,\n without: _.without,\n debounce: _.debounce,\n clone: _.clone,\n\n merge: function() {\n if (_.mergeWith) {\n const args = Array.from(arguments)\n const last = args[args.length - 1]\n\n const customizer = this.isFunction(last) ? last : this.noop\n args.push(function(a,b) {\n const customResult = customizer(a, b)\n if (customResult !== undefined) {\n return customResult\n }\n\n if (Array.isArray(a) && !Array.isArray(b)) {\n return b\n }\n })\n\n return _.mergeWith.apply(this, args)\n }\n return _.merge.apply(this, arguments)\n },\n\n /* global _:false */\n\n noop: function() {},\n\n isBoolean: function(value) {\n const {toString} = Object.prototype\n return value === true || value === false || (!!value && typeof value === 'object' && toString.call(value) === '[object Boolean]')\n },\n\n isObject: function(value) {\n return !!value && (typeof value === 'object' || typeof value === 'function')\n },\n\n isNumber: function(value) {\n const {toString} = Object.prototype\n return typeof value === 'number' || (!!value && typeof value === 'object' && toString.call(value) === '[object Number]')\n },\n\n isString: function(value) {\n const {toString} = Object.prototype\n return typeof value === 'string' || (!!value && typeof value === 'object' && toString.call(value) === '[object String]')\n }\n }\n}\n\n\njoint.mvc.View = Backbone.View.extend({\n\n options: {},\n theme: null,\n themeClassNamePrefix: joint.util.addClassNamePrefix('theme-'),\n requireSetThemeOverride: false,\n defaultTheme: joint.config.defaultTheme,\n children: null,\n childNodes: null,\n\n constructor: function(options) {\n\n this.requireSetThemeOverride = options && !!options.theme\n this.options = joint.util.assign({}, this.options, options)\n\n Backbone.View.call(this, options)\n },\n\n initialize: function(options) {\n\n joint.util.bindAll(this, 'setTheme', 'onSetTheme', 'remove', 'onRemove')\n\n joint.mvc.views[this.cid] = this\n\n this.setTheme(this.options.theme || this.defaultTheme)\n this.init()\n },\n\n renderChildren: function(children) {\n children || (children = this.children)\n if (children) {\n const namespace = V.namespace[this.svgElement ? 'xmlns' : 'xhtml']\n const doc = joint.util.parseDOMJSON(children, namespace)\n this.vel.empty().append(doc.fragment)\n this.childNodes = doc.selectors\n }\n return this\n },\n\n // Override the Backbone `_ensureElement()` method in order to create an\n // svg element (e.g., ``) node that wraps all the nodes of the Cell view.\n // Expose class name setter as a separate method.\n _ensureElement: function() {\n if (!this.el) {\n const tagName = joint.util.result(this, 'tagName')\n const attrs = joint.util.assign({}, joint.util.result(this, 'attributes'))\n if (this.id) attrs.id = joint.util.result(this, 'id')\n this.setElement(this._createElement(tagName))\n this._setAttributes(attrs)\n } else {\n this.setElement(joint.util.result(this, 'el'))\n }\n this._ensureElClassName()\n },\n\n _setAttributes: function(attrs) {\n if (this.svgElement) {\n this.vel.attr(attrs)\n } else {\n this.$el.attr(attrs)\n }\n },\n\n _createElement: function(tagName) {\n if (this.svgElement) {\n return document.createElementNS(V.namespace.xmlns, tagName)\n } else {\n return document.createElement(tagName)\n }\n },\n\n // Utilize an alternative DOM manipulation API by\n // adding an element reference wrapped in Vectorizer.\n _setElement: function(el) {\n this.$el = el instanceof Backbone.$ ? el : Backbone.$(el)\n this.el = this.$el[0]\n if (this.svgElement) this.vel = V(this.el)\n },\n\n _ensureElClassName: function() {\n const className = joint.util.result(this, 'className')\n const prefixedClassName = joint.util.addClassNamePrefix(className)\n // Note: className removal here kept for backwards compatibility only\n if (this.svgElement) {\n this.vel.removeClass(className).addClass(prefixedClassName)\n } else {\n this.$el.removeClass(className).addClass(prefixedClassName)\n }\n },\n\n init: function() {\n // Intentionally empty.\n // This method is meant to be overriden.\n },\n\n onRender: function() {\n // Intentionally empty.\n // This method is meant to be overriden.\n },\n\n setTheme: function(theme, opt) {\n\n opt = opt || {}\n\n // Theme is already set, override is required, and override has not been set.\n // Don't set the theme.\n if (this.theme && this.requireSetThemeOverride && !opt.override) {\n return this\n }\n\n this.removeThemeClassName()\n this.addThemeClassName(theme)\n this.onSetTheme(this.theme/* oldTheme */, theme/* newTheme */)\n this.theme = theme\n\n return this\n },\n\n addThemeClassName: function(theme) {\n\n theme = theme || this.theme\n\n const className = this.themeClassNamePrefix + theme\n\n if (this.svgElement) {\n this.vel.addClass(className)\n } else {\n this.$el.addClass(className)\n }\n\n return this\n },\n\n removeThemeClassName: function(theme) {\n\n theme = theme || this.theme\n\n const className = this.themeClassNamePrefix + theme\n\n if (this.svgElement) {\n this.vel.removeClass(className)\n } else {\n this.$el.removeClass(className)\n }\n\n return this\n },\n\n onSetTheme: function(oldTheme, newTheme) {\n // Intentionally empty.\n // This method is meant to be overriden.\n },\n\n remove: function() {\n\n this.onRemove()\n this.undelegateDocumentEvents()\n\n joint.mvc.views[this.cid] = null\n\n Backbone.View.prototype.remove.apply(this, arguments)\n\n return this\n },\n\n onRemove: function() {\n // Intentionally empty.\n // This method is meant to be overriden.\n },\n\n getEventNamespace: function() {\n // Returns a per-session unique namespace\n return `.joint-event-ns-${ this.cid}`\n },\n\n delegateElementEvents: function(element, events, data) {\n if (!events) return this\n data || (data = {})\n const eventNS = this.getEventNamespace()\n for (const eventName in events) {\n let method = events[eventName]\n if (typeof method !== 'function') method = this[method]\n if (!method) continue\n $(element).on(eventName + eventNS, data, method.bind(this))\n }\n return this\n },\n\n undelegateElementEvents: function(element) {\n $(element).off(this.getEventNamespace())\n return this\n },\n\n delegateDocumentEvents: function(events, data) {\n events || (events = joint.util.result(this, 'documentEvents'))\n return this.delegateElementEvents(document, events, data)\n },\n\n undelegateDocumentEvents: function() {\n return this.undelegateElementEvents(document)\n },\n\n eventData: function(evt, data) {\n if (!evt) throw new Error('eventData(): event object required.')\n let currentData = evt.data\n const key = `__${ this.cid }__`\n if (data === undefined) {\n if (!currentData) return {}\n return currentData[key] || {}\n }\n currentData || (currentData = evt.data = {})\n currentData[key] || (currentData[key] = {})\n joint.util.assign(currentData[key], data)\n return this\n }\n\n}, {\n\n extend: function() {\n\n const args = Array.from(arguments)\n\n // Deep clone the prototype and static properties objects.\n // This prevents unexpected behavior where some properties are overwritten outside of this function.\n const protoProps = args[0] && joint.util.assign({}, args[0]) || {}\n const staticProps = args[1] && joint.util.assign({}, args[1]) || {}\n\n // Need the real render method so that we can wrap it and call it later.\n const renderFn = protoProps.render || (this.prototype && this.prototype.render) || null\n\n /*\n Wrap the real render method so that:\n .. `onRender` is always called.\n .. `this` is always returned.\n */\n protoProps.render = function() {\n\n if (renderFn) {\n // Call the original render method.\n renderFn.apply(this, arguments)\n }\n\n // Should always call onRender() method.\n this.onRender()\n\n // Should always return itself.\n return this\n }\n\n return Backbone.View.extend.call(this, protoProps, staticProps)\n }\n})\n\n\n\njoint.dia.GraphCells = Backbone.Collection.extend({\n\n cellNamespace: joint.shapes,\n\n initialize: function(models, opt) {\n\n // Set the optional namespace where all model classes are defined.\n if (opt.cellNamespace) {\n this.cellNamespace = opt.cellNamespace\n }\n\n this.graph = opt.graph\n },\n\n model: function(attrs, opt) {\n\n const {collection} = opt\n const namespace = collection.cellNamespace\n\n // Find the model class in the namespace or use the default one.\n const ModelClass = (attrs.type === 'link')\n ? joint.dia.Link\n : joint.util.getByPath(namespace, attrs.type, '.') || joint.dia.Element\n\n const cell = new ModelClass(attrs, opt)\n // Add a reference to the graph. It is necessary to do this here because this is the earliest place\n // where a new model is created from a plain JS object. For other objects, see `joint.dia.Graph>>_prepareCell()`.\n if (!opt.dry) {\n cell.graph = collection.graph\n }\n\n return cell\n },\n\n // `comparator` makes it easy to sort cells based on their `z` index.\n comparator: function(model) {\n\n return model.get('z') || 0\n }\n})\n\n\njoint.dia.Graph = Backbone.Model.extend({\n\n _batches: {},\n\n initialize: function(attrs, opt) {\n\n opt = opt || {}\n\n // Passing `cellModel` function in the options object to graph allows for\n // setting models based on attribute objects. This is especially handy\n // when processing JSON graphs that are in a different than JointJS format.\n const cells = new joint.dia.GraphCells([], {\n model: opt.cellModel,\n cellNamespace: opt.cellNamespace,\n graph: this\n })\n Backbone.Model.prototype.set.call(this, 'cells', cells)\n\n // Make all the events fired in the `cells` collection available.\n // to the outside world.\n cells.on('all', this.trigger, this)\n\n // Backbone automatically doesn't trigger re-sort if models attributes are changed later when\n // they're already in the collection. Therefore, we're triggering sort manually here.\n this.on('change:z', this._sortOnChangeZ, this)\n\n // `joint.dia.Graph` keeps an internal data structure (an adjacency list)\n // for fast graph queries. All changes that affect the structure of the graph\n // must be reflected in the `al` object. This object provides fast answers to\n // questions such as \"what are the neighbours of this node\" or \"what\n // are the sibling links of this link\".\n\n // Outgoing edges per node. Note that we use a hash-table for the list\n // of outgoing edges for a faster lookup.\n // [nodeId] -> Object [edgeId] -> true\n this._out = {}\n // Ingoing edges per node.\n // [nodeId] -> Object [edgeId] -> true\n this._in = {}\n // `_nodes` is useful for quick lookup of all the elements in the graph, without\n // having to go through the whole cells array.\n // [node ID] -> true\n this._nodes = {}\n // `_edges` is useful for quick lookup of all the links in the graph, without\n // having to go through the whole cells array.\n // [edgeId] -> true\n this._edges = {}\n\n cells.on('add', this._restructureOnAdd, this)\n cells.on('remove', this._restructureOnRemove, this)\n cells.on('reset', this._restructureOnReset, this)\n cells.on('change:source', this._restructureOnChangeSource, this)\n cells.on('change:target', this._restructureOnChangeTarget, this)\n cells.on('remove', this._removeCell, this)\n },\n\n _sortOnChangeZ: function() {\n\n this.get('cells').sort()\n },\n\n _restructureOnAdd: function(cell) {\n\n if (cell.isLink()) {\n this._edges[cell.id] = true\n const source = cell.source()\n const target = cell.target()\n if (source.id) {\n (this._out[source.id] || (this._out[source.id] = {}))[cell.id] = true\n }\n if (target.id) {\n (this._in[target.id] || (this._in[target.id] = {}))[cell.id] = true\n }\n } else {\n this._nodes[cell.id] = true\n }\n },\n\n _restructureOnRemove: function(cell) {\n\n if (cell.isLink()) {\n delete this._edges[cell.id]\n const source = cell.source()\n const target = cell.target()\n if (source.id && this._out[source.id] && this._out[source.id][cell.id]) {\n delete this._out[source.id][cell.id]\n }\n if (target.id && this._in[target.id] && this._in[target.id][cell.id]) {\n delete this._in[target.id][cell.id]\n }\n } else {\n delete this._nodes[cell.id]\n }\n },\n\n _restructureOnReset: function(cells) {\n\n // Normalize into an array of cells. The original `cells` is GraphCells Backbone collection.\n cells = cells.models\n\n this._out = {}\n this._in = {}\n this._nodes = {}\n this._edges = {}\n\n cells.forEach(this._restructureOnAdd, this)\n },\n\n _restructureOnChangeSource: function(link) {\n\n const prevSource = link.previous('source')\n if (prevSource.id && this._out[prevSource.id]) {\n delete this._out[prevSource.id][link.id]\n }\n const source = link.source()\n if (source.id) {\n (this._out[source.id] || (this._out[source.id] = {}))[link.id] = true\n }\n },\n\n _restructureOnChangeTarget: function(link) {\n\n const prevTarget = link.previous('target')\n if (prevTarget.id && this._in[prevTarget.id]) {\n delete this._in[prevTarget.id][link.id]\n }\n const target = link.get('target')\n if (target.id) {\n (this._in[target.id] || (this._in[target.id] = {}))[link.id] = true\n }\n },\n\n // Return all outbound edges for the node. Return value is an object\n // of the form: [edgeId] -> true\n getOutboundEdges: function(node) {\n\n return (this._out && this._out[node]) || {}\n },\n\n // Return all inbound edges for the node. Return value is an object\n // of the form: [edgeId] -> true\n getInboundEdges: function(node) {\n\n return (this._in && this._in[node]) || {}\n },\n\n toJSON: function() {\n\n // Backbone does not recursively call `toJSON()` on attributes that are themselves models/collections.\n // It just clones the attributes. Therefore, we must call `toJSON()` on the cells collection explicitely.\n const json = Backbone.Model.prototype.toJSON.apply(this, arguments)\n json.cells = this.get('cells').toJSON()\n return json\n },\n\n fromJSON: function(json, opt) {\n\n if (!json.cells) {\n\n throw new Error('Graph JSON must contain cells array.')\n }\n\n return this.set(json, opt)\n },\n\n set: function(key, val, opt) {\n\n let attrs\n\n // Handle both `key`, value and {key: value} style arguments.\n if (typeof key === 'object') {\n attrs = key\n opt = val\n } else {\n (attrs = {})[key] = val\n }\n\n // Make sure that `cells` attribute is handled separately via resetCells().\n if (attrs.hasOwnProperty('cells')) {\n this.resetCells(attrs.cells, opt)\n attrs = joint.util.omit(attrs, 'cells')\n }\n\n // The rest of the attributes are applied via original set method.\n return Backbone.Model.prototype.set.call(this, attrs, opt)\n },\n\n clear: function(opt) {\n\n opt = joint.util.assign({}, opt, { clear: true })\n\n const collection = this.get('cells')\n\n if (collection.length === 0) return this\n\n this.startBatch('clear', opt)\n\n // The elements come after the links.\n const cells = collection.sortBy(function(cell) {\n return cell.isLink() ? 1 : 2\n })\n\n do {\n\n // Remove all the cells one by one.\n // Note that all the links are removed first, so it's\n // safe to remove the elements without removing the connected\n // links first.\n cells.shift().remove(opt)\n\n } while (cells.length > 0)\n\n this.stopBatch('clear')\n\n return this\n },\n\n _prepareCell: function(cell, opt) {\n\n let attrs\n if (cell instanceof Backbone.Model) {\n attrs = cell.attributes\n if (!cell.graph && (!opt || !opt.dry)) {\n // An element can not be member of more than one graph.\n // A cell stops being the member of the graph after it's explicitly removed.\n cell.graph = this\n }\n } else {\n // In case we're dealing with a plain JS object, we have to set the reference\n // to the `graph` right after the actual model is created. This happens in the `model()` function\n // of `joint.dia.GraphCells`.\n attrs = cell\n }\n\n if (!joint.util.isString(attrs.type)) {\n throw new TypeError('dia.Graph: cell type must be a string.')\n }\n\n return cell\n },\n\n minZIndex: function() {\n\n const firstCell = this.get('cells').first()\n return firstCell ? (firstCell.get('z') || 0) : 0\n },\n\n maxZIndex: function() {\n\n const lastCell = this.get('cells').last()\n return lastCell ? (lastCell.get('z') || 0) : 0\n },\n\n addCell: function(cell, opt) {\n\n if (Array.isArray(cell)) {\n\n return this.addCells(cell, opt)\n }\n\n if (cell instanceof Backbone.Model) {\n\n if (!cell.has('z')) {\n cell.set('z', this.maxZIndex() + 1)\n }\n\n } else if (cell.z === undefined) {\n\n cell.z = this.maxZIndex() + 1\n }\n\n this.get('cells').add(this._prepareCell(cell, opt), opt || {})\n\n return this\n },\n\n addCells: function(cells, opt) {\n\n if (cells.length) {\n\n cells = joint.util.flattenDeep(cells)\n opt.position = cells.length\n\n this.startBatch('add')\n cells.forEach(function(cell) {\n opt.position--\n this.addCell(cell, opt)\n }, this)\n this.stopBatch('add')\n }\n\n return this\n },\n\n // When adding a lot of cells, it is much more efficient to\n // reset the entire cells collection in one go.\n // Useful for bulk operations and optimizations.\n resetCells: function(cells, opt) {\n\n const preparedCells = joint.util.toArray(cells).map(function(cell) {\n return this._prepareCell(cell, opt)\n }, this)\n this.get('cells').reset(preparedCells, opt)\n\n return this\n },\n\n removeCells: function(cells, opt) {\n\n if (cells.length) {\n\n this.startBatch('remove')\n joint.util.invoke(cells, 'remove', opt)\n this.stopBatch('remove')\n }\n\n return this\n },\n\n _removeCell: function(cell, collection, options) {\n\n options = options || {}\n\n if (!options.clear) {\n // Applications might provide a `disconnectLinks` option set to `true` in order to\n // disconnect links when a cell is removed rather then removing them. The default\n // is to remove all the associated links.\n if (options.disconnectLinks) {\n\n this.disconnectLinks(cell, options)\n\n } else {\n\n this.removeLinks(cell, options)\n }\n }\n // Silently remove the cell from the cells collection. Silently, because\n // `joint.dia.Cell.prototype.remove` already triggers the `remove` event which is\n // then propagated to the graph model. If we didn't remove the cell silently, two `remove` events\n // would be triggered on the graph model.\n this.get('cells').remove(cell, { silent: true })\n\n if (cell.graph === this) {\n // Remove the element graph reference only if the cell is the member of this graph.\n cell.graph = null\n }\n },\n\n // Get a cell by `id`.\n getCell: function(id) {\n\n return this.get('cells').get(id)\n },\n\n getCells: function() {\n\n return this.get('cells').toArray()\n },\n\n getElements: function() {\n\n return Object.keys(this._nodes).map(this.getCell, this)\n },\n\n getLinks: function() {\n\n return Object.keys(this._edges).map(this.getCell, this)\n },\n\n getFirstCell: function() {\n\n return this.get('cells').first()\n },\n\n getLastCell: function() {\n\n return this.get('cells').last()\n },\n\n // Get all inbound and outbound links connected to the cell `model`.\n getConnectedLinks: function(model, opt) {\n\n opt = opt || {}\n\n let {inbound} = opt\n let {outbound} = opt\n if ((inbound === undefined) && (outbound === undefined)) {\n inbound = outbound = true\n }\n\n // the final array of connected link models\n const links = []\n // a hash table of connected edges of the form: [edgeId] -> true\n // used for quick lookups to check if we already added a link\n const edges = {}\n\n if (outbound) {\n joint.util.forIn(this.getOutboundEdges(model.id), function(exists, edge) {\n if (!edges[edge]) {\n links.push(this.getCell(edge))\n edges[edge] = true\n }\n }.bind(this))\n }\n if (inbound) {\n joint.util.forIn(this.getInboundEdges(model.id), function(exists, edge) {\n // skip links that were already added\n // (those must be self-loop links)\n // (because they are inbound and outbound edges of the same two elements)\n if (!edges[edge]) {\n links.push(this.getCell(edge))\n edges[edge] = true\n }\n }.bind(this))\n }\n\n // if `deep` option is `true`, check also all the links that are connected to any of the descendant cells\n if (opt.deep) {\n\n const embeddedCells = model.getEmbeddedCells({ deep: true })\n\n // in the first round, we collect all the embedded elements\n const embeddedElements = {}\n embeddedCells.forEach(function(cell) {\n if (cell.isElement()) {\n embeddedElements[cell.id] = true\n }\n })\n\n embeddedCells.forEach(function(cell) {\n if (cell.isLink()) return\n if (outbound) {\n joint.util.forIn(this.getOutboundEdges(cell.id), function(exists, edge) {\n if (!edges[edge]) {\n const edgeCell = this.getCell(edge)\n const sourceId = edgeCell.source().id\n const targetId = edgeCell.target().id\n\n // if `includeEnclosed` option is falsy, skip enclosed links\n if (!opt.includeEnclosed\n && (sourceId && embeddedElements[sourceId])\n && (targetId && embeddedElements[targetId])) {\n return\n }\n\n links.push(this.getCell(edge))\n edges[edge] = true\n }\n }.bind(this))\n }\n if (inbound) {\n joint.util.forIn(this.getInboundEdges(cell.id), function(exists, edge) {\n if (!edges[edge]) {\n const edgeCell = this.getCell(edge)\n const sourceId = edgeCell.source().id\n const targetId = edgeCell.target().id\n\n // if `includeEnclosed` option is falsy, skip enclosed links\n if (!opt.includeEnclosed\n && (sourceId && embeddedElements[sourceId])\n && (targetId && embeddedElements[targetId])) {\n return\n }\n\n links.push(this.getCell(edge))\n edges[edge] = true\n }\n }.bind(this))\n }\n }, this)\n }\n\n return links\n },\n\n getNeighbors: function(model, opt) {\n\n opt = opt || {}\n\n let {inbound} = opt\n let {outbound} = opt\n if (inbound === undefined && outbound === undefined) {\n inbound = outbound = true\n }\n\n const neighbors = this.getConnectedLinks(model, opt).reduce(function(res, link) {\n\n const source = link.source()\n const target = link.target()\n const loop = link.hasLoop(opt)\n\n // Discard if it is a point, or if the neighbor was already added.\n if (inbound && joint.util.has(source, 'id') && !res[source.id]) {\n\n const sourceElement = this.getCell(source.id)\n\n if (loop || (sourceElement && sourceElement !== model && (!opt.deep || !sourceElement.isEmbeddedIn(model)))) {\n res[source.id] = sourceElement\n }\n }\n\n // Discard if it is a point, or if the neighbor was already added.\n if (outbound && joint.util.has(target, 'id') && !res[target.id]) {\n\n const targetElement = this.getCell(target.id)\n\n if (loop || (targetElement && targetElement !== model && (!opt.deep || !targetElement.isEmbeddedIn(model)))) {\n res[target.id] = targetElement\n }\n }\n\n return res\n }.bind(this), {})\n\n return joint.util.toArray(neighbors)\n },\n\n getCommonAncestor: function(/* cells */) {\n\n let cellsAncestors = Array.from(arguments).map(function(cell) {\n\n const ancestors = []\n let parentId = cell.get('parent')\n\n while (parentId) {\n\n ancestors.push(parentId)\n parentId = this.getCell(parentId).get('parent')\n }\n\n return ancestors\n\n }, this)\n\n cellsAncestors = cellsAncestors.sort(function(a, b) {\n return a.length - b.length\n })\n\n const commonAncestor = joint.util.toArray(cellsAncestors.shift()).find(function(ancestor) {\n return cellsAncestors.every(function(cellAncestors) {\n return cellAncestors.includes(ancestor)\n })\n })\n\n return this.getCell(commonAncestor)\n },\n\n // Find the whole branch starting at `element`.\n // If `opt.deep` is `true`, take into account embedded elements too.\n // If `opt.breadthFirst` is `true`, use the Breadth-first search algorithm, otherwise use Depth-first search.\n getSuccessors: function(element, opt) {\n\n opt = opt || {}\n const res = []\n // Modify the options so that it includes the `outbound` neighbors only. In other words, search forwards.\n this.search(element, function(el) {\n if (el !== element) {\n res.push(el)\n }\n }, joint.util.assign({}, opt, { outbound: true }))\n return res\n },\n\n // Clone `cells` returning an object that maps the original cell ID to the clone. The number\n // of clones is exactly the same as the `cells.length`.\n // This function simply clones all the `cells`. However, it also reconstructs\n // all the `source/target` and `parent/embed` references within the `cells`.\n // This is the main difference from the `cell.clone()` method. The\n // `cell.clone()` method works on one single cell only.\n // For example, for a graph: `A --- L ---> B`, `cloneCells([A, L, B])`\n // returns `[A2, L2, B2]` resulting to a graph: `A2 --- L2 ---> B2`, i.e.\n // the source and target of the link `L2` is changed to point to `A2` and `B2`.\n cloneCells: function(cells) {\n\n cells = joint.util.uniq(cells)\n\n // A map of the form [original cell ID] -> [clone] helping\n // us to reconstruct references for source/target and parent/embeds.\n // This is also the returned value.\n const cloneMap = joint.util.toArray(cells).reduce(function(map, cell) {\n map[cell.id] = cell.clone()\n return map\n }, {})\n\n joint.util.toArray(cells).forEach(function(cell) {\n\n const clone = cloneMap[cell.id]\n // assert(clone exists)\n\n if (clone.isLink()) {\n const source = clone.source()\n const target = clone.target()\n if (source.id && cloneMap[source.id]) {\n // Source points to an element and the element is among the clones.\n // => Update the source of the cloned link.\n clone.prop('source/id', cloneMap[source.id].id)\n }\n if (target.id && cloneMap[target.id]) {\n // Target points to an element and the element is among the clones.\n // => Update the target of the cloned link.\n clone.prop('target/id', cloneMap[target.id].id)\n }\n }\n\n // Find the parent of the original cell\n const parent = cell.get('parent')\n if (parent && cloneMap[parent]) {\n clone.set('parent', cloneMap[parent].id)\n }\n\n // Find the embeds of the original cell\n const embeds = joint.util.toArray(cell.get('embeds')).reduce(function(newEmbeds, embed) {\n // Embedded cells that are not being cloned can not be carried\n // over with other embedded cells.\n if (cloneMap[embed]) {\n newEmbeds.push(cloneMap[embed].id)\n }\n return newEmbeds\n }, [])\n\n if (!joint.util.isEmpty(embeds)) {\n clone.set('embeds', embeds)\n }\n })\n\n return cloneMap\n },\n\n // Clone the whole subgraph (including all the connected links whose source/target is in the subgraph).\n // If `opt.deep` is `true`, also take into account all the embedded cells of all the subgraph cells.\n // Return a map of the form: [original cell ID] -> [clone].\n cloneSubgraph: function(cells, opt) {\n\n const subgraph = this.getSubgraph(cells, opt)\n return this.cloneCells(subgraph)\n },\n\n // Return `cells` and all the connected links that connect cells in the `cells` array.\n // If `opt.deep` is `true`, return all the cells including all their embedded cells\n // and all the links that connect any of the returned cells.\n // For example, for a single shallow element, the result is that very same element.\n // For two elements connected with a link: `A --- L ---> B`, the result for\n // `getSubgraph([A, B])` is `[A, L, B]`. The same goes for `getSubgraph([L])`, the result is again `[A, L, B]`.\n getSubgraph: function(cells, opt) {\n\n opt = opt || {}\n\n const subgraph = []\n // `cellMap` is used for a quick lookup of existance of a cell in the `cells` array.\n const cellMap = {}\n const elements = []\n const links = []\n\n joint.util.toArray(cells).forEach(function(cell) {\n if (!cellMap[cell.id]) {\n subgraph.push(cell)\n cellMap[cell.id] = cell\n if (cell.isLink()) {\n links.push(cell)\n } else {\n elements.push(cell)\n }\n }\n\n if (opt.deep) {\n const embeds = cell.getEmbeddedCells({ deep: true })\n embeds.forEach(function(embed) {\n if (!cellMap[embed.id]) {\n subgraph.push(embed)\n cellMap[embed.id] = embed\n if (embed.isLink()) {\n links.push(embed)\n } else {\n elements.push(embed)\n }\n }\n })\n }\n })\n\n links.forEach(function(link) {\n // For links, return their source & target (if they are elements - not points).\n const source = link.source()\n const target = link.target()\n if (source.id && !cellMap[source.id]) {\n const sourceElement = this.getCell(source.id)\n subgraph.push(sourceElement)\n cellMap[sourceElement.id] = sourceElement\n elements.push(sourceElement)\n }\n if (target.id && !cellMap[target.id]) {\n const targetElement = this.getCell(target.id)\n subgraph.push(this.getCell(target.id))\n cellMap[targetElement.id] = targetElement\n elements.push(targetElement)\n }\n }, this)\n\n elements.forEach(function(element) {\n // For elements, include their connected links if their source/target is in the subgraph;\n const links = this.getConnectedLinks(element, opt)\n links.forEach(function(link) {\n const source = link.source()\n const target = link.target()\n if (!cellMap[link.id] && source.id && cellMap[source.id] && target.id && cellMap[target.id]) {\n subgraph.push(link)\n cellMap[link.id] = link\n }\n })\n }, this)\n\n return subgraph\n },\n\n // Find all the predecessors of `element`. This is a reverse operation of `getSuccessors()`.\n // If `opt.deep` is `true`, take into account embedded elements too.\n // If `opt.breadthFirst` is `true`, use the Breadth-first search algorithm, otherwise use Depth-first search.\n getPredecessors: function(element, opt) {\n\n opt = opt || {}\n const res = []\n // Modify the options so that it includes the `inbound` neighbors only. In other words, search backwards.\n this.search(element, function(el) {\n if (el !== element) {\n res.push(el)\n }\n }, joint.util.assign({}, opt, { inbound: true }))\n return res\n },\n\n // Perform search on the graph.\n // If `opt.breadthFirst` is `true`, use the Breadth-first Search algorithm, otherwise use Depth-first search.\n // By setting `opt.inbound` to `true`, you can reverse the direction of the search.\n // If `opt.deep` is `true`, take into account embedded elements too.\n // `iteratee` is a function of the form `function(element) {}`.\n // If `iteratee` explicitely returns `false`, the searching stops.\n search: function(element, iteratee, opt) {\n\n opt = opt || {}\n if (opt.breadthFirst) {\n this.bfs(element, iteratee, opt)\n } else {\n this.dfs(element, iteratee, opt)\n }\n },\n\n // Breadth-first search.\n // If `opt.deep` is `true`, take into account embedded elements too.\n // If `opt.inbound` is `true`, reverse the search direction (it's like reversing all the link directions).\n // `iteratee` is a function of the form `function(element, distance) {}`.\n // where `element` is the currently visited element and `distance` is the distance of that element\n // from the root `element` passed the `bfs()`, i.e. the element we started the search from.\n // Note that the `distance` is not the shortest or longest distance, it is simply the number of levels\n // crossed till we visited the `element` for the first time. It is especially useful for tree graphs.\n // If `iteratee` explicitely returns `false`, the searching stops.\n bfs: function(element, iteratee, opt) {\n\n opt = opt || {}\n const visited = {}\n const distance = {}\n const queue = []\n\n queue.push(element)\n distance[element.id] = 0\n\n while (queue.length > 0) {\n var next = queue.shift()\n if (!visited[next.id]) {\n visited[next.id] = true\n if (iteratee(next, distance[next.id]) === false) return\n this.getNeighbors(next, opt).forEach(function(neighbor) {\n distance[neighbor.id] = distance[next.id] + 1\n queue.push(neighbor)\n })\n }\n }\n },\n\n // Depth-first search.\n // If `opt.deep` is `true`, take into account embedded elements too.\n // If `opt.inbound` is `true`, reverse the search direction (it's like reversing all the link directions).\n // `iteratee` is a function of the form `function(element, distance) {}`.\n // If `iteratee` explicitely returns `false`, the search stops.\n dfs: function(element, iteratee, opt, _visited, _distance) {\n\n opt = opt || {}\n const visited = _visited || {}\n const distance = _distance || 0\n if (iteratee(element, distance) === false) return\n visited[element.id] = true\n\n this.getNeighbors(element, opt).forEach(function(neighbor) {\n if (!visited[neighbor.id]) {\n this.dfs(neighbor, iteratee, opt, visited, distance + 1)\n }\n }, this)\n },\n\n // Get all the roots of the graph. Time complexity: O(|V|).\n getSources: function() {\n\n const sources = []\n joint.util.forIn(this._nodes, function(exists, node) {\n if (!this._in[node] || joint.util.isEmpty(this._in[node])) {\n sources.push(this.getCell(node))\n }\n }.bind(this))\n return sources\n },\n\n // Get all the leafs of the graph. Time complexity: O(|V|).\n getSinks: function() {\n\n const sinks = []\n joint.util.forIn(this._nodes, function(exists, node) {\n if (!this._out[node] || joint.util.isEmpty(this._out[node])) {\n sinks.push(this.getCell(node))\n }\n }.bind(this))\n return sinks\n },\n\n // Return `true` if `element` is a root. Time complexity: O(1).\n isSource: function(element) {\n\n return !this._in[element.id] || joint.util.isEmpty(this._in[element.id])\n },\n\n // Return `true` if `element` is a leaf. Time complexity: O(1).\n isSink: function(element) {\n\n return !this._out[element.id] || joint.util.isEmpty(this._out[element.id])\n },\n\n // Return `true` is `elementB` is a successor of `elementA`. Return `false` otherwise.\n isSuccessor: function(elementA, elementB) {\n\n let isSuccessor = false\n this.search(elementA, function(element) {\n if (element === elementB && element !== elementA) {\n isSuccessor = true\n return false\n }\n }, { outbound: true })\n return isSuccessor\n },\n\n // Return `true` is `elementB` is a predecessor of `elementA`. Return `false` otherwise.\n isPredecessor: function(elementA, elementB) {\n\n let isPredecessor = false\n this.search(elementA, function(element) {\n if (element === elementB && element !== elementA) {\n isPredecessor = true\n return false\n }\n }, { inbound: true })\n return isPredecessor\n },\n\n // Return `true` is `elementB` is a neighbor of `elementA`. Return `false` otherwise.\n // `opt.deep` controls whether to take into account embedded elements as well. See `getNeighbors()`\n // for more details.\n // If `opt.outbound` is set to `true`, return `true` only if `elementB` is a successor neighbor.\n // Similarly, if `opt.inbound` is set to `true`, return `true` only if `elementB` is a predecessor neighbor.\n isNeighbor: function(elementA, elementB, opt) {\n\n opt = opt || {}\n\n let {inbound} = opt\n let {outbound} = opt\n if ((inbound === undefined) && (outbound === undefined)) {\n inbound = outbound = true\n }\n\n let isNeighbor = false\n\n this.getConnectedLinks(elementA, opt).forEach(function(link) {\n\n const source = link.source()\n const target = link.target()\n\n // Discard if it is a point.\n if (inbound && joint.util.has(source, 'id') && (source.id === elementB.id)) {\n isNeighbor = true\n return false\n }\n\n // Discard if it is a point, or if the neighbor was already added.\n if (outbound && joint.util.has(target, 'id') && (target.id === elementB.id)) {\n isNeighbor = true\n return false\n }\n })\n\n return isNeighbor\n },\n\n // Disconnect links connected to the cell `model`.\n disconnectLinks: function(model, opt) {\n\n this.getConnectedLinks(model).forEach(function(link) {\n\n link.set((link.source().id === model.id ? 'source' : 'target'), { x: 0, y: 0 }, opt)\n })\n },\n\n // Remove links connected to the cell `model` completely.\n removeLinks: function(model, opt) {\n\n joint.util.invoke(this.getConnectedLinks(model), 'remove', opt)\n },\n\n // Find all elements at given point\n findModelsFromPoint: function(p) {\n\n return this.getElements().filter(function(el) {\n return el.getBBox().containsPoint(p)\n })\n },\n\n // Find all elements in given area\n findModelsInArea: function(rect, opt) {\n\n rect = g.rect(rect)\n opt = joint.util.defaults(opt || {}, { strict: false })\n\n const method = opt.strict ? 'containsRect' : 'intersect'\n\n return this.getElements().filter(function(el) {\n return rect[method](el.getBBox())\n })\n },\n\n // Find all elements under the given element.\n findModelsUnderElement: function(element, opt) {\n\n opt = joint.util.defaults(opt || {}, { searchBy: 'bbox' })\n\n const bbox = element.getBBox()\n const elements = (opt.searchBy === 'bbox')\n ? this.findModelsInArea(bbox)\n : this.findModelsFromPoint(bbox[opt.searchBy]())\n\n // don't account element itself or any of its descendents\n return elements.filter(function(el) {\n return element.id !== el.id && !el.isEmbeddedIn(element)\n })\n },\n\n\n // Return bounding box of all elements.\n getBBox: function(cells, opt) {\n\n return this.getCellsBBox(cells || this.getElements(), opt)\n },\n\n // Return the bounding box of all cells in array provided.\n // Links are being ignored.\n getCellsBBox: function(cells, opt) {\n\n return joint.util.toArray(cells).reduce(function(memo, cell) {\n if (cell.isLink()) return memo\n if (memo) {\n return memo.union(cell.getBBox(opt))\n } else {\n return cell.getBBox(opt)\n }\n }, null)\n },\n\n translate: function(dx, dy, opt) {\n\n // Don't translate cells that are embedded in any other cell.\n const cells = this.getCells().filter(function(cell) {\n return !cell.isEmbedded()\n })\n\n joint.util.invoke(cells, 'translate', dx, dy, opt)\n\n return this\n },\n\n resize: function(width, height, opt) {\n\n return this.resizeCells(width, height, this.getCells(), opt)\n },\n\n resizeCells: function(width, height, cells, opt) {\n\n // `getBBox` method returns `null` if no elements provided.\n // i.e. cells can be an array of links\n const bbox = this.getCellsBBox(cells)\n if (bbox) {\n const sx = Math.max(width / bbox.width, 0)\n const sy = Math.max(height / bbox.height, 0)\n joint.util.invoke(cells, 'scale', sx, sy, bbox.origin(), opt)\n }\n\n return this\n },\n\n startBatch: function(name, data) {\n\n data = data || {}\n this._batches[name] = (this._batches[name] || 0) + 1\n\n return this.trigger('batch:start', joint.util.assign({}, data, { batchName: name }))\n },\n\n stopBatch: function(name, data) {\n\n data = data || {}\n this._batches[name] = (this._batches[name] || 0) - 1\n\n return this.trigger('batch:stop', joint.util.assign({}, data, { batchName: name }))\n },\n\n hasActiveBatch: function(name) {\n\n if (arguments.length === 0) {\n return joint.util.toArray(this._batches).some(function(batches) {\n return batches > 0\n })\n }\n if (Array.isArray(name)) {\n return name.some(function(name) {\n return !!this._batches[name]\n }, this)\n }\n return !!this._batches[name]\n }\n\n}, {\n\n validations: {\n\n multiLinks: function(graph, link) {\n\n // Do not allow multiple links to have the same source and target.\n const source = link.source()\n const target = link.target()\n\n if (source.id && target.id) {\n\n const sourceModel = link.getSourceElement()\n if (sourceModel) {\n\n const connectedLinks = graph.getConnectedLinks(sourceModel, { outbound: true })\n const sameLinks = connectedLinks.filter(function(_link) {\n\n const _source = _link.source()\n const _target = _link.target()\n\n return _source && _source.id === source.id &&\n (!_source.port || (_source.port === source.port)) &&\n _target && _target.id === target.id &&\n (!_target.port || (_target.port === target.port))\n\n })\n\n if (sameLinks.length > 1) {\n return false\n }\n }\n }\n\n return true\n },\n\n linkPinning: function(graph, link) {\n return link.source().id && link.target().id\n }\n }\n\n})\n\njoint.util.wrapWith(joint.dia.Graph.prototype, ['resetCells', 'addCells', 'removeCells'], 'cells');\n\n(function(joint, V, g, $, util) {\n\n function setWrapper(attrName, dimension) {\n return function(value, refBBox) {\n const isValuePercentage = util.isPercentage(value)\n value = parseFloat(value)\n if (isValuePercentage) {\n value /= 100\n }\n\n const attrs = {}\n if (isFinite(value)) {\n const attrValue = (isValuePercentage || value >= 0 && value <= 1)\n ? value * refBBox[dimension]\n : Math.max(value + refBBox[dimension], 0)\n attrs[attrName] = attrValue\n }\n\n return attrs\n }\n }\n\n function positionWrapper(axis, dimension, origin) {\n return function(value, refBBox) {\n const valuePercentage = util.isPercentage(value)\n value = parseFloat(value)\n if (valuePercentage) {\n value /= 100\n }\n\n let delta\n if (isFinite(value)) {\n const refOrigin = refBBox[origin]()\n if (valuePercentage || value > 0 && value < 1) {\n delta = refOrigin[axis] + refBBox[dimension] * value\n } else {\n delta = refOrigin[axis] + value\n }\n }\n\n const point = g.Point()\n point[axis] = delta || 0\n return point\n }\n }\n\n function offsetWrapper(axis, dimension, corner) {\n return function(value, nodeBBox) {\n let delta\n if (value === 'middle') {\n delta = nodeBBox[dimension] / 2\n } else if (value === corner) {\n delta = nodeBBox[dimension]\n } else if (isFinite(value)) {\n // TODO: or not to do a breaking change?\n delta = (value > -1 && value < 1) ? (-nodeBBox[dimension] * value) : -value\n } else if (util.isPercentage(value)) {\n delta = nodeBBox[dimension] * parseFloat(value) / 100\n } else {\n delta = 0\n }\n\n const point = g.Point()\n point[axis] = -(nodeBBox[axis] + delta)\n return point\n }\n }\n\n function shapeWrapper(shapeConstructor, opt) {\n const cacheName = 'joint-shape'\n const resetOffset = opt && opt.resetOffset\n return function(value, refBBox, node) {\n const $node = $(node)\n let cache = $node.data(cacheName)\n if (!cache || cache.value !== value) {\n // only recalculate if value has changed\n const cachedShape = shapeConstructor(value)\n cache = {\n value: value,\n shape: cachedShape,\n shapeBBox: cachedShape.bbox()\n }\n $node.data(cacheName, cache)\n }\n\n const shape = cache.shape.clone()\n const shapeBBox = cache.shapeBBox.clone()\n const shapeOrigin = shapeBBox.origin()\n const refOrigin = refBBox.origin()\n\n shapeBBox.x = refOrigin.x\n shapeBBox.y = refOrigin.y\n\n const fitScale = refBBox.maxRectScaleToFit(shapeBBox, refOrigin)\n // `maxRectScaleToFit` can give Infinity if width or height is 0\n const sx = (shapeBBox.width === 0 || refBBox.width === 0) ? 1 : fitScale.sx\n const sy = (shapeBBox.height === 0 || refBBox.height === 0) ? 1 : fitScale.sy\n\n shape.scale(sx, sy, shapeOrigin)\n if (resetOffset) {\n shape.translate(-shapeOrigin.x, -shapeOrigin.y)\n }\n\n return shape\n }\n }\n\n // `d` attribute for SVGPaths\n function dWrapper(opt) {\n function pathConstructor(value) {\n return new g.Path(V.normalizePathData(value))\n }\n const shape = shapeWrapper(pathConstructor, opt)\n return function(value, refBBox, node) {\n const path = shape(value, refBBox, node)\n return {\n d: path.serialize()\n }\n }\n }\n\n // `points` attribute for SVGPolylines and SVGPolygons\n function pointsWrapper(opt) {\n const shape = shapeWrapper(g.Polyline, opt)\n return function(value, refBBox, node) {\n const polyline = shape(value, refBBox, node)\n return {\n points: polyline.serialize()\n }\n }\n }\n\n function atConnectionWrapper(method, opt) {\n const zeroVector = new g.Point(1, 0)\n return function(value) {\n let p; let angle\n const tangent = this[method](value)\n if (tangent) {\n angle = (opt.rotate) ? tangent.vector().vectorAngle(zeroVector) : 0\n p = tangent.start\n } else {\n p = this.path.start\n angle = 0\n }\n if (angle === 0) return { transform: `translate(${ p.x },${ p.y })` }\n return { transform: `translate(${ p.x },${ p.y }) rotate(${ angle })` }\n }\n }\n\n function isTextInUse(lineHeight, node, attrs) {\n return (attrs.text !== undefined)\n }\n\n function isLinkView() {\n return this instanceof joint.dia.LinkView\n }\n\n function contextMarker(context) {\n const marker = {}\n // Stroke\n // The context 'fill' is disregared here. The usual case is to use the marker with a connection\n // (for which 'fill' attribute is set to 'none').\n const {stroke} = context\n if (typeof stroke === 'string') {\n marker.stroke = stroke\n marker.fill = stroke\n }\n // Opacity\n // Again the context 'fill-opacity' is ignored.\n let {strokeOpacity} = context\n if (strokeOpacity === undefined) strokeOpacity = context['stroke-opacity']\n if (strokeOpacity === undefined) strokeOpacity = context.opacity\n if (strokeOpacity !== undefined) {\n marker['stroke-opacity'] = strokeOpacity\n marker['fill-opacity'] = strokeOpacity\n }\n return marker\n }\n\n const attributesNS = joint.dia.attributes = {\n\n xlinkHref: {\n set: 'xlink:href'\n },\n\n xlinkShow: {\n set: 'xlink:show'\n },\n\n xlinkRole: {\n set: 'xlink:role'\n },\n\n xlinkType: {\n set: 'xlink:type'\n },\n\n xlinkArcrole: {\n set: 'xlink:arcrole'\n },\n\n xlinkTitle: {\n set: 'xlink:title'\n },\n\n xlinkActuate: {\n set: 'xlink:actuate'\n },\n\n xmlSpace: {\n set: 'xml:space'\n },\n\n xmlBase: {\n set: 'xml:base'\n },\n\n xmlLang: {\n set: 'xml:lang'\n },\n\n preserveAspectRatio: {\n set: 'preserveAspectRatio'\n },\n\n requiredExtension: {\n set: 'requiredExtension'\n },\n\n requiredFeatures: {\n set: 'requiredFeatures'\n },\n\n systemLanguage: {\n set: 'systemLanguage'\n },\n\n externalResourcesRequired: {\n set: 'externalResourceRequired'\n },\n\n filter: {\n qualify: util.isPlainObject,\n set: function(filter) {\n return `url(#${ this.paper.defineFilter(filter) })`\n }\n },\n\n fill: {\n qualify: util.isPlainObject,\n set: function(fill) {\n return `url(#${ this.paper.defineGradient(fill) })`\n }\n },\n\n stroke: {\n qualify: util.isPlainObject,\n set: function(stroke) {\n return `url(#${ this.paper.defineGradient(stroke) })`\n }\n },\n\n sourceMarker: {\n qualify: util.isPlainObject,\n set: function(marker, refBBox, node, attrs) {\n marker = util.assign(contextMarker(attrs), marker)\n return { 'marker-start': `url(#${ this.paper.defineMarker(marker) })` }\n }\n },\n\n targetMarker: {\n qualify: util.isPlainObject,\n set: function(marker, refBBox, node, attrs) {\n marker = util.assign(contextMarker(attrs), { transform: 'rotate(180)' }, marker)\n return { 'marker-end': `url(#${ this.paper.defineMarker(marker) })` }\n }\n },\n\n vertexMarker: {\n qualify: util.isPlainObject,\n set: function(marker, refBBox, node, attrs) {\n marker = util.assign(contextMarker(attrs), marker)\n return { 'marker-mid': `url(#${ this.paper.defineMarker(marker) })` }\n }\n },\n\n text: {\n qualify: function(text, node, attrs) {\n return !attrs.textWrap || !util.isPlainObject(attrs.textWrap)\n },\n set: function(text, refBBox, node, attrs) {\n const $node = $(node)\n const cacheName = 'joint-text'\n const cache = $node.data(cacheName)\n const textAttrs = joint.util.pick(attrs, 'lineHeight', 'annotations', 'textPath', 'x', 'textVerticalAnchor', 'eol')\n const fontSize = textAttrs.fontSize = attrs['font-size'] || attrs.fontSize\n const textHash = JSON.stringify([text, textAttrs])\n // Update the text only if there was a change in the string\n // or any of its attributes.\n if (cache === undefined || cache !== textHash) {\n // Chrome bug:\n // Tspans positions defined as `em` are not updated\n // when container `font-size` change.\n if (fontSize) node.setAttribute('font-size', fontSize)\n // Text Along Path Selector\n const {textPath} = textAttrs\n if (util.isObject(textPath)) {\n const pathSelector = textPath.selector\n if (typeof pathSelector === 'string') {\n const pathNode = this.findBySelector(pathSelector)[0]\n if (pathNode instanceof SVGPathElement) {\n textAttrs.textPath = util.assign({ 'xlink:href': `#${ pathNode.id}` }, textPath)\n }\n }\n }\n V(node).text(`${ text}`, textAttrs)\n $node.data(cacheName, textHash)\n }\n }\n },\n\n textWrap: {\n qualify: util.isPlainObject,\n set: function(value, refBBox, node, attrs) {\n // option `width`\n const width = value.width || 0\n if (util.isPercentage(width)) {\n refBBox.width *= parseFloat(width) / 100\n } else if (width <= 0) {\n refBBox.width += width\n } else {\n refBBox.width = width\n }\n // option `height`\n const height = value.height || 0\n if (util.isPercentage(height)) {\n refBBox.height *= parseFloat(height) / 100\n } else if (height <= 0) {\n refBBox.height += height\n } else {\n refBBox.height = height\n }\n // option `text`\n let wrappedText\n let {text} = value\n if (text === undefined) text = attrs.text\n if (text !== undefined) {\n wrappedText = joint.util.breakText(`${ text}`, refBBox, {\n 'font-weight': attrs['font-weight'] || attrs.fontWeight,\n 'font-size': attrs['font-size'] || attrs.fontSize,\n 'font-family': attrs['font-family'] || attrs.fontFamily,\n lineHeight: attrs.lineHeight\n }, {\n // Provide an existing SVG Document here\n // instead of creating a temporary one over again.\n svgDocument: this.paper.svg\n })\n } else {\n wrappedText = ''\n }\n joint.dia.attributes.text.set.call(this, wrappedText, refBBox, node, attrs)\n }\n },\n\n title: {\n qualify: function(title, node) {\n // HTMLElement title is specified via an attribute (i.e. not an element)\n return node instanceof SVGElement\n },\n set: function(title, refBBox, node) {\n const $node = $(node)\n const cacheName = 'joint-title'\n const cache = $node.data(cacheName)\n if (cache === undefined || cache !== title) {\n $node.data(cacheName, title)\n // Generally element should be the first child element of its parent.\n const {firstChild} = node\n if (firstChild && firstChild.tagName.toUpperCase() === 'TITLE') {\n // Update an existing title\n firstChild.textContent = title\n } else {\n // Create a new title\n const titleNode = document.createElementNS(node.namespaceURI, 'title')\n titleNode.textContent = title\n node.insertBefore(titleNode, firstChild)\n }\n }\n }\n },\n\n lineHeight: {\n qualify: isTextInUse\n },\n\n textVerticalAnchor: {\n qualify: isTextInUse\n },\n\n textPath: {\n qualify: isTextInUse\n },\n\n annotations: {\n qualify: isTextInUse\n },\n\n // `port` attribute contains the `id` of the port that the underlying magnet represents.\n port: {\n set: function(port) {\n return (port === null || port.id === undefined) ? port : port.id\n }\n },\n\n // `style` attribute is special in the sense that it sets the CSS style of the subelement.\n style: {\n qualify: util.isPlainObject,\n set: function(styles, refBBox, node) {\n $(node).css(styles)\n }\n },\n\n html: {\n set: function(html, refBBox, node) {\n $(node).html(`${html }`)\n }\n },\n\n ref: {\n // We do not set `ref` attribute directly on an element.\n // The attribute itself does not qualify for relative positioning.\n },\n\n // if `refX` is in [0, 1] then `refX` is a fraction of bounding box width\n // if `refX` is < 0 then `refX`'s absolute values is the right coordinate of the bounding box\n // otherwise, `refX` is the left coordinate of the bounding box\n\n refX: {\n position: positionWrapper('x', 'width', 'origin')\n },\n\n refY: {\n position: positionWrapper('y', 'height', 'origin')\n },\n\n // `ref-dx` and `ref-dy` define the offset of the subelement relative to the right and/or bottom\n // coordinate of the reference element.\n\n refDx: {\n position: positionWrapper('x', 'width', 'corner')\n },\n\n refDy: {\n position: positionWrapper('y', 'height', 'corner')\n },\n\n // 'ref-width'/'ref-height' defines the width/height of the subelement relatively to\n // the reference element size\n // val in 0..1 ref-width = 0.75 sets the width to 75% of the ref. el. width\n // val < 0 || val > 1 ref-height = -20 sets the height to the the ref. el. height shorter by 20\n\n refWidth: {\n set: setWrapper('width', 'width')\n },\n\n refHeight: {\n set: setWrapper('height', 'height')\n },\n\n refRx: {\n set: setWrapper('rx', 'width')\n },\n\n refRy: {\n set: setWrapper('ry', 'height')\n },\n\n refRInscribed: {\n set: (function(attrName) {\n const widthFn = setWrapper(attrName, 'width')\n const heightFn = setWrapper(attrName, 'height')\n return function(value, refBBox) {\n const fn = (refBBox.height > refBBox.width) ? widthFn : heightFn\n return fn(value, refBBox)\n }\n })('r')\n },\n\n refRCircumscribed: {\n set: function(value, refBBox) {\n const isValuePercentage = util.isPercentage(value)\n value = parseFloat(value)\n if (isValuePercentage) {\n value /= 100\n }\n\n const diagonalLength = Math.sqrt((refBBox.height * refBBox.height) + (refBBox.width * refBBox.width))\n\n let rValue\n if (isFinite(value)) {\n if (isValuePercentage || value >= 0 && value <= 1) rValue = value * diagonalLength\n else rValue = Math.max(value + diagonalLength, 0)\n }\n\n return { r: rValue }\n }\n },\n\n refCx: {\n set: setWrapper('cx', 'width')\n },\n\n refCy: {\n set: setWrapper('cy', 'height')\n },\n\n // `x-alignment` when set to `middle` causes centering of the subelement around its new x coordinate.\n // `x-alignment` when set to `right` uses the x coordinate as referenced to the right of the bbox.\n\n xAlignment: {\n offset: offsetWrapper('x', 'width', 'right')\n },\n\n // `y-alignment` when set to `middle` causes centering of the subelement around its new y coordinate.\n // `y-alignment` when set to `bottom` uses the y coordinate as referenced to the bottom of the bbox.\n\n yAlignment: {\n offset: offsetWrapper('y', 'height', 'bottom')\n },\n\n resetOffset: {\n offset: function(val, nodeBBox) {\n return (val)\n ? { x: -nodeBBox.x, y: -nodeBBox.y }\n : { x: 0, y: 0 }\n }\n\n },\n\n refDResetOffset: {\n set: dWrapper({ resetOffset: true })\n },\n\n refDKeepOffset: {\n set: dWrapper({ resetOffset: false })\n },\n\n refPointsResetOffset: {\n set: pointsWrapper({ resetOffset: true })\n },\n\n refPointsKeepOffset: {\n set: pointsWrapper({ resetOffset: false })\n },\n\n // LinkView Attributes\n\n connection: {\n qualify: isLinkView,\n set: function() {\n return { d: this.getSerializedConnection() }\n }\n },\n\n atConnectionLengthKeepGradient: {\n qualify: isLinkView,\n set: atConnectionWrapper('getTangentAtLength', { rotate: true })\n },\n\n atConnectionLengthIgnoreGradient: {\n qualify: isLinkView,\n set: atConnectionWrapper('getTangentAtLength', { rotate: false })\n },\n\n atConnectionRatioKeepGradient: {\n qualify: isLinkView,\n set: atConnectionWrapper('getTangentAtRatio', { rotate: true })\n },\n\n atConnectionRatioIgnoreGradient: {\n qualify: isLinkView,\n set: atConnectionWrapper('getTangentAtRatio', { rotate: false })\n }\n }\n\n // Aliases\n attributesNS.refR = attributesNS.refRInscribed\n attributesNS.refD = attributesNS.refDResetOffset\n attributesNS.refPoints = attributesNS.refPointsResetOffset\n attributesNS.atConnectionLength = attributesNS.atConnectionLengthKeepGradient\n attributesNS.atConnectionRatio = attributesNS.atConnectionRatioKeepGradient\n\n // This allows to combine both absolute and relative positioning\n // refX: 50%, refX2: 20\n attributesNS.refX2 = attributesNS.refX\n attributesNS.refY2 = attributesNS.refY\n\n // Aliases for backwards compatibility\n attributesNS['ref-x'] = attributesNS.refX\n attributesNS['ref-y'] = attributesNS.refY\n attributesNS['ref-dy'] = attributesNS.refDy\n attributesNS['ref-dx'] = attributesNS.refDx\n attributesNS['ref-width'] = attributesNS.refWidth\n attributesNS['ref-height'] = attributesNS.refHeight\n attributesNS['x-alignment'] = attributesNS.xAlignment\n attributesNS['y-alignment'] = attributesNS.yAlignment\n\n})(joint, V, g, $, joint.util);\n\n(function(joint, util) {\n\n const ToolView = joint.mvc.View.extend({\n name: null,\n tagName: 'g',\n className: 'tool',\n svgElement: true,\n _visible: true,\n\n init: function() {\n const {name} = this\n if (name) this.vel.attr('data-tool-name', name)\n },\n\n configure: function(view, toolsView) {\n this.relatedView = view\n this.paper = view.paper\n this.parentView = toolsView\n this.simulateRelatedView(this.el)\n return this\n },\n\n simulateRelatedView: function(el) {\n if (el) el.setAttribute('model-id', this.relatedView.model.id)\n },\n\n getName: function() {\n return this.name\n },\n\n show: function() {\n this.el.style.display = ''\n this._visible = true\n },\n\n hide: function() {\n this.el.style.display = 'none'\n this._visible = false\n },\n\n isVisible: function() {\n return !!this._visible\n },\n\n focus: function() {\n const opacity = this.options.focusOpacity\n if (isFinite(opacity)) this.el.style.opacity = opacity\n this.parentView.focusTool(this)\n },\n\n blur: function() {\n this.el.style.opacity = ''\n this.parentView.blurTool(this)\n },\n\n update: function() {\n // to be overriden\n }\n })\n\n const ToolsView = joint.mvc.View.extend({\n tagName: 'g',\n className: 'tools',\n svgElement: true,\n tools: null,\n options: {\n tools: null,\n relatedView: null,\n name: null,\n component: false\n },\n\n configure: function(options) {\n options = util.assign(this.options, options)\n const {tools} = options\n if (!Array.isArray(tools)) return this\n const {relatedView} = options\n if (!(relatedView instanceof joint.dia.CellView)) return this\n const views = this.tools = []\n for (let i = 0, n = tools.length; i < n; i++) {\n const tool = tools[i]\n if (!(tool instanceof ToolView)) continue\n tool.configure(relatedView, this)\n tool.render()\n this.vel.append(tool.el)\n views.push(tool)\n }\n return this\n },\n\n getName: function() {\n return this.options.name\n },\n\n update: function(opt) {\n\n opt || (opt = {})\n const {tools} = this\n if (!tools) return\n for (let i = 0, n = tools.length; i < n; i++) {\n const tool = tools[i]\n if (opt.tool !== tool.cid && tool.isVisible()) {\n tool.update()\n }\n }\n return this\n },\n\n focusTool: function(focusedTool) {\n\n const {tools} = this\n if (!tools) return this\n for (let i = 0, n = tools.length; i < n; i++) {\n const tool = tools[i]\n if (focusedTool === tool) {\n tool.show()\n } else {\n tool.hide()\n }\n }\n return this\n },\n\n blurTool: function(blurredTool) {\n const {tools} = this\n if (!tools) return this\n for (let i = 0, n = tools.length; i < n; i++) {\n const tool = tools[i]\n if (tool !== blurredTool && !tool.isVisible()) {\n tool.show()\n tool.update()\n }\n }\n return this\n },\n\n hide: function() {\n return this.focusTool(null)\n },\n\n show: function() {\n return this.blurTool(null)\n },\n\n onRemove: function() {\n\n const {tools} = this\n if (!tools) return this\n for (let i = 0, n = tools.length; i < n; i++) {\n tools[i].remove()\n }\n this.tools = null\n },\n\n mount: function() {\n const {options} = this\n const {relatedView} = options\n if (relatedView) {\n const container = (options.component) ? relatedView.el : relatedView.paper.tools\n container.appendChild(this.el)\n }\n return this\n }\n\n })\n\n joint.dia.ToolsView = ToolsView\n joint.dia.ToolView = ToolView\n\n})(joint, joint.util)\n\n\n// joint.dia.Cell base model.\n// --------------------------\n\njoint.dia.Cell = Backbone.Model.extend({\n\n // This is the same as Backbone.Model with the only difference that is uses joint.util.merge\n // instead of just _.extend. The reason is that we want to mixin attributes set in upper classes.\n constructor: function(attributes, options) {\n\n let defaults\n let attrs = attributes || {}\n this.cid = joint.util.uniqueId('c')\n this.attributes = {}\n if (options && options.collection) this.collection = options.collection\n if (options && options.parse) attrs = this.parse(attrs, options) || {}\n if ((defaults = joint.util.result(this, 'defaults'))) {\n //<custom code>\n // Replaced the call to _.defaults with joint.util.merge.\n attrs = joint.util.merge({}, defaults, attrs)\n //</custom code>\n }\n this.set(attrs, options)\n this.changed = {}\n this.initialize.apply(this, arguments)\n },\n\n translate: function(dx, dy, opt) {\n\n throw new Error('Must define a translate() method.')\n },\n\n toJSON: function() {\n\n const defaultAttrs = this.constructor.prototype.defaults.attrs || {}\n const {attrs} = this.attributes\n const finalAttrs = {}\n\n // Loop through all the attributes and\n // omit the default attributes as they are implicitly reconstructable by the cell 'type'.\n joint.util.forIn(attrs, function(attr, selector) {\n\n const defaultAttr = defaultAttrs[selector]\n\n joint.util.forIn(attr, function(value, name) {\n\n // attr is mainly flat though it might have one more level (consider the `style` attribute).\n // Check if the `value` is object and if yes, go one level deep.\n if (joint.util.isObject(value) && !Array.isArray(value)) {\n\n joint.util.forIn(value, function(value2, name2) {\n\n if (!defaultAttr || !defaultAttr[name] || !joint.util.isEqual(defaultAttr[name][name2], value2)) {\n\n finalAttrs[selector] = finalAttrs[selector] || {};\n (finalAttrs[selector][name] || (finalAttrs[selector][name] = {}))[name2] = value2\n }\n })\n\n } else if (!defaultAttr || !joint.util.isEqual(defaultAttr[name], value)) {\n // `value` is not an object, default attribute for such a selector does not exist\n // or it is different than the attribute value set on the model.\n\n finalAttrs[selector] = finalAttrs[selector] || {}\n finalAttrs[selector][name] = value\n }\n })\n })\n\n const attributes = joint.util.cloneDeep(joint.util.omit(this.attributes, 'attrs'))\n //var attributes = JSON.parse(JSON.stringify(_.omit(this.attributes, 'attrs')));\n attributes.attrs = finalAttrs\n\n return attributes\n },\n\n initialize: function(options) {\n\n if (!options || !options.id) {\n\n this.set('id', joint.util.uuid(), { silent: true })\n }\n\n this._transitionIds = {}\n\n // Collect ports defined in `attrs` and keep collecting whenever `attrs` object changes.\n this.processPorts()\n this.on('change:attrs', this.processPorts, this)\n },\n\n /**\n * @deprecated\n */\n processPorts: function() {\n\n // Whenever `attrs` changes, we extract ports from the `attrs` object and store it\n // in a more accessible way. Also, if any port got removed and there were links that had `target`/`source`\n // set to that port, we remove those links as well (to follow the same behaviour as\n // with a removed element).\n\n const previousPorts = this.ports\n\n // Collect ports from the `attrs` object.\n const ports = {}\n joint.util.forIn(this.get('attrs'), function(attrs, selector) {\n\n if (attrs && attrs.port) {\n\n // `port` can either be directly an `id` or an object containing an `id` (and potentially other data).\n if (attrs.port.id !== undefined) {\n ports[attrs.port.id] = attrs.port\n } else {\n ports[attrs.port] = { id: attrs.port }\n }\n }\n })\n\n // Collect ports that have been removed (compared to the previous ports) - if any.\n // Use hash table for quick lookup.\n const removedPorts = {}\n joint.util.forIn(previousPorts, function(port, id) {\n\n if (!ports[id]) removedPorts[id] = true\n })\n\n // Remove all the incoming/outgoing links that have source/target port set to any of the removed ports.\n if (this.graph && !joint.util.isEmpty(removedPorts)) {\n\n const inboundLinks = this.graph.getConnectedLinks(this, { inbound: true })\n inboundLinks.forEach(function(link) {\n\n if (removedPorts[link.get('target').port]) link.remove()\n })\n\n const outboundLinks = this.graph.getConnectedLinks(this, { outbound: true })\n outboundLinks.forEach(function(link) {\n\n if (removedPorts[link.get('source').port]) link.remove()\n })\n }\n\n // Update the `ports` object.\n this.ports = ports\n },\n\n remove: function(opt) {\n\n opt = opt || {}\n\n // Store the graph in a variable because `this.graph` won't' be accessbile after `this.trigger('remove', ...)` down below.\n const {graph} = this\n if (graph) {\n graph.startBatch('remove')\n }\n\n // First, unembed this cell from its parent cell if there is one.\n const parentCell = this.getParentCell()\n if (parentCell) parentCell.unembed(this)\n\n joint.util.invoke(this.getEmbeddedCells(), 'remove', opt)\n\n this.trigger('remove', this, this.collection, opt)\n\n if (graph) {\n graph.stopBatch('remove')\n }\n\n return this\n },\n\n toFront: function(opt) {\n\n const {graph} = this\n if (graph) {\n\n opt = opt || {}\n\n let z = graph.maxZIndex()\n\n let cells\n\n if (opt.deep) {\n cells = this.getEmbeddedCells({ deep: true, breadthFirst: true })\n cells.unshift(this)\n } else {\n cells = [this]\n }\n\n z = z - cells.length + 1\n\n const collection = graph.get('cells')\n let shouldUpdate = (collection.indexOf(this) !== (collection.length - cells.length))\n if (!shouldUpdate) {\n shouldUpdate = cells.some(function(cell, index) {\n return cell.get('z') !== z + index\n })\n }\n\n if (shouldUpdate) {\n this.startBatch('to-front')\n\n z += cells.length\n\n cells.forEach(function(cell, index) {\n cell.set('z', z + index, opt)\n })\n\n this.stopBatch('to-front')\n }\n }\n\n return this\n },\n\n toBack: function(opt) {\n\n const {graph} = this\n if (graph) {\n\n opt = opt || {}\n\n let z = graph.minZIndex()\n\n let cells\n\n if (opt.deep) {\n cells = this.getEmbeddedCells({ deep: true, breadthFirst: true })\n cells.unshift(this)\n } else {\n cells = [this]\n }\n\n const collection = graph.get('cells')\n let shouldUpdate = (collection.indexOf(this) !== 0)\n if (!shouldUpdate) {\n shouldUpdate = cells.some(function(cell, index) {\n return cell.get('z') !== z + index\n })\n }\n\n if (shouldUpdate) {\n this.startBatch('to-back')\n\n z -= cells.length\n\n cells.forEach(function(cell, index) {\n cell.set('z', z + index, opt)\n })\n\n this.stopBatch('to-back')\n }\n }\n\n return this\n },\n\n parent: function(parent, opt) {\n\n // getter\n if (parent === undefined) return this.get('parent')\n // setter\n return this.set('parent', parent, opt)\n },\n\n embed: function(cell, opt) {\n\n if (this === cell || this.isEmbeddedIn(cell)) {\n\n throw new Error('Recursive embedding not allowed.')\n\n } else {\n\n this.startBatch('embed')\n\n const embeds = joint.util.assign([], this.get('embeds'))\n\n // We keep all element ids after link ids.\n embeds[cell.isLink() ? 'unshift' : 'push'](cell.id)\n\n cell.parent(this.id, opt)\n this.set('embeds', joint.util.uniq(embeds), opt)\n\n this.stopBatch('embed')\n }\n\n return this\n },\n\n unembed: function(cell, opt) {\n\n this.startBatch('unembed')\n\n cell.unset('parent', opt)\n this.set('embeds', joint.util.without(this.get('embeds'), cell.id), opt)\n\n this.stopBatch('unembed')\n\n return this\n },\n\n getParentCell: function() {\n\n // unlike link.source/target, cell.parent stores id directly as a string\n const parentId = this.parent()\n const {graph} = this\n\n return (parentId && graph && graph.getCell(parentId)) || null\n },\n\n // Return an array of ancestor cells.\n // The array is ordered from the parent of the cell\n // to the most distant ancestor.\n getAncestors: function() {\n\n const ancestors = []\n\n if (!this.graph) {\n return ancestors\n }\n\n let parentCell = this.getParentCell()\n while (parentCell) {\n ancestors.push(parentCell)\n parentCell = parentCell.getParentCell()\n }\n\n return ancestors\n },\n\n getEmbeddedCells: function(opt) {\n\n opt = opt || {}\n\n // Cell models can only be retrieved when this element is part of a collection.\n // There is no way this element knows about other cells otherwise.\n // This also means that calling e.g. `translate()` on an element with embeds before\n // adding it to a graph does not translate its embeds.\n if (this.graph) {\n\n let cells\n\n if (opt.deep) {\n\n if (opt.breadthFirst) {\n\n // breadthFirst algorithm\n cells = []\n const queue = this.getEmbeddedCells()\n\n while (queue.length > 0) {\n\n const parent = queue.shift()\n cells.push(parent)\n queue.push.apply(queue, parent.getEmbeddedCells())\n }\n\n } else {\n\n // depthFirst algorithm\n cells = this.getEmbeddedCells()\n cells.forEach(function(cell) {\n cells.push.apply(cells, cell.getEmbeddedCells(opt))\n })\n }\n\n } else {\n\n cells = joint.util.toArray(this.get('embeds')).map(this.graph.getCell, this.graph)\n }\n\n return cells\n }\n return []\n },\n\n isEmbeddedIn: function(cell, opt) {\n\n const cellId = joint.util.isString(cell) ? cell : cell.id\n let parentId = this.parent()\n\n opt = joint.util.defaults({ deep: true }, opt)\n\n // See getEmbeddedCells().\n if (this.graph && opt.deep) {\n\n while (parentId) {\n if (parentId === cellId) {\n return true\n }\n parentId = this.graph.getCell(parentId).parent()\n }\n\n return false\n\n } else {\n\n // When this cell is not part of a collection check\n // at least whether it's a direct child of given cell.\n return parentId === cellId\n }\n },\n\n // Whether or not the cell is embedded in any other cell.\n isEmbedded: function() {\n\n return !!this.parent()\n },\n\n // Isolated cloning. Isolated cloning has two versions: shallow and deep (pass `{ deep: true }` in `opt`).\n // Shallow cloning simply clones the cell and returns a new cell with different ID.\n // Deep cloning clones the cell and all its embedded cells recursively.\n clone: function(opt) {\n\n opt = opt || {}\n\n if (!opt.deep) {\n // Shallow cloning.\n\n const clone = Backbone.Model.prototype.clone.apply(this, arguments)\n // We don't want the clone to have the same ID as the original.\n clone.set('id', joint.util.uuid())\n // A shallow cloned element does not carry over the original embeds.\n clone.unset('embeds')\n // And can not be embedded in any cell\n // as the clone is not part of the graph.\n clone.unset('parent')\n\n return clone\n\n } else {\n // Deep cloning.\n\n // For a deep clone, simply call `graph.cloneCells()` with the cell and all its embedded cells.\n return joint.util.toArray(joint.dia.Graph.prototype.cloneCells.call(null, [this].concat(this.getEmbeddedCells({ deep: true }))))\n }\n },\n\n // A convenient way to set nested properties.\n // This method merges the properties you'd like to set with the ones\n // stored in the cell and makes sure change events are properly triggered.\n // You can either set a nested property with one object\n // or use a property path.\n // The most simple use case is:\n // `cell.prop('name/first', 'John')` or\n // `cell.prop({ name: { first: 'John' } })`.\n // Nested arrays are supported too:\n // `cell.prop('series/0/data/0/degree', 50)` or\n // `cell.prop({ series: [ { data: [ { degree: 50 } ] } ] })`.\n prop: function(props, value, opt) {\n\n const delim = '/'\n const isString = joint.util.isString(props)\n\n if (isString || Array.isArray(props)) {\n // Get/set an attribute by a special path syntax that delimits\n // nested objects by the colon character.\n\n if (arguments.length > 1) {\n\n let path\n let pathArray\n\n if (isString) {\n path = props\n pathArray = path.split('/')\n } else {\n path = props.join(delim)\n pathArray = props.slice()\n }\n\n const property = pathArray[0]\n const pathArrayLength = pathArray.length\n\n opt = opt || {}\n opt.propertyPath = path\n opt.propertyValue = value\n opt.propertyPathArray = pathArray\n\n if (pathArrayLength === 1) {\n // Property is not nested. We can simply use `set()`.\n return this.set(property, value, opt)\n }\n\n let update = {}\n // Initialize the nested object. Subobjects are either arrays or objects.\n // An empty array is created if the sub-key is an integer. Otherwise, an empty object is created.\n // Note that this imposes a limitation on object keys one can use with Inspector.\n // Pure integer keys will cause issues and are therefore not allowed.\n let initializer = update\n let prevProperty = property\n\n for (let i = 1; i < pathArrayLength; i++) {\n const pathItem = pathArray[i]\n const isArrayIndex = Number.isFinite(isString ? Number(pathItem) : pathItem)\n initializer = initializer[prevProperty] = isArrayIndex ? [] : {}\n prevProperty = pathItem\n }\n\n // Fill update with the `value` on `path`.\n update = joint.util.setByPath(update, pathArray, value, '/')\n\n const baseAttributes = joint.util.merge({}, this.attributes)\n // if rewrite mode enabled, we replace value referenced by path with\n // the new one (we don't merge).\n opt.rewrite && joint.util.unsetByPath(baseAttributes, path, '/')\n\n // Merge update with the model attributes.\n const attributes = joint.util.merge(baseAttributes, update)\n // Finally, set the property to the updated attributes.\n return this.set(property, attributes[property], opt)\n\n } else {\n\n return joint.util.getByPath(this.attributes, props, delim)\n }\n }\n\n return this.set(joint.util.merge({}, this.attributes, props), value)\n },\n\n // A convient way to unset nested properties\n removeProp: function(path, opt) {\n\n // Once a property is removed from the `attrs` attribute\n // the cellView will recognize a `dirty` flag and rerender itself\n // in order to remove the attribute from SVG element.\n opt = opt || {}\n opt.dirty = true\n\n const pathArray = Array.isArray(path) ? path : path.split('/')\n\n if (pathArray.length === 1) {\n // A top level property\n return this.unset(path, opt)\n }\n\n // A nested property\n const property = pathArray[0]\n const nestedPath = pathArray.slice(1)\n const propertyValue = joint.util.cloneDeep(this.get(property))\n\n joint.util.unsetByPath(propertyValue, nestedPath, '/')\n\n return this.set(property, propertyValue, opt)\n },\n\n // A convenient way to set nested attributes.\n attr: function(attrs, value, opt) {\n\n const args = Array.from(arguments)\n if (args.length === 0) {\n return this.get('attrs')\n }\n\n if (Array.isArray(attrs)) {\n args[0] = ['attrs'].concat(attrs)\n } else if (joint.util.isString(attrs)) {\n // Get/set an attribute by a special path syntax that delimits\n // nested objects by the colon character.\n args[0] = `attrs/${ attrs}`\n\n } else {\n\n args[0] = { attrs: attrs }\n }\n\n return this.prop.apply(this, args)\n },\n\n // A convenient way to unset nested attributes\n removeAttr: function(path, opt) {\n\n if (Array.isArray(path)) {\n\n return this.removeProp(['attrs'].concat(path))\n }\n\n return this.removeProp(`attrs/${ path}`, opt)\n },\n\n transition: function(path, value, opt, delim) {\n\n delim = delim || '/'\n\n const defaults = {\n duration: 100,\n delay: 10,\n timingFunction: joint.util.timing.linear,\n valueFunction: joint.util.interpolate.number\n }\n\n opt = joint.util.assign(defaults, opt)\n\n let firstFrameTime = 0\n let interpolatingFunction\n\n var setter = function(runtime) {\n\n let id; let progress; let propertyValue\n\n firstFrameTime = firstFrameTime || runtime\n runtime -= firstFrameTime\n progress = runtime / opt.duration\n\n if (progress < 1) {\n this._transitionIds[path] = id = joint.util.nextFrame(setter)\n } else {\n progress = 1\n delete this._transitionIds[path]\n }\n\n propertyValue = interpolatingFunction(opt.timingFunction(progress))\n\n opt.transitionId = id\n\n this.prop(path, propertyValue, opt)\n\n if (!id) this.trigger('transition:end', this, path)\n\n }.bind(this)\n\n const initiator = function(callback) {\n\n this.stopTransitions(path)\n\n interpolatingFunction = opt.valueFunction(joint.util.getByPath(this.attributes, path, delim), value)\n\n this._transitionIds[path] = joint.util.nextFrame(callback)\n\n this.trigger('transition:start', this, path)\n\n }.bind(this)\n\n return setTimeout(initiator, opt.delay, setter)\n },\n\n getTransitions: function() {\n\n return Object.keys(this._transitionIds)\n },\n\n stopTransitions: function(path, delim) {\n\n delim = delim || '/'\n\n const pathArray = path && path.split(delim)\n\n Object.keys(this._transitionIds).filter(pathArray && function(key) {\n\n return joint.util.isEqual(pathArray, key.split(delim).slice(0, pathArray.length))\n\n }).forEach(function(key) {\n\n joint.util.cancelFrame(this._transitionIds[key])\n\n delete this._transitionIds[key]\n\n this.trigger('transition:end', this, key)\n\n }, this)\n\n return this\n },\n\n // A shorcut making it easy to create constructs like the following:\n // `var el = (new joint.shapes.basic.Rect).addTo(graph)`.\n addTo: function(graph, opt) {\n\n graph.addCell(this, opt)\n return this\n },\n\n // A shortcut for an equivalent call: `paper.findViewByModel(cell)`\n // making it easy to create constructs like the following:\n // `cell.findView(paper).highlight()`\n findView: function(paper) {\n\n return paper.findViewByModel(this)\n },\n\n isElement: function() {\n\n return false\n },\n\n isLink: function() {\n\n return false\n },\n\n startBatch: function(name, opt) {\n\n if (this.graph) { this.graph.startBatch(name, joint.util.assign({}, opt, { cell: this })) }\n return this\n },\n\n stopBatch: function(name, opt) {\n\n if (this.graph) { this.graph.stopBatch(name, joint.util.assign({}, opt, { cell: this })) }\n return this\n }\n\n}, {\n\n getAttributeDefinition: function(attrName) {\n\n const defNS = this.attributes\n const globalDefNS = joint.dia.attributes\n return (defNS && defNS[attrName]) || globalDefNS[attrName]\n },\n\n define: function(type, defaults, protoProps, staticProps) {\n\n protoProps = joint.util.assign({\n defaults: joint.util.defaultsDeep({ type: type }, defaults, this.prototype.defaults)\n }, protoProps)\n\n const Cell = this.extend(protoProps, staticProps)\n joint.util.setByPath(joint.shapes, type, Cell, '.')\n return Cell\n }\n})\n\n// joint.dia.CellView base view and controller.\n// --------------------------------------------\n\n// This is the base view and controller for `joint.dia.ElementView` and `joint.dia.LinkView`.\n\njoint.dia.CellView = joint.mvc.View.extend({\n\n tagName: 'g',\n\n svgElement: true,\n\n selector: 'root',\n\n className: function() {\n\n const classNames = ['cell']\n const type = this.model.get('type')\n\n if (type) {\n\n type.toLowerCase().split('.').forEach(function(value, index, list) {\n classNames.push(`type-${ list.slice(0, index + 1).join('-')}`)\n })\n }\n\n return classNames.join(' ')\n },\n\n attributes: function() {\n\n return { 'model-id': this.model.id }\n },\n\n constructor: function(options) {\n\n // Make sure a global unique id is assigned to this view. Store this id also to the properties object.\n // The global unique id makes sure that the same view can be rendered on e.g. different machines and\n // still be associated to the same object among all those clients. This is necessary for real-time\n // collaboration mechanism.\n options.id = options.id || joint.util.guid(this)\n\n joint.mvc.View.call(this, options)\n },\n\n init: function() {\n\n joint.util.bindAll(this, 'remove', 'update')\n\n // Store reference to this to the <g> DOM element so that the view is accessible through the DOM tree.\n this.$el.data('view', this)\n\n // Add the cell's type to the view's element as a data attribute.\n this.$el.attr('data-type', this.model.get('type'))\n\n this.listenTo(this.model, 'change:attrs', this.onChangeAttrs)\n },\n\n onChangeAttrs: function(cell, attrs, opt) {\n\n if (opt.dirty) {\n\n // dirty flag could be set when a model attribute was removed and it needs to be cleared\n // also from the DOM element. See cell.removeAttr().\n return this.render()\n }\n\n return this.update(cell, attrs, opt)\n },\n\n // Return `true` if cell link is allowed to perform a certain UI `feature`.\n // Example: `can('vertexMove')`, `can('labelMove')`.\n can: function(feature) {\n\n const interactive = joint.util.isFunction(this.options.interactive)\n ? this.options.interactive(this)\n : this.options.interactive\n\n return (joint.util.isObject(interactive) && interactive[feature] !== false) ||\n (joint.util.isBoolean(interactive) && interactive !== false)\n },\n\n findBySelector: function(selector, root, selectors) {\n\n root || (root = this.el)\n selectors || (selectors = this.selectors)\n\n // These are either descendants of `this.$el` of `this.$el` itself.\n // `.` is a special selector used to select the wrapping `<g>` element.\n if (!selector || selector === '.') return [root]\n if (selectors && selectors[selector]) return [selectors[selector]]\n // Maintaining backwards compatibility\n // e.g. `circle:first` would fail with querySelector() call\n return $(root).find(selector).toArray()\n },\n\n notify: function(eventName) {\n\n if (this.paper) {\n\n const args = Array.prototype.slice.call(arguments, 1)\n\n // Trigger the event on both the element itself and also on the paper.\n this.trigger.apply(this, [eventName].concat(args))\n\n // Paper event handlers receive the view object as the first argument.\n this.paper.trigger.apply(this.paper, [eventName, this].concat(args))\n }\n },\n\n // ** Deprecated **\n getStrokeBBox: function(el) {\n // Return a bounding box rectangle that takes into account stroke.\n // Note that this is a naive and ad-hoc implementation that does not\n // works only in certain cases and should be replaced as soon as browsers will\n // start supporting the getStrokeBBox() SVG method.\n // @TODO any better solution is very welcome!\n\n const isMagnet = !!el\n\n el = el || this.el\n const bbox = V(el).getBBox({ target: this.paper.viewport })\n let strokeWidth\n if (isMagnet) {\n\n strokeWidth = V(el).attr('stroke-width')\n\n } else {\n\n strokeWidth = this.model.attr('rect/stroke-width') || this.model.attr('circle/stroke-width') || this.model.attr('ellipse/stroke-width') || this.model.attr('path/stroke-width')\n }\n\n strokeWidth = parseFloat(strokeWidth) || 0\n\n return g.rect(bbox).moveAndExpand({ x: -strokeWidth / 2, y: -strokeWidth / 2, width: strokeWidth, height: strokeWidth })\n },\n\n getBBox: function() {\n\n return this.vel.getBBox({ target: this.paper.svg })\n },\n\n highlight: function(el, opt) {\n\n el = !el ? this.el : this.$(el)[0] || this.el\n\n // set partial flag if the highlighted element is not the entire view.\n opt = opt || {}\n opt.partial = (el !== this.el)\n\n this.notify('cell:highlight', el, opt)\n return this\n },\n\n unhighlight: function(el, opt) {\n\n el = !el ? this.el : this.$(el)[0] || this.el\n\n opt = opt || {}\n opt.partial = el != this.el\n\n this.notify('cell:unhighlight', el, opt)\n return this\n },\n\n // Find the closest element that has the `magnet` attribute set to `true`. If there was not such\n // an element found, return the root element of the cell view.\n findMagnet: function(el) {\n\n let $el = this.$(el)\n const $rootEl = this.$el\n\n if ($el.length === 0) {\n $el = $rootEl\n }\n\n do {\n\n const magnet = $el.attr('magnet')\n if ((magnet || $el.is($rootEl)) && magnet !== 'false') {\n return $el[0]\n }\n\n $el = $el.parent()\n\n } while ($el.length > 0)\n\n // If the overall cell has set `magnet === false`, then return `undefined` to\n // announce there is no magnet found for this cell.\n // This is especially useful to set on cells that have 'ports'. In this case,\n // only the ports have set `magnet === true` and the overall element has `magnet === false`.\n return undefined\n },\n\n // Construct a unique selector for the `el` element within this view.\n // `prevSelector` is being collected through the recursive call.\n // No value for `prevSelector` is expected when using this method.\n getSelector: function(el, prevSelector) {\n\n let selector\n\n if (el === this.el) {\n if (typeof prevSelector === 'string') selector = `> ${ prevSelector}`\n return selector\n }\n\n if (el) {\n\n const nthChild = V(el).index() + 1\n selector = `${el.tagName }:nth-child(${ nthChild })`\n\n if (prevSelector) {\n selector += ` > ${ prevSelector}`\n }\n\n selector = this.getSelector(el.parentNode, selector)\n }\n\n return selector\n },\n\n getLinkEnd: function(magnet, x, y, link, endType) {\n\n const {model} = this\n const {id} = model\n const port = this.findAttribute('port', magnet)\n // Find a unique `selector` of the element under pointer that is a magnet.\n const selector = magnet.getAttribute('joint-selector')\n\n let end = { id: id }\n if (selector != null) end.magnet = selector\n if (port != null) {\n end.port = port\n if (!model.hasPort(port) && !selector) {\n // port created via the `port` attribute (not API)\n end.selector = this.getSelector(magnet)\n }\n } else if (selector == null && this.el !== magnet) {\n end.selector = this.getSelector(magnet)\n }\n\n const {paper} = this\n const {connectionStrategy} = paper.options\n if (typeof connectionStrategy === 'function') {\n const strategy = connectionStrategy.call(paper, end, this, magnet, new g.Point(x, y), link, endType)\n if (strategy) end = strategy\n }\n\n return end\n },\n\n getMagnetFromLinkEnd: function(end) {\n\n const root = this.el\n const {port} = end\n let selector = end.magnet\n let magnet\n if (port != null && this.model.hasPort(port)) {\n magnet = this.findPortNode(port, selector) || root\n } else {\n if (!selector) selector = end.selector\n if (!selector && port != null) {\n // link end has only `id` and `port` property referencing\n // a port created via the `port` attribute (not API).\n selector = `[port=\"${ port }\"]`\n }\n magnet = this.findBySelector(selector, root, this.selectors)[0]\n }\n\n return magnet\n },\n\n findAttribute: function(attributeName, node) {\n\n if (!node) return null\n\n let attributeValue = node.getAttribute(attributeName)\n if (attributeValue === null) {\n if (node === this.el) return null\n let currentNode = node.parentNode\n while (currentNode && currentNode !== this.el && currentNode.nodeType === 1) {\n attributeValue = currentNode.getAttribute(attributeName)\n if (attributeValue !== null) break\n currentNode = currentNode.parentNode\n }\n }\n return attributeValue\n },\n\n getAttributeDefinition: function(attrName) {\n\n return this.model.constructor.getAttributeDefinition(attrName)\n },\n\n setNodeAttributes: function(node, attrs) {\n\n if (!joint.util.isEmpty(attrs)) {\n if (node instanceof SVGElement) {\n V(node).attr(attrs)\n } else {\n $(node).attr(attrs)\n }\n }\n },\n\n processNodeAttributes: function(node, attrs) {\n\n let attrName; let attrVal; let def; let i; let n\n let normalAttrs; let setAttrs; let positionAttrs; let offsetAttrs\n const relatives = []\n // divide the attributes between normal and special\n for (attrName in attrs) {\n if (!attrs.hasOwnProperty(attrName)) continue\n attrVal = attrs[attrName]\n def = this.getAttributeDefinition(attrName)\n if (def && (!joint.util.isFunction(def.qualify) || def.qualify.call(this, attrVal, node, attrs))) {\n if (joint.util.isString(def.set)) {\n normalAttrs || (normalAttrs = {})\n normalAttrs[def.set] = attrVal\n }\n if (attrVal !== null) {\n relatives.push(attrName, def)\n }\n } else {\n normalAttrs || (normalAttrs = {})\n normalAttrs[joint.util.toKebabCase(attrName)] = attrVal\n }\n }\n\n // handle the rest of attributes via related method\n // from the special attributes namespace.\n for (i = 0, n = relatives.length; i < n; i+=2) {\n attrName = relatives[i]\n def = relatives[i+1]\n attrVal = attrs[attrName]\n if (joint.util.isFunction(def.set)) {\n setAttrs || (setAttrs = {})\n setAttrs[attrName] = attrVal\n }\n if (joint.util.isFunction(def.position)) {\n positionAttrs || (positionAttrs = {})\n positionAttrs[attrName] = attrVal\n }\n if (joint.util.isFunction(def.offset)) {\n offsetAttrs || (offsetAttrs = {})\n offsetAttrs[attrName] = attrVal\n }\n }\n\n return {\n raw: attrs,\n normal: normalAttrs,\n set: setAttrs,\n position: positionAttrs,\n offset: offsetAttrs\n }\n },\n\n updateRelativeAttributes: function(node, attrs, refBBox, opt) {\n\n opt || (opt = {})\n\n let attrName; let attrVal; let def\n const rawAttrs = attrs.raw || {}\n let nodeAttrs = attrs.normal || {}\n const setAttrs = attrs.set\n const positionAttrs = attrs.position\n const offsetAttrs = attrs.offset\n\n for (attrName in setAttrs) {\n attrVal = setAttrs[attrName]\n def = this.getAttributeDefinition(attrName)\n // SET - set function should return attributes to be set on the node,\n // which will affect the node dimensions based on the reference bounding\n // box. e.g. `width`, `height`, `d`, `rx`, `ry`, `points\n const setResult = def.set.call(this, attrVal, refBBox.clone(), node, rawAttrs)\n if (joint.util.isObject(setResult)) {\n joint.util.assign(nodeAttrs, setResult)\n } else if (setResult !== undefined) {\n nodeAttrs[attrName] = setResult\n }\n }\n\n if (node instanceof HTMLElement) {\n // TODO: setting the `transform` attribute on HTMLElements\n // via `node.style.transform = 'matrix(...)';` would introduce\n // a breaking change (e.g. basic.TextBlock).\n this.setNodeAttributes(node, nodeAttrs)\n return\n }\n\n // The final translation of the subelement.\n const nodeTransform = nodeAttrs.transform\n const nodeMatrix = V.transformStringToMatrix(nodeTransform)\n const nodePosition = g.Point(nodeMatrix.e, nodeMatrix.f)\n if (nodeTransform) {\n nodeAttrs = joint.util.omit(nodeAttrs, 'transform')\n nodeMatrix.e = nodeMatrix.f = 0\n }\n\n // Calculate node scale determined by the scalable group\n // only if later needed.\n let sx; let sy; let translation\n if (positionAttrs || offsetAttrs) {\n const nodeScale = this.getNodeScale(node, opt.scalableNode)\n sx = nodeScale.sx\n sy = nodeScale.sy\n }\n\n let positioned = false\n for (attrName in positionAttrs) {\n attrVal = positionAttrs[attrName]\n def = this.getAttributeDefinition(attrName)\n // POSITION - position function should return a point from the\n // reference bounding box. The default position of the node is x:0, y:0 of\n // the reference bounding box or could be further specify by some\n // SVG attributes e.g. `x`, `y`\n translation = def.position.call(this, attrVal, refBBox.clone(), node, rawAttrs)\n if (translation) {\n nodePosition.offset(g.Point(translation).scale(sx, sy))\n positioned || (positioned = true)\n }\n }\n\n // The node bounding box could depend on the `size` set from the previous loop.\n // Here we know, that all the size attributes have been already set.\n this.setNodeAttributes(node, nodeAttrs)\n\n let offseted = false\n if (offsetAttrs) {\n // Check if the node is visible\n const nodeClientRect = node.getBoundingClientRect()\n if (nodeClientRect.width > 0 && nodeClientRect.height > 0) {\n const nodeBBox = V.transformRect(node.getBBox(), nodeMatrix).scale(1 / sx, 1 / sy)\n for (attrName in offsetAttrs) {\n attrVal = offsetAttrs[attrName]\n def = this.getAttributeDefinition(attrName)\n // OFFSET - offset function should return a point from the element\n // bounding box. The default offset point is x:0, y:0 (origin) or could be further\n // specify with some SVG attributes e.g. `text-anchor`, `cx`, `cy`\n translation = def.offset.call(this, attrVal, nodeBBox, node, rawAttrs)\n if (translation) {\n nodePosition.offset(g.Point(translation).scale(sx, sy))\n offseted || (offseted = true)\n }\n }\n }\n }\n\n // Do not touch node's transform attribute if there is no transformation applied.\n if (nodeTransform !== undefined || positioned || offseted) {\n // Round the coordinates to 1 decimal point.\n nodePosition.round(1)\n nodeMatrix.e = nodePosition.x\n nodeMatrix.f = nodePosition.y\n node.setAttribute('transform', V.matrixToTransformString(nodeMatrix))\n // TODO: store nodeMatrix metrics?\n }\n },\n\n getNodeScale: function(node, scalableNode) {\n\n // Check if the node is a descendant of the scalable group.\n let sx; let sy\n if (scalableNode && scalableNode.contains(node)) {\n const scale = scalableNode.scale()\n sx = 1 / scale.sx\n sy = 1 / scale.sy\n } else {\n sx = 1\n sy = 1\n }\n\n return { sx: sx, sy: sy }\n },\n\n findNodesAttributes: function(attrs, root, selectorCache, selectors) {\n\n // TODO: merge attributes in order defined by `index` property\n\n // most browsers sort elements in attrs by order of addition\n // which is useful but not required\n\n // link.updateLabels() relies on that assumption for merging label attrs over default label attrs\n\n const nodesAttrs = {}\n\n for (const selector in attrs) {\n if (!attrs.hasOwnProperty(selector)) continue\n const selected = selectorCache[selector] = this.findBySelector(selector, root, selectors)\n for (let i = 0, n = selected.length; i < n; i++) {\n const node = selected[i]\n const nodeId = V.ensureId(node)\n const nodeAttrs = attrs[selector]\n const prevNodeAttrs = nodesAttrs[nodeId]\n if (prevNodeAttrs) {\n if (!prevNodeAttrs.merged) {\n prevNodeAttrs.merged = true\n // if prevNode attrs is `null`, replace with `{}`\n prevNodeAttrs.attributes = joint.util.cloneDeep(prevNodeAttrs.attributes) || {}\n }\n // if prevNode attrs not set (or `null` or`{}`), use node attrs\n // if node attrs not set (or `null` or `{}`), use prevNode attrs\n joint.util.merge(prevNodeAttrs.attributes, nodeAttrs)\n } else {\n nodesAttrs[nodeId] = {\n attributes: nodeAttrs,\n node: node,\n merged: false\n }\n }\n }\n }\n\n return nodesAttrs\n },\n\n // Default is to process the `model.attributes.attrs` object and set attributes on subelements based on the selectors,\n // unless `attrs` parameter was passed.\n updateDOMSubtreeAttributes: function(rootNode, attrs, opt) {\n\n opt || (opt = {})\n opt.rootBBox || (opt.rootBBox = g.Rect())\n opt.selectors || (opt.selectors = this.selectors) // selector collection to use\n\n // Cache table for query results and bounding box calculation.\n // Note that `selectorCache` needs to be invalidated for all\n // `updateAttributes` calls, as the selectors might pointing\n // to nodes designated by an attribute or elements dynamically\n // created.\n const selectorCache = {}\n const bboxCache = {}\n const relativeItems = []\n let item; let node; let nodeAttrs; let nodeData; let processedAttrs\n\n const roAttrs = opt.roAttributes\n const nodesAttrs = this.findNodesAttributes(roAttrs || attrs, rootNode, selectorCache, opt.selectors)\n // `nodesAttrs` are different from all attributes, when\n // rendering only attributes sent to this method.\n var nodesAllAttrs = (roAttrs)\n ? nodesAllAttrs = this.findNodesAttributes(attrs, rootNode, selectorCache, opt.selectors)\n : nodesAttrs\n\n for (const nodeId in nodesAttrs) {\n nodeData = nodesAttrs[nodeId]\n nodeAttrs = nodeData.attributes\n node = nodeData.node\n processedAttrs = this.processNodeAttributes(node, nodeAttrs)\n\n if (!processedAttrs.set && !processedAttrs.position && !processedAttrs.offset) {\n // Set all the normal attributes right on the SVG/HTML element.\n this.setNodeAttributes(node, processedAttrs.normal)\n\n } else {\n\n const nodeAllAttrs = nodesAllAttrs[nodeId] && nodesAllAttrs[nodeId].attributes\n const refSelector = (nodeAllAttrs && (nodeAttrs.ref === undefined))\n ? nodeAllAttrs.ref\n : nodeAttrs.ref\n\n var refNode\n if (refSelector) {\n refNode = (selectorCache[refSelector] || this.findBySelector(refSelector, rootNode, opt.selectors))[0]\n if (!refNode) {\n throw new Error(`dia.ElementView: \"${ refSelector }\" reference does not exist.`)\n }\n } else {\n refNode = null\n }\n\n item = {\n node: node,\n refNode: refNode,\n processedAttributes: processedAttrs,\n allAttributes: nodeAllAttrs\n }\n\n // If an element in the list is positioned relative to this one, then\n // we want to insert this one before it in the list.\n const itemIndex = relativeItems.findIndex(function(item) {\n return item.refNode === node\n })\n\n if (itemIndex > -1) {\n relativeItems.splice(itemIndex, 0, item)\n } else {\n relativeItems.push(item)\n }\n }\n }\n\n for (let i = 0, n = relativeItems.length; i < n; i++) {\n item = relativeItems[i]\n node = item.node\n refNode = item.refNode\n\n // Find the reference element bounding box. If no reference was provided, we\n // use the optional bounding box.\n const refNodeId = refNode ? V.ensureId(refNode) : ''\n let refBBox = bboxCache[refNodeId]\n if (!refBBox) {\n // Get the bounding box of the reference element relative to the `rotatable` `<g>` (without rotation)\n // or to the root `<g>` element if no rotatable group present if reference node present.\n // Uses the bounding box provided.\n refBBox = bboxCache[refNodeId] = (refNode)\n ? V(refNode).getBBox({ target: (opt.rotatableNode || rootNode) })\n : opt.rootBBox\n }\n\n if (roAttrs) {\n // if there was a special attribute affecting the position amongst passed-in attributes\n // we have to merge it with the rest of the element's attributes as they are necessary\n // to update the position relatively (i.e `ref-x` && 'ref-dx')\n processedAttrs = this.processNodeAttributes(node, item.allAttributes)\n this.mergeProcessedAttributes(processedAttrs, item.processedAttributes)\n\n } else {\n processedAttrs = item.processedAttributes\n }\n\n this.updateRelativeAttributes(node, processedAttrs, refBBox, opt)\n }\n },\n\n mergeProcessedAttributes: function(processedAttrs, roProcessedAttrs) {\n\n processedAttrs.set || (processedAttrs.set = {})\n processedAttrs.position || (processedAttrs.position = {})\n processedAttrs.offset || (processedAttrs.offset = {})\n\n joint.util.assign(processedAttrs.set, roProcessedAttrs.set)\n joint.util.assign(processedAttrs.position, roProcessedAttrs.position)\n joint.util.assign(processedAttrs.offset, roProcessedAttrs.offset)\n\n // Handle also the special transform property.\n const transform = processedAttrs.normal && processedAttrs.normal.transform\n if (transform !== undefined && roProcessedAttrs.normal) {\n roProcessedAttrs.normal.transform = transform\n }\n processedAttrs.normal = roProcessedAttrs.normal\n },\n\n onRemove: function() {\n this.removeTools()\n },\n\n _toolsView: null,\n\n hasTools: function(name) {\n const toolsView = this._toolsView\n if (!toolsView) return false\n if (!name) return true\n return (toolsView.getName() === name)\n },\n\n addTools: function(toolsView) {\n\n this.removeTools()\n\n if (toolsView instanceof joint.dia.ToolsView) {\n this._toolsView = toolsView\n toolsView.configure({ relatedView: this })\n toolsView.listenTo(this.paper, 'tools:event', this.onToolEvent.bind(this))\n toolsView.mount()\n }\n return this\n },\n\n updateTools: function(opt) {\n\n const toolsView = this._toolsView\n if (toolsView) toolsView.update(opt)\n return this\n },\n\n removeTools: function() {\n\n const toolsView = this._toolsView\n if (toolsView) {\n toolsView.remove()\n this._toolsView = null\n }\n return this\n },\n\n hideTools: function() {\n\n const toolsView = this._toolsView\n if (toolsView) toolsView.hide()\n return this\n },\n\n showTools: function() {\n\n const toolsView = this._toolsView\n if (toolsView) toolsView.show()\n return this\n },\n\n onToolEvent: function(event) {\n switch (event) {\n case 'remove':\n this.removeTools()\n break\n case 'hide':\n this.hideTools()\n break\n case 'show':\n this.showTools()\n break\n }\n },\n\n // Interaction. The controller part.\n // ---------------------------------\n\n // Interaction is handled by the paper and delegated to the view in interest.\n // `x` & `y` parameters passed to these functions represent the coordinates already snapped to the paper grid.\n // If necessary, real coordinates can be obtained from the `evt` event object.\n\n // These functions are supposed to be overriden by the views that inherit from `joint.dia.Cell`,\n // i.e. `joint.dia.Element` and `joint.dia.Link`.\n\n pointerdblclick: function(evt, x, y) {\n\n this.notify('cell:pointerdblclick', evt, x, y)\n },\n\n pointerclick: function(evt, x, y) {\n\n this.notify('cell:pointerclick', evt, x, y)\n },\n\n contextmenu: function(evt, x, y) {\n\n this.notify('cell:contextmenu', evt, x, y)\n },\n\n pointerdown: function(evt, x, y) {\n\n if (this.model.graph) {\n this.model.startBatch('pointer')\n this._graph = this.model.graph\n }\n\n this.notify('cell:pointerdown', evt, x, y)\n },\n\n pointermove: function(evt, x, y) {\n\n this.notify('cell:pointermove', evt, x, y)\n },\n\n pointerup: function(evt, x, y) {\n\n this.notify('cell:pointerup', evt, x, y)\n\n if (this._graph) {\n // we don't want to trigger event on model as model doesn't\n // need to be member of collection anymore (remove)\n this._graph.stopBatch('pointer', { cell: this.model })\n delete this._graph\n }\n },\n\n mouseover: function(evt) {\n\n this.notify('cell:mouseover', evt)\n },\n\n mouseout: function(evt) {\n\n this.notify('cell:mouseout', evt)\n },\n\n mouseenter: function(evt) {\n\n this.notify('cell:mouseenter', evt)\n },\n\n mouseleave: function(evt) {\n\n this.notify('cell:mouseleave', evt)\n },\n\n mousewheel: function(evt, x, y, delta) {\n\n this.notify('cell:mousewheel', evt, x, y, delta)\n },\n\n onevent: function(evt, eventName, x, y) {\n\n this.notify(eventName, evt, x, y)\n },\n\n onmagnet: function() {\n\n // noop\n },\n\n setInteractivity: function(value) {\n\n this.options.interactive = value\n }\n}, {\n\n dispatchToolsEvent: function(paper, event) {\n if ((typeof event === 'string') && (paper instanceof joint.dia.Paper)) {\n paper.trigger('tools:event', event)\n }\n }\n})\n\n\n// joint.dia.Element base model.\n// -----------------------------\n\njoint.dia.Element = joint.dia.Cell.extend({\n\n defaults: {\n position: { x: 0, y: 0 },\n size: { width: 1, height: 1 },\n angle: 0\n },\n\n initialize: function() {\n\n this._initializePorts()\n joint.dia.Cell.prototype.initialize.apply(this, arguments)\n },\n\n /**\n * @abstract\n */\n _initializePorts: function() {\n // implemented in ports.js\n },\n\n isElement: function() {\n\n return true\n },\n\n position: function(x, y, opt) {\n\n const isSetter = joint.util.isNumber(y)\n\n opt = (isSetter ? opt : x) || {}\n\n // option `parentRelative` for setting the position relative to the element's parent.\n if (opt.parentRelative) {\n\n // Getting the parent's position requires the collection.\n // Cell.parent() holds cell id only.\n if (!this.graph) throw new Error('Element must be part of a graph.')\n\n const parent = this.getParentCell()\n var parentPosition = parent && !parent.isLink()\n ? parent.get('position')\n : { x: 0, y: 0 }\n }\n\n if (isSetter) {\n\n if (opt.parentRelative) {\n x += parentPosition.x\n y += parentPosition.y\n }\n\n if (opt.deep) {\n const currentPosition = this.get('position')\n this.translate(x - currentPosition.x, y - currentPosition.y, opt)\n } else {\n this.set('position', { x: x, y: y }, opt)\n }\n\n return this\n\n } else { // Getter returns a geometry point.\n\n const elementPosition = g.point(this.get('position'))\n\n return opt.parentRelative\n ? elementPosition.difference(parentPosition)\n : elementPosition\n }\n },\n\n translate: function(tx, ty, opt) {\n\n tx = tx || 0\n ty = ty || 0\n\n if (tx === 0 && ty === 0) {\n // Like nothing has happened.\n return this\n }\n\n opt = opt || {}\n // Pass the initiator of the translation.\n opt.translateBy = opt.translateBy || this.id\n\n const position = this.get('position') || { x: 0, y: 0 }\n\n if (opt.restrictedArea && opt.translateBy === this.id) {\n\n // We are restricting the translation for the element itself only. We get\n // the bounding box of the element including all its embeds.\n // All embeds have to be translated the exact same way as the element.\n const bbox = this.getBBox({ deep: true })\n const ra = opt.restrictedArea\n //- - - - - - - - - - - - -> ra.x + ra.width\n // - - - -> position.x |\n // -> bbox.x\n // ▓▓▓▓▓▓▓ |\n // ░░░░░░░▓▓▓▓▓▓▓\n // ░░░░░░░░░ |\n // ▓▓▓▓▓▓▓▓░░░░░░░\n // ▓▓▓▓▓▓▓▓ |\n // <-dx-> | restricted area right border\n // <-width-> | ░ translated element\n // <- - bbox.width - -> ▓ embedded element\n const dx = position.x - bbox.x\n const dy = position.y - bbox.y\n // Find the maximal/minimal coordinates that the element can be translated\n // while complies the restrictions.\n const x = Math.max(ra.x + dx, Math.min(ra.x + ra.width + dx - bbox.width, position.x + tx))\n const y = Math.max(ra.y + dy, Math.min(ra.y + ra.height + dy - bbox.height, position.y + ty))\n // recalculate the translation taking the resctrictions into account.\n tx = x - position.x\n ty = y - position.y\n }\n\n const translatedPosition = {\n x: position.x + tx,\n y: position.y + ty\n }\n\n // To find out by how much an element was translated in event 'change:position' handlers.\n opt.tx = tx\n opt.ty = ty\n\n if (opt.transition) {\n\n if (!joint.util.isObject(opt.transition)) opt.transition = {}\n\n this.transition('position', translatedPosition, joint.util.assign({}, opt.transition, {\n valueFunction: joint.util.interpolate.object\n }))\n\n } else {\n\n this.set('position', translatedPosition, opt)\n }\n\n // Recursively call `translate()` on all the embeds cells.\n joint.util.invoke(this.getEmbeddedCells(), 'translate', tx, ty, opt)\n\n return this\n },\n\n size: function(width, height, opt) {\n\n const currentSize = this.get('size')\n // Getter\n // () signature\n if (width === undefined) {\n return {\n width: currentSize.width,\n height: currentSize.height\n }\n }\n // Setter\n // (size, opt) signature\n if (joint.util.isObject(width)) {\n opt = height\n height = joint.util.isNumber(width.height) ? width.height : currentSize.height\n width = joint.util.isNumber(width.width) ? width.width : currentSize.width\n }\n\n return this.resize(width, height, opt)\n },\n\n resize: function(width, height, opt) {\n\n opt = opt || {}\n\n this.startBatch('resize', opt)\n\n if (opt.direction) {\n\n const currentSize = this.get('size')\n\n switch (opt.direction) {\n\n case 'left':\n case 'right':\n // Don't change height when resizing horizontally.\n height = currentSize.height\n break\n\n case 'top':\n case 'bottom':\n // Don't change width when resizing vertically.\n width = currentSize.width\n break\n }\n\n // Get the angle and clamp its value between 0 and 360 degrees.\n const angle = g.normalizeAngle(this.get('angle') || 0)\n\n let quadrant = {\n 'top-right': 0,\n right: 0,\n 'top-left': 1,\n top: 1,\n 'bottom-left': 2,\n left: 2,\n 'bottom-right': 3,\n bottom: 3\n }[opt.direction]\n\n if (opt.absolute) {\n\n // We are taking the element's rotation into account\n quadrant += Math.floor((angle + 45) / 90)\n quadrant %= 4\n }\n\n // This is a rectangle in size of the unrotated element.\n const bbox = this.getBBox()\n\n // Pick the corner point on the element, which meant to stay on its place before and\n // after the rotation.\n const fixedPoint = bbox[['bottomLeft', 'corner', 'topRight', 'origin'][quadrant]]()\n\n // Find an image of the previous indent point. This is the position, where is the\n // point actually located on the screen.\n const imageFixedPoint = g.point(fixedPoint).rotate(bbox.center(), -angle)\n\n // Every point on the element rotates around a circle with the centre of rotation\n // in the middle of the element while the whole element is being rotated. That means\n // that the distance from a point in the corner of the element (supposed its always rect) to\n // the center of the element doesn't change during the rotation and therefore it equals\n // to a distance on unrotated element.\n // We can find the distance as DISTANCE = (ELEMENTWIDTH/2)^2 + (ELEMENTHEIGHT/2)^2)^0.5.\n const radius = Math.sqrt((width * width) + (height * height)) / 2\n\n // Now we are looking for an angle between x-axis and the line starting at image of fixed point\n // and ending at the center of the element. We call this angle `alpha`.\n\n // The image of a fixed point is located in n-th quadrant. For each quadrant passed\n // going anti-clockwise we have to add 90 degrees. Note that the first quadrant has index 0.\n //\n // 3 | 2\n // --c-- Quadrant positions around the element's center `c`\n // 0 | 1\n //\n let alpha = quadrant * Math.PI / 2\n\n // Add an angle between the beginning of the current quadrant (line parallel with x-axis or y-axis\n // going through the center of the element) and line crossing the indent of the fixed point and the center\n // of the element. This is the angle we need but on the unrotated element.\n alpha += Math.atan(quadrant % 2 == 0 ? height / width : width / height)\n\n // Lastly we have to deduct the original angle the element was rotated by and that's it.\n alpha -= g.toRad(angle)\n\n // With this angle and distance we can easily calculate the centre of the unrotated element.\n // Note that fromPolar constructor accepts an angle in radians.\n const center = g.point.fromPolar(radius, alpha, imageFixedPoint)\n\n // The top left corner on the unrotated element has to be half a width on the left\n // and half a height to the top from the center. This will be the origin of rectangle\n // we were looking for.\n const origin = g.point(center).offset(width / -2, height / -2)\n\n // Resize the element (before re-positioning it).\n this.set('size', { width: width, height: height }, opt)\n\n // Finally, re-position the element.\n this.position(origin.x, origin.y, opt)\n\n } else {\n\n // Resize the element.\n this.set('size', { width: width, height: height }, opt)\n }\n\n this.stopBatch('resize', opt)\n\n return this\n },\n\n scale: function(sx, sy, origin, opt) {\n\n const scaledBBox = this.getBBox().scale(sx, sy, origin)\n this.startBatch('scale', opt)\n this.position(scaledBBox.x, scaledBBox.y, opt)\n this.resize(scaledBBox.width, scaledBBox.height, opt)\n this.stopBatch('scale')\n return this\n },\n\n fitEmbeds: function(opt) {\n\n opt = opt || {}\n\n // Getting the children's size and position requires the collection.\n // Cell.get('embdes') helds an array of cell ids only.\n if (!this.graph) throw new Error('Element must be part of a graph.')\n\n const embeddedCells = this.getEmbeddedCells()\n\n if (embeddedCells.length > 0) {\n\n this.startBatch('fit-embeds', opt)\n\n if (opt.deep) {\n // Recursively apply fitEmbeds on all embeds first.\n joint.util.invoke(embeddedCells, 'fitEmbeds', opt)\n }\n\n // Compute cell's size and position based on the children bbox\n // and given padding.\n const bbox = this.graph.getCellsBBox(embeddedCells)\n const padding = joint.util.normalizeSides(opt.padding)\n\n // Apply padding computed above to the bbox.\n bbox.moveAndExpand({\n x: -padding.left,\n y: -padding.top,\n width: padding.right + padding.left,\n height: padding.bottom + padding.top\n })\n\n // Set new element dimensions finally.\n this.set({\n position: { x: bbox.x, y: bbox.y },\n size: { width: bbox.width, height: bbox.height }\n }, opt)\n\n this.stopBatch('fit-embeds')\n }\n\n return this\n },\n\n // Rotate element by `angle` degrees, optionally around `origin` point.\n // If `origin` is not provided, it is considered to be the center of the element.\n // If `absolute` is `true`, the `angle` is considered is abslute, i.e. it is not\n // the difference from the previous angle.\n rotate: function(angle, absolute, origin, opt) {\n\n if (origin) {\n\n const center = this.getBBox().center()\n const size = this.get('size')\n const position = this.get('position')\n center.rotate(origin, this.get('angle') - angle)\n const dx = center.x - size.width / 2 - position.x\n const dy = center.y - size.height / 2 - position.y\n this.startBatch('rotate', { angle: angle, absolute: absolute, origin: origin })\n this.position(position.x + dx, position.y + dy, opt)\n this.rotate(angle, absolute, null, opt)\n this.stopBatch('rotate')\n\n } else {\n\n this.set('angle', absolute ? angle : (this.get('angle') + angle) % 360, opt)\n }\n\n return this\n },\n\n angle: function() {\n return g.normalizeAngle(this.get('angle') || 0)\n },\n\n getBBox: function(opt) {\n\n opt = opt || {}\n\n if (opt.deep && this.graph) {\n\n // Get all the embedded elements using breadth first algorithm,\n // that doesn't use recursion.\n const elements = this.getEmbeddedCells({ deep: true, breadthFirst: true })\n // Add the model itself.\n elements.push(this)\n\n return this.graph.getCellsBBox(elements)\n }\n\n const position = this.get('position')\n const size = this.get('size')\n\n return new g.Rect(position.x, position.y, size.width, size.height)\n }\n})\n\n// joint.dia.Element base view and controller.\n// -------------------------------------------\n\njoint.dia.ElementView = joint.dia.CellView.extend({\n\n /**\n * @abstract\n */\n _removePorts: function() {\n // implemented in ports.js\n },\n\n /**\n *\n * @abstract\n */\n _renderPorts: function() {\n // implemented in ports.js\n },\n\n className: function() {\n\n const classNames = joint.dia.CellView.prototype.className.apply(this).split(' ')\n\n classNames.push('element')\n\n return classNames.join(' ')\n },\n\n metrics: null,\n\n initialize: function() {\n\n joint.dia.CellView.prototype.initialize.apply(this, arguments)\n\n const {model} = this\n\n this.listenTo(model, 'change:position', this.translate)\n this.listenTo(model, 'change:size', this.resize)\n this.listenTo(model, 'change:angle', this.rotate)\n this.listenTo(model, 'change:markup', this.render)\n\n this._initializePorts()\n\n this.metrics = {}\n },\n\n /**\n * @abstract\n */\n _initializePorts: function() {\n\n },\n\n update: function(cell, renderingOnlyAttrs) {\n\n this.metrics = {}\n\n this._removePorts()\n\n const {model} = this\n const modelAttrs = model.attr()\n this.updateDOMSubtreeAttributes(this.el, modelAttrs, {\n rootBBox: new g.Rect(model.size()),\n selectors: this.selectors,\n scalableNode: this.scalableNode,\n rotatableNode: this.rotatableNode,\n // Use rendering only attributes if they differs from the model attributes\n roAttributes: (renderingOnlyAttrs === modelAttrs) ? null : renderingOnlyAttrs\n })\n\n this._renderPorts()\n },\n\n rotatableSelector: 'rotatable',\n scalableSelector: 'scalable',\n scalableNode: null,\n rotatableNode: null,\n\n // `prototype.markup` is rendered by default. Set the `markup` attribute on the model if the\n // default markup is not desirable.\n renderMarkup: function() {\n\n const element = this.model\n const markup = element.get('markup') || element.markup\n if (!markup) throw new Error('dia.ElementView: markup required')\n if (Array.isArray(markup)) return this.renderJSONMarkup(markup)\n if (typeof markup === 'string') return this.renderStringMarkup(markup)\n throw new Error('dia.ElementView: invalid markup')\n },\n\n renderJSONMarkup: function(markup) {\n\n const doc = joint.util.parseDOMJSON(markup)\n // Selectors\n const selectors = this.selectors = doc.selectors\n const rootSelector = this.selector\n if (selectors[rootSelector]) throw new Error('dia.ElementView: ambiguous root selector.')\n selectors[rootSelector] = this.el\n // Cache transformation groups\n this.rotatableNode = V(selectors[this.rotatableSelector]) || null\n this.scalableNode = V(selectors[this.scalableSelector]) || null\n // Fragment\n this.vel.append(doc.fragment)\n },\n\n renderStringMarkup: function(markup) {\n\n const {vel} = this\n vel.append(V(markup))\n // Cache transformation groups\n this.rotatableNode = vel.findOne('.rotatable')\n this.scalableNode = vel.findOne('.scalable')\n\n const selectors = this.selectors = {}\n selectors[this.selector] = this.el\n },\n\n render: function() {\n\n this.vel.empty()\n this.renderMarkup()\n if (this.scalableNode) {\n // Double update is necessary for elements with the scalable group only\n // Note the resize() triggers the other `update`.\n this.update()\n }\n this.resize()\n if (this.rotatableNode) {\n // Translate transformation is applied on `this.el` while the rotation transformation\n // on `this.rotatableNode`\n this.rotate()\n this.translate()\n return this\n }\n this.updateTransformation()\n return this\n },\n\n resize: function() {\n\n if (this.scalableNode) return this.sgResize.apply(this, arguments)\n if (this.model.attributes.angle) this.rotate()\n this.update()\n },\n\n translate: function() {\n\n if (this.rotatableNode) return this.rgTranslate()\n this.updateTransformation()\n },\n\n rotate: function() {\n\n if (this.rotatableNode) return this.rgRotate()\n this.updateTransformation()\n },\n\n updateTransformation: function() {\n\n let transformation = this.getTranslateString()\n const rotateString = this.getRotateString()\n if (rotateString) transformation += ` ${ rotateString}`\n this.vel.attr('transform', transformation)\n },\n\n getTranslateString: function() {\n\n const {position} = this.model.attributes\n return `translate(${ position.x },${ position.y })`\n },\n\n getRotateString: function() {\n const {attributes} = this.model\n const {angle} = attributes\n if (!angle) return null\n const {size} = attributes\n return `rotate(${ angle },${ size.width / 2 },${ size.height / 2 })`\n },\n\n getBBox: function(opt) {\n\n let bbox\n if (opt && opt.useModelGeometry) {\n const {model} = this\n bbox = model.getBBox().bbox(model.angle())\n } else {\n bbox = this.getNodeBBox(this.el)\n }\n\n return this.paper.localToPaperRect(bbox)\n },\n\n nodeCache: function(magnet) {\n\n const id = V.ensureId(magnet)\n let metrics = this.metrics[id]\n if (!metrics) metrics = this.metrics[id] = {}\n return metrics\n },\n\n getNodeData: function(magnet) {\n\n const metrics = this.nodeCache(magnet)\n if (!metrics.data) metrics.data = {}\n return metrics.data\n },\n\n getNodeBBox: function(magnet) {\n\n const rect = this.getNodeBoundingRect(magnet)\n const magnetMatrix = this.getNodeMatrix(magnet)\n const translateMatrix = this.getRootTranslateMatrix()\n const rotateMatrix = this.getRootRotateMatrix()\n return V.transformRect(rect, translateMatrix.multiply(rotateMatrix).multiply(magnetMatrix))\n },\n\n getNodeBoundingRect: function(magnet) {\n\n const metrics = this.nodeCache(magnet)\n if (metrics.boundingRect === undefined) metrics.boundingRect = V(magnet).getBBox()\n return new g.Rect(metrics.boundingRect)\n },\n\n getNodeUnrotatedBBox: function(magnet) {\n\n const rect = this.getNodeBoundingRect(magnet)\n const magnetMatrix = this.getNodeMatrix(magnet)\n const translateMatrix = this.getRootTranslateMatrix()\n return V.transformRect(rect, translateMatrix.multiply(magnetMatrix))\n },\n\n getNodeShape: function(magnet) {\n\n const metrics = this.nodeCache(magnet)\n if (metrics.geometryShape === undefined) metrics.geometryShape = V(magnet).toGeometryShape()\n return metrics.geometryShape.clone()\n },\n\n getNodeMatrix: function(magnet) {\n\n const metrics = this.nodeCache(magnet)\n if (metrics.magnetMatrix === undefined) {\n const target = this.rotatableNode || this.el\n metrics.magnetMatrix = V(magnet).getTransformToElement(target)\n }\n return V.createSVGMatrix(metrics.magnetMatrix)\n },\n\n getRootTranslateMatrix: function() {\n\n const {model} = this\n const position = model.position()\n const mt = V.createSVGMatrix().translate(position.x, position.y)\n return mt\n },\n\n getRootRotateMatrix: function() {\n\n let mr = V.createSVGMatrix()\n const {model} = this\n const angle = model.angle()\n if (angle) {\n const bbox = model.getBBox()\n const cx = bbox.width / 2\n const cy = bbox.height / 2\n mr = mr.translate(cx, cy).rotate(angle).translate(-cx, -cy)\n }\n return mr\n },\n\n // Rotatable & Scalable Group\n // always slower, kept mainly for backwards compatibility\n\n rgRotate: function() {\n\n this.rotatableNode.attr('transform', this.getRotateString())\n },\n\n rgTranslate: function() {\n\n this.vel.attr('transform', this.getTranslateString())\n },\n\n sgResize: function(cell, changed, opt) {\n\n const {model} = this\n const angle = model.get('angle') || 0\n const size = model.get('size') || { width: 1, height: 1 }\n const scalable = this.scalableNode\n\n // Getting scalable group's bbox.\n // Due to a bug in webkit's native SVG .getBBox implementation, the bbox of groups with path children includes the paths' control points.\n // To work around the issue, we need to check whether there are any path elements inside the scalable group.\n let recursive = false\n if (scalable.node.getElementsByTagName('path').length > 0) {\n // If scalable has at least one descendant that is a path, we need to switch to recursive bbox calculation.\n // If there are no path descendants, group bbox calculation works and so we can use the (faster) native function directly.\n recursive = true\n }\n const scalableBBox = scalable.getBBox({ recursive: recursive })\n\n // Make sure `scalableBbox.width` and `scalableBbox.height` are not zero which can happen if the element does not have any content. By making\n // the width/height 1, we prevent HTML errors of the type `scale(Infinity, Infinity)`.\n const sx = (size.width / (scalableBBox.width || 1))\n const sy = (size.height / (scalableBBox.height || 1))\n scalable.attr('transform', `scale(${ sx },${ sy })`)\n\n // Now the interesting part. The goal is to be able to store the object geometry via just `x`, `y`, `angle`, `width` and `height`\n // Order of transformations is significant but we want to reconstruct the object always in the order:\n // resize(), rotate(), translate() no matter of how the object was transformed. For that to work,\n // we must adjust the `x` and `y` coordinates of the object whenever we resize it (because the origin of the\n // rotation changes). The new `x` and `y` coordinates are computed by canceling the previous rotation\n // around the center of the resized object (which is a different origin then the origin of the previous rotation)\n // and getting the top-left corner of the resulting object. Then we clean up the rotation back to what it originally was.\n\n // Cancel the rotation but now around a different origin, which is the center of the scaled object.\n const rotatable = this.rotatableNode\n const rotation = rotatable && rotatable.attr('transform')\n if (rotation && rotation !== null) {\n\n rotatable.attr('transform', `${rotation } rotate(${ -angle },${ size.width / 2 },${ size.height / 2 })`)\n const rotatableBBox = scalable.getBBox({ target: this.paper.viewport })\n\n // Store new x, y and perform rotate() again against the new rotation origin.\n model.set('position', { x: rotatableBBox.x, y: rotatableBBox.y }, opt)\n this.rotate()\n }\n\n // Update must always be called on non-rotated element. Otherwise, relative positioning\n // would work with wrong (rotated) bounding boxes.\n this.update()\n },\n\n // Embedding mode methods.\n // -----------------------\n\n prepareEmbedding: function(data) {\n\n data || (data = {})\n\n const model = data.model || this.model\n const paper = data.paper || this.paper\n const graph = paper.model\n\n model.startBatch('to-front')\n\n // Bring the model to the front with all his embeds.\n model.toFront({ deep: true, ui: true })\n\n // Note that at this point cells in the collection are not sorted by z index (it's running in the batch, see\n // the dia.Graph._sortOnChangeZ), so we can't assume that the last cell in the collection has the highest z.\n const maxZ = graph.get('cells').max('z').get('z')\n const connectedLinks = graph.getConnectedLinks(model, { deep: true, includeEnclosed: true })\n\n // Move to front also all the inbound and outbound links that are connected\n // to any of the element descendant. If we bring to front only embedded elements,\n // links connected to them would stay in the background.\n joint.util.invoke(connectedLinks, 'set', 'z', maxZ + 1, { ui: true })\n\n model.stopBatch('to-front')\n\n // Before we start looking for suitable parent we remove the current one.\n const parentId = model.parent()\n parentId && graph.getCell(parentId).unembed(model, { ui: true })\n },\n\n processEmbedding: function(data) {\n\n data || (data = {})\n\n const model = data.model || this.model\n const paper = data.paper || this.paper\n const paperOptions = paper.options\n\n let candidates = []\n if (joint.util.isFunction(paperOptions.findParentBy)) {\n const parents = joint.util.toArray(paperOptions.findParentBy.call(paper.model, this))\n candidates = parents.filter(function(el) {\n return el instanceof joint.dia.Cell && this.model.id !== el.id && !el.isEmbeddedIn(this.model)\n }.bind(this))\n } else {\n candidates = paper.model.findModelsUnderElement(model, { searchBy: paperOptions.findParentBy })\n }\n\n if (paperOptions.frontParentOnly) {\n // pick the element with the highest `z` index\n candidates = candidates.slice(-1)\n }\n\n let newCandidateView = null\n const prevCandidateView = data.candidateEmbedView\n\n // iterate over all candidates starting from the last one (has the highest z-index).\n for (let i = candidates.length - 1; i >= 0; i--) {\n\n const candidate = candidates[i]\n\n if (prevCandidateView && prevCandidateView.model.id == candidate.id) {\n\n // candidate remains the same\n newCandidateView = prevCandidateView\n break\n\n } else {\n\n const view = candidate.findView(paper)\n if (paperOptions.validateEmbedding.call(paper, this, view)) {\n\n // flip to the new candidate\n newCandidateView = view\n break\n }\n }\n }\n\n if (newCandidateView && newCandidateView != prevCandidateView) {\n // A new candidate view found. Highlight the new one.\n this.clearEmbedding(data)\n data.candidateEmbedView = newCandidateView.highlight(null, { embedding: true })\n }\n\n if (!newCandidateView && prevCandidateView) {\n // No candidate view found. Unhighlight the previous candidate.\n this.clearEmbedding(data)\n }\n },\n\n clearEmbedding: function(data) {\n\n data || (data = {})\n\n const candidateView = data.candidateEmbedView\n if (candidateView) {\n // No candidate view found. Unhighlight the previous candidate.\n candidateView.unhighlight(null, { embedding: true })\n data.candidateEmbedView = null\n }\n },\n\n finalizeEmbedding: function(data) {\n\n data || (data = {})\n\n const candidateView = data.candidateEmbedView\n const model = data.model || this.model\n const paper = data.paper || this.paper\n\n if (candidateView) {\n\n // We finished embedding. Candidate view is chosen to become the parent of the model.\n candidateView.model.embed(model, { ui: true })\n candidateView.unhighlight(null, { embedding: true })\n\n data.candidateEmbedView = null\n }\n\n joint.util.invoke(paper.model.getConnectedLinks(model, { deep: true }), 'reparent', { ui: true })\n },\n\n // Interaction. The controller part.\n // ---------------------------------\n\n pointerdblclick: function(evt, x, y) {\n\n joint.dia.CellView.prototype.pointerdblclick.apply(this, arguments)\n this.notify('element:pointerdblclick', evt, x, y)\n },\n\n pointerclick: function(evt, x, y) {\n\n joint.dia.CellView.prototype.pointerclick.apply(this, arguments)\n this.notify('element:pointerclick', evt, x, y)\n },\n\n contextmenu: function(evt, x, y) {\n\n joint.dia.CellView.prototype.contextmenu.apply(this, arguments)\n this.notify('element:contextmenu', evt, x, y)\n },\n\n pointerdown: function(evt, x, y) {\n\n joint.dia.CellView.prototype.pointerdown.apply(this, arguments)\n this.notify('element:pointerdown', evt, x, y)\n\n this.dragStart(evt, x, y)\n },\n\n pointermove: function(evt, x, y) {\n\n const data = this.eventData(evt)\n switch (data.action) {\n case 'move':\n this.drag(evt, x, y)\n break\n case 'magnet':\n this.dragMagnet(evt, x, y)\n break\n }\n\n if (!data.stopPropagation) {\n joint.dia.CellView.prototype.pointermove.apply(this, arguments)\n this.notify('element:pointermove', evt, x, y)\n }\n\n // Make sure the element view data is passed along.\n // It could have been wiped out in the handlers above.\n this.eventData(evt, data)\n },\n\n pointerup: function(evt, x, y) {\n\n const data = this.eventData(evt)\n switch (data.action) {\n case 'move':\n this.dragEnd(evt, x, y)\n break\n case 'magnet':\n this.dragMagnetEnd(evt, x, y)\n return\n }\n\n if (!data.stopPropagation) {\n this.notify('element:pointerup', evt, x, y)\n joint.dia.CellView.prototype.pointerup.apply(this, arguments)\n }\n },\n\n mouseover: function(evt) {\n\n joint.dia.CellView.prototype.mouseover.apply(this, arguments)\n this.notify('element:mouseover', evt)\n },\n\n mouseout: function(evt) {\n\n joint.dia.CellView.prototype.mouseout.apply(this, arguments)\n this.notify('element:mouseout', evt)\n },\n\n mouseenter: function(evt) {\n\n joint.dia.CellView.prototype.mouseenter.apply(this, arguments)\n this.notify('element:mouseenter', evt)\n },\n\n mouseleave: function(evt) {\n\n joint.dia.CellView.prototype.mouseleave.apply(this, arguments)\n this.notify('element:mouseleave', evt)\n },\n\n mousewheel: function(evt, x, y, delta) {\n\n joint.dia.CellView.prototype.mousewheel.apply(this, arguments)\n this.notify('element:mousewheel', evt, x, y, delta)\n },\n\n onmagnet: function(evt, x, y) {\n\n this.dragMagnetStart(evt, x, y)\n\n const {stopPropagation} = this.eventData(evt)\n if (stopPropagation) evt.stopPropagation()\n },\n\n // Drag Start Handlers\n\n dragStart: function(evt, x, y) {\n\n if (!this.can('elementMove')) return\n\n this.eventData(evt, {\n action: 'move',\n x: x,\n y: y,\n restrictedArea: this.paper.getRestrictedArea(this)\n })\n },\n\n dragMagnetStart: function(evt, x, y) {\n\n if (!this.can('addLinkFromMagnet')) return\n\n this.model.startBatch('add-link')\n\n const {paper} = this\n const graph = paper.model\n const magnet = evt.target\n const link = paper.getDefaultLink(this, magnet)\n const sourceEnd = this.getLinkEnd(magnet, x, y, link, 'source')\n const targetEnd = { x: x, y: y }\n\n link.set({ source: sourceEnd, target: targetEnd })\n link.addTo(graph, { async: false, ui: true })\n\n const linkView = link.findView(paper)\n joint.dia.CellView.prototype.pointerdown.apply(linkView, arguments)\n linkView.notify('link:pointerdown', evt, x, y)\n const data = linkView.startArrowheadMove('target', { whenNotAllowed: 'remove' })\n linkView.eventData(evt, data)\n\n this.eventData(evt, {\n action: 'magnet',\n linkView: linkView,\n stopPropagation: true\n })\n\n this.paper.delegateDragEvents(this, evt.data)\n },\n\n // Drag Handlers\n\n drag: function(evt, x, y) {\n\n const {paper} = this\n const grid = paper.options.gridSize\n const element = this.model\n const position = element.position()\n const data = this.eventData(evt)\n\n // Make sure the new element's position always snaps to the current grid after\n // translate as the previous one could be calculated with a different grid size.\n const tx = g.snapToGrid(position.x, grid) - position.x + g.snapToGrid(x - data.x, grid)\n const ty = g.snapToGrid(position.y, grid) - position.y + g.snapToGrid(y - data.y, grid)\n\n element.translate(tx, ty, { restrictedArea: data.restrictedArea, ui: true })\n\n let embedding = !!data.embedding\n if (paper.options.embeddingMode) {\n if (!embedding) {\n // Prepare the element for embedding only if the pointer moves.\n // We don't want to do unnecessary action with the element\n // if an user only clicks/dblclicks on it.\n this.prepareEmbedding(data)\n embedding = true\n }\n this.processEmbedding(data)\n }\n\n this.eventData(evt, {\n x: g.snapToGrid(x, grid),\n y: g.snapToGrid(y, grid),\n embedding: embedding\n })\n },\n\n dragMagnet: function(evt, x, y) {\n\n const data = this.eventData(evt)\n const {linkView} = data\n if (linkView) linkView.pointermove(evt, x, y)\n },\n\n // Drag End Handlers\n\n dragEnd: function(evt, x, y) {\n\n const data = this.eventData(evt)\n if (data.embedding) this.finalizeEmbedding(data)\n },\n\n dragMagnetEnd: function(evt, x, y) {\n\n const data = this.eventData(evt)\n const {linkView} = data\n if (linkView) linkView.pointerup(evt, x, y)\n\n this.model.stopBatch('add-link')\n }\n\n})\n\n\n// joint.dia.Link base model.\n// --------------------------\n\njoint.dia.Link = joint.dia.Cell.extend({\n\n // The default markup for links.\n markup: [\n '<path class=\"connection\" stroke=\"black\" d=\"M 0 0 0 0\"/>',\n '<path class=\"marker-source\" fill=\"black\" stroke=\"black\" d=\"M 0 0 0 0\"/>',\n '<path class=\"marker-target\" fill=\"black\" stroke=\"black\" d=\"M 0 0 0 0\"/>',\n '<path class=\"connection-wrap\" d=\"M 0 0 0 0\"/>',\n '<g class=\"labels\"/>',\n '<g class=\"marker-vertices\"/>',\n '<g class=\"marker-arrowheads\"/>',\n '<g class=\"link-tools\"/>'\n ].join(''),\n\n toolMarkup: [\n '<g class=\"link-tool\">',\n '<g class=\"tool-remove\" event=\"remove\">',\n '<circle r=\"11\" />',\n '<path transform=\"scale(.8) translate(-16, -16)\" d=\"M24.778,21.419 19.276,15.917 24.777,10.415 21.949,7.585 16.447,13.087 10.945,7.585 8.117,10.415 13.618,15.917 8.116,21.419 10.946,24.248 16.447,18.746 21.948,24.248z\" />',\n '<title>Remove link.',\n '',\n '',\n '',\n '',\n 'Link options.',\n '',\n ''\n ].join(''),\n\n doubleToolMarkup: undefined,\n\n // The default markup for showing/removing vertices. These elements are the children of the .marker-vertices element (see `this.markup`).\n // Only .marker-vertex and .marker-vertex-remove element have special meaning. The former is used for\n // dragging vertices (changin their position). The latter is used for removing vertices.\n vertexMarkup: [\n ', <%= y %>)\">',\n '\" r=\"10\" />',\n '\" d=\"M16,5.333c-7.732,0-14,4.701-14,10.5c0,1.982,0.741,3.833,2.016,5.414L2,25.667l5.613-1.441c2.339,1.317,5.237,2.107,8.387,2.107c7.732,0,14-4.701,14-10.5C30,10.034,23.732,5.333,16,5.333z\" transform=\"translate(5, -33)\"/>',\n '\" transform=\"scale(.8) translate(9.5, -37)\" d=\"M24.778,21.419 19.276,15.917 24.777,10.415 21.949,7.585 16.447,13.087 10.945,7.585 8.117,10.415 13.618,15.917 8.116,21.419 10.946,24.248 16.447,18.746 21.948,24.248z\">',\n 'Remove vertex.',\n '',\n ''\n ].join(''),\n\n arrowheadMarkup: [\n '\">',\n '\" d=\"M 26 0 L 0 13 L 26 26 z\" />',\n ''\n ].join(''),\n\n // may be overwritten by user to change default label (its markup, attrs, position)\n defaultLabel: undefined,\n\n // deprecated\n // may be overwritten by user to change default label markup\n // lower priority than defaultLabel.markup\n labelMarkup: undefined,\n\n // private\n _builtins: {\n defaultLabel: {\n // builtin default markup:\n // used if neither defaultLabel.markup\n // nor label.markup is set\n markup: [\n {\n tagName: 'rect',\n selector: 'rect' // faster than tagName CSS selector\n }, {\n tagName: 'text',\n selector: 'text' // faster than tagName CSS selector\n }\n ],\n // builtin default attributes:\n // applied only if builtin default markup is used\n attrs: {\n text: {\n fill: '#000000',\n fontSize: 14,\n textAnchor: 'middle',\n yAlignment: 'middle',\n pointerEvents: 'none'\n },\n rect: {\n ref: 'text',\n fill: '#ffffff',\n rx: 3,\n ry: 3,\n refWidth: 1,\n refHeight: 1,\n refX: 0,\n refY: 0\n }\n },\n // builtin default position:\n // used if neither defaultLabel.position\n // nor label.position is set\n position: {\n distance: 0.5\n }\n }\n },\n\n defaults: {\n type: 'link',\n source: {},\n target: {}\n },\n\n isLink: function() {\n\n return true\n },\n\n disconnect: function(opt) {\n\n return this.set({\n source: { x: 0, y: 0 },\n target: { x: 0, y: 0 }\n }, opt)\n },\n\n source: function(source, args, opt) {\n\n // getter\n if (source === undefined) {\n return joint.util.clone(this.get('source'))\n }\n\n // setter\n let localSource\n let localOpt\n\n // `source` is a cell\n // take only its `id` and combine with `args`\n const isCellProvided = source instanceof joint.dia.Cell\n if (isCellProvided) { // three arguments\n localSource = joint.util.clone(args) || {}\n localSource.id = source.id\n localOpt = opt\n return this.set('source', localSource, localOpt)\n }\n\n // `source` is a g.Point\n // take only its `x` and `y` and combine with `args`\n const isPointProvided = source instanceof g.Point\n if (isPointProvided) { // three arguments\n localSource = joint.util.clone(args) || {}\n localSource.x = source.x\n localSource.y = source.y\n localOpt = opt\n return this.set('source', localSource, localOpt)\n }\n\n // `source` is an object\n // no checking\n // two arguments\n localSource = source\n localOpt = args\n return this.set('source', localSource, localOpt)\n },\n\n target: function(target, args, opt) {\n\n // getter\n if (target === undefined) {\n return joint.util.clone(this.get('target'))\n }\n\n // setter\n let localTarget\n let localOpt\n\n // `target` is a cell\n // take only its `id` argument and combine with `args`\n const isCellProvided = target instanceof joint.dia.Cell\n if (isCellProvided) { // three arguments\n localTarget = joint.util.clone(args) || {}\n localTarget.id = target.id\n localOpt = opt\n return this.set('target', localTarget, localOpt)\n }\n\n // `target` is a g.Point\n // take only its `x` and `y` and combine with `args`\n const isPointProvided = target instanceof g.Point\n if (isPointProvided) { // three arguments\n localTarget = joint.util.clone(args) || {}\n localTarget.x = target.x\n localTarget.y = target.y\n localOpt = opt\n return this.set('target', localTarget, localOpt)\n }\n\n // `target` is an object\n // no checking\n // two arguments\n localTarget = target\n localOpt = args\n return this.set('target', localTarget, localOpt)\n },\n\n router: function(name, args, opt) {\n\n // getter\n if (name === undefined) {\n const router = this.get('router')\n if (!router) {\n if (this.get('manhattan')) return { name: 'orthogonal' } // backwards compatibility\n return null\n }\n if (typeof router === 'object') return joint.util.clone(router)\n return router // e.g. a function\n }\n\n // setter\n const isRouterProvided = ((typeof name === 'object') || (typeof name === 'function'))\n const localRouter = isRouterProvided ? name : { name: name, args: args }\n const localOpt = isRouterProvided ? args : opt\n\n return this.set('router', localRouter, localOpt)\n },\n\n connector: function(name, args, opt) {\n\n // getter\n if (name === undefined) {\n const connector = this.get('connector')\n if (!connector) {\n if (this.get('smooth')) return { name: 'smooth' } // backwards compatibility\n return null\n }\n if (typeof connector === 'object') return joint.util.clone(connector)\n return connector // e.g. a function\n }\n\n // setter\n const isConnectorProvided = ((typeof name === 'object' || typeof name === 'function'))\n const localConnector = isConnectorProvided ? name : { name: name, args: args }\n const localOpt = isConnectorProvided ? args : opt\n\n return this.set('connector', localConnector, localOpt)\n },\n\n // Labels API\n\n // A convenient way to set labels. Currently set values will be mixined with `value` if used as a setter.\n label: function(idx, label, opt) {\n\n const labels = this.labels()\n\n idx = (isFinite(idx) && idx !== null) ? (idx | 0) : 0\n if (idx < 0) idx = labels.length + idx\n\n // getter\n if (arguments.length <= 1) return this.prop(['labels', idx])\n // setter\n return this.prop(['labels', idx], label, opt)\n },\n\n labels: function(labels, opt) {\n\n // getter\n if (arguments.length === 0) {\n labels = this.get('labels')\n if (!Array.isArray(labels)) return []\n return labels.slice()\n }\n // setter\n if (!Array.isArray(labels)) labels = []\n return this.set('labels', labels, opt)\n },\n\n insertLabel: function(idx, label, opt) {\n\n if (!label) throw new Error('dia.Link: no label provided')\n\n const labels = this.labels()\n const n = labels.length\n idx = (isFinite(idx) && idx !== null) ? (idx | 0) : n\n if (idx < 0) idx = n + idx + 1\n\n labels.splice(idx, 0, label)\n return this.labels(labels, opt)\n },\n\n // convenience function\n // add label to end of labels array\n appendLabel: function(label, opt) {\n\n return this.insertLabel(-1, label, opt)\n },\n\n removeLabel: function(idx, opt) {\n\n const labels = this.labels()\n idx = (isFinite(idx) && idx !== null) ? (idx | 0) : -1\n\n labels.splice(idx, 1)\n return this.labels(labels, opt)\n },\n\n // Vertices API\n\n vertex: function(idx, vertex, opt) {\n\n const vertices = this.vertices()\n\n idx = (isFinite(idx) && idx !== null) ? (idx | 0) : 0\n if (idx < 0) idx = vertices.length + idx\n\n // getter\n if (arguments.length <= 1) return this.prop(['vertices', idx])\n // setter\n return this.prop(['vertices', idx], vertex, opt)\n },\n\n vertices: function(vertices, opt) {\n\n // getter\n if (arguments.length === 0) {\n vertices = this.get('vertices')\n if (!Array.isArray(vertices)) return []\n return vertices.slice()\n }\n // setter\n if (!Array.isArray(vertices)) vertices = []\n return this.set('vertices', vertices, opt)\n },\n\n insertVertex: function(idx, vertex, opt) {\n\n if (!vertex) throw new Error('dia.Link: no vertex provided')\n\n const vertices = this.vertices()\n const n = vertices.length\n idx = (isFinite(idx) && idx !== null) ? (idx | 0) : n\n if (idx < 0) idx = n + idx + 1\n\n vertices.splice(idx, 0, vertex)\n return this.vertices(vertices, opt)\n },\n\n removeVertex: function(idx, opt) {\n\n const vertices = this.vertices()\n idx = (isFinite(idx) && idx !== null) ? (idx | 0) : -1\n\n vertices.splice(idx, 1)\n return this.vertices(vertices, opt)\n },\n\n // Transformations\n\n translate: function(tx, ty, opt) {\n\n // enrich the option object\n opt = opt || {}\n opt.translateBy = opt.translateBy || this.id\n opt.tx = tx\n opt.ty = ty\n\n return this.applyToPoints(function(p) {\n return { x: (p.x || 0) + tx, y: (p.y || 0) + ty }\n }, opt)\n },\n\n scale: function(sx, sy, origin, opt) {\n\n return this.applyToPoints(function(p) {\n return g.point(p).scale(sx, sy, origin).toJSON()\n }, opt)\n },\n\n applyToPoints: function(fn, opt) {\n\n if (!joint.util.isFunction(fn)) {\n throw new TypeError('dia.Link: applyToPoints expects its first parameter to be a function.')\n }\n\n const attrs = {}\n\n const source = this.source()\n if (!source.id) {\n attrs.source = fn(source)\n }\n\n const target = this.target()\n if (!target.id) {\n attrs.target = fn(target)\n }\n\n const vertices = this.vertices()\n if (vertices.length > 0) {\n attrs.vertices = vertices.map(fn)\n }\n\n return this.set(attrs, opt)\n },\n\n reparent: function(opt) {\n\n let newParent\n\n if (this.graph) {\n\n const source = this.getSourceElement()\n const target = this.getTargetElement()\n const prevParent = this.getParentCell()\n\n if (source && target) {\n if (source === target || source.isEmbeddedIn(target)) {\n newParent = target\n } else if (target.isEmbeddedIn(source)) {\n newParent = source\n } else {\n newParent = this.graph.getCommonAncestor(source, target)\n }\n }\n\n if (prevParent && (!newParent || newParent.id !== prevParent.id)) {\n // Unembed the link if source and target has no common ancestor\n // or common ancestor changed\n prevParent.unembed(this, opt)\n }\n\n if (newParent) {\n newParent.embed(this, opt)\n }\n }\n\n return newParent\n },\n\n hasLoop: function(opt) {\n\n opt = opt || {}\n\n const sourceId = this.source().id\n const targetId = this.target().id\n\n if (!sourceId || !targetId) {\n // Link \"pinned\" to the paper does not have a loop.\n return false\n }\n\n let loop = sourceId === targetId\n\n // Note that there in the deep mode a link can have a loop,\n // even if it connects only a parent and its embed.\n // A loop \"target equals source\" is valid in both shallow and deep mode.\n if (!loop && opt.deep && this.graph) {\n\n const sourceElement = this.getSourceElement()\n const targetElement = this.getTargetElement()\n\n loop = sourceElement.isEmbeddedIn(targetElement) || targetElement.isEmbeddedIn(sourceElement)\n }\n\n return loop\n },\n\n // unlike source(), this method returns null if source is a point\n getSourceElement: function() {\n\n const source = this.source()\n const {graph} = this\n\n return (source && source.id && graph && graph.getCell(source.id)) || null\n },\n\n // unlike target(), this method returns null if target is a point\n getTargetElement: function() {\n\n const target = this.target()\n const {graph} = this\n\n return (target && target.id && graph && graph.getCell(target.id)) || null\n },\n\n // Returns the common ancestor for the source element,\n // target element and the link itself.\n getRelationshipAncestor: function() {\n\n let connectionAncestor\n\n if (this.graph) {\n\n const cells = [\n this,\n this.getSourceElement(), // null if source is a point\n this.getTargetElement() // null if target is a point\n ].filter(function(item) {\n return !!item\n })\n\n connectionAncestor = this.graph.getCommonAncestor.apply(this.graph, cells)\n }\n\n return connectionAncestor || null\n },\n\n // Is source, target and the link itself embedded in a given cell?\n isRelationshipEmbeddedIn: function(cell) {\n\n const cellId = (joint.util.isString(cell) || joint.util.isNumber(cell)) ? cell : cell.id\n const ancestor = this.getRelationshipAncestor()\n\n return !!ancestor && (ancestor.id === cellId || ancestor.isEmbeddedIn(cellId))\n },\n\n // Get resolved default label.\n _getDefaultLabel: function() {\n\n const defaultLabel = this.get('defaultLabel') || this.defaultLabel || {}\n\n const label = {}\n label.markup = defaultLabel.markup || this.get('labelMarkup') || this.labelMarkup\n label.position = defaultLabel.position\n label.attrs = defaultLabel.attrs\n label.size = defaultLabel.size\n\n return label\n }\n}, {\n\n endsEqual: function(a, b) {\n\n const portsEqual = a.port === b.port || !a.port && !b.port\n return a.id === b.id && portsEqual\n }\n})\n\n\n// joint.dia.Link base view and controller.\n// ----------------------------------------\n\njoint.dia.LinkView = joint.dia.CellView.extend({\n\n className: function() {\n\n const classNames = joint.dia.CellView.prototype.className.apply(this).split(' ')\n\n classNames.push('link')\n\n return classNames.join(' ')\n },\n\n options: {\n\n shortLinkLength: 105,\n doubleLinkTools: false,\n longLinkLength: 155,\n linkToolsOffset: 40,\n doubleLinkToolsOffset: 65,\n sampleInterval: 50,\n },\n\n _labelCache: null,\n _labelSelectors: null,\n _markerCache: null,\n _V: null,\n _dragData: null, // deprecated\n\n metrics: null,\n decimalsRounding: 2,\n\n initialize: function(options) {\n\n joint.dia.CellView.prototype.initialize.apply(this, arguments)\n\n // create methods in prototype, so they can be accessed from any instance and\n // don't need to be create over and over\n if (typeof this.constructor.prototype.watchSource !== 'function') {\n this.constructor.prototype.watchSource = this.createWatcher('source')\n this.constructor.prototype.watchTarget = this.createWatcher('target')\n }\n\n // `_.labelCache` is a mapping of indexes of labels in the `this.get('labels')` array to\n // `` nodes wrapped by Vectorizer. This allows for quick access to the\n // nodes in `updateLabelPosition()` in order to update the label positions.\n this._labelCache = {}\n\n // a cache of label selectors\n this._labelSelectors = {}\n\n // keeps markers bboxes and positions again for quicker access\n this._markerCache = {}\n\n // cache of default markup nodes\n this._V = {},\n\n // connection path metrics\n this.metrics = {},\n\n // bind events\n this.startListening()\n },\n\n startListening: function() {\n\n const {model} = this\n\n this.listenTo(model, 'change:markup', this.render)\n this.listenTo(model, 'change:smooth change:manhattan change:router change:connector', this.update)\n this.listenTo(model, 'change:toolMarkup', this.onToolsChange)\n this.listenTo(model, 'change:labels change:labelMarkup', this.onLabelsChange)\n this.listenTo(model, 'change:vertices change:vertexMarkup', this.onVerticesChange)\n this.listenTo(model, 'change:source', this.onSourceChange)\n this.listenTo(model, 'change:target', this.onTargetChange)\n },\n\n onSourceChange: function(cell, source, opt) {\n\n // Start watching the new source model.\n this.watchSource(cell, source)\n // This handler is called when the source attribute is changed.\n // This can happen either when someone reconnects the link (or moves arrowhead),\n // or when an embedded link is translated by its ancestor.\n // 1. Always do update.\n // 2. Do update only if the opposite end ('target') is also a point.\n const {model} = this\n if (!opt.translateBy || !model.get('target').id || !source.id) {\n this.update(model, null, opt)\n }\n },\n\n onTargetChange: function(cell, target, opt) {\n\n // Start watching the new target model.\n this.watchTarget(cell, target)\n // See `onSourceChange` method.\n const {model} = this\n if (!opt.translateBy || (model.get('source').id && !target.id && joint.util.isEmpty(model.get('vertices')))) {\n this.update(model, null, opt)\n }\n },\n\n onVerticesChange: function(cell, changed, opt) {\n\n this.renderVertexMarkers()\n\n // If the vertices have been changed by a translation we do update only if the link was\n // the only link that was translated. If the link was translated via another element which the link\n // is embedded in, this element will be translated as well and that triggers an update.\n // Note that all embeds in a model are sorted - first comes links, then elements.\n if (!opt.translateBy || opt.translateBy === this.model.id || this.model.hasLoop()) {\n // Vertices were changed (not as a reaction on translate)\n // or link.translate() was called or\n this.update(cell, null, opt)\n }\n },\n\n onToolsChange: function() {\n\n this.renderTools().updateToolsPosition()\n },\n\n onLabelsChange: function(link, labels, opt) {\n\n let requireRender = true\n\n const previousLabels = this.model.previous('labels')\n\n if (previousLabels) {\n // Here is an optimalization for cases when we know, that change does\n // not require rerendering of all labels.\n if (('propertyPathArray' in opt) && ('propertyValue' in opt)) {\n // The label is setting by `prop()` method\n const pathArray = opt.propertyPathArray || []\n const pathLength = pathArray.length\n if (pathLength > 1) {\n // We are changing a single label here e.g. 'labels/0/position'\n const labelExists = !!previousLabels[pathArray[1]]\n if (labelExists) {\n if (pathLength === 2) {\n // We are changing the entire label. Need to check if the\n // markup is also being changed.\n requireRender = ('markup' in Object(opt.propertyValue))\n } else if (pathArray[2] !== 'markup') {\n // We are changing a label property but not the markup\n requireRender = false\n }\n }\n }\n }\n }\n\n if (requireRender) {\n this.renderLabels()\n } else {\n this.updateLabels()\n }\n\n this.updateLabelPositions()\n },\n\n // Rendering.\n // ----------\n\n render: function() {\n\n this.vel.empty()\n this._V = {}\n this.renderMarkup()\n // rendering labels has to be run after the link is appended to DOM tree. (otherwise bbox\n // returns zero values)\n this.renderLabels()\n // start watching the ends of the link for changes\n const {model} = this\n this.watchSource(model, model.source())\n .watchTarget(model, model.target())\n .update()\n\n return this\n },\n\n renderMarkup: function() {\n\n const link = this.model\n const markup = link.get('markup') || link.markup\n if (!markup) throw new Error('dia.LinkView: markup required')\n if (Array.isArray(markup)) return this.renderJSONMarkup(markup)\n if (typeof markup === 'string') return this.renderStringMarkup(markup)\n throw new Error('dia.LinkView: invalid markup')\n },\n\n renderJSONMarkup: function(markup) {\n\n const doc = joint.util.parseDOMJSON(markup)\n // Selectors\n const selectors = this.selectors = doc.selectors\n const rootSelector = this.selector\n if (selectors[rootSelector]) throw new Error('dia.LinkView: ambiguous root selector.')\n selectors[rootSelector] = this.el\n // Fragment\n this.vel.append(doc.fragment)\n },\n\n renderStringMarkup: function(markup) {\n\n // A special markup can be given in the `properties.markup` property. This might be handy\n // if e.g. arrowhead markers should be `` elements or any other element than ``s.\n // `.connection`, `.connection-wrap`, `.marker-source` and `.marker-target` selectors\n // of elements with special meaning though. Therefore, those classes should be preserved in any\n // special markup passed in `properties.markup`.\n let children = V(markup)\n // custom markup may contain only one children\n if (!Array.isArray(children)) children = [children]\n // Cache all children elements for quicker access.\n const cache = this._V // vectorized markup;\n for (let i = 0, n = children.length; i < n; i++) {\n const child = children[i]\n let className = child.attr('class')\n if (className) {\n // Strip the joint class name prefix, if there is one.\n className = joint.util.removeClassNamePrefix(className)\n cache[$.camelCase(className)] = child\n }\n }\n // partial rendering\n this.renderTools()\n this.renderVertexMarkers()\n this.renderArrowheadMarkers()\n this.vel.append(children)\n },\n\n _getLabelMarkup: function(labelMarkup) {\n\n if (!labelMarkup) return undefined\n\n if (Array.isArray(labelMarkup)) return this._getLabelJSONMarkup(labelMarkup)\n if (typeof labelMarkup === 'string') return this._getLabelStringMarkup(labelMarkup)\n throw new Error('dia.linkView: invalid label markup')\n },\n\n _getLabelJSONMarkup: function(labelMarkup) {\n\n return joint.util.parseDOMJSON(labelMarkup) // fragment and selectors\n },\n\n _getLabelStringMarkup: function(labelMarkup) {\n\n const children = V(labelMarkup)\n const fragment = document.createDocumentFragment()\n\n if (!Array.isArray(children)) {\n fragment.append(children.node)\n\n } else {\n for (let i = 0, n = children.length; i < n; i++) {\n const currentChild = children[i].node\n fragment.appendChild(currentChild)\n }\n }\n\n return { fragment: fragment, selectors: {}} // no selectors\n },\n\n // Label markup fragment may come wrapped in , or not.\n // If it doesn't, add the container here.\n _normalizeLabelMarkup: function(markup) {\n\n if (!markup) return undefined\n\n const {fragment} = markup\n if (!(markup.fragment instanceof DocumentFragment) || !markup.fragment.hasChildNodes()) throw new Error('dia.LinkView: invalid label markup.')\n\n let vNode\n const {childNodes} = fragment\n\n if ((childNodes.length > 1) || childNodes[0].nodeName.toUpperCase() !== 'G') {\n // default markup fragment is not wrapped in \n // add a container\n\n vNode = V('g')\n vNode.append(fragment)\n vNode.addClass('label')\n\n } else {\n vNode = V(childNodes[0])\n vNode.addClass('label')\n }\n\n return { node: vNode.node, selectors: markup.selectors }\n },\n\n renderLabels: function() {\n\n const cache = this._V\n let vLabels = cache.labels\n const labelCache = this._labelCache = {}\n const labelSelectors = this._labelSelectors = {}\n\n if (vLabels) vLabels.empty()\n\n const {model} = this\n const labels = model.get('labels') || []\n const labelsCount = labels.length\n if (labelsCount === 0) return this\n\n if (!vLabels) {\n // there is no label container in the markup but some labels are defined\n // add a container\n vLabels = cache.labels = V('g').addClass('labels').appendTo(this.el)\n }\n\n for (let i = 0; i < labelsCount; i++) {\n\n const label = labels[i]\n const labelMarkup = this._normalizeLabelMarkup(this._getLabelMarkup(label.markup))\n\n var node\n var selectors\n if (labelMarkup) {\n node = labelMarkup.node\n selectors = labelMarkup.selectors\n\n } else {\n const builtinDefaultLabel = model._builtins.defaultLabel\n const builtinDefaultLabelMarkup = this._normalizeLabelMarkup(this._getLabelMarkup(builtinDefaultLabel.markup))\n\n const defaultLabel = model._getDefaultLabel()\n const defaultLabelMarkup = this._normalizeLabelMarkup(this._getLabelMarkup(defaultLabel.markup))\n\n const defaultMarkup = defaultLabelMarkup || builtinDefaultLabelMarkup\n\n node = defaultMarkup.node\n selectors = defaultMarkup.selectors\n }\n\n const vLabel = V(node)\n vLabel.attr('label-idx', i) // assign label-idx\n vLabel.appendTo(vLabels)\n labelCache[i] = vLabel // cache node for `updateLabels()` so it can just update label node positions\n\n selectors[this.selector] = vLabel.node\n labelSelectors[i] = selectors // cache label selectors for `updateLabels()`\n }\n\n this.updateLabels()\n\n return this\n },\n\n // merge default label attrs into label attrs\n // keep `undefined` or `null` because `{}` means something else\n _mergeLabelAttrs: function(hasCustomMarkup, labelAttrs, defaultLabelAttrs, builtinDefaultLabelAttrs) {\n\n if (labelAttrs === null) return null\n if (labelAttrs === undefined) {\n\n if (defaultLabelAttrs === null) return null\n if (defaultLabelAttrs === undefined) {\n\n if (hasCustomMarkup) return undefined\n return builtinDefaultLabelAttrs\n }\n\n if (hasCustomMarkup) return defaultLabelAttrs\n return joint.util.merge({}, builtinDefaultLabelAttrs, defaultLabelAttrs)\n }\n\n if (hasCustomMarkup) return joint.util.merge({}, defaultLabelAttrs, labelAttrs)\n return joint.util.merge({}, builtinDefaultLabelAttrs, defaultLabelAttrs, labelAttrs)\n },\n\n updateLabels: function() {\n\n if (!this._V.labels) return this\n\n const {model} = this\n const labels = model.get('labels') || []\n const canLabelMove = this.can('labelMove')\n\n const builtinDefaultLabel = model._builtins.defaultLabel\n const builtinDefaultLabelAttrs = builtinDefaultLabel.attrs\n\n const defaultLabel = model._getDefaultLabel()\n const defaultLabelMarkup = defaultLabel.markup\n const defaultLabelAttrs = defaultLabel.attrs\n\n for (let i = 0, n = labels.length; i < n; i++) {\n\n const vLabel = this._labelCache[i]\n vLabel.attr('cursor', (canLabelMove ? 'move' : 'default'))\n\n const selectors = this._labelSelectors[i]\n\n const label = labels[i]\n const labelMarkup = label.markup\n const labelAttrs = label.attrs\n\n const attrs = this._mergeLabelAttrs(\n (labelMarkup || defaultLabelMarkup),\n labelAttrs,\n defaultLabelAttrs,\n builtinDefaultLabelAttrs\n )\n\n this.updateDOMSubtreeAttributes(vLabel.node, attrs, {\n rootBBox: new g.Rect(label.size),\n selectors: selectors\n })\n }\n\n return this\n },\n\n renderTools: function() {\n\n if (!this._V.linkTools) return this\n\n // Tools are a group of clickable elements that manipulate the whole link.\n // A good example of this is the remove tool that removes the whole link.\n // Tools appear after hovering the link close to the `source` element/point of the link\n // but are offset a bit so that they don't cover the `marker-arrowhead`.\n\n const $tools = $(this._V.linkTools.node).empty()\n let toolTemplate = joint.util.template(this.model.get('toolMarkup') || this.model.toolMarkup)\n const tool = V(toolTemplate())\n\n $tools.append(tool.node)\n\n // Cache the tool node so that the `updateToolsPosition()` can update the tool position quickly.\n this._toolCache = tool\n\n // If `doubleLinkTools` is enabled, we render copy of the tools on the other side of the\n // link as well but only if the link is longer than `longLinkLength`.\n if (this.options.doubleLinkTools) {\n\n let tool2\n if (this.model.get('doubleToolMarkup') || this.model.doubleToolMarkup) {\n toolTemplate = joint.util.template(this.model.get('doubleToolMarkup') || this.model.doubleToolMarkup)\n tool2 = V(toolTemplate())\n } else {\n tool2 = tool.clone()\n }\n\n $tools.append(tool2.node)\n this._tool2Cache = tool2\n }\n\n return this\n },\n\n renderVertexMarkers: function() {\n\n if (!this._V.markerVertices) return this\n\n const $markerVertices = $(this._V.markerVertices.node).empty()\n\n // A special markup can be given in the `properties.vertexMarkup` property. This might be handy\n // if default styling (elements) are not desired. This makes it possible to use any\n // SVG elements for .marker-vertex and .marker-vertex-remove tools.\n const markupTemplate = joint.util.template(this.model.get('vertexMarkup') || this.model.vertexMarkup)\n\n this.model.vertices().forEach(function(vertex, idx) {\n\n $markerVertices.append(V(markupTemplate(joint.util.assign({ idx: idx }, vertex))).node)\n })\n\n return this\n },\n\n renderArrowheadMarkers: function() {\n\n // Custom markups might not have arrowhead markers. Therefore, jump of this function immediately if that's the case.\n if (!this._V.markerArrowheads) return this\n\n const $markerArrowheads = $(this._V.markerArrowheads.node)\n\n $markerArrowheads.empty()\n\n // A special markup can be given in the `properties.vertexMarkup` property. This might be handy\n // if default styling (elements) are not desired. This makes it possible to use any\n // SVG elements for .marker-vertex and .marker-vertex-remove tools.\n const markupTemplate = joint.util.template(this.model.get('arrowheadMarkup') || this.model.arrowheadMarkup)\n\n this._V.sourceArrowhead = V(markupTemplate({ end: 'source' }))\n this._V.targetArrowhead = V(markupTemplate({ end: 'target' }))\n\n $markerArrowheads.append(this._V.sourceArrowhead.node, this._V.targetArrowhead.node)\n\n return this\n },\n\n // Updating.\n // ---------\n\n // Default is to process the `attrs` object and set attributes on subelements based on the selectors.\n update: function(model, attributes, opt) {\n\n opt || (opt = {})\n\n // update the link path\n this.updateConnection(opt)\n\n // update SVG attributes defined by 'attrs/'.\n this.updateDOMSubtreeAttributes(this.el, this.model.attr(), { selectors: this.selectors })\n\n this.updateDefaultConnectionPath()\n\n // update the label position etc.\n this.updateLabelPositions()\n this.updateToolsPosition()\n this.updateArrowheadMarkers()\n\n this.updateTools(opt)\n // Local perpendicular flag (as opposed to one defined on paper).\n // Could be enabled inside a connector/router. It's valid only\n // during the update execution.\n this.options.perpendicular = null\n // Mark that postponed update has been already executed.\n this.updatePostponed = false\n\n return this\n },\n\n removeRedundantLinearVertices: function(opt) {\n const link = this.model\n const vertices = link.vertices()\n const conciseVertices = []\n const n = vertices.length\n let m = 0\n for (let i = 0; i < n; i++) {\n const current = new g.Point(vertices[i]).round()\n const prev = new g.Point(conciseVertices[m - 1] || this.sourceAnchor).round()\n if (prev.equals(current)) continue\n const next = new g.Point(vertices[i + 1] || this.targetAnchor).round()\n if (prev.equals(next)) continue\n const line = new g.Line(prev, next)\n if (line.pointOffset(current) === 0) continue\n conciseVertices.push(vertices[i])\n m++\n }\n if (n === m) return 0\n link.vertices(conciseVertices, opt)\n return (n - m)\n },\n\n updateDefaultConnectionPath: function() {\n\n const cache = this._V\n\n if (cache.connection) {\n cache.connection.attr('d', this.getSerializedConnection())\n }\n\n if (cache.connectionWrap) {\n cache.connectionWrap.attr('d', this.getSerializedConnection())\n }\n\n if (cache.markerSource && cache.markerTarget) {\n this._translateAndAutoOrientArrows(cache.markerSource, cache.markerTarget)\n }\n },\n\n getEndView: function(type) {\n switch (type) {\n case 'source':\n return this.sourceView || null\n case 'target':\n return this.targetView || null\n default:\n throw new Error('dia.LinkView: type parameter required.')\n }\n },\n\n getEndAnchor: function(type) {\n switch (type) {\n case 'source':\n return new g.Point(this.sourceAnchor)\n case 'target':\n return new g.Point(this.targetAnchor)\n default:\n throw new Error('dia.LinkView: type parameter required.')\n }\n },\n\n getEndMagnet: function(type) {\n switch (type) {\n case 'source':\n var {sourceView} = this\n if (!sourceView) break\n return this.sourceMagnet || sourceView.el\n case 'target':\n var {targetView} = this\n if (!targetView) break\n return this.targetMagnet || targetView.el\n default:\n throw new Error('dia.LinkView: type parameter required.')\n }\n return null\n },\n\n updateConnection: function(opt) {\n\n opt = opt || {}\n\n const {model} = this\n let route; let path\n\n if (opt.translateBy && model.isRelationshipEmbeddedIn(opt.translateBy)) {\n // The link is being translated by an ancestor that will\n // shift source point, target point and all vertices\n // by an equal distance.\n const tx = opt.tx || 0\n const ty = opt.ty || 0\n\n route = (new g.Polyline(this.route)).translate(tx, ty).points\n\n // translate source and target connection and marker points.\n this._translateConnectionPoints(tx, ty)\n\n // translate the path itself\n path = this.path\n path.translate(tx, ty)\n\n } else {\n\n const vertices = model.vertices()\n // 1. Find Anchors\n\n const anchors = this.findAnchors(vertices)\n const sourceAnchor = this.sourceAnchor = anchors.source\n const targetAnchor = this.targetAnchor = anchors.target\n\n // 2. Find Route\n route = this.findRoute(vertices, opt)\n\n // 3. Find Connection Points\n const connectionPoints = this.findConnectionPoints(route, sourceAnchor, targetAnchor)\n const sourcePoint = this.sourcePoint = connectionPoints.source\n const targetPoint = this.targetPoint = connectionPoints.target\n\n // 3b. Find Marker Connection Point - Backwards Compatibility\n const markerPoints = this.findMarkerPoints(route, sourcePoint, targetPoint)\n\n // 4. Find Connection\n path = this.findPath(route, markerPoints.source || sourcePoint, markerPoints.target || targetPoint)\n }\n\n this.route = route\n this.path = path\n this.metrics = {}\n },\n\n findMarkerPoints: function(route, sourcePoint, targetPoint) {\n\n const firstWaypoint = route[0]\n const lastWaypoint = route[route.length - 1]\n\n // Move the source point by the width of the marker taking into account\n // its scale around x-axis. Note that scale is the only transform that\n // makes sense to be set in `.marker-source` attributes object\n // as all other transforms (translate/rotate) will be replaced\n // by the `translateAndAutoOrient()` function.\n const cache = this._markerCache\n // cache source and target points\n let sourceMarkerPoint; let targetMarkerPoint\n\n if (this._V.markerSource) {\n\n cache.sourceBBox = cache.sourceBBox || this._V.markerSource.getBBox()\n sourceMarkerPoint = g.point(sourcePoint).move(\n firstWaypoint || targetPoint,\n cache.sourceBBox.width * this._V.markerSource.scale().sx * -1\n ).round()\n }\n\n if (this._V.markerTarget) {\n\n cache.targetBBox = cache.targetBBox || this._V.markerTarget.getBBox()\n targetMarkerPoint = g.point(targetPoint).move(\n lastWaypoint || sourcePoint,\n cache.targetBBox.width * this._V.markerTarget.scale().sx * -1\n ).round()\n }\n\n // if there was no markup for the marker, use the connection point.\n cache.sourcePoint = sourceMarkerPoint || sourcePoint.clone()\n cache.targetPoint = targetMarkerPoint || targetPoint.clone()\n\n return {\n source: sourceMarkerPoint,\n target: targetMarkerPoint\n }\n },\n\n findAnchors: function(vertices) {\n\n const {model} = this\n const firstVertex = vertices[0]\n const lastVertex = vertices[vertices.length - 1]\n const sourceDef = model.get('source')\n const targetDef = model.get('target')\n const {sourceView} = this\n const {targetView} = this\n let sourceMagnet; let targetMagnet\n\n // Anchor Source\n let sourceAnchor\n if (sourceView) {\n sourceMagnet = (this.sourceMagnet || sourceView.el)\n let sourceAnchorRef\n if (firstVertex) {\n sourceAnchorRef = new g.Point(firstVertex)\n } else if (targetView) {\n // TODO: the source anchor reference is not a point, how to deal with this?\n sourceAnchorRef = this.targetMagnet || targetView.el\n } else {\n sourceAnchorRef = new g.Point(targetDef)\n }\n sourceAnchor = this.getAnchor(sourceDef.anchor, sourceView, sourceMagnet, sourceAnchorRef, 'source')\n } else {\n sourceAnchor = new g.Point(sourceDef)\n }\n\n // Anchor Target\n let targetAnchor\n if (targetView) {\n targetMagnet = (this.targetMagnet || targetView.el)\n const targetAnchorRef = new g.Point(lastVertex || sourceAnchor)\n targetAnchor = this.getAnchor(targetDef.anchor, targetView, targetMagnet, targetAnchorRef, 'target')\n } else {\n targetAnchor = new g.Point(targetDef)\n }\n\n // Con\n return {\n source: sourceAnchor,\n target: targetAnchor\n }\n },\n\n findConnectionPoints: function(route, sourceAnchor, targetAnchor) {\n\n const firstWaypoint = route[0]\n const lastWaypoint = route[route.length - 1]\n const {model} = this\n const sourceDef = model.get('source')\n const targetDef = model.get('target')\n const {sourceView} = this\n const {targetView} = this\n const paperOptions = this.paper.options\n let sourceMagnet; let targetMagnet\n\n // Connection Point Source\n let sourcePoint\n if (sourceView) {\n sourceMagnet = (this.sourceMagnet || sourceView.el)\n const sourceConnectionPointDef = sourceDef.connectionPoint || paperOptions.defaultConnectionPoint\n const sourcePointRef = firstWaypoint || targetAnchor\n const sourceLine = new g.Line(sourcePointRef, sourceAnchor)\n sourcePoint = this.getConnectionPoint(sourceConnectionPointDef, sourceView, sourceMagnet, sourceLine, 'source')\n } else {\n sourcePoint = sourceAnchor\n }\n // Connection Point Target\n let targetPoint\n if (targetView) {\n targetMagnet = (this.targetMagnet || targetView.el)\n const targetConnectionPointDef = targetDef.connectionPoint || paperOptions.defaultConnectionPoint\n const targetPointRef = lastWaypoint || sourceAnchor\n const targetLine = new g.Line(targetPointRef, targetAnchor)\n targetPoint = this.getConnectionPoint(targetConnectionPointDef, targetView, targetMagnet, targetLine, 'target')\n } else {\n targetPoint = targetAnchor\n }\n\n return {\n source: sourcePoint,\n target: targetPoint\n }\n },\n\n getAnchor: function(anchorDef, cellView, magnet, ref, endType) {\n\n if (!anchorDef) {\n const paperOptions = this.paper.options\n if (paperOptions.perpendicularLinks || this.options.perpendicular) {\n // Backwards compatibility\n // If `perpendicularLinks` flag is set on the paper and there are vertices\n // on the link, then try to find a connection point that makes the link perpendicular\n // even though the link won't point to the center of the targeted object.\n anchorDef = { name: 'perpendicular' }\n } else {\n anchorDef = paperOptions.defaultAnchor\n }\n }\n\n if (!anchorDef) throw new Error('Anchor required.')\n let anchorFn\n if (typeof anchorDef === 'function') {\n anchorFn = anchorDef\n } else {\n const anchorName = anchorDef.name\n anchorFn = joint.anchors[anchorName]\n if (typeof anchorFn !== 'function') throw new Error(`Unknown anchor: ${ anchorName}`)\n }\n const anchor = anchorFn.call(this, cellView, magnet, ref, anchorDef.args || {}, endType, this)\n if (anchor) return anchor.round(this.decimalsRounding)\n return new g.Point()\n },\n\n\n getConnectionPoint: function(connectionPointDef, view, magnet, line, endType) {\n\n let connectionPoint\n const anchor = line.end\n // Backwards compatibility\n const paperOptions = this.paper.options\n if (typeof paperOptions.linkConnectionPoint === 'function') {\n const linkConnectionMagnet = (magnet === view.el) ? undefined : magnet\n connectionPoint = paperOptions.linkConnectionPoint(this, view, linkConnectionMagnet, line.start, endType)\n if (connectionPoint) return connectionPoint\n }\n\n if (!connectionPointDef) return anchor\n let connectionPointFn\n if (typeof connectionPointDef === 'function') {\n connectionPointFn = connectionPointDef\n } else {\n const connectionPointName = connectionPointDef.name\n connectionPointFn = joint.connectionPoints[connectionPointName]\n if (typeof connectionPointFn !== 'function') throw new Error(`Unknown connection point: ${ connectionPointName}`)\n }\n connectionPoint = connectionPointFn.call(this, line, view, magnet, connectionPointDef.args || {}, endType, this)\n if (connectionPoint) return connectionPoint.round(this.decimalsRounding)\n return anchor\n },\n\n _translateConnectionPoints: function(tx, ty) {\n\n const cache = this._markerCache\n\n cache.sourcePoint.offset(tx, ty)\n cache.targetPoint.offset(tx, ty)\n this.sourcePoint.offset(tx, ty)\n this.targetPoint.offset(tx, ty)\n this.sourceAnchor.offset(tx, ty)\n this.targetAnchor.offset(tx, ty)\n },\n\n // if label position is a number, normalize it to a position object\n // this makes sure that label positions can be merged properly\n _normalizeLabelPosition: function(labelPosition) {\n\n if (typeof labelPosition === 'number') return { distance: labelPosition, offset: null, args: null }\n return labelPosition\n },\n\n updateLabelPositions: function() {\n\n if (!this._V.labels) return this\n\n const {path} = this\n if (!path) return this\n\n // This method assumes all the label nodes are stored in the `this._labelCache` hash table\n // by their indices in the `this.get('labels')` array. This is done in the `renderLabels()` method.\n\n const {model} = this\n const labels = model.get('labels') || []\n if (!labels.length) return this\n\n const builtinDefaultLabel = model._builtins.defaultLabel\n const builtinDefaultLabelPosition = builtinDefaultLabel.position\n\n const defaultLabel = model._getDefaultLabel()\n const defaultLabelPosition = this._normalizeLabelPosition(defaultLabel.position)\n\n const defaultPosition = joint.util.merge({}, builtinDefaultLabelPosition, defaultLabelPosition)\n\n for (let idx = 0, n = labels.length; idx < n; idx++) {\n\n const label = labels[idx]\n const labelPosition = this._normalizeLabelPosition(label.position)\n\n const position = joint.util.merge({}, defaultPosition, labelPosition)\n\n const labelPoint = this.getLabelCoordinates(position)\n this._labelCache[idx].attr('transform', `translate(${ labelPoint.x }, ${ labelPoint.y })`)\n }\n\n return this\n },\n\n updateToolsPosition: function() {\n\n if (!this._V.linkTools) return this\n\n // Move the tools a bit to the target position but don't cover the `sourceArrowhead` marker.\n // Note that the offset is hardcoded here. The offset should be always\n // more than the `this.$('.marker-arrowhead[end=\"source\"]')[0].bbox().width` but looking\n // this up all the time would be slow.\n\n let scale = ''\n let offset = this.options.linkToolsOffset\n const connectionLength = this.getConnectionLength()\n\n // Firefox returns connectionLength=NaN in odd cases (for bezier curves).\n // In that case we won't update tools position at all.\n if (!Number.isNaN(connectionLength)) {\n\n // If the link is too short, make the tools half the size and the offset twice as low.\n if (connectionLength < this.options.shortLinkLength) {\n scale = 'scale(.5)'\n offset /= 2\n }\n\n let toolPosition = this.getPointAtLength(offset)\n\n this._toolCache.attr('transform', `translate(${ toolPosition.x }, ${ toolPosition.y }) ${ scale}`)\n\n if (this.options.doubleLinkTools && connectionLength >= this.options.longLinkLength) {\n\n const doubleLinkToolsOffset = this.options.doubleLinkToolsOffset || offset\n\n toolPosition = this.getPointAtLength(connectionLength - doubleLinkToolsOffset)\n this._tool2Cache.attr('transform', `translate(${ toolPosition.x }, ${ toolPosition.y }) ${ scale}`)\n this._tool2Cache.attr('visibility', 'visible')\n\n } else if (this.options.doubleLinkTools) {\n\n this._tool2Cache.attr('visibility', 'hidden')\n }\n }\n\n return this\n },\n\n updateArrowheadMarkers: function() {\n\n if (!this._V.markerArrowheads) return this\n\n // getting bbox of an element with `display=\"none\"` in IE9 ends up with access violation\n if ($.css(this._V.markerArrowheads.node, 'display') === 'none') return this\n\n const sx = this.getConnectionLength() < this.options.shortLinkLength ? .5 : 1\n this._V.sourceArrowhead.scale(sx)\n this._V.targetArrowhead.scale(sx)\n\n this._translateAndAutoOrientArrows(this._V.sourceArrowhead, this._V.targetArrowhead)\n\n return this\n },\n\n // Returns a function observing changes on an end of the link. If a change happens and new end is a new model,\n // it stops listening on the previous one and starts listening to the new one.\n createWatcher: function(endType) {\n\n // create handler for specific end type (source|target).\n const onModelChange = function(endModel, opt) {\n this.onEndModelChange(endType, endModel, opt)\n }\n\n function watchEndModel(link, end) {\n\n end = end || {}\n\n let endModel = null\n const previousEnd = link.previous(endType) || {}\n\n if (previousEnd.id) {\n this.stopListening(this.paper.getModelById(previousEnd.id), 'change', onModelChange)\n }\n\n if (end.id) {\n // If the observed model changes, it caches a new bbox and do the link update.\n endModel = this.paper.getModelById(end.id)\n this.listenTo(endModel, 'change', onModelChange)\n }\n\n onModelChange.call(this, endModel, { cacheOnly: true })\n\n return this\n }\n\n return watchEndModel\n },\n\n onEndModelChange: function(endType, endModel, opt) {\n\n let doUpdate = !opt.cacheOnly\n const {model} = this\n const end = model.get(endType) || {}\n\n if (endModel) {\n\n const selector = this.constructor.makeSelector(end)\n const oppositeEndType = endType == 'source' ? 'target' : 'source'\n const oppositeEnd = model.get(oppositeEndType) || {}\n const endId = end.id\n const oppositeEndId = oppositeEnd.id\n const oppositeSelector = oppositeEndId && this.constructor.makeSelector(oppositeEnd)\n\n // Caching end models bounding boxes.\n // If `opt.handleBy` equals the client-side ID of this link view and it is a loop link, then we already cached\n // the bounding boxes in the previous turn (e.g. for loop link, the change:source event is followed\n // by change:target and so on change:source, we already chached the bounding boxes of - the same - element).\n if (opt.handleBy === this.cid && (endId === oppositeEndId) && selector == oppositeSelector) {\n\n // Source and target elements are identical. We're dealing with a loop link. We are handling `change` event for the\n // second time now. There is no need to calculate bbox and find magnet element again.\n // It was calculated already for opposite link end.\n this[`${endType }View`] = this[`${oppositeEndType }View`]\n this[`${endType }Magnet`] = this[`${oppositeEndType }Magnet`]\n\n } else if (opt.translateBy) {\n // `opt.translateBy` optimizes the way we calculate bounding box of the source/target element.\n // If `opt.translateBy` is an ID of the element that was originally translated.\n\n // Noop\n\n } else {\n // The slowest path, source/target could have been rotated or resized or any attribute\n // that affects the bounding box of the view might have been changed.\n\n const connectedModel = this.paper.model.getCell(endId)\n if (!connectedModel) throw new Error(`LinkView: invalid ${ endType } cell.`)\n const connectedView = connectedModel.findView(this.paper)\n if (connectedView) {\n let connectedMagnet = connectedView.getMagnetFromLinkEnd(end)\n if (connectedMagnet === connectedView.el) connectedMagnet = null\n this[`${endType }View`] = connectedView\n this[`${endType }Magnet`] = connectedMagnet\n } else {\n // the view is not rendered yet\n this[`${endType }View`] = this[`${endType }Magnet`] = null\n }\n }\n\n if (opt.handleBy === this.cid && opt.translateBy &&\n model.isEmbeddedIn(endModel) &&\n !joint.util.isEmpty(model.get('vertices'))) {\n // Loop link whose element was translated and that has vertices (that need to be translated with\n // the parent in which my element is embedded).\n // If the link is embedded, has a loop and vertices and the end model\n // has been translated, do not update yet. There are vertices still to be updated (change:vertices\n // event will come in the next turn).\n doUpdate = false\n }\n\n if (!this.updatePostponed && oppositeEndId) {\n // The update was not postponed (that can happen e.g. on the first change event) and the opposite\n // end is a model (opposite end is the opposite end of the link we're just updating, e.g. if\n // we're reacting on change:source event, the oppositeEnd is the target model).\n\n const oppositeEndModel = this.paper.getModelById(oppositeEndId)\n\n // Passing `handleBy` flag via event option.\n // Note that if we are listening to the same model for event 'change' twice.\n // The same event will be handled by this method also twice.\n if (end.id === oppositeEnd.id) {\n // We're dealing with a loop link. Tell the handlers in the next turn that they should update\n // the link instead of me. (We know for sure there will be a next turn because\n // loop links react on at least two events: change on the source model followed by a change on\n // the target model).\n if (!opt.translateBy || joint.util.isEmpty(model.get('vertices')) || !model.isEmbeddedIn(opt.translateBy)) {\n opt.handleBy = this.cid\n } else {\n doUpdate = false\n }\n }\n\n if (doUpdate && (opt.handleBy === this.cid || (opt.translateBy && oppositeEndModel.isEmbeddedIn(opt.translateBy)))) {\n\n // Here are two options:\n // - Source and target are connected to the same model (not necessarily the same port).\n // - Both end models are translated by the same ancestor. We know that opposite end\n // model will be translated in the next turn as well.\n // In both situations there will be more changes on the model that trigger an\n // update. So there is no need to update the linkView yet.\n this.updatePostponed = true\n doUpdate = false\n }\n }\n\n } else {\n\n // the link end is a point ~ rect 1x1\n this[`${endType }View`] = this[`${endType }Magnet`] = null\n }\n\n if (doUpdate) {\n this.update(model, null, opt)\n }\n },\n\n _translateAndAutoOrientArrows: function(sourceArrow, targetArrow) {\n\n // Make the markers \"point\" to their sticky points being auto-oriented towards\n // `targetPosition`/`sourcePosition`. And do so only if there is a markup for them.\n const route = joint.util.toArray(this.route)\n if (sourceArrow) {\n sourceArrow.translateAndAutoOrient(\n this.sourcePoint,\n route[0] || this.targetPoint,\n this.paper.viewport\n )\n }\n\n if (targetArrow) {\n targetArrow.translateAndAutoOrient(\n this.targetPoint,\n route[route.length - 1] || this.sourcePoint,\n this.paper.viewport\n )\n }\n },\n\n _getDefaultLabelPositionArgs: function() {\n\n const defaultLabel = this.model._getDefaultLabel()\n const defaultLabelPosition = defaultLabel.position || {}\n return defaultLabelPosition.args\n },\n\n _getLabelPositionArgs: function(idx) {\n\n const labelPosition = this.model.label(idx).position || {}\n return labelPosition.args\n },\n\n // merge default label position args into label position args\n // keep `undefined` or `null` because `{}` means something else\n _mergeLabelPositionArgs: function(labelPositionArgs, defaultLabelPositionArgs) {\n\n if (labelPositionArgs === null) return null\n if (labelPositionArgs === undefined) {\n\n if (defaultLabelPositionArgs === null) return null\n return defaultLabelPositionArgs\n }\n\n return joint.util.merge({}, defaultLabelPositionArgs, labelPositionArgs)\n },\n\n // Add default label at given position at end of `labels` array.\n // Assigns relative coordinates by default.\n // `opt.absoluteDistance` forces absolute coordinates.\n // `opt.reverseDistance` forces reverse absolute coordinates (if absoluteDistance = true).\n // `opt.absoluteOffset` forces absolute coordinates for offset.\n addLabel: function(x, y, opt) {\n\n // accept input in form `{ x, y }, opt` or `x, y, opt`\n const isPointProvided = (typeof x !== 'number')\n const localX = isPointProvided ? x.x : x\n const localY = isPointProvided ? x.y : y\n const localOpt = isPointProvided ? y : opt\n\n const defaultLabelPositionArgs = this._getDefaultLabelPositionArgs()\n const labelPositionArgs = localOpt\n const positionArgs = this._mergeLabelPositionArgs(labelPositionArgs, defaultLabelPositionArgs)\n\n const label = { position: this.getLabelPosition(localX, localY, positionArgs) }\n const idx = -1\n this.model.insertLabel(idx, label, localOpt)\n return idx\n },\n\n // Add a new vertex at calculated index to the `vertices` array.\n addVertex: function(x, y, opt) {\n\n // accept input in form `{ x, y }, opt` or `x, y, opt`\n const isPointProvided = (typeof x !== 'number')\n const localX = isPointProvided ? x.x : x\n const localY = isPointProvided ? x.y : y\n const localOpt = isPointProvided ? y : opt\n\n const vertex = { x: localX, y: localY }\n const idx = this.getVertexIndex(localX, localY)\n this.model.insertVertex(idx, vertex, localOpt)\n return idx\n },\n\n // Send a token (an SVG element, usually a circle) along the connection path.\n // Example: `link.findView(paper).sendToken(V('circle', { r: 7, fill: 'green' }).node)`\n // `opt.duration` is optional and is a time in milliseconds that the token travels from the source to the target of the link. Default is `1000`.\n // `opt.directon` is optional and it determines whether the token goes from source to target or other way round (`reverse`)\n // `opt.connection` is an optional selector to the connection path.\n // `callback` is optional and is a function to be called once the token reaches the target.\n sendToken: function(token, opt, callback) {\n\n function onAnimationEnd(vToken, callback) {\n return function() {\n vToken.remove()\n if (typeof callback === 'function') {\n callback()\n }\n }\n }\n\n let duration; let isReversed; let selector\n if (joint.util.isObject(opt)) {\n duration = opt.duration\n isReversed = (opt.direction === 'reverse')\n selector = opt.connection\n } else {\n // Backwards compatibility\n duration = opt\n isReversed = false\n selector = null\n }\n\n duration = duration || 1000\n\n const animationAttributes = {\n dur: `${duration }ms`,\n repeatCount: 1,\n calcMode: 'linear',\n fill: 'freeze'\n }\n\n if (isReversed) {\n animationAttributes.keyPoints = '1;0'\n animationAttributes.keyTimes = '0;1'\n }\n\n const vToken = V(token)\n let connection\n if (typeof selector === 'string') {\n // Use custom connection path.\n connection = this.findBySelector(selector, this.el, this.selectors)[0]\n } else {\n // Select connection path automatically.\n const cache = this._V\n connection = (cache.connection) ? cache.connection.node : this.el.querySelector('path')\n }\n\n if (!(connection instanceof SVGPathElement)) {\n throw new Error('dia.LinkView: token animation requires a valid connection path.')\n }\n\n vToken\n .appendTo(this.paper.viewport)\n .animateAlongPath(animationAttributes, connection)\n\n setTimeout(onAnimationEnd(vToken, callback), duration)\n },\n\n findRoute: function(vertices) {\n\n vertices || (vertices = [])\n\n const namespace = joint.routers\n let router = this.model.router()\n const {defaultRouter} = this.paper.options\n\n if (!router) {\n if (defaultRouter) router = defaultRouter\n else return vertices.map(g.Point, g) // no router specified\n }\n\n const routerFn = joint.util.isFunction(router) ? router : namespace[router.name]\n if (!joint.util.isFunction(routerFn)) {\n throw new Error(`dia.LinkView: unknown router: \"${ router.name }\".`)\n }\n\n const args = router.args || {}\n\n const route = routerFn.call(\n this, // context\n vertices, // vertices\n args, // options\n this // linkView\n )\n\n if (!route) return vertices.map(g.Point, g)\n return route\n },\n\n // Return the `d` attribute value of the `` element representing the link\n // between `source` and `target`.\n findPath: function(route, sourcePoint, targetPoint) {\n\n const namespace = joint.connectors\n let connector = this.model.connector()\n const {defaultConnector} = this.paper.options\n\n if (!connector) {\n connector = defaultConnector || {}\n }\n\n const connectorFn = joint.util.isFunction(connector) ? connector : namespace[connector.name]\n if (!joint.util.isFunction(connectorFn)) {\n throw new Error(`dia.LinkView: unknown connector: \"${ connector.name }\".`)\n }\n\n const args = joint.util.clone(connector.args || {})\n args.raw = true // Request raw g.Path as the result.\n\n let path = connectorFn.call(\n this, // context\n sourcePoint, // start point\n targetPoint, // end point\n route, // vertices\n args, // options\n this // linkView\n )\n\n if (typeof path === 'string') {\n // Backwards compatibility for connectors not supporting `raw` option.\n path = new g.Path(V.normalizePathData(path))\n }\n\n return path\n },\n\n // Public API.\n // -----------\n\n getConnection: function() {\n\n const {path} = this\n if (!path) return null\n\n return path.clone()\n },\n\n getSerializedConnection: function() {\n\n const {path} = this\n if (!path) return null\n\n const {metrics} = this\n if (metrics.hasOwnProperty('data')) return metrics.data\n const data = path.serialize()\n metrics.data = data\n return data\n },\n\n getConnectionSubdivisions: function() {\n\n const {path} = this\n if (!path) return null\n\n const {metrics} = this\n if (metrics.hasOwnProperty('segmentSubdivisions')) return metrics.segmentSubdivisions\n const subdivisions = path.getSegmentSubdivisions()\n metrics.segmentSubdivisions = subdivisions\n return subdivisions\n },\n\n getConnectionLength: function() {\n\n const {path} = this\n if (!path) return 0\n\n const {metrics} = this\n if (metrics.hasOwnProperty('length')) return metrics.length\n const length = path.length({ segmentSubdivisions: this.getConnectionSubdivisions() })\n metrics.length = length\n return length\n },\n\n getPointAtLength: function(length) {\n\n const {path} = this\n if (!path) return null\n\n return path.pointAtLength(length, { segmentSubdivisions: this.getConnectionSubdivisions() })\n },\n\n getPointAtRatio: function(ratio) {\n\n const {path} = this\n if (!path) return null\n\n return path.pointAt(ratio, { segmentSubdivisions: this.getConnectionSubdivisions() })\n },\n\n getTangentAtLength: function(length) {\n\n const {path} = this\n if (!path) return null\n\n return path.tangentAtLength(length, { segmentSubdivisions: this.getConnectionSubdivisions() })\n },\n\n getTangentAtRatio: function(ratio) {\n\n const {path} = this\n if (!path) return null\n\n return path.tangentAt(ratio, { segmentSubdivisions: this.getConnectionSubdivisions() })\n },\n\n getClosestPoint: function(point) {\n\n const {path} = this\n if (!path) return null\n\n return path.closestPoint(point, { segmentSubdivisions: this.getConnectionSubdivisions() })\n },\n\n getClosestPointLength: function(point) {\n\n const {path} = this\n if (!path) return null\n\n return path.closestPointLength(point, { segmentSubdivisions: this.getConnectionSubdivisions() })\n },\n\n getClosestPointRatio: function(point) {\n\n const {path} = this\n if (!path) return null\n\n return path.closestPointNormalizedLength(point, { segmentSubdivisions: this.getConnectionSubdivisions() })\n },\n\n // accepts options `absoluteDistance: boolean`, `reverseDistance: boolean`, `absoluteOffset: boolean`\n // to move beyond connection endpoints, absoluteOffset has to be set\n getLabelPosition: function(x, y, opt) {\n\n const position = {}\n\n const localOpt = opt || {}\n if (opt) position.args = opt\n\n const isDistanceRelative = !localOpt.absoluteDistance // relative by default\n const isDistanceAbsoluteReverse = (localOpt.absoluteDistance && localOpt.reverseDistance) // non-reverse by default\n const isOffsetAbsolute = localOpt.absoluteOffset // offset is non-absolute by default\n\n const {path} = this\n const pathOpt = { segmentSubdivisions: this.getConnectionSubdivisions() }\n\n const labelPoint = new g.Point(x, y)\n const t = path.closestPointT(labelPoint, pathOpt)\n\n // GET DISTANCE:\n\n let labelDistance = path.lengthAtT(t, pathOpt)\n if (isDistanceRelative) labelDistance = (labelDistance / this.getConnectionLength()) || 0 // fix to prevent NaN for 0 length\n if (isDistanceAbsoluteReverse) labelDistance = (-1 * (this.getConnectionLength() - labelDistance)) || 1 // fix for end point (-0 => 1)\n\n position.distance = labelDistance\n\n // GET OFFSET:\n // use absolute offset if:\n // - opt.absoluteOffset is true,\n // - opt.absoluteOffset is not true but there is no tangent\n\n let tangent\n if (!isOffsetAbsolute) tangent = path.tangentAtT(t)\n\n let labelOffset\n if (tangent) {\n labelOffset = tangent.pointOffset(labelPoint)\n\n } else {\n const closestPoint = path.pointAtT(t)\n const labelOffsetDiff = labelPoint.difference(closestPoint)\n labelOffset = { x: labelOffsetDiff.x, y: labelOffsetDiff.y }\n }\n\n position.offset = labelOffset\n\n return position\n },\n\n getLabelCoordinates: function(labelPosition) {\n\n let labelDistance\n if (typeof labelPosition === 'number') labelDistance = labelPosition\n else if (typeof labelPosition.distance === 'number') labelDistance = labelPosition.distance\n else throw new Error('dia.LinkView: invalid label position distance.')\n\n const isDistanceRelative = ((labelDistance > 0) && (labelDistance <= 1))\n\n let labelOffset = 0\n const labelOffsetCoordinates = { x: 0, y: 0 }\n if (labelPosition.offset) {\n const positionOffset = labelPosition.offset\n if (typeof positionOffset === 'number') labelOffset = positionOffset\n if (positionOffset.x) labelOffsetCoordinates.x = positionOffset.x\n if (positionOffset.y) labelOffsetCoordinates.y = positionOffset.y\n }\n\n const isOffsetAbsolute = ((labelOffsetCoordinates.x !== 0) || (labelOffsetCoordinates.y !== 0) || labelOffset === 0)\n\n const {path} = this\n const pathOpt = { segmentSubdivisions: this.getConnectionSubdivisions() }\n\n const distance = isDistanceRelative ? (labelDistance * this.getConnectionLength()) : labelDistance\n\n let point\n\n if (isOffsetAbsolute) {\n point = path.pointAtLength(distance, pathOpt)\n point.offset(labelOffsetCoordinates)\n\n } else {\n const tangent = path.tangentAtLength(distance, pathOpt)\n\n if (tangent) {\n tangent.rotate(tangent.start, -90)\n tangent.setLength(labelOffset)\n point = tangent.end\n\n } else {\n // fallback - the connection has zero length\n point = path.start\n }\n }\n\n return point\n },\n\n getVertexIndex: function(x, y) {\n\n const {model} = this\n const vertices = model.vertices()\n\n const vertexLength = this.getClosestPointLength(new g.Point(x, y))\n\n let idx = 0\n for (let n = vertices.length; idx < n; idx++) {\n const currentVertex = vertices[idx]\n const currentVertexLength = this.getClosestPointLength(currentVertex)\n if (vertexLength < currentVertexLength) break\n }\n\n return idx\n },\n\n // Interaction. The controller part.\n // ---------------------------------\n\n pointerdblclick: function(evt, x, y) {\n\n joint.dia.CellView.prototype.pointerdblclick.apply(this, arguments)\n this.notify('link:pointerdblclick', evt, x, y)\n },\n\n pointerclick: function(evt, x, y) {\n\n joint.dia.CellView.prototype.pointerclick.apply(this, arguments)\n this.notify('link:pointerclick', evt, x, y)\n },\n\n contextmenu: function(evt, x, y) {\n\n joint.dia.CellView.prototype.contextmenu.apply(this, arguments)\n this.notify('link:contextmenu', evt, x, y)\n },\n\n pointerdown: function(evt, x, y) {\n\n joint.dia.CellView.prototype.pointerdown.apply(this, arguments)\n this.notify('link:pointerdown', evt, x, y)\n\n // Backwards compatibility for the default markup\n const className = evt.target.getAttribute('class')\n switch (className) {\n\n case 'marker-vertex':\n this.dragVertexStart(evt, x, y)\n return\n\n case 'marker-vertex-remove':\n case 'marker-vertex-remove-area':\n this.dragVertexRemoveStart(evt, x, y)\n return\n\n case 'marker-arrowhead':\n this.dragArrowheadStart(evt, x, y)\n return\n\n case 'connection':\n case 'connection-wrap':\n this.dragConnectionStart(evt, x, y)\n return\n\n case 'marker-source':\n case 'marker-target':\n return\n }\n\n this.dragStart(evt, x, y)\n },\n\n pointermove: function(evt, x, y) {\n\n // Backwards compatibility\n const dragData = this._dragData\n if (dragData) this.eventData(evt, dragData)\n\n const data = this.eventData(evt)\n switch (data.action) {\n\n case 'vertex-move':\n this.dragVertex(evt, x, y)\n break\n\n case 'label-move':\n this.dragLabel(evt, x, y)\n break\n\n case 'arrowhead-move':\n this.dragArrowhead(evt, x, y)\n break\n\n case 'move':\n this.drag(evt, x, y)\n break\n }\n\n // Backwards compatibility\n if (dragData) joint.util.assign(dragData, this.eventData(evt))\n\n joint.dia.CellView.prototype.pointermove.apply(this, arguments)\n this.notify('link:pointermove', evt, x, y)\n },\n\n pointerup: function(evt, x, y) {\n\n // Backwards compatibility\n const dragData = this._dragData\n if (dragData) {\n this.eventData(evt, dragData)\n this._dragData = null\n }\n\n const data = this.eventData(evt)\n switch (data.action) {\n\n case 'vertex-move':\n this.dragVertexEnd(evt, x, y)\n break\n\n case 'label-move':\n this.dragLabelEnd(evt, x, y)\n break\n\n case 'arrowhead-move':\n this.dragArrowheadEnd(evt, x, y)\n break\n\n case 'move':\n this.dragEnd(evt, x, y)\n }\n\n this.notify('link:pointerup', evt, x, y)\n joint.dia.CellView.prototype.pointerup.apply(this, arguments)\n },\n\n mouseover: function(evt) {\n\n joint.dia.CellView.prototype.mouseover.apply(this, arguments)\n this.notify('link:mouseover', evt)\n },\n\n mouseout: function(evt) {\n\n joint.dia.CellView.prototype.mouseout.apply(this, arguments)\n this.notify('link:mouseout', evt)\n },\n\n mouseenter: function(evt) {\n\n joint.dia.CellView.prototype.mouseenter.apply(this, arguments)\n this.notify('link:mouseenter', evt)\n },\n\n mouseleave: function(evt) {\n\n joint.dia.CellView.prototype.mouseleave.apply(this, arguments)\n this.notify('link:mouseleave', evt)\n },\n\n mousewheel: function(evt, x, y, delta) {\n\n joint.dia.CellView.prototype.mousewheel.apply(this, arguments)\n this.notify('link:mousewheel', evt, x, y, delta)\n },\n\n onevent: function(evt, eventName, x, y) {\n\n // Backwards compatibility\n const linkTool = V(evt.target).findParentByClass('link-tool', this.el)\n if (linkTool) {\n // Allow `interactive.useLinkTools=false`\n if (this.can('useLinkTools')) {\n if (eventName === 'remove') {\n // No further action to be executed\n evt.stopPropagation()\n // Built-in remove event\n this.model.remove({ ui: true })\n\n } else {\n // link:options and other custom events inside the link tools\n this.notify(eventName, evt, x, y)\n }\n }\n\n } else {\n joint.dia.CellView.prototype.onevent.apply(this, arguments)\n }\n },\n\n onlabel: function(evt, x, y) {\n\n joint.dia.CellView.prototype.pointerdown.apply(this, arguments)\n this.notify('link:pointerdown', evt, x, y)\n\n this.dragLabelStart(evt, x, y)\n\n const {stopPropagation} = this.eventData(evt)\n if (stopPropagation) evt.stopPropagation()\n },\n\n // Drag Start Handlers\n\n dragConnectionStart: function(evt, x, y) {\n\n if (!this.can('vertexAdd')) return\n\n // Store the index at which the new vertex has just been placed.\n // We'll be update the very same vertex position in `pointermove()`.\n const vertexIdx = this.addVertex({ x: x, y: y }, { ui: true })\n this.eventData(evt, {\n action: 'vertex-move',\n vertexIdx: vertexIdx\n })\n },\n\n dragLabelStart: function(evt, x, y) {\n\n if (!this.can('labelMove')) {\n // Backwards compatibility:\n // If labels can't be dragged no default action is triggered.\n this.eventData(evt, { stopPropagation: true })\n return\n }\n\n const labelNode = evt.currentTarget\n const labelIdx = parseInt(labelNode.getAttribute('label-idx'), 10)\n\n const defaultLabelPositionArgs = this._getDefaultLabelPositionArgs()\n const labelPositionArgs = this._getLabelPositionArgs(labelIdx)\n const positionArgs = this._mergeLabelPositionArgs(labelPositionArgs, defaultLabelPositionArgs)\n\n this.eventData(evt, {\n action: 'label-move',\n labelIdx: labelIdx,\n positionArgs: positionArgs,\n stopPropagation: true\n })\n\n this.paper.delegateDragEvents(this, evt.data)\n },\n\n dragVertexStart: function(evt, x, y) {\n\n if (!this.can('vertexMove')) return\n\n const vertexNode = evt.target\n const vertexIdx = parseInt(vertexNode.getAttribute('idx'), 10)\n this.eventData(evt, {\n action: 'vertex-move',\n vertexIdx: vertexIdx\n })\n },\n\n dragVertexRemoveStart: function(evt, x, y) {\n\n if (!this.can('vertexRemove')) return\n\n const removeNode = evt.target\n const vertexIdx = parseInt(removeNode.getAttribute('idx'), 10)\n this.model.removeVertex(vertexIdx)\n },\n\n dragArrowheadStart: function(evt, x, y) {\n\n if (!this.can('arrowheadMove')) return\n\n const arrowheadNode = evt.target\n const arrowheadType = arrowheadNode.getAttribute('end')\n const data = this.startArrowheadMove(arrowheadType, { ignoreBackwardsCompatibility: true })\n\n this.eventData(evt, data)\n },\n\n dragStart: function(evt, x, y) {\n\n if (!this.can('linkMove')) return\n\n this.eventData(evt, {\n action: 'move',\n dx: x,\n dy: y\n })\n },\n\n // Drag Handlers\n\n dragLabel: function(evt, x, y) {\n\n const data = this.eventData(evt)\n const label = { position: this.getLabelPosition(x, y, data.positionArgs) }\n this.model.label(data.labelIdx, label)\n },\n\n dragVertex: function(evt, x, y) {\n\n const data = this.eventData(evt)\n this.model.vertex(data.vertexIdx, { x: x, y: y }, { ui: true })\n },\n\n dragArrowhead: function(evt, x, y) {\n\n const data = this.eventData(evt)\n\n if (this.paper.options.snapLinks) {\n\n this._snapArrowhead(x, y, data)\n\n } else {\n // Touchmove event's target is not reflecting the element under the coordinates as mousemove does.\n // It holds the element when a touchstart triggered.\n const target = (evt.type === 'mousemove')\n ? evt.target\n : document.elementFromPoint(evt.clientX, evt.clientY)\n\n this._connectArrowhead(target, x, y, data)\n }\n },\n\n drag: function(evt, x, y) {\n\n const data = this.eventData(evt)\n this.model.translate(x - data.dx, y - data.dy, { ui: true })\n this.eventData(evt, {\n dx: x,\n dy: y\n })\n },\n\n // Drag End Handlers\n\n dragLabelEnd: function() {\n // noop\n },\n\n dragVertexEnd: function() {\n // noop\n },\n\n dragArrowheadEnd: function(evt, x, y) {\n\n const data = this.eventData(evt)\n const {paper} = this\n\n if (paper.options.snapLinks) {\n this._snapArrowheadEnd(data)\n } else {\n this._connectArrowheadEnd(data, x, y)\n }\n\n if (!paper.linkAllowed(this)) {\n // If the changed link is not allowed, revert to its previous state.\n this._disallow(data)\n } else {\n this._finishEmbedding(data)\n this._notifyConnectEvent(data, evt)\n }\n\n this._afterArrowheadMove(data)\n\n // mouseleave event is not triggered due to changing pointer-events to `none`.\n if (!this.vel.contains(evt.target)) {\n this.mouseleave(evt)\n }\n },\n\n dragEnd: function() {\n // noop\n },\n\n _disallow: function(data) {\n\n switch (data.whenNotAllowed) {\n\n case 'remove':\n this.model.remove({ ui: true })\n break\n\n case 'revert':\n default:\n this.model.set(data.arrowhead, data.initialEnd, { ui: true })\n break\n }\n },\n\n _finishEmbedding: function(data) {\n\n // Reparent the link if embedding is enabled\n if (this.paper.options.embeddingMode && this.model.reparent()) {\n // Make sure we don't reverse to the original 'z' index (see afterArrowheadMove()).\n data.z = null\n }\n },\n\n _notifyConnectEvent: function(data, evt) {\n\n const {arrowhead} = data\n const {initialEnd} = data\n const currentEnd = this.model.prop(arrowhead)\n const endChanged = currentEnd && !joint.dia.Link.endsEqual(initialEnd, currentEnd)\n if (endChanged) {\n const {paper} = this\n if (initialEnd.id) {\n this.notify('link:disconnect', evt, paper.findViewByModel(initialEnd.id), data.initialMagnet, arrowhead)\n }\n if (currentEnd.id) {\n this.notify('link:connect', evt, paper.findViewByModel(currentEnd.id), data.magnetUnderPointer, arrowhead)\n }\n }\n },\n\n _snapArrowhead: function(x, y, data) {\n\n // checking view in close area of the pointer\n\n const r = this.paper.options.snapLinks.radius || 50\n const viewsInArea = this.paper.findViewsInArea({ x: x - r, y: y - r, width: 2 * r, height: 2 * r })\n\n if (data.closestView) {\n data.closestView.unhighlight(data.closestMagnet, {\n connecting: true,\n snapping: true\n })\n }\n data.closestView = data.closestMagnet = null\n\n let distance\n let minDistance = Number.MAX_VALUE\n const pointer = g.point(x, y)\n const {paper} = this\n\n viewsInArea.forEach(function(view) {\n\n // skip connecting to the element in case '.': { magnet: false } attribute present\n if (view.el.getAttribute('magnet') !== 'false') {\n\n // find distance from the center of the model to pointer coordinates\n distance = view.model.getBBox().center().distance(pointer)\n\n // the connection is looked up in a circle area by `distance < r`\n if (distance < r && distance < minDistance) {\n\n if (paper.options.validateConnection.apply(\n paper, data.validateConnectionArgs(view, null)\n )) {\n minDistance = distance\n data.closestView = view\n data.closestMagnet = view.el\n }\n }\n }\n\n view.$('[magnet]').each(function(index, magnet) {\n\n const bbox = view.getNodeBBox(magnet)\n\n distance = pointer.distance({\n x: bbox.x + bbox.width / 2,\n y: bbox.y + bbox.height / 2\n })\n\n if (distance < r && distance < minDistance) {\n\n if (paper.options.validateConnection.apply(\n paper, data.validateConnectionArgs(view, magnet)\n )) {\n minDistance = distance\n data.closestView = view\n data.closestMagnet = magnet\n }\n }\n\n })\n\n }, this)\n\n let end\n const {closestView} = data\n const {closestMagnet} = data\n const endType = data.arrowhead\n if (closestView) {\n closestView.highlight(closestMagnet, {\n connecting: true,\n snapping: true\n })\n end = closestView.getLinkEnd(closestMagnet, x, y, this.model, endType)\n } else {\n end = { x: x, y: y }\n }\n\n this.model.set(endType, end || { x: x, y: y }, { ui: true })\n },\n\n _snapArrowheadEnd: function(data) {\n\n // Finish off link snapping.\n // Everything except view unhighlighting was already done on pointermove.\n const {closestView} = data\n const {closestMagnet} = data\n if (closestView && closestMagnet) {\n\n closestView.unhighlight(closestMagnet, { connecting: true, snapping: true })\n data.magnetUnderPointer = closestView.findMagnet(closestMagnet)\n }\n\n data.closestView = data.closestMagnet = null\n },\n\n _connectArrowhead: function(target, x, y, data) {\n\n // checking views right under the pointer\n\n if (data.eventTarget !== target) {\n // Unhighlight the previous view under pointer if there was one.\n if (data.magnetUnderPointer) {\n data.viewUnderPointer.unhighlight(data.magnetUnderPointer, {\n connecting: true\n })\n }\n\n data.viewUnderPointer = this.paper.findView(target)\n if (data.viewUnderPointer) {\n // If we found a view that is under the pointer, we need to find the closest\n // magnet based on the real target element of the event.\n data.magnetUnderPointer = data.viewUnderPointer.findMagnet(target)\n\n if (data.magnetUnderPointer && this.paper.options.validateConnection.apply(\n this.paper,\n data.validateConnectionArgs(data.viewUnderPointer, data.magnetUnderPointer)\n )) {\n // If there was no magnet found, do not highlight anything and assume there\n // is no view under pointer we're interested in reconnecting to.\n // This can only happen if the overall element has the attribute `'.': { magnet: false }`.\n if (data.magnetUnderPointer) {\n data.viewUnderPointer.highlight(data.magnetUnderPointer, {\n connecting: true\n })\n }\n } else {\n // This type of connection is not valid. Disregard this magnet.\n data.magnetUnderPointer = null\n }\n } else {\n // Make sure we'll unset previous magnet.\n data.magnetUnderPointer = null\n }\n }\n\n data.eventTarget = target\n\n this.model.set(data.arrowhead, { x: x, y: y }, { ui: true })\n },\n\n _connectArrowheadEnd: function(data, x, y) {\n\n const view = data.viewUnderPointer\n const magnet = data.magnetUnderPointer\n if (!magnet || !view) return\n\n view.unhighlight(magnet, { connecting: true })\n\n const endType = data.arrowhead\n const end = view.getLinkEnd(magnet, x, y, this.model, endType)\n this.model.set(endType, end, { ui: true })\n },\n\n _beforeArrowheadMove: function(data) {\n\n data.z = this.model.get('z')\n this.model.toFront()\n\n // Let the pointer propagate throught the link view elements so that\n // the `evt.target` is another element under the pointer, not the link itself.\n this.el.style.pointerEvents = 'none'\n\n if (this.paper.options.markAvailable) {\n this._markAvailableMagnets(data)\n }\n },\n\n _afterArrowheadMove: function(data) {\n\n if (data.z !== null) {\n this.model.set('z', data.z, { ui: true })\n data.z = null\n }\n\n // Put `pointer-events` back to its original value. See `startArrowheadMove()` for explanation.\n // Value `auto` doesn't work in IE9. We force to use `visiblePainted` instead.\n // See `https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events`.\n this.el.style.pointerEvents = 'visiblePainted'\n\n if (this.paper.options.markAvailable) {\n this._unmarkAvailableMagnets(data)\n }\n },\n\n _createValidateConnectionArgs: function(arrowhead) {\n // It makes sure the arguments for validateConnection have the following form:\n // (source view, source magnet, target view, target magnet and link view)\n const args = []\n\n args[4] = arrowhead\n args[5] = this\n\n let oppositeArrowhead\n let i = 0\n let j = 0\n\n if (arrowhead === 'source') {\n i = 2\n oppositeArrowhead = 'target'\n } else {\n j = 2\n oppositeArrowhead = 'source'\n }\n\n const end = this.model.get(oppositeArrowhead)\n\n if (end.id) {\n const view = args[i] = this.paper.findViewByModel(end.id)\n let magnet = view.getMagnetFromLinkEnd(end)\n if (magnet === view.el) magnet = undefined\n args[i + 1] = magnet\n }\n\n function validateConnectionArgs(cellView, magnet) {\n args[j] = cellView\n args[j + 1] = cellView.el === magnet ? undefined : magnet\n return args\n }\n\n return validateConnectionArgs\n },\n\n _markAvailableMagnets: function(data) {\n\n function isMagnetAvailable(view, magnet) {\n const {paper} = view\n const validate = paper.options.validateConnection\n return validate.apply(paper, this.validateConnectionArgs(view, magnet))\n }\n\n const {paper} = this\n const elements = paper.model.getElements()\n data.marked = {}\n\n for (let i = 0, n = elements.length; i < n; i++) {\n const view = elements[i].findView(paper)\n\n if (!view) {\n continue\n }\n\n const magnets = Array.prototype.slice.call(view.el.querySelectorAll('[magnet]'))\n if (view.el.getAttribute('magnet') !== 'false') {\n // Element wrapping group is also a magnet\n magnets.push(view.el)\n }\n\n const availableMagnets = magnets.filter(isMagnetAvailable.bind(data, view))\n\n if (availableMagnets.length > 0) {\n // highlight all available magnets\n for (let j = 0, m = availableMagnets.length; j < m; j++) {\n view.highlight(availableMagnets[j], { magnetAvailability: true })\n }\n // highlight the entire view\n view.highlight(null, { elementAvailability: true })\n\n data.marked[view.model.id] = availableMagnets\n }\n }\n },\n\n _unmarkAvailableMagnets: function(data) {\n\n const markedKeys = Object.keys(data.marked)\n let id\n let markedMagnets\n\n for (let i = 0, n = markedKeys.length; i < n; i++) {\n id = markedKeys[i]\n markedMagnets = data.marked[id]\n\n const view = this.paper.findViewByModel(id)\n if (view) {\n for (let j = 0, m = markedMagnets.length; j < m; j++) {\n view.unhighlight(markedMagnets[j], { magnetAvailability: true })\n }\n view.unhighlight(null, { elementAvailability: true })\n }\n }\n\n data.marked = null\n },\n\n startArrowheadMove: function(end, opt) {\n\n opt || (opt = {})\n\n // Allow to delegate events from an another view to this linkView in order to trigger arrowhead\n // move without need to click on the actual arrowhead dom element.\n const data = {\n action: 'arrowhead-move',\n arrowhead: end,\n whenNotAllowed: opt.whenNotAllowed || 'revert',\n initialMagnet: this[`${end }Magnet`] || (this[`${end }View`] ? this[`${end }View`].el : null),\n initialEnd: joint.util.clone(this.model.get(end)),\n validateConnectionArgs: this._createValidateConnectionArgs(end)\n }\n\n this._beforeArrowheadMove(data)\n\n if (opt.ignoreBackwardsCompatibility !== true) {\n this._dragData = data\n }\n\n return data\n }\n}, {\n\n makeSelector: function(end) {\n\n let selector = ''\n // `port` has a higher precendence over `selector`. This is because the selector to the magnet\n // might change while the name of the port can stay the same.\n if (end.port) {\n selector += `[port=\"${ end.port }\"]`\n } else if (end.selector) {\n selector += end.selector\n }\n\n return selector\n }\n\n})\n\n\nObject.defineProperty(joint.dia.LinkView.prototype, 'sourceBBox', {\n\n enumerable: true,\n\n get: function() {\n const {sourceView} = this\n let {sourceMagnet} = this\n if (sourceView) {\n if (!sourceMagnet) sourceMagnet = sourceView.el\n return sourceView.getNodeBBox(sourceMagnet)\n }\n const sourceDef = this.model.source()\n return new g.Rect(sourceDef.x, sourceDef.y, 1, 1)\n }\n\n})\n\nObject.defineProperty(joint.dia.LinkView.prototype, 'targetBBox', {\n\n enumerable: true,\n\n get: function() {\n const {targetView} = this\n let {targetMagnet} = this\n if (targetView) {\n if (!targetMagnet) targetMagnet = targetView.el\n return targetView.getNodeBBox(targetMagnet)\n }\n const targetDef = this.model.target()\n return new g.Rect(targetDef.x, targetDef.y, 1, 1)\n }\n})\n\n\njoint.dia.Paper = joint.mvc.View.extend({\n\n className: 'paper',\n\n options: {\n\n width: 800,\n height: 600,\n origin: { x: 0, y: 0 }, // x,y coordinates in top-left corner\n gridSize: 1,\n\n // Whether or not to draw the grid lines on the paper's DOM element.\n // e.g drawGrid: true, drawGrid: { color: 'red', thickness: 2 }\n drawGrid: false,\n\n // Whether or not to draw the background on the paper's DOM element.\n // e.g. background: { color: 'lightblue', image: '/paper-background.png', repeat: 'flip-xy' }\n background: false,\n\n perpendicularLinks: false,\n elementView: joint.dia.ElementView,\n linkView: joint.dia.LinkView,\n snapLinks: false, // false, true, { radius: value }\n\n // When set to FALSE, an element may not have more than 1 link with the same source and target element.\n multiLinks: true,\n\n // For adding custom guard logic.\n guard: function(evt, view) {\n\n // FALSE means the event isn't guarded.\n return false\n },\n\n highlighting: {\n default: {\n name: 'stroke',\n options: {\n padding: 3\n }\n },\n magnetAvailability: {\n name: 'addClass',\n options: {\n className: 'available-magnet'\n }\n },\n elementAvailability: {\n name: 'addClass',\n options: {\n className: 'available-cell'\n }\n }\n },\n\n // Prevent the default context menu from being displayed.\n preventContextMenu: true,\n\n // Prevent the default action for blank:pointer.\n preventDefaultBlankAction: true,\n\n // Restrict the translation of elements by given bounding box.\n // Option accepts a boolean:\n // true - the translation is restricted to the paper area\n // false - no restrictions\n // A method:\n // restrictTranslate: function(elementView) {\n // var parentId = elementView.model.get('parent');\n // return parentId && this.model.getCell(parentId).getBBox();\n // },\n // Or a bounding box:\n // restrictTranslate: { x: 10, y: 10, width: 790, height: 590 }\n restrictTranslate: false,\n\n // Marks all available magnets with 'available-magnet' class name and all available cells with\n // 'available-cell' class name. Marks them when dragging a link is started and unmark\n // when the dragging is stopped.\n markAvailable: false,\n\n // Defines what link model is added to the graph after an user clicks on an active magnet.\n // Value could be the Backbone.model or a function returning the Backbone.model\n // defaultLink: function(elementView, magnet) { return condition ? new customLink1() : new customLink2() }\n defaultLink: new joint.dia.Link,\n\n // A connector that is used by links with no connector defined on the model.\n // e.g. { name: 'rounded', args: { radius: 5 }} or a function\n defaultConnector: { name: 'normal' },\n\n // A router that is used by links with no router defined on the model.\n // e.g. { name: 'oneSide', args: { padding: 10 }} or a function\n defaultRouter: { name: 'normal' },\n\n defaultAnchor: { name: 'center' },\n\n defaultConnectionPoint: { name: 'bbox' },\n\n /* CONNECTING */\n\n connectionStrategy: null,\n\n // Check whether to add a new link to the graph when user clicks on an a magnet.\n validateMagnet: function(cellView, magnet) {\n return magnet.getAttribute('magnet') !== 'passive'\n },\n\n // Check whether to allow or disallow the link connection while an arrowhead end (source/target)\n // being changed.\n validateConnection: function(cellViewS, magnetS, cellViewT, magnetT, end, linkView) {\n return (end === 'target' ? cellViewT : cellViewS) instanceof joint.dia.ElementView\n },\n\n /* EMBEDDING */\n\n // Enables embedding. Reparents the dragged element with elements under it and makes sure that\n // all links and elements are visible taken the level of embedding into account.\n embeddingMode: false,\n\n // Check whether to allow or disallow the element embedding while an element being translated.\n validateEmbedding: function(childView, parentView) {\n // by default all elements can be in relation child-parent\n return true\n },\n\n // Determines the way how a cell finds a suitable parent when it's dragged over the paper.\n // The cell with the highest z-index (visually on the top) will be chosen.\n findParentBy: 'bbox', // 'bbox'|'center'|'origin'|'corner'|'topRight'|'bottomLeft'\n\n // If enabled only the element on the very front is taken into account for the embedding.\n // If disabled the elements under the dragged view are tested one by one\n // (from front to back) until a valid parent found.\n frontParentOnly: true,\n\n // Interactive flags. See online docs for the complete list of interactive flags.\n interactive: {\n labelMove: false\n },\n\n // When set to true the links can be pinned to the paper.\n // i.e. link source/target can be a point e.g. link.get('source') ==> { x: 100, y: 100 };\n linkPinning: true,\n\n // Custom validation after an interaction with a link ends.\n // Recognizes a function. If `false` is returned, the link is disallowed (removed or reverted)\n // (linkView, paper) => boolean\n allowLink: null,\n\n // Allowed number of mousemove events after which the pointerclick event will be still triggered.\n clickThreshold: 0,\n\n // Number of required mousemove events before the first pointermove event will be triggered.\n moveThreshold: 0,\n\n // The namespace, where all the cell views are defined.\n cellViewNamespace: joint.shapes,\n\n // The namespace, where all the cell views are defined.\n highlighterNamespace: joint.highlighters\n },\n\n events: {\n dblclick: 'pointerdblclick',\n click: 'pointerclick', // triggered alongside pointerdown and pointerup if no movement\n touchend: 'pointerclick', // triggered alongside pointerdown and pointerup if no movement\n contextmenu: 'contextmenu',\n mousedown: 'pointerdown',\n touchstart: 'pointerdown',\n mouseover: 'mouseover',\n mouseout: 'mouseout',\n mouseenter: 'mouseenter',\n mouseleave: 'mouseleave',\n mousewheel: 'mousewheel',\n DOMMouseScroll: 'mousewheel',\n 'mouseenter .joint-cell': 'mouseenter',\n 'mouseleave .joint-cell': 'mouseleave',\n 'mouseenter .joint-tools': 'mouseenter',\n 'mouseleave .joint-tools': 'mouseleave',\n 'mousedown .joint-cell [event]': 'onevent', // interaction with cell with `event` attribute set\n 'touchstart .joint-cell [event]': 'onevent',\n 'mousedown .joint-cell [magnet]': 'onmagnet', // interaction with cell with `magnet` attribute set\n 'touchstart .joint-cell [magnet]': 'onmagnet',\n 'mousedown .joint-link .label': 'onlabel', // interaction with link label\n 'touchstart .joint-link .label': 'onlabel',\n 'dragstart .joint-cell image': 'onImageDragStart' // firefox fix\n },\n\n documentEvents: {\n mousemove: 'pointermove',\n touchmove: 'pointermove',\n mouseup: 'pointerup',\n touchend: 'pointerup',\n touchcancel: 'pointerup'\n },\n\n _highlights: {},\n\n init: function() {\n\n joint.util.bindAll(this, 'pointerup')\n\n const model = this.model = this.options.model || new joint.dia.Graph\n\n this.setGrid(this.options.drawGrid)\n this.cloneOptions()\n this.render()\n this.setDimensions()\n\n this.listenTo(model, 'add', this.onCellAdded)\n .listenTo(model, 'remove', this.removeView)\n .listenTo(model, 'reset', this.resetViews)\n .listenTo(model, 'sort', this._onSort)\n .listenTo(model, 'batch:stop', this._onBatchStop)\n\n this.on('cell:highlight', this.onCellHighlight)\n .on('cell:unhighlight', this.onCellUnhighlight)\n .on('scale translate', this.update)\n\n // Hold the value when mouse has been moved: when mouse moved, no click event will be triggered.\n this._mousemoved = 0\n // Hash of all cell views.\n this._views = {}\n // Reference to the paper owner document\n this.$document = $(this.el.ownerDocument)\n },\n\n cloneOptions: function() {\n\n const {options} = this\n\n // This is a fix for the case where two papers share the same options.\n // Changing origin.x for one paper would change the value of origin.x for the other.\n // This prevents that behavior.\n options.origin = joint.util.assign({}, options.origin)\n options.defaultConnector = joint.util.assign({}, options.defaultConnector)\n // Return the default highlighting options into the user specified options.\n options.highlighting = joint.util.defaultsDeep(\n {},\n options.highlighting,\n this.constructor.prototype.options.highlighting\n )\n },\n\n render: function() {\n\n this.$el.empty()\n\n this.svg = V('svg').attr({ width: '100%', height: '100%' }).node\n this.viewport = V('g').addClass(joint.util.addClassNamePrefix('viewport')).node\n this.defs = V('defs').node\n this.tools = V('g').addClass(joint.util.addClassNamePrefix('tools-container')).node\n // Append `` element to the SVG document. This is useful for filters and gradients.\n // It's desired to have the defs defined before the viewport (e.g. to make a PDF document pick up defs properly).\n V(this.svg).append([this.defs, this.viewport, this.tools])\n\n this.$background = $('
').addClass(joint.util.addClassNamePrefix('paper-background'))\n if (this.options.background) {\n this.drawBackground(this.options.background)\n }\n\n this.$grid = $('
').addClass(joint.util.addClassNamePrefix('paper-grid'))\n if (this.options.drawGrid) {\n this.drawGrid()\n }\n\n this.$el.append(this.$background, this.$grid, this.svg)\n\n return this\n },\n\n update: function() {\n\n if (this.options.drawGrid) {\n this.drawGrid()\n }\n\n if (this._background) {\n this.updateBackgroundImage(this._background)\n }\n\n return this\n },\n\n // For storing the current transformation matrix (CTM) of the paper's viewport.\n _viewportMatrix: null,\n\n // For verifying whether the CTM is up-to-date. The viewport transform attribute\n // could have been manipulated directly.\n _viewportTransformString: null,\n\n matrix: function(ctm) {\n\n const {viewport} = this\n\n // Getter:\n if (ctm === undefined) {\n\n const transformString = viewport.getAttribute('transform')\n\n if ((this._viewportTransformString || null) === transformString) {\n // It's ok to return the cached matrix. The transform attribute has not changed since\n // the matrix was stored.\n ctm = this._viewportMatrix\n } else {\n // The viewport transform attribute has changed. Measure the matrix and cache again.\n ctm = viewport.getCTM()\n this._viewportMatrix = ctm\n this._viewportTransformString = transformString\n }\n\n // Clone the cached current transformation matrix.\n // If no matrix previously stored the identity matrix is returned.\n return V.createSVGMatrix(ctm)\n }\n\n // Setter:\n ctm = V.createSVGMatrix(ctm)\n const ctmString = V.matrixToTransformString(ctm)\n viewport.setAttribute('transform', ctmString)\n this.tools.setAttribute('transform', ctmString)\n\n this._viewportMatrix = ctm\n this._viewportTransformString = viewport.getAttribute('transform')\n\n return this\n },\n\n clientMatrix: function() {\n\n return V.createSVGMatrix(this.viewport.getScreenCTM())\n },\n\n _sortDelayingBatches: ['add', 'to-front', 'to-back'],\n\n _onSort: function() {\n if (!this.model.hasActiveBatch(this._sortDelayingBatches)) {\n this.sortViews()\n }\n },\n\n _onBatchStop: function(data) {\n const name = data && data.batchName\n if (this._sortDelayingBatches.includes(name) &&\n !this.model.hasActiveBatch(this._sortDelayingBatches)) {\n this.sortViews()\n }\n },\n\n onRemove: function() {\n\n //clean up all DOM elements/views to prevent memory leaks\n this.removeViews()\n },\n\n setDimensions: function(width, height) {\n\n width = this.options.width = width || this.options.width\n height = this.options.height = height || this.options.height\n\n this.$el.css({\n width: Math.round(width),\n height: Math.round(height)\n })\n\n this.trigger('resize', width, height)\n },\n\n setOrigin: function(ox, oy) {\n\n return this.translate(ox || 0, oy || 0, { absolute: true })\n },\n\n // Expand/shrink the paper to fit the content. Snap the width/height to the grid\n // defined in `gridWidth`, `gridHeight`. `padding` adds to the resulting width/height of the paper.\n // When options { fitNegative: true } it also translates the viewport in order to make all\n // the content visible.\n fitToContent: function(gridWidth, gridHeight, padding, opt) { // alternatively function(opt)\n\n if (joint.util.isObject(gridWidth)) {\n // first parameter is an option object\n opt = gridWidth\n gridWidth = opt.gridWidth || 1\n gridHeight = opt.gridHeight || 1\n padding = opt.padding || 0\n\n } else {\n\n opt = opt || {}\n gridWidth = gridWidth || 1\n gridHeight = gridHeight || 1\n padding = padding || 0\n }\n\n padding = joint.util.normalizeSides(padding)\n\n // Calculate the paper size to accomodate all the graph's elements.\n const bbox = V(this.viewport).getBBox()\n\n const currentScale = this.scale()\n const currentTranslate = this.translate()\n\n bbox.x *= currentScale.sx\n bbox.y *= currentScale.sy\n bbox.width *= currentScale.sx\n bbox.height *= currentScale.sy\n\n let calcWidth = Math.max(Math.ceil((bbox.width + bbox.x) / gridWidth), 1) * gridWidth\n let calcHeight = Math.max(Math.ceil((bbox.height + bbox.y) / gridHeight), 1) * gridHeight\n\n let tx = 0\n let ty = 0\n\n if ((opt.allowNewOrigin == 'negative' && bbox.x < 0) || (opt.allowNewOrigin == 'positive' && bbox.x >= 0) || opt.allowNewOrigin == 'any') {\n tx = Math.ceil(-bbox.x / gridWidth) * gridWidth\n tx += padding.left\n calcWidth += tx\n }\n\n if ((opt.allowNewOrigin == 'negative' && bbox.y < 0) || (opt.allowNewOrigin == 'positive' && bbox.y >= 0) || opt.allowNewOrigin == 'any') {\n ty = Math.ceil(-bbox.y / gridHeight) * gridHeight\n ty += padding.top\n calcHeight += ty\n }\n\n calcWidth += padding.right\n calcHeight += padding.bottom\n\n // Make sure the resulting width and height are greater than minimum.\n calcWidth = Math.max(calcWidth, opt.minWidth || 0)\n calcHeight = Math.max(calcHeight, opt.minHeight || 0)\n\n // Make sure the resulting width and height are lesser than maximum.\n calcWidth = Math.min(calcWidth, opt.maxWidth || Number.MAX_VALUE)\n calcHeight = Math.min(calcHeight, opt.maxHeight || Number.MAX_VALUE)\n\n const dimensionChange = calcWidth != this.options.width || calcHeight != this.options.height\n const originChange = tx != currentTranslate.tx || ty != currentTranslate.ty\n\n // Change the dimensions only if there is a size discrepency or an origin change\n if (originChange) {\n this.translate(tx, ty)\n }\n if (dimensionChange) {\n this.setDimensions(calcWidth, calcHeight)\n }\n },\n\n scaleContentToFit: function(opt) {\n\n const contentBBox = this.getContentBBox()\n\n if (!contentBBox.width || !contentBBox.height) return\n\n opt = opt || {}\n\n joint.util.defaults(opt, {\n padding: 0,\n preserveAspectRatio: true,\n scaleGrid: null,\n minScale: 0,\n maxScale: Number.MAX_VALUE\n //minScaleX\n //minScaleY\n //maxScaleX\n //maxScaleY\n //fittingBBox\n })\n\n const {padding} = opt\n\n const minScaleX = opt.minScaleX || opt.minScale\n const maxScaleX = opt.maxScaleX || opt.maxScale\n const minScaleY = opt.minScaleY || opt.minScale\n const maxScaleY = opt.maxScaleY || opt.maxScale\n\n let fittingBBox\n if (opt.fittingBBox) {\n fittingBBox = opt.fittingBBox\n } else {\n const currentTranslate = this.translate()\n fittingBBox = {\n x: currentTranslate.tx,\n y: currentTranslate.ty,\n width: this.options.width,\n height: this.options.height\n }\n }\n\n fittingBBox = g.rect(fittingBBox).moveAndExpand({\n x: padding,\n y: padding,\n width: -2 * padding,\n height: -2 * padding\n })\n\n const currentScale = this.scale()\n\n let newSx = fittingBBox.width / contentBBox.width * currentScale.sx\n let newSy = fittingBBox.height / contentBBox.height * currentScale.sy\n\n if (opt.preserveAspectRatio) {\n newSx = newSy = Math.min(newSx, newSy)\n }\n\n // snap scale to a grid\n if (opt.scaleGrid) {\n\n const gridSize = opt.scaleGrid\n\n newSx = gridSize * Math.floor(newSx / gridSize)\n newSy = gridSize * Math.floor(newSy / gridSize)\n }\n\n // scale min/max boundaries\n newSx = Math.min(maxScaleX, Math.max(minScaleX, newSx))\n newSy = Math.min(maxScaleY, Math.max(minScaleY, newSy))\n\n this.scale(newSx, newSy)\n\n const contentTranslation = this.getContentBBox()\n\n const newOx = fittingBBox.x - contentTranslation.x\n const newOy = fittingBBox.y - contentTranslation.y\n\n this.translate(newOx, newOy)\n },\n\n // Return the dimensions of the content area in local units (without transformations).\n getContentArea: function() {\n\n return V(this.viewport).getBBox()\n },\n\n // Return the dimensions of the content bbox in client units (as it appears on screen).\n getContentBBox: function() {\n\n const crect = this.viewport.getBoundingClientRect()\n\n // Using Screen CTM was the only way to get the real viewport bounding box working in both\n // Google Chrome and Firefox.\n const clientCTM = this.clientMatrix()\n\n // for non-default origin we need to take the viewport translation into account\n const currentTranslate = this.translate()\n\n return g.rect({\n x: crect.left - clientCTM.e + currentTranslate.tx,\n y: crect.top - clientCTM.f + currentTranslate.ty,\n width: crect.width,\n height: crect.height\n })\n },\n\n // Returns a geometry rectangle represeting the entire\n // paper area (coordinates from the left paper border to the right one\n // and the top border to the bottom one).\n getArea: function() {\n\n return this.paperToLocalRect({\n x: 0,\n y: 0,\n width: this.options.width,\n height: this.options.height\n })\n },\n\n getRestrictedArea: function() {\n\n let restrictedArea\n\n if (joint.util.isFunction(this.options.restrictTranslate)) {\n // A method returning a bounding box\n restrictedArea = this.options.restrictTranslate.apply(this, arguments)\n } else if (this.options.restrictTranslate === true) {\n // The paper area\n restrictedArea = this.getArea()\n } else {\n // Either false or a bounding box\n restrictedArea = this.options.restrictTranslate || null\n }\n\n return restrictedArea\n },\n\n createViewForModel: function(cell) {\n\n // A class taken from the paper options.\n let optionalViewClass\n\n // A default basic class (either dia.ElementView or dia.LinkView)\n let defaultViewClass\n\n // A special class defined for this model in the corresponding namespace.\n // e.g. joint.shapes.basic.Rect searches for joint.shapes.basic.RectView\n const namespace = this.options.cellViewNamespace\n const type = `${cell.get('type') }View`\n const namespaceViewClass = joint.util.getByPath(namespace, type, '.')\n\n if (cell.isLink()) {\n optionalViewClass = this.options.linkView\n defaultViewClass = joint.dia.LinkView\n } else {\n optionalViewClass = this.options.elementView\n defaultViewClass = joint.dia.ElementView\n }\n\n // a) the paper options view is a class (deprecated)\n // 1. search the namespace for a view\n // 2. if no view was found, use view from the paper options\n // b) the paper options view is a function\n // 1. call the function from the paper options\n // 2. if no view was return, search the namespace for a view\n // 3. if no view was found, use the default\n const ViewClass = (optionalViewClass.prototype instanceof Backbone.View)\n ? namespaceViewClass || optionalViewClass\n : optionalViewClass.call(this, cell) || namespaceViewClass || defaultViewClass\n\n return new ViewClass({\n model: cell,\n interactive: this.options.interactive\n })\n },\n\n onCellAdded: function(cell, graph, opt) {\n\n if (this.options.async && opt.async !== false && joint.util.isNumber(opt.position)) {\n\n this._asyncCells = this._asyncCells || []\n this._asyncCells.push(cell)\n\n if (opt.position == 0) {\n\n if (this._frameId) throw new Error('another asynchronous rendering in progress')\n\n this.asyncRenderViews(this._asyncCells, opt)\n delete this._asyncCells\n }\n\n } else {\n\n this.renderView(cell)\n }\n },\n\n removeView: function(cell) {\n\n const view = this._views[cell.id]\n\n if (view) {\n view.remove()\n delete this._views[cell.id]\n }\n\n return view\n },\n\n renderView: function(cell) {\n\n const view = this._views[cell.id] = this.createViewForModel(cell)\n\n V(this.viewport).append(view.el)\n view.paper = this\n view.render()\n\n return view\n },\n\n onImageDragStart: function() {\n // This is the only way to prevent image dragging in Firefox that works.\n // Setting -moz-user-select: none, draggable=\"false\" attribute or user-drag: none didn't help.\n\n return false\n },\n\n beforeRenderViews: function(cells) {\n\n // Make sure links are always added AFTER elements.\n // They wouldn't find their sources/targets in the DOM otherwise.\n cells.sort(function(a) { return (a.isLink()) ? 1 : -1 })\n\n return cells\n },\n\n afterRenderViews: function() {\n\n this.sortViews()\n },\n\n resetViews: function(cellsCollection, opt) {\n\n // clearing views removes any event listeners\n this.removeViews()\n\n let cells = cellsCollection.models.slice()\n\n // `beforeRenderViews()` can return changed cells array (e.g sorted).\n cells = this.beforeRenderViews(cells, opt) || cells\n\n this.cancelRenderViews()\n\n if (this.options.async) {\n\n this.asyncRenderViews(cells, opt)\n // Sort the cells once all elements rendered (see asyncRenderViews()).\n\n } else {\n\n for (let i = 0, n = cells.length; i < n; i++) {\n this.renderView(cells[i])\n }\n\n // Sort the cells in the DOM manually as we might have changed the order they\n // were added to the DOM (see above).\n this.sortViews()\n }\n },\n\n cancelRenderViews: function() {\n if (this._frameId) {\n joint.util.cancelFrame(this._frameId)\n delete this._frameId\n }\n },\n\n removeViews: function() {\n\n joint.util.invoke(this._views, 'remove')\n\n this._views = {}\n },\n\n asyncBatchAdded: joint.util.noop,\n\n asyncRenderViews: function(cells, opt) {\n\n if (this._frameId) {\n\n const batchSize = (this.options.async && this.options.async.batchSize) || 50\n const batchCells = cells.splice(0, batchSize)\n\n batchCells.forEach(function(cell) {\n\n // The cell has to be part of the graph.\n // There is a chance in asynchronous rendering\n // that a cell was removed before it's rendered to the paper.\n if (cell.graph === this.model) this.renderView(cell)\n\n }, this)\n\n this.asyncBatchAdded()\n }\n\n if (!cells.length) {\n\n // No cells left to render.\n delete this._frameId\n this.afterRenderViews(opt)\n this.trigger('render:done', opt)\n\n } else {\n\n // Schedule a next batch to render.\n this._frameId = joint.util.nextFrame(function() {\n this.asyncRenderViews(cells, opt)\n }, this)\n }\n },\n\n sortViews: function() {\n\n // Run insertion sort algorithm in order to efficiently sort DOM elements according to their\n // associated model `z` attribute.\n\n const $cells = $(this.viewport).children('[model-id]')\n const cells = this.model.get('cells')\n\n joint.util.sortElements($cells, function(a, b) {\n\n const cellA = cells.get($(a).attr('model-id'))\n const cellB = cells.get($(b).attr('model-id'))\n\n return (cellA.get('z') || 0) > (cellB.get('z') || 0) ? 1 : -1\n })\n },\n\n scale: function(sx, sy, ox, oy) {\n\n // getter\n if (sx === undefined) {\n return V.matrixToScale(this.matrix())\n }\n\n // setter\n if (sy === undefined) {\n sy = sx\n }\n if (ox === undefined) {\n ox = 0\n oy = 0\n }\n\n const translate = this.translate()\n\n if (ox || oy || translate.tx || translate.ty) {\n const newTx = translate.tx - ox * (sx - 1)\n const newTy = translate.ty - oy * (sy - 1)\n this.translate(newTx, newTy)\n }\n\n const ctm = this.matrix()\n ctm.a = sx || 0\n ctm.d = sy || 0\n\n this.matrix(ctm)\n\n this.trigger('scale', sx, sy, ox, oy)\n\n return this\n },\n\n // Experimental - do not use in production.\n rotate: function(angle, cx, cy) {\n\n // getter\n if (angle === undefined) {\n return V.matrixToRotate(this.matrix())\n }\n\n // setter\n\n // If the origin is not set explicitely, rotate around the center. Note that\n // we must use the plain bounding box (`this.el.getBBox()` instead of the one that gives us\n // the real bounding box (`bbox()`) including transformations).\n if (cx === undefined) {\n const bbox = this.viewport.getBBox()\n cx = bbox.width / 2\n cy = bbox.height / 2\n }\n\n const ctm = this.matrix().translate(cx,cy).rotate(angle).translate(-cx,-cy)\n this.matrix(ctm)\n\n return this\n },\n\n translate: function(tx, ty) {\n\n // getter\n if (tx === undefined) {\n return V.matrixToTranslate(this.matrix())\n }\n\n // setter\n\n const ctm = this.matrix()\n ctm.e = tx || 0\n ctm.f = ty || 0\n\n this.matrix(ctm)\n\n const newTranslate = this.translate()\n const {origin} = this.options\n origin.x = newTranslate.tx\n origin.y = newTranslate.ty\n\n this.trigger('translate', newTranslate.tx, newTranslate.ty)\n\n if (this.options.drawGrid) {\n this.drawGrid()\n }\n\n return this\n },\n\n // Find the first view climbing up the DOM tree starting at element `el`. Note that `el` can also\n // be a selector or a jQuery object.\n findView: function($el) {\n\n let el = joint.util.isString($el)\n ? this.viewport.querySelector($el)\n : $el instanceof $ ? $el[0] : $el\n\n while (el && el !== this.el && el !== document) {\n\n const id = el.getAttribute('model-id')\n if (id) return this._views[id]\n\n el = el.parentNode\n }\n\n return undefined\n },\n\n // Find a view for a model `cell`. `cell` can also be a string or number representing a model `id`.\n findViewByModel: function(cell) {\n\n const id = (joint.util.isString(cell) || joint.util.isNumber(cell)) ? cell : (cell && cell.id)\n\n return this._views[id]\n },\n\n // Find all views at given point\n findViewsFromPoint: function(p) {\n\n p = g.point(p)\n\n const views = this.model.getElements().map(this.findViewByModel, this)\n\n return views.filter(function(view) {\n return view && view.vel.getBBox({ target: this.viewport }).containsPoint(p)\n }, this)\n },\n\n // Find all views in given area\n findViewsInArea: function(rect, opt) {\n\n opt = joint.util.defaults(opt || {}, { strict: false })\n rect = g.rect(rect)\n\n const views = this.model.getElements().map(this.findViewByModel, this)\n const method = opt.strict ? 'containsRect' : 'intersect'\n\n return views.filter(function(view) {\n return view && rect[method](view.vel.getBBox({ target: this.viewport }))\n }, this)\n },\n\n removeTools: function() {\n joint.dia.CellView.dispatchToolsEvent(this, 'remove')\n return this\n },\n\n hideTools: function() {\n joint.dia.CellView.dispatchToolsEvent(this, 'hide')\n return this\n },\n\n showTools: function() {\n joint.dia.CellView.dispatchToolsEvent(this, 'show')\n return this\n },\n\n getModelById: function(id) {\n\n return this.model.getCell(id)\n },\n\n snapToGrid: function(x, y) {\n\n // Convert global coordinates to the local ones of the `viewport`. Otherwise,\n // improper transformation would be applied when the viewport gets transformed (scaled/rotated).\n return this.clientToLocalPoint(x, y).snapToGrid(this.options.gridSize)\n },\n\n localToPaperPoint: function(x, y) {\n // allow `x` to be a point and `y` undefined\n const localPoint = g.Point(x, y)\n const paperPoint = V.transformPoint(localPoint, this.matrix())\n return g.Point(paperPoint)\n },\n\n localToPaperRect: function(x, y, width, height) {\n // allow `x` to be a rectangle and rest arguments undefined\n const localRect = g.Rect(x, y)\n const paperRect = V.transformRect(localRect, this.matrix())\n return g.Rect(paperRect)\n },\n\n paperToLocalPoint: function(x, y) {\n // allow `x` to be a point and `y` undefined\n const paperPoint = g.Point(x, y)\n const localPoint = V.transformPoint(paperPoint, this.matrix().inverse())\n return g.Point(localPoint)\n },\n\n paperToLocalRect: function(x, y, width, height) {\n // allow `x` to be a rectangle and rest arguments undefined\n const paperRect = g.Rect(x, y, width, height)\n const localRect = V.transformRect(paperRect, this.matrix().inverse())\n return g.Rect(localRect)\n },\n\n localToClientPoint: function(x, y) {\n // allow `x` to be a point and `y` undefined\n const localPoint = g.Point(x, y)\n const clientPoint = V.transformPoint(localPoint, this.clientMatrix())\n return g.Point(clientPoint)\n },\n\n localToClientRect: function(x, y, width, height) {\n // allow `x` to be a point and `y` undefined\n const localRect = g.Rect(x, y, width, height)\n const clientRect = V.transformRect(localRect, this.clientMatrix())\n return g.Rect(clientRect)\n },\n\n // Transform client coordinates to the paper local coordinates.\n // Useful when you have a mouse event object and you'd like to get coordinates\n // inside the paper that correspond to `evt.clientX` and `evt.clientY` point.\n // Example: var localPoint = paper.clientToLocalPoint({ x: evt.clientX, y: evt.clientY });\n clientToLocalPoint: function(x, y) {\n // allow `x` to be a point and `y` undefined\n const clientPoint = g.Point(x, y)\n const localPoint = V.transformPoint(clientPoint, this.clientMatrix().inverse())\n return g.Point(localPoint)\n },\n\n clientToLocalRect: function(x, y, width, height) {\n // allow `x` to be a point and `y` undefined\n const clientRect = g.Rect(x, y, width, height)\n const localRect = V.transformRect(clientRect, this.clientMatrix().inverse())\n return g.Rect(localRect)\n },\n\n localToPagePoint: function(x, y) {\n\n return this.localToPaperPoint(x, y).offset(this.pageOffset())\n },\n\n localToPageRect: function(x, y, width, height) {\n\n return this.localToPaperRect(x, y, width, height).moveAndExpand(this.pageOffset())\n },\n\n pageToLocalPoint: function(x, y) {\n\n const pagePoint = g.Point(x, y)\n const paperPoint = pagePoint.difference(this.pageOffset())\n return this.paperToLocalPoint(paperPoint)\n },\n\n pageToLocalRect: function(x, y, width, height) {\n\n const pageOffset = this.pageOffset()\n const paperRect = g.Rect(x, y, width, height)\n paperRect.x -= pageOffset.x\n paperRect.y -= pageOffset.y\n return this.paperToLocalRect(paperRect)\n },\n\n clientOffset: function() {\n\n const clientRect = this.svg.getBoundingClientRect()\n return g.Point(clientRect.left, clientRect.top)\n },\n\n pageOffset: function() {\n\n return this.clientOffset().offset(window.scrollX, window.scrollY)\n },\n\n linkAllowed: function(linkView) {\n\n if (!(linkView instanceof joint.dia.LinkView)) {\n throw new Error('Must provide a linkView.')\n }\n\n const link = linkView.model\n const paperOptions = this.options\n const graph = this.model\n const ns = graph.constructor.validations\n\n if (!paperOptions.multiLinks) {\n if (!ns.multiLinks.call(this, graph, link)) return false\n }\n\n if (!paperOptions.linkPinning) {\n // Link pinning is not allowed and the link is not connected to the target.\n if (!ns.linkPinning.call(this, graph, link)) return false\n }\n\n if (typeof paperOptions.allowLink === 'function') {\n if (!paperOptions.allowLink.call(this, linkView, this)) return false\n }\n\n return true\n },\n\n getDefaultLink: function(cellView, magnet) {\n\n return joint.util.isFunction(this.options.defaultLink)\n // default link is a function producing link model\n ? this.options.defaultLink.call(this, cellView, magnet)\n // default link is the Backbone model\n : this.options.defaultLink.clone()\n },\n\n // Cell highlighting.\n // ------------------\n\n resolveHighlighter: function(opt) {\n\n opt = opt || {}\n let highlighterDef = opt.highlighter\n const paperOpt = this.options\n\n /*\n Expecting opt.highlighter to have the following structure:\n {\n name: 'highlighter-name',\n options: {\n some: 'value'\n }\n }\n */\n if (highlighterDef === undefined) {\n\n // check for built-in types\n const type = ['embedding', 'connecting', 'magnetAvailability', 'elementAvailability'].find(function(type) {\n return !!opt[type]\n })\n\n highlighterDef = (type && paperOpt.highlighting[type]) || paperOpt.highlighting.default\n }\n\n // Do nothing if opt.highlighter is falsey.\n // This allows the case to not highlight cell(s) in certain cases.\n // For example, if you want to NOT highlight when embedding elements.\n if (!highlighterDef) return false\n\n // Allow specifying a highlighter by name.\n if (joint.util.isString(highlighterDef)) {\n highlighterDef = {\n name: highlighterDef\n }\n }\n\n const {name} = highlighterDef\n const highlighter = paperOpt.highlighterNamespace[name]\n\n // Highlighter validation\n if (!highlighter) {\n throw new Error(`Unknown highlighter (\"${ name }\")`)\n }\n if (typeof highlighter.highlight !== 'function') {\n throw new Error(`Highlighter (\"${ name }\") is missing required highlight() method`)\n }\n if (typeof highlighter.unhighlight !== 'function') {\n throw new Error(`Highlighter (\"${ name }\") is missing required unhighlight() method`)\n }\n\n return {\n highlighter: highlighter,\n options: highlighterDef.options || {},\n name: name\n }\n },\n\n onCellHighlight: function(cellView, magnetEl, opt) {\n\n opt = this.resolveHighlighter(opt)\n if (!opt) return\n if (!magnetEl.id) {\n magnetEl.id = V.uniqueId()\n }\n\n const key = opt.name + magnetEl.id + JSON.stringify(opt.options)\n if (!this._highlights[key]) {\n\n const {highlighter} = opt\n highlighter.highlight(cellView, magnetEl, joint.util.assign({}, opt.options))\n\n this._highlights[key] = {\n cellView: cellView,\n magnetEl: magnetEl,\n opt: opt.options,\n highlighter: highlighter\n }\n }\n },\n\n onCellUnhighlight: function(cellView, magnetEl, opt) {\n\n opt = this.resolveHighlighter(opt)\n if (!opt) return\n\n const key = opt.name + magnetEl.id + JSON.stringify(opt.options)\n const highlight = this._highlights[key]\n if (highlight) {\n\n // Use the cellView and magnetEl that were used by the highlighter.highlight() method.\n highlight.highlighter.unhighlight(highlight.cellView, highlight.magnetEl, highlight.opt)\n\n this._highlights[key] = null\n }\n },\n\n // Interaction.\n // ------------\n\n pointerdblclick: function(evt) {\n\n evt.preventDefault()\n\n evt = joint.util.normalizeEvent(evt)\n\n const view = this.findView(evt.target)\n if (this.guard(evt, view)) return\n\n const localPoint = this.snapToGrid({ x: evt.clientX, y: evt.clientY })\n\n if (view) {\n view.pointerdblclick(evt, localPoint.x, localPoint.y)\n\n } else {\n this.trigger('blank:pointerdblclick', evt, localPoint.x, localPoint.y)\n }\n },\n\n pointerclick: function(evt) {\n\n // Trigger event only if mouse has not moved.\n if (this._mousemoved <= this.options.clickThreshold) {\n\n evt = joint.util.normalizeEvent(evt)\n\n const view = this.findView(evt.target)\n if (this.guard(evt, view)) return\n\n const localPoint = this.snapToGrid({ x: evt.clientX, y: evt.clientY })\n\n if (view) {\n view.pointerclick(evt, localPoint.x, localPoint.y)\n\n } else {\n this.trigger('blank:pointerclick', evt, localPoint.x, localPoint.y)\n }\n }\n },\n\n contextmenu: function(evt) {\n\n if (this.options.preventContextMenu) evt.preventDefault()\n\n evt = joint.util.normalizeEvent(evt)\n\n const view = this.findView(evt.target)\n if (this.guard(evt, view)) return\n\n const localPoint = this.snapToGrid({ x: evt.clientX, y: evt.clientY })\n\n if (view) {\n view.contextmenu(evt, localPoint.x, localPoint.y)\n\n } else {\n this.trigger('blank:contextmenu', evt, localPoint.x, localPoint.y)\n }\n },\n\n pointerdown: function(evt) {\n\n evt = joint.util.normalizeEvent(evt)\n\n const view = this.findView(evt.target)\n if (this.guard(evt, view)) return\n\n const localPoint = this.snapToGrid({ x: evt.clientX, y: evt.clientY })\n\n if (view) {\n\n evt.preventDefault()\n view.pointerdown(evt, localPoint.x, localPoint.y)\n\n } else {\n\n if (this.options.preventDefaultBlankAction) evt.preventDefault()\n\n this.trigger('blank:pointerdown', evt, localPoint.x, localPoint.y)\n }\n\n this.delegateDragEvents(view, evt.data)\n },\n\n pointermove: function(evt) {\n\n evt.preventDefault()\n\n // mouse moved counter\n const data = this.eventData(evt)\n data.mousemoved || (data.mousemoved = 0)\n const mousemoved = ++data.mousemoved\n if (mousemoved <= this.options.moveThreshold) return\n\n evt = joint.util.normalizeEvent(evt)\n\n const localPoint = this.snapToGrid({ x: evt.clientX, y: evt.clientY })\n\n const view = data.sourceView\n if (view) {\n view.pointermove(evt, localPoint.x, localPoint.y)\n } else {\n this.trigger('blank:pointermove', evt, localPoint.x, localPoint.y)\n }\n\n this.eventData(evt, data)\n },\n\n pointerup: function(evt) {\n\n this.undelegateDocumentEvents()\n\n evt = joint.util.normalizeEvent(evt)\n\n const localPoint = this.snapToGrid({ x: evt.clientX, y: evt.clientY })\n\n const view = this.eventData(evt).sourceView\n if (view) {\n view.pointerup(evt, localPoint.x, localPoint.y)\n } else {\n this.trigger('blank:pointerup', evt, localPoint.x, localPoint.y)\n }\n\n this.delegateEvents()\n },\n\n mouseover: function(evt) {\n\n evt = joint.util.normalizeEvent(evt)\n\n const view = this.findView(evt.target)\n if (this.guard(evt, view)) return\n\n if (view) {\n view.mouseover(evt)\n\n } else {\n if (this.el === evt.target) return // prevent border of paper from triggering this\n this.trigger('blank:mouseover', evt)\n }\n },\n\n mouseout: function(evt) {\n\n evt = joint.util.normalizeEvent(evt)\n\n const view = this.findView(evt.target)\n if (this.guard(evt, view)) return\n\n if (view) {\n view.mouseout(evt)\n\n } else {\n if (this.el === evt.target) return // prevent border of paper from triggering this\n this.trigger('blank:mouseout', evt)\n }\n },\n\n mouseenter: function(evt) {\n\n evt = joint.util.normalizeEvent(evt)\n\n const view = this.findView(evt.target)\n if (this.guard(evt, view)) return\n const relatedView = this.findView(evt.relatedTarget)\n if (view) {\n // mouse moved from tool over view?\n if (relatedView === view) return\n view.mouseenter(evt)\n } else {\n if (relatedView) return\n // `paper` (more descriptive), not `blank`\n this.trigger('paper:mouseenter', evt)\n }\n },\n\n mouseleave: function(evt) {\n\n evt = joint.util.normalizeEvent(evt)\n\n const view = this.findView(evt.target)\n if (this.guard(evt, view)) return\n const relatedView = this.findView(evt.relatedTarget)\n if (view) {\n // mouse moved from view over tool?\n if (relatedView === view) return\n view.mouseleave(evt)\n } else {\n if (relatedView) return\n // `paper` (more descriptive), not `blank`\n this.trigger('paper:mouseleave', evt)\n }\n },\n\n mousewheel: function(evt) {\n\n evt = joint.util.normalizeEvent(evt)\n\n const view = this.findView(evt.target)\n if (this.guard(evt, view)) return\n\n const {originalEvent} = evt\n const localPoint = this.snapToGrid({ x: originalEvent.clientX, y: originalEvent.clientY })\n const delta = Math.max(-1, Math.min(1, (originalEvent.wheelDelta || -originalEvent.detail)))\n\n if (view) {\n view.mousewheel(evt, localPoint.x, localPoint.y, delta)\n\n } else {\n this.trigger('blank:mousewheel', evt, localPoint.x, localPoint.y, delta)\n }\n },\n\n onevent: function(evt) {\n\n const eventNode = evt.currentTarget\n const eventName = eventNode.getAttribute('event')\n if (eventName) {\n const view = this.findView(eventNode)\n if (view) {\n\n evt = joint.util.normalizeEvent(evt)\n if (this.guard(evt, view)) return\n\n const localPoint = this.snapToGrid({ x: evt.clientX, y: evt.clientY })\n view.onevent(evt, eventName, localPoint.x, localPoint.y)\n }\n }\n },\n\n onmagnet: function(evt) {\n\n const magnetNode = evt.currentTarget\n const magnetValue = magnetNode.getAttribute('magnet')\n if (magnetValue) {\n const view = this.findView(magnetNode)\n if (view) {\n\n evt = joint.util.normalizeEvent(evt)\n if (this.guard(evt, view)) return\n if (!this.options.validateMagnet(view, magnetNode)) return\n\n const localPoint = this.snapToGrid(evt.clientX, evt.clientY)\n view.onmagnet(evt, localPoint.x, localPoint.y)\n }\n }\n },\n\n onlabel: function(evt) {\n\n const labelNode = evt.currentTarget\n const view = this.findView(labelNode)\n if (view) {\n\n evt = joint.util.normalizeEvent(evt)\n if (this.guard(evt, view)) return\n\n const localPoint = this.snapToGrid(evt.clientX, evt.clientY)\n view.onlabel(evt, localPoint.x, localPoint.y)\n }\n },\n\n delegateDragEvents: function(view, data) {\n\n data || (data = {})\n this.eventData({ data: data }, { sourceView: view || null, mousemoved: 0 })\n this.delegateDocumentEvents(null, data)\n this.undelegateEvents()\n },\n\n // Guard the specified event. If the event is not interesting, guard returns `true`.\n // Otherwise, it returns `false`.\n guard: function(evt, view) {\n\n if (evt.type === 'mousedown' && evt.button === 2) {\n // handled as `contextmenu` type\n return true\n }\n\n if (this.options.guard && this.options.guard(evt, view)) {\n return true\n }\n\n if (evt.data && evt.data.guarded !== undefined) {\n return evt.data.guarded\n }\n\n if (view && view.model && (view.model instanceof joint.dia.Cell)) {\n return false\n }\n\n if (this.svg === evt.target || this.el === evt.target || $.contains(this.svg, evt.target)) {\n return false\n }\n\n return true // Event guarded. Paper should not react on it in any way.\n },\n\n setGridSize: function(gridSize) {\n\n this.options.gridSize = gridSize\n\n if (this.options.drawGrid) {\n this.drawGrid()\n }\n\n return this\n },\n\n clearGrid: function() {\n\n if (this.$grid) {\n this.$grid.css('backgroundImage', 'none')\n }\n return this\n },\n\n _getGriRefs: function() {\n\n if (!this._gridCache) {\n\n this._gridCache = {\n root: V('svg', { width: '100%', height: '100%' }, V('defs')),\n patterns: {},\n add: function(id, vel) {\n V(this.root.node.childNodes[0]).append(vel)\n this.patterns[id] = vel\n this.root.append(V('rect', { width: '100%', height: '100%', fill: `url(#${ id })` }))\n },\n get: function(id) {\n return this.patterns[id]\n },\n exist: function(id) {\n return this.patterns[id] !== undefined\n }\n }\n }\n\n return this._gridCache\n },\n\n setGrid: function(drawGrid) {\n\n this.clearGrid()\n\n this._gridCache = null\n this._gridSettings = []\n\n const optionsList = Array.isArray(drawGrid) ? drawGrid : [drawGrid || {}]\n optionsList.forEach(function(item) {\n this._gridSettings.push.apply(this._gridSettings, this._resolveDrawGridOption(item))\n }, this)\n return this\n },\n\n _resolveDrawGridOption: function(opt) {\n\n const namespace = this.constructor.gridPatterns\n if (joint.util.isString(opt) && Array.isArray(namespace[opt])) {\n return namespace[opt].map(function(item) {\n return joint.util.assign({}, item)\n })\n }\n\n const options = opt || { args: [{}] }\n const isArray = Array.isArray(options)\n let {name} = options\n\n if (!isArray && !name && !options.markup ) {\n name = 'dot'\n }\n\n if (name && Array.isArray(namespace[name])) {\n const pattern = namespace[name].map(function(item) {\n return joint.util.assign({}, item)\n })\n\n const args = Array.isArray(options.args) ? options.args : [options.args || {}]\n\n joint.util.defaults(args[0], joint.util.omit(opt, 'args'))\n for (let i = 0; i < args.length; i++) {\n if (pattern[i]) {\n joint.util.assign(pattern[i], args[i])\n }\n }\n return pattern\n }\n\n return isArray ? options : [options]\n },\n\n drawGrid: function(opt) {\n\n const {gridSize} = this.options\n if (gridSize <= 1) {\n return this.clearGrid()\n }\n\n const localOptions = Array.isArray(opt) ? opt : [opt]\n\n const ctm = this.matrix()\n const refs = this._getGriRefs()\n\n this._gridSettings.forEach(function(gridLayerSetting, index) {\n\n const id = `pattern_${ index}`\n const options = joint.util.merge(gridLayerSetting, localOptions[index], {\n sx: ctm.a || 1,\n sy: ctm.d || 1,\n ox: ctm.e || 0,\n oy: ctm.f || 0\n })\n\n options.width = gridSize * (ctm.a || 1) * (options.scaleFactor || 1)\n options.height = gridSize * (ctm.d || 1) * (options.scaleFactor || 1)\n\n if (!refs.exist(id)) {\n refs.add(id, V('pattern', { id: id, patternUnits: 'userSpaceOnUse' }, V(options.markup)))\n }\n\n const patternDefVel = refs.get(id)\n\n if (joint.util.isFunction(options.update)) {\n options.update(patternDefVel.node.childNodes[0], options)\n }\n\n let x = options.ox % options.width\n if (x < 0) x += options.width\n\n let y = options.oy % options.height\n if (y < 0) y += options.height\n\n patternDefVel.attr({\n x: x,\n y: y,\n width: options.width,\n height: options.height\n })\n })\n\n let patternUri = new XMLSerializer().serializeToString(refs.root.node)\n patternUri = `url(data:image/svg+xml;base64,${ btoa(patternUri) })`\n\n this.$grid.css('backgroundImage', patternUri)\n\n return this\n },\n\n updateBackgroundImage: function(opt) {\n\n opt = opt || {}\n\n let backgroundPosition = opt.position || 'center'\n let backgroundSize = opt.size || 'auto auto'\n\n const currentScale = this.scale()\n const currentTranslate = this.translate()\n\n // backgroundPosition\n if (joint.util.isObject(backgroundPosition)) {\n const x = currentTranslate.tx + (currentScale.sx * (backgroundPosition.x || 0))\n const y = currentTranslate.ty + (currentScale.sy * (backgroundPosition.y || 0))\n backgroundPosition = `${x }px ${ y }px`\n }\n\n // backgroundSize\n if (joint.util.isObject(backgroundSize)) {\n backgroundSize = g.rect(backgroundSize).scale(currentScale.sx, currentScale.sy)\n backgroundSize = `${backgroundSize.width }px ${ backgroundSize.height }px`\n }\n\n this.$background.css({\n backgroundSize: backgroundSize,\n backgroundPosition: backgroundPosition\n })\n },\n\n drawBackgroundImage: function(img, opt) {\n\n // Clear the background image if no image provided\n if (!(img instanceof HTMLImageElement)) {\n this.$background.css('backgroundImage', '')\n return\n }\n\n opt = opt || {}\n\n let backgroundImage\n const backgroundSize = opt.size\n let backgroundRepeat = opt.repeat || 'no-repeat'\n const backgroundOpacity = opt.opacity || 1\n const backgroundQuality = Math.abs(opt.quality) || 1\n const backgroundPattern = this.constructor.backgroundPatterns[joint.util.camelCase(backgroundRepeat)]\n\n if (joint.util.isFunction(backgroundPattern)) {\n // 'flip-x', 'flip-y', 'flip-xy', 'watermark' and custom\n img.width *= backgroundQuality\n img.height *= backgroundQuality\n const canvas = backgroundPattern(img, opt)\n if (!(canvas instanceof HTMLCanvasElement)) {\n throw new Error('dia.Paper: background pattern must return an HTML Canvas instance')\n }\n\n backgroundImage = canvas.toDataURL('image/png')\n backgroundRepeat = 'repeat'\n if (joint.util.isObject(backgroundSize)) {\n // recalculate the tile size if an object passed in\n backgroundSize.width *= canvas.width / img.width\n backgroundSize.height *= canvas.height / img.height\n } else if (backgroundSize === undefined) {\n // calcule the tile size if no provided\n opt.size = {\n width: canvas.width / backgroundQuality,\n height: canvas.height / backgroundQuality\n }\n }\n } else {\n // backgroundRepeat:\n // no-repeat', 'round', 'space', 'repeat', 'repeat-x', 'repeat-y'\n backgroundImage = img.src\n if (backgroundSize === undefined) {\n // pass the image size for the backgroundSize if no size provided\n opt.size = {\n width: img.width,\n height: img.height\n }\n }\n }\n\n this.$background.css({\n opacity: backgroundOpacity,\n backgroundRepeat: backgroundRepeat,\n backgroundImage: `url(${ backgroundImage })`\n })\n\n this.updateBackgroundImage(opt)\n },\n\n updateBackgroundColor: function(color) {\n\n this.$el.css('backgroundColor', color || '')\n },\n\n drawBackground: function(opt) {\n\n opt = opt || {}\n\n this.updateBackgroundColor(opt.color)\n\n if (opt.image) {\n opt = this._background = joint.util.cloneDeep(opt)\n const img = document.createElement('img')\n img.onload = this.drawBackgroundImage.bind(this, img, opt)\n img.src = opt.image\n } else {\n this.drawBackgroundImage(null)\n this._background = null\n }\n\n return this\n },\n\n setInteractivity: function(value) {\n\n this.options.interactive = value\n\n joint.util.invoke(this._views, 'setInteractivity', value)\n },\n\n // Paper definitions.\n // ------------------\n\n isDefined: function(defId) {\n\n return !!this.svg.getElementById(defId)\n },\n\n defineFilter: function(filter) {\n\n if (!joint.util.isObject(filter)) {\n throw new TypeError('dia.Paper: defineFilter() requires 1. argument to be an object.')\n }\n\n let filterId = filter.id\n const {name} = filter\n // Generate a hash code from the stringified filter definition. This gives us\n // a unique filter ID for different definitions.\n if (!filterId) {\n filterId = name + this.svg.id + joint.util.hashCode(JSON.stringify(filter))\n }\n // If the filter already exists in the document,\n // we're done and we can just use it (reference it using `url()`).\n // If not, create one.\n if (!this.isDefined(filterId)) {\n\n const namespace = joint.util.filter\n const filterSVGString = namespace[name] && namespace[name](filter.args || {})\n if (!filterSVGString) {\n throw new Error(`Non-existing filter ${ name}`)\n }\n\n // Set the filter area to be 3x the bounding box of the cell\n // and center the filter around the cell.\n const filterAttrs = joint.util.assign({\n filterUnits: 'objectBoundingBox',\n x: -1,\n y: -1,\n width: 3,\n height: 3\n }, filter.attrs, {\n id: filterId\n })\n\n V(filterSVGString, filterAttrs).appendTo(this.defs)\n }\n\n return filterId\n },\n\n defineGradient: function(gradient) {\n\n if (!joint.util.isObject(gradient)) {\n throw new TypeError('dia.Paper: defineGradient() requires 1. argument to be an object.')\n }\n\n let gradientId = gradient.id\n const {type} = gradient\n const {stops} = gradient\n // Generate a hash code from the stringified filter definition. This gives us\n // a unique filter ID for different definitions.\n if (!gradientId) {\n gradientId = type + this.svg.id + joint.util.hashCode(JSON.stringify(gradient))\n }\n // If the gradient already exists in the document,\n // we're done and we can just use it (reference it using `url()`).\n // If not, create one.\n if (!this.isDefined(gradientId)) {\n\n const stopTemplate = joint.util.template('')\n const gradientStopsStrings = joint.util.toArray(stops).map(function(stop) {\n return stopTemplate({\n offset: stop.offset,\n color: stop.color,\n opacity: Number.isFinite(stop.opacity) ? stop.opacity : 1\n })\n })\n\n const gradientSVGString = [\n `<${ type }>`,\n gradientStopsStrings.join(''),\n ``\n ].join('')\n\n const gradientAttrs = joint.util.assign({ id: gradientId }, gradient.attrs)\n\n V(gradientSVGString, gradientAttrs).appendTo(this.defs)\n }\n\n return gradientId\n },\n\n defineMarker: function(marker) {\n\n if (!joint.util.isObject(marker)) {\n throw new TypeError('dia.Paper: defineMarker() requires 1. argument to be an object.')\n }\n\n let markerId = marker.id\n\n // Generate a hash code from the stringified filter definition. This gives us\n // a unique filter ID for different definitions.\n if (!markerId) {\n markerId = this.svg.id + joint.util.hashCode(JSON.stringify(marker))\n }\n\n if (!this.isDefined(markerId)) {\n\n const attrs = joint.util.omit(marker, 'type', 'userSpaceOnUse')\n const pathMarker = V('marker', {\n id: markerId,\n orient: 'auto',\n overflow: 'visible',\n markerUnits: marker.markerUnits || 'userSpaceOnUse'\n }, [\n V(marker.type || 'path', attrs)\n ])\n\n pathMarker.appendTo(this.defs)\n }\n\n return markerId\n }\n}, {\n\n backgroundPatterns: {\n\n flipXy: function(img) {\n // d b\n // q p\n\n const canvas = document.createElement('canvas')\n const imgWidth = img.width\n const imgHeight = img.height\n\n canvas.width = 2 * imgWidth\n canvas.height = 2 * imgHeight\n\n const ctx = canvas.getContext('2d')\n // top-left image\n ctx.drawImage(img, 0, 0, imgWidth, imgHeight)\n // xy-flipped bottom-right image\n ctx.setTransform(-1, 0, 0, -1, canvas.width, canvas.height)\n ctx.drawImage(img, 0, 0, imgWidth, imgHeight)\n // x-flipped top-right image\n ctx.setTransform(-1, 0, 0, 1, canvas.width, 0)\n ctx.drawImage(img, 0, 0, imgWidth, imgHeight)\n // y-flipped bottom-left image\n ctx.setTransform(1, 0, 0, -1, 0, canvas.height)\n ctx.drawImage(img, 0, 0, imgWidth, imgHeight)\n\n return canvas\n },\n\n flipX: function(img) {\n // d b\n // d b\n\n const canvas = document.createElement('canvas')\n const imgWidth = img.width\n const imgHeight = img.height\n\n canvas.width = imgWidth * 2\n canvas.height = imgHeight\n\n const ctx = canvas.getContext('2d')\n // left image\n ctx.drawImage(img, 0, 0, imgWidth, imgHeight)\n // flipped right image\n ctx.translate(2 * imgWidth, 0)\n ctx.scale(-1, 1)\n ctx.drawImage(img, 0, 0, imgWidth, imgHeight)\n\n return canvas\n },\n\n flipY: function(img) {\n // d d\n // q q\n\n const canvas = document.createElement('canvas')\n const imgWidth = img.width\n const imgHeight = img.height\n\n canvas.width = imgWidth\n canvas.height = imgHeight * 2\n\n const ctx = canvas.getContext('2d')\n // top image\n ctx.drawImage(img, 0, 0, imgWidth, imgHeight)\n // flipped bottom image\n ctx.translate(0, 2 * imgHeight)\n ctx.scale(1, -1)\n ctx.drawImage(img, 0, 0, imgWidth, imgHeight)\n\n return canvas\n },\n\n watermark: function(img, opt) {\n // d\n // d\n\n opt = opt || {}\n\n const imgWidth = img.width\n const imgHeight = img.height\n\n const canvas = document.createElement('canvas')\n canvas.width = imgWidth * 3\n canvas.height = imgHeight * 3\n\n const ctx = canvas.getContext('2d')\n const angle = joint.util.isNumber(opt.watermarkAngle) ? -opt.watermarkAngle : -20\n const radians = g.toRad(angle)\n const stepX = canvas.width / 4\n const stepY = canvas.height / 4\n\n for (let i = 0; i < 4; i ++) {\n for (let j = 0; j < 4; j++) {\n if ((i + j) % 2 > 0) {\n // reset the current transformations\n ctx.setTransform(1, 0, 0, 1, (2 * i - 1) * stepX, (2 * j - 1) * stepY)\n ctx.rotate(radians)\n ctx.drawImage(img, -imgWidth / 2, -imgHeight / 2, imgWidth, imgHeight)\n }\n }\n }\n\n return canvas\n }\n },\n\n gridPatterns: {\n dot: [{\n color: '#AAAAAA',\n thickness: 1,\n markup: 'rect',\n update: function(el, opt) {\n V(el).attr({\n width: opt.thickness * opt.sx,\n height: opt.thickness * opt.sy,\n fill: opt.color\n })\n }\n }],\n fixedDot: [{\n color: '#AAAAAA',\n thickness: 1,\n markup: 'rect',\n update: function(el, opt) {\n const size = opt.sx <= 1 ? opt.thickness * opt.sx : opt.thickness\n V(el).attr({ width: size, height: size, fill: opt.color })\n }\n }],\n mesh: [{\n color: '#AAAAAA',\n thickness: 1,\n markup: 'path',\n update: function(el, opt) {\n\n let d\n const {width} = opt\n const {height} = opt\n const {thickness} = opt\n\n if (width - thickness >= 0 && height - thickness >= 0) {\n d = ['M', width, 0, 'H0 M0 0 V0', height].join(' ')\n } else {\n d = 'M 0 0 0 0'\n }\n\n V(el).attr({ d: d, stroke: opt.color, 'stroke-width': opt.thickness })\n }\n }],\n doubleMesh: [{\n color: '#AAAAAA',\n thickness: 1,\n markup: 'path',\n update: function(el, opt) {\n\n let d\n const {width} = opt\n const {height} = opt\n const {thickness} = opt\n\n if (width - thickness >= 0 && height - thickness >= 0) {\n d = ['M', width, 0, 'H0 M0 0 V0', height].join(' ')\n } else {\n d = 'M 0 0 0 0'\n }\n\n V(el).attr({ d: d, stroke: opt.color, 'stroke-width': opt.thickness })\n }\n }, {\n color: '#000000',\n thickness: 3,\n scaleFactor: 4,\n markup: 'path',\n update: function(el, opt) {\n\n let d\n const {width} = opt\n const {height} = opt\n const {thickness} = opt\n\n if (width - thickness >= 0 && height - thickness >= 0) {\n d = ['M', width, 0, 'H0 M0 0 V0', height].join(' ')\n } else {\n d = 'M 0 0 0 0'\n }\n\n V(el).attr({ d: d, stroke: opt.color, 'stroke-width': opt.thickness })\n }\n }]\n }\n});\n\n(function(joint, util) {\n\n const PortData = function(data) {\n\n const clonedData = util.cloneDeep(data) || {}\n this.ports = []\n this.groups = {}\n this.portLayoutNamespace = joint.layout.Port\n this.portLabelLayoutNamespace = joint.layout.PortLabel\n\n this._init(clonedData)\n }\n\n PortData.prototype = {\n\n getPorts: function() {\n return this.ports\n },\n\n getGroup: function(name) {\n return this.groups[name] || {}\n },\n\n getPortsByGroup: function(groupName) {\n\n return this.ports.filter(function(port) {\n return port.group === groupName\n })\n },\n\n getGroupPortsMetrics: function(groupName, elBBox) {\n\n const group = this.getGroup(groupName)\n const ports = this.getPortsByGroup(groupName)\n\n const groupPosition = group.position || {}\n let groupPositionName = groupPosition.name\n const namespace = this.portLayoutNamespace\n if (!namespace[groupPositionName]) {\n groupPositionName = 'left'\n }\n\n const groupArgs = groupPosition.args || {}\n const portsArgs = ports.map(function(port) {\n return port && port.position && port.position.args\n })\n const groupPortTransformations = namespace[groupPositionName](portsArgs, elBBox, groupArgs)\n\n const accumulator = {\n ports: ports,\n result: []\n }\n\n util.toArray(groupPortTransformations).reduce(function(res, portTransformation, index) {\n const port = res.ports[index]\n res.result.push({\n portId: port.id,\n portTransformation: portTransformation,\n labelTransformation: this._getPortLabelLayout(port, g.Point(portTransformation), elBBox),\n portAttrs: port.attrs,\n portSize: port.size,\n labelSize: port.label.size\n })\n return res\n }.bind(this), accumulator)\n\n return accumulator.result\n },\n\n _getPortLabelLayout: function(port, portPosition, elBBox) {\n\n const namespace = this.portLabelLayoutNamespace\n const labelPosition = port.label.position.name || 'left'\n\n if (namespace[labelPosition]) {\n return namespace[labelPosition](portPosition, elBBox, port.label.position.args)\n }\n\n return null\n },\n\n _init: function(data) {\n\n // prepare groups\n if (util.isObject(data.groups)) {\n const groups = Object.keys(data.groups)\n for (let i = 0, n = groups.length; i < n; i++) {\n const key = groups[i]\n this.groups[key] = this._evaluateGroup(data.groups[key])\n }\n }\n\n // prepare ports\n const ports = util.toArray(data.items)\n for (let j = 0, m = ports.length; j < m; j++) {\n this.ports.push(this._evaluatePort(ports[j]))\n }\n },\n\n _evaluateGroup: function(group) {\n\n return util.merge(group, {\n position: this._getPosition(group.position, true),\n label: this._getLabel(group, true)\n })\n },\n\n _evaluatePort: function(port) {\n\n const evaluated = util.assign({}, port)\n\n const group = this.getGroup(port.group)\n\n evaluated.markup = evaluated.markup || group.markup\n evaluated.attrs = util.merge({}, group.attrs, evaluated.attrs)\n evaluated.position = this._createPositionNode(group, evaluated)\n evaluated.label = util.merge({}, group.label, this._getLabel(evaluated))\n evaluated.z = this._getZIndex(group, evaluated)\n evaluated.size = util.assign({}, group.size, evaluated.size)\n\n return evaluated\n },\n\n _getZIndex: function(group, port) {\n\n if (util.isNumber(port.z)) {\n return port.z\n }\n if (util.isNumber(group.z) || group.z === 'auto') {\n return group.z\n }\n return 'auto'\n },\n\n _createPositionNode: function(group, port) {\n\n return util.merge({\n name: 'left',\n args: {}\n }, group.position, { args: port.args })\n },\n\n _getPosition: function(position, setDefault) {\n\n const args = {}\n let positionName\n\n if (util.isFunction(position)) {\n positionName = 'fn'\n args.fn = position\n } else if (util.isString(position)) {\n positionName = position\n } else if (position === undefined) {\n positionName = setDefault ? 'left' : null\n } else if (Array.isArray(position)) {\n positionName = 'absolute'\n args.x = position[0]\n args.y = position[1]\n } else if (util.isObject(position)) {\n positionName = position.name\n util.assign(args, position.args)\n }\n\n const result = { args: args }\n\n if (positionName) {\n result.name = positionName\n }\n return result\n },\n\n _getLabel: function(item, setDefaults) {\n\n const label = item.label || {}\n\n const ret = label\n ret.position = this._getPosition(label.position, setDefaults)\n\n return ret\n }\n }\n\n util.assign(joint.dia.Element.prototype, {\n\n _initializePorts: function() {\n\n this._createPortData()\n this.on('change:ports', function() {\n\n this._processRemovedPort()\n this._createPortData()\n }, this)\n },\n\n /**\n * remove links tied wiht just removed element\n * @private\n */\n _processRemovedPort: function() {\n\n const current = this.get('ports') || {}\n const currentItemsMap = {}\n\n util.toArray(current.items).forEach(function(item) {\n currentItemsMap[item.id] = true\n })\n\n const previous = this.previous('ports') || {}\n const removed = {}\n\n util.toArray(previous.items).forEach(function(item) {\n if (!currentItemsMap[item.id]) {\n removed[item.id] = true\n }\n })\n\n const {graph} = this\n if (graph && !util.isEmpty(removed)) {\n\n const inboundLinks = graph.getConnectedLinks(this, { inbound: true })\n inboundLinks.forEach(function(link) {\n\n if (removed[link.get('target').port]) link.remove()\n })\n\n const outboundLinks = graph.getConnectedLinks(this, { outbound: true })\n outboundLinks.forEach(function(link) {\n\n if (removed[link.get('source').port]) link.remove()\n })\n }\n },\n\n /**\n * @returns {boolean}\n */\n hasPorts: function() {\n\n const ports = this.prop('ports/items')\n return Array.isArray(ports) && ports.length > 0\n },\n\n /**\n * @param {string} id\n * @returns {boolean}\n */\n hasPort: function(id) {\n\n return this.getPortIndex(id) !== -1\n },\n\n /**\n * @returns {Array}\n */\n getPorts: function() {\n\n return util.cloneDeep(this.prop('ports/items')) || []\n },\n\n /**\n * @param {string} id\n * @returns {object}\n */\n getPort: function(id) {\n\n return util.cloneDeep(util.toArray(this.prop('ports/items')).find(function(port) {\n return port.id && port.id === id\n }))\n },\n\n /**\n * @param {string} groupName\n * @returns {Object}\n */\n getPortsPositions: function(groupName) {\n\n const portsMetrics = this._portSettingsData.getGroupPortsMetrics(groupName, g.Rect(this.size()))\n\n return portsMetrics.reduce(function(positions, metrics) {\n const transformation = metrics.portTransformation\n positions[metrics.portId] = {\n x: transformation.x,\n y: transformation.y,\n angle: transformation.angle\n }\n return positions\n }, {})\n },\n\n /**\n * @param {string|Port} port port id or port\n * @returns {number} port index\n */\n getPortIndex: function(port) {\n\n const id = util.isObject(port) ? port.id : port\n\n if (!this._isValidPortId(id)) {\n return -1\n }\n\n return util.toArray(this.prop('ports/items')).findIndex(function(item) {\n return item.id === id\n })\n },\n\n /**\n * @param {object} port\n * @param {object} [opt]\n * @returns {joint.dia.Element}\n */\n addPort: function(port, opt) {\n\n if (!util.isObject(port) || Array.isArray(port)) {\n throw new Error('Element: addPort requires an object.')\n }\n\n const ports = util.assign([], this.prop('ports/items'))\n ports.push(port)\n this.prop('ports/items', ports, opt)\n\n return this\n },\n\n /**\n * @param {string} portId\n * @param {string|object=} path\n * @param {*=} value\n * @param {object=} opt\n * @returns {joint.dia.Element}\n */\n portProp: function(portId, path, value, opt) {\n\n const index = this.getPortIndex(portId)\n\n if (index === -1) {\n throw new Error(`Element: unable to find port with id ${ portId}`)\n }\n\n let args = Array.prototype.slice.call(arguments, 1)\n if (Array.isArray(path)) {\n args[0] = ['ports', 'items', index].concat(path)\n } else if (util.isString(path)) {\n\n // Get/set an attribute by a special path syntax that delimits\n // nested objects by the colon character.\n args[0] = ['ports/items/', index, '/', path].join('')\n\n } else {\n\n args = [`ports/items/${ index}`]\n if (util.isPlainObject(path)) {\n args.push(path)\n args.push(value)\n }\n }\n\n return this.prop.apply(this, args)\n },\n\n _validatePorts: function() {\n\n let portsAttr = this.get('ports') || {}\n\n const errorMessages = []\n portsAttr = portsAttr || {}\n const ports = util.toArray(portsAttr.items)\n\n ports.forEach(function(p) {\n\n if (typeof p !== 'object') {\n errorMessages.push('Element: invalid port ', p)\n }\n\n if (!this._isValidPortId(p.id)) {\n p.id = util.uuid()\n }\n }, this)\n\n if (joint.util.uniq(ports, 'id').length !== ports.length) {\n errorMessages.push('Element: found id duplicities in ports.')\n }\n\n return errorMessages\n },\n\n /**\n * @param {string} id port id\n * @returns {boolean}\n * @private\n */\n _isValidPortId: function(id) {\n\n return id !== null && id !== undefined && !util.isObject(id)\n },\n\n addPorts: function(ports, opt) {\n\n if (ports.length) {\n this.prop('ports/items', util.assign([], this.prop('ports/items')).concat(ports), opt)\n }\n\n return this\n },\n\n removePort: function(port, opt) {\n\n const options = opt || {}\n const ports = util.assign([], this.prop('ports/items'))\n\n const index = this.getPortIndex(port)\n\n if (index !== -1) {\n ports.splice(index, 1)\n options.rewrite = true\n this.prop('ports/items', ports, options)\n }\n\n return this\n },\n\n /**\n * @private\n */\n _createPortData: function() {\n\n const err = this._validatePorts()\n\n if (err.length > 0) {\n this.set('ports', this.previous('ports'))\n throw new Error(err.join(' '))\n }\n\n let prevPortData\n\n if (this._portSettingsData) {\n\n prevPortData = this._portSettingsData.getPorts()\n }\n\n this._portSettingsData = new PortData(this.get('ports'))\n\n const curPortData = this._portSettingsData.getPorts()\n\n if (prevPortData) {\n\n const added = curPortData.filter(function(item) {\n if (!prevPortData.find(function(prevPort) { return prevPort.id === item.id})) {\n return item\n }\n })\n\n const removed = prevPortData.filter(function(item) {\n if (!curPortData.find(function(curPort) { return curPort.id === item.id})) {\n return item\n }\n })\n\n if (removed.length > 0) {\n this.trigger('ports:remove', this, removed)\n }\n\n if (added.length > 0) {\n this.trigger('ports:add', this, added)\n }\n }\n }\n })\n\n util.assign(joint.dia.ElementView.prototype, {\n\n portContainerMarkup: 'g',\n portMarkup: [{\n tagName: 'circle',\n selector: 'circle',\n attributes: {\n r: 10,\n fill: '#FFFFFF',\n stroke: '#000000'\n }\n }],\n portLabelMarkup: [{\n tagName: 'text',\n selector: 'text',\n attributes: {\n fill: '#000000'\n }\n }],\n /** @type {Object} */\n _portElementsCache: null,\n\n /**\n * @private\n */\n _initializePorts: function() {\n\n this._portElementsCache = {}\n\n this.listenTo(this.model, 'change:ports', function() {\n\n this._refreshPorts()\n })\n },\n\n /**\n * @typedef {Object} Port\n *\n * @property {string} id\n * @property {Object} position\n * @property {Object} label\n * @property {Object} attrs\n * @property {string} markup\n * @property {string} group\n */\n\n /**\n * @private\n */\n _refreshPorts: function() {\n\n this._removePorts()\n this._portElementsCache = {}\n\n this._renderPorts()\n },\n\n /**\n * @private\n */\n _renderPorts: function() {\n\n // references to rendered elements without z-index\n const elementReferences = []\n const elem = this._getContainerElement()\n\n for (let i = 0, count = elem.node.childNodes.length; i < count; i++) {\n elementReferences.push(elem.node.childNodes[i])\n }\n\n const portsGropsByZ = util.groupBy(this.model._portSettingsData.getPorts(), 'z')\n const withoutZKey = 'auto'\n\n // render non-z first\n util.toArray(portsGropsByZ[withoutZKey]).forEach(function(port) {\n const portElement = this._getPortElement(port)\n elem.append(portElement)\n elementReferences.push(portElement)\n }, this)\n\n const groupNames = Object.keys(portsGropsByZ)\n for (let k = 0; k < groupNames.length; k++) {\n const groupName = groupNames[k]\n if (groupName !== withoutZKey) {\n const z = parseInt(groupName, 10)\n this._appendPorts(portsGropsByZ[groupName], z, elementReferences)\n }\n }\n\n this._updatePorts()\n },\n\n /**\n * @returns {V}\n * @private\n */\n _getContainerElement: function() {\n\n return this.rotatableNode || this.vel\n },\n\n /**\n * @param {Array}ports\n * @param {number} z\n * @param refs\n * @private\n */\n _appendPorts: function(ports, z, refs) {\n\n const containerElement = this._getContainerElement()\n const portElements = util.toArray(ports).map(this._getPortElement, this)\n\n if (refs[z] || z < 0) {\n V(refs[Math.max(z, 0)]).before(portElements)\n } else {\n containerElement.append(portElements)\n }\n },\n\n /**\n * Try to get element from cache,\n * @param port\n * @returns {*}\n * @private\n */\n _getPortElement: function(port) {\n\n if (this._portElementsCache[port.id]) {\n return this._portElementsCache[port.id].portElement\n }\n return this._createPortElement(port)\n },\n\n findPortNode: function(portId, selector) {\n const portCache = this._portElementsCache[portId]\n if (!portCache) return null\n const portRoot = portCache.portContentElement.node\n const portSelectors = portCache.portContentSelectors\n return this.findBySelector(selector, portRoot, portSelectors)[0]\n },\n\n /**\n * @private\n */\n _updatePorts: function() {\n\n // layout ports without group\n this._updatePortGroup(undefined)\n // layout ports with explicit group\n const groupsNames = Object.keys(this.model._portSettingsData.groups)\n groupsNames.forEach(this._updatePortGroup, this)\n },\n\n /**\n * @private\n */\n _removePorts: function() {\n util.invoke(this._portElementsCache, 'portElement.remove')\n },\n\n /**\n * @param {Port} port\n * @returns {V}\n * @private\n */\n _createPortElement: function(port) {\n\n\n let portElement\n let labelElement\n\n const portMarkup = this._getPortMarkup(port)\n let portSelectors\n if (Array.isArray(portMarkup)) {\n const portDoc = util.parseDOMJSON(portMarkup)\n const portFragment = portDoc.fragment\n if (portFragment.childNodes.length > 1) {\n portElement = V('g').append(portFragment)\n } else {\n portElement = V(portFragment.firstChild)\n }\n portSelectors = portDoc.selectors\n } else {\n portElement = V(portMarkup)\n if (Array.isArray(portElement)) {\n portElement = V('g').append(portElement)\n }\n }\n\n if (!portElement) {\n throw new Error('ElementView: Invalid port markup.')\n }\n\n portElement.attr({\n port: port.id,\n 'port-group': port.group\n })\n\n const labelMarkup = this._getPortLabelMarkup(port.label)\n let labelSelectors\n if (Array.isArray(labelMarkup)) {\n const labelDoc = util.parseDOMJSON(labelMarkup)\n const labelFragment = labelDoc.fragment\n if (labelFragment.childNodes.length > 1) {\n labelElement = V('g').append(labelFragment)\n } else {\n labelElement = V(labelFragment.firstChild)\n }\n labelSelectors = labelDoc.selectors\n } else {\n labelElement = V(labelMarkup)\n if (Array.isArray(labelElement)) {\n labelElement = V('g').append(labelElement)\n }\n }\n\n if (!labelElement) {\n throw new Error('ElementView: Invalid port label markup.')\n }\n\n let portContainerSelectors\n if (portSelectors && labelSelectors) {\n for (const key in labelSelectors) {\n if (portSelectors[key]) throw new Error('ElementView: selectors within port must be unique.')\n }\n portContainerSelectors = util.assign({}, portSelectors, labelSelectors)\n } else {\n portContainerSelectors = portSelectors || labelSelectors\n }\n\n const portContainerElement = V(this.portContainerMarkup)\n .addClass('joint-port')\n .append([\n portElement.addClass('joint-port-body'),\n labelElement.addClass('joint-port-label')\n ])\n\n this._portElementsCache[port.id] = {\n portElement: portContainerElement,\n portLabelElement: labelElement,\n portSelectors: portContainerSelectors,\n portLabelSelectors: labelSelectors,\n portContentElement: portElement,\n portContentSelectors: portSelectors\n }\n\n return portContainerElement\n },\n\n /**\n * @param {string=} groupName\n * @private\n */\n _updatePortGroup: function(groupName) {\n\n const elementBBox = g.Rect(this.model.size())\n const portsMetrics = this.model._portSettingsData.getGroupPortsMetrics(groupName, elementBBox)\n\n for (let i = 0, n = portsMetrics.length; i < n; i++) {\n const metrics = portsMetrics[i]\n const {portId} = metrics\n const cached = this._portElementsCache[portId] || {}\n const {portTransformation} = metrics\n this.applyPortTransform(cached.portElement, portTransformation)\n this.updateDOMSubtreeAttributes(cached.portElement.node, metrics.portAttrs, {\n rootBBox: new g.Rect(metrics.portSize),\n selectors: cached.portSelectors\n })\n\n const {labelTransformation} = metrics\n if (labelTransformation) {\n this.applyPortTransform(cached.portLabelElement, labelTransformation, (-portTransformation.angle || 0))\n this.updateDOMSubtreeAttributes(cached.portLabelElement.node, labelTransformation.attrs, {\n rootBBox: new g.Rect(metrics.labelSize),\n selectors: cached.portLabelSelectors\n })\n }\n }\n },\n\n /**\n * @param {Vectorizer} element\n * @param {{dx:number, dy:number, angle: number, attrs: Object, x:number: y:number}} transformData\n * @param {number=} initialAngle\n * @constructor\n */\n applyPortTransform: function(element, transformData, initialAngle) {\n\n const matrix = V.createSVGMatrix()\n .rotate(initialAngle || 0)\n .translate(transformData.x || 0, transformData.y || 0)\n .rotate(transformData.angle || 0)\n\n element.transform(matrix, { absolute: true })\n },\n\n /**\n * @param {Port} port\n * @returns {string}\n * @private\n */\n _getPortMarkup: function(port) {\n\n return port.markup || this.model.get('portMarkup') || this.model.portMarkup || this.portMarkup\n },\n\n /**\n * @param {Object} label\n * @returns {string}\n * @private\n */\n _getPortLabelMarkup: function(label) {\n\n return label.markup || this.model.get('portLabelMarkup') || this.model.portLabelMarkup || this.portLabelMarkup\n }\n\n })\n}(joint, joint.util))\n\njoint.dia.Element.define('basic.Generic', {\n attrs: {\n '.': { fill: '#ffffff', stroke: 'none' }\n }\n})\n\njoint.shapes.basic.Generic.define('basic.Rect', {\n attrs: {\n rect: {\n fill: '#ffffff',\n stroke: '#000000',\n width: 100,\n height: 60\n },\n text: {\n fill: '#000000',\n text: '',\n 'font-size': 14,\n 'ref-x': .5,\n 'ref-y': .5,\n 'text-anchor': 'middle',\n 'y-alignment': 'middle',\n 'font-family': 'Arial, helvetica, sans-serif'\n }\n }\n}, {\n markup: ''\n})\n\njoint.shapes.basic.TextView = joint.dia.ElementView.extend({\n\n initialize: function() {\n joint.dia.ElementView.prototype.initialize.apply(this, arguments)\n // The element view is not automatically rescaled to fit the model size\n // when the attribute 'attrs' is changed.\n this.listenTo(this.model, 'change:attrs', this.resize)\n }\n})\n\njoint.shapes.basic.Generic.define('basic.Text', {\n attrs: {\n text: {\n 'font-size': 18,\n fill: '#000000'\n }\n }\n}, {\n markup: '',\n})\n\njoint.shapes.basic.Generic.define('basic.Circle', {\n size: { width: 60, height: 60 },\n attrs: {\n circle: {\n fill: '#ffffff',\n stroke: '#000000',\n r: 30,\n cx: 30,\n cy: 30\n },\n text: {\n 'font-size': 14,\n text: '',\n 'text-anchor': 'middle',\n 'ref-x': .5,\n 'ref-y': .5,\n 'y-alignment': 'middle',\n fill: '#000000',\n 'font-family': 'Arial, helvetica, sans-serif'\n }\n }\n}, {\n markup: '',\n})\n\njoint.shapes.basic.Generic.define('basic.Ellipse', {\n size: { width: 60, height: 40 },\n attrs: {\n ellipse: {\n fill: '#ffffff',\n stroke: '#000000',\n rx: 30,\n ry: 20,\n cx: 30,\n cy: 20\n },\n text: {\n 'font-size': 14,\n text: '',\n 'text-anchor': 'middle',\n 'ref-x': .5,\n 'ref-y': .5,\n 'y-alignment': 'middle',\n fill: '#000000',\n 'font-family': 'Arial, helvetica, sans-serif'\n }\n }\n}, {\n markup: '',\n})\n\njoint.shapes.basic.Generic.define('basic.Polygon', {\n size: { width: 60, height: 40 },\n attrs: {\n polygon: {\n fill: '#ffffff',\n stroke: '#000000'\n },\n text: {\n 'font-size': 14,\n text: '',\n 'text-anchor': 'middle',\n 'ref-x': .5,\n 'ref-dy': 20,\n 'y-alignment': 'middle',\n fill: '#000000',\n 'font-family': 'Arial, helvetica, sans-serif'\n }\n }\n}, {\n markup: '',\n})\n\njoint.shapes.basic.Generic.define('basic.Polyline', {\n size: { width: 60, height: 40 },\n attrs: {\n polyline: {\n fill: '#ffffff',\n stroke: '#000000'\n },\n text: {\n 'font-size': 14,\n text: '',\n 'text-anchor': 'middle',\n 'ref-x': .5,\n 'ref-dy': 20,\n 'y-alignment': 'middle',\n fill: '#000000',\n 'font-family': 'Arial, helvetica, sans-serif'\n }\n }\n}, {\n markup: '',\n})\n\njoint.shapes.basic.Generic.define('basic.Image', {\n attrs: {\n text: {\n 'font-size': 14,\n text: '',\n 'text-anchor': 'middle',\n 'ref-x': .5,\n 'ref-dy': 20,\n 'y-alignment': 'middle',\n fill: '#000000',\n 'font-family': 'Arial, helvetica, sans-serif'\n }\n }\n}, {\n markup: '',\n})\n\njoint.shapes.basic.Generic.define('basic.Path', {\n size: { width: 60, height: 60 },\n attrs: {\n path: {\n fill: '#ffffff',\n stroke: '#000000'\n },\n text: {\n 'font-size': 14,\n text: '',\n 'text-anchor': 'middle',\n ref: 'path',\n 'ref-x': .5,\n 'ref-dy': 10,\n fill: '#000000',\n 'font-family': 'Arial, helvetica, sans-serif'\n }\n }\n\n}, {\n markup: '',\n})\n\njoint.shapes.basic.Path.define('basic.Rhombus', {\n attrs: {\n path: {\n d: 'M 30 0 L 60 30 30 60 0 30 z'\n },\n text: {\n 'ref-y': .5,\n 'ref-dy': null,\n 'y-alignment': 'middle'\n }\n }\n})\n\n\n/**\n * @deprecated use the port api instead\n */\n// PortsModelInterface is a common interface for shapes that have ports. This interface makes it easy\n// to create new shapes with ports functionality. It is assumed that the new shapes have\n// `inPorts` and `outPorts` array properties. Only these properties should be used to set ports.\n// In other words, using this interface, it is no longer recommended to set ports directly through the\n// `attrs` object.\n\n// Usage:\n// joint.shapes.custom.MyElementWithPorts = joint.shapes.basic.Path.extend(_.extend({}, joint.shapes.basic.PortsModelInterface, {\n// getPortAttrs: function(portName, index, total, selector, type) {\n// var attrs = {};\n// var portClass = 'port' + index;\n// var portSelector = selector + '>.' + portClass;\n// var portTextSelector = portSelector + '>text';\n// var portBodySelector = portSelector + '>.port-body';\n//\n// attrs[portTextSelector] = { text: portName };\n// attrs[portBodySelector] = { port: { id: portName || _.uniqueId(type) , type: type } };\n// attrs[portSelector] = { ref: 'rect', 'ref-y': (index + 0.5) * (1 / total) };\n//\n// if (selector === '.outPorts') { attrs[portSelector]['ref-dx'] = 0; }\n//\n// return attrs;\n// }\n//}));\njoint.shapes.basic.PortsModelInterface = {\n\n initialize: function() {\n\n this.updatePortsAttrs()\n this.on('change:inPorts change:outPorts', this.updatePortsAttrs, this)\n\n // Call the `initialize()` of the parent.\n this.constructor.__super__.constructor.__super__.initialize.apply(this, arguments)\n },\n\n updatePortsAttrs: function(eventName) {\n\n if (this._portSelectors) {\n\n const newAttrs = joint.util.omit(this.get('attrs'), this._portSelectors)\n this.set('attrs', newAttrs, { silent: true })\n }\n\n // This holds keys to the `attrs` object for all the port specific attribute that\n // we set in this method. This is necessary in order to remove previously set\n // attributes for previous ports.\n this._portSelectors = []\n\n const attrs = {}\n\n joint.util.toArray(this.get('inPorts')).forEach(function(portName, index, ports) {\n const portAttributes = this.getPortAttrs(portName, index, ports.length, '.inPorts', 'in')\n this._portSelectors = this._portSelectors.concat(Object.keys(portAttributes))\n joint.util.assign(attrs, portAttributes)\n }, this)\n\n joint.util.toArray(this.get('outPorts')).forEach(function(portName, index, ports) {\n const portAttributes = this.getPortAttrs(portName, index, ports.length, '.outPorts', 'out')\n this._portSelectors = this._portSelectors.concat(Object.keys(portAttributes))\n joint.util.assign(attrs, portAttributes)\n }, this)\n\n // Silently set `attrs` on the cell so that noone knows the attrs have changed. This makes sure\n // that, for example, command manager does not register `change:attrs` command but only\n // the important `change:inPorts`/`change:outPorts` command.\n this.attr(attrs, { silent: true })\n // Manually call the `processPorts()` method that is normally called on `change:attrs` (that we just made silent).\n this.processPorts()\n // Let the outside world (mainly the `ModelView`) know that we're done configuring the `attrs` object.\n this.trigger('process:ports')\n },\n\n getPortSelector: function(name) {\n\n let selector = '.inPorts'\n let index = this.get('inPorts').indexOf(name)\n\n if (index < 0) {\n selector = '.outPorts'\n index = this.get('outPorts').indexOf(name)\n\n if (index < 0) throw new Error('getPortSelector(): Port doesn\\'t exist.')\n }\n\n return `${selector }>g:nth-child(${ index + 1 })>.port-body`\n }\n}\n\njoint.shapes.basic.PortsViewInterface = {\n\n initialize: function() {\n\n // `Model` emits the `process:ports` whenever it's done configuring the `attrs` object for ports.\n this.listenTo(this.model, 'process:ports', this.update)\n\n joint.dia.ElementView.prototype.initialize.apply(this, arguments)\n },\n\n update: function() {\n\n // First render ports so that `attrs` can be applied to those newly created DOM elements\n // in `ElementView.prototype.update()`.\n this.renderPorts()\n joint.dia.ElementView.prototype.update.apply(this, arguments)\n },\n\n renderPorts: function() {\n\n const $inPorts = this.$('.inPorts').empty()\n const $outPorts = this.$('.outPorts').empty()\n\n const portTemplate = joint.util.template(this.model.portMarkup)\n\n const ports = this.model.ports || []\n ports.filter(function(p) {\n return p.type === 'in'\n }).forEach(function(port, index) {\n\n $inPorts.append(V(portTemplate({ id: index, port: port })).node)\n })\n\n ports.filter(function(p) {\n return p.type === 'out'\n }).forEach(function(port, index) {\n\n $outPorts.append(V(portTemplate({ id: index, port: port })).node)\n })\n }\n}\n\njoint.shapes.basic.Generic.define('basic.TextBlock', {\n // see joint.css for more element styles\n attrs: {\n rect: {\n fill: '#ffffff',\n stroke: '#000000',\n width: 80,\n height: 100\n },\n text: {\n fill: '#000000',\n 'font-size': 14,\n 'font-family': 'Arial, helvetica, sans-serif'\n },\n '.content': {\n text: '',\n 'ref-x': .5,\n 'ref-y': .5,\n 'y-alignment': 'middle',\n 'x-alignment': 'middle'\n }\n },\n\n content: ''\n}, {\n markup: [\n '',\n '',\n joint.env.test('svgforeignobject') ? '
' : '',\n ''\n ].join(''),\n\n initialize: function() {\n\n this.listenTo(this, 'change:size', this.updateSize)\n this.listenTo(this, 'change:content', this.updateContent)\n this.updateSize(this, this.get('size'))\n this.updateContent(this, this.get('content'))\n joint.shapes.basic.Generic.prototype.initialize.apply(this, arguments)\n },\n\n updateSize: function(cell, size) {\n\n // Selector `foreignObject' doesn't work across all browsers, we're using class selector instead.\n // We have to clone size as we don't want attributes.div.style to be same object as attributes.size.\n this.attr({\n '.fobj': joint.util.assign({}, size),\n div: {\n style: joint.util.assign({}, size)\n }\n })\n },\n\n updateContent: function(cell, content) {\n\n if (joint.env.test('svgforeignobject')) {\n\n // Content element is a
element.\n this.attr({\n '.content': {\n html: joint.util.sanitizeHTML(content)\n }\n })\n\n } else {\n\n // Content element is a element.\n // SVG elements don't have innerHTML attribute.\n this.attr({\n '.content': {\n text: content\n }\n })\n }\n },\n\n // Here for backwards compatibility:\n setForeignObjectSize: function() {\n\n this.updateSize.apply(this, arguments)\n },\n\n // Here for backwards compatibility:\n setDivContent: function() {\n\n this.updateContent.apply(this, arguments)\n }\n})\n\n// TextBlockView implements the fallback for IE when no foreignObject exists and\n// the text needs to be manually broken.\njoint.shapes.basic.TextBlockView = joint.dia.ElementView.extend({\n\n initialize: function() {\n\n joint.dia.ElementView.prototype.initialize.apply(this, arguments)\n\n // Keep this for backwards compatibility:\n this.noSVGForeignObjectElement = !joint.env.test('svgforeignobject')\n\n if (!joint.env.test('svgforeignobject')) {\n\n this.listenTo(this.model, 'change:content change:size', function(cell) {\n // avoiding pass of extra paramters\n this.updateContent(cell)\n })\n }\n },\n\n update: function(cell, renderingOnlyAttrs) {\n\n const {model} = this\n\n if (!joint.env.test('svgforeignobject')) {\n\n // Update everything but the content first.\n const noTextAttrs = joint.util.omit(renderingOnlyAttrs || model.get('attrs'), '.content')\n joint.dia.ElementView.prototype.update.call(this, model, noTextAttrs)\n\n if (!renderingOnlyAttrs || joint.util.has(renderingOnlyAttrs, '.content')) {\n // Update the content itself.\n this.updateContent(model, renderingOnlyAttrs)\n }\n\n } else {\n\n joint.dia.ElementView.prototype.update.call(this, model, renderingOnlyAttrs)\n }\n },\n\n updateContent: function(cell, renderingOnlyAttrs) {\n\n // Create copy of the text attributes\n let textAttrs = joint.util.merge({}, (renderingOnlyAttrs || cell.get('attrs'))['.content'])\n\n textAttrs = joint.util.omit(textAttrs, 'text')\n\n // Break the content to fit the element size taking into account the attributes\n // set on the model.\n const text = joint.util.breakText(cell.get('content'), cell.get('size'), textAttrs, {\n // measuring sandbox svg document\n svgDocument: this.paper.svg\n })\n\n // Create a new attrs with same structure as the model attrs { text: { *textAttributes* }}\n const attrs = joint.util.setByPath({}, '.content', textAttrs, '/')\n\n // Replace text attribute with the one we just processed.\n attrs['.content'].text = text\n\n // Update the view using renderingOnlyAttributes parameter.\n joint.dia.ElementView.prototype.update.call(this, cell, attrs)\n }\n});\n\n(function(dia, util, env, V) {\n\n \n\n // ELEMENTS\n\n const {Element} = dia\n\n Element.define('standard.Rectangle', {\n attrs: {\n body: {\n refWidth: '100%',\n refHeight: '100%',\n strokeWidth: 2,\n stroke: '#000000',\n fill: '#FFFFFF'\n },\n label: {\n textVerticalAnchor: 'middle',\n textAnchor: 'middle',\n refX: '50%',\n refY: '50%',\n fontSize: 14,\n fill: '#333333'\n }\n }\n }, {\n markup: [{\n tagName: 'rect',\n selector: 'body',\n }, {\n tagName: 'text',\n selector: 'label'\n }]\n })\n\n Element.define('standard.Circle', {\n attrs: {\n body: {\n refCx: '50%',\n refCy: '50%',\n refR: '50%',\n strokeWidth: 2,\n stroke: '#333333',\n fill: '#FFFFFF'\n },\n label: {\n textVerticalAnchor: 'middle',\n textAnchor: 'middle',\n refX: '50%',\n refY: '50%',\n fontSize: 14,\n fill: '#333333'\n }\n }\n }, {\n markup: [{\n tagName: 'circle',\n selector: 'body'\n }, {\n tagName: 'text',\n selector: 'label'\n }]\n })\n\n Element.define('standard.Ellipse', {\n attrs: {\n body: {\n refCx: '50%',\n refCy: '50%',\n refRx: '50%',\n refRy: '50%',\n strokeWidth: 2,\n stroke: '#333333',\n fill: '#FFFFFF'\n },\n label: {\n textVerticalAnchor: 'middle',\n textAnchor: 'middle',\n refX: '50%',\n refY: '50%',\n fontSize: 14,\n fill: '#333333'\n }\n }\n }, {\n markup: [{\n tagName: 'ellipse',\n selector: 'body'\n }, {\n tagName: 'text',\n selector: 'label'\n }]\n })\n\n Element.define('standard.Path', {\n attrs: {\n body: {\n refD: 'M 0 0 L 10 0 10 10 0 10 Z',\n strokeWidth: 2,\n stroke: '#333333',\n fill: '#FFFFFF'\n },\n label: {\n textVerticalAnchor: 'middle',\n textAnchor: 'middle',\n refX: '50%',\n refY: '50%',\n fontSize: 14,\n fill: '#333333'\n }\n }\n }, {\n markup: [{\n tagName: 'path',\n selector: 'body'\n }, {\n tagName: 'text',\n selector: 'label'\n }]\n })\n\n Element.define('standard.Polygon', {\n attrs: {\n body: {\n refPoints: '0 0 10 0 10 10 0 10',\n strokeWidth: 2,\n stroke: '#333333',\n fill: '#FFFFFF'\n },\n label: {\n textVerticalAnchor: 'middle',\n textAnchor: 'middle',\n refX: '50%',\n refY: '50%',\n fontSize: 14,\n fill: '#333333'\n }\n }\n }, {\n markup: [{\n tagName: 'polygon',\n selector: 'body'\n }, {\n tagName: 'text',\n selector: 'label'\n }]\n })\n\n Element.define('standard.Polyline', {\n attrs: {\n body: {\n refPoints: '0 0 10 0 10 10 0 10 0 0',\n strokeWidth: 2,\n stroke: '#333333',\n fill: '#FFFFFF'\n },\n label: {\n textVerticalAnchor: 'middle',\n textAnchor: 'middle',\n refX: '50%',\n refY: '50%',\n fontSize: 14,\n fill: '#333333'\n }\n }\n }, {\n markup: [{\n tagName: 'polyline',\n selector: 'body'\n }, {\n tagName: 'text',\n selector: 'label'\n }]\n })\n\n Element.define('standard.Image', {\n attrs: {\n image: {\n refWidth: '100%',\n refHeight: '100%',\n // xlinkHref: '[URL]'\n },\n label: {\n textVerticalAnchor: 'top',\n textAnchor: 'middle',\n refX: '50%',\n refY: '100%',\n refY2: 10,\n fontSize: 14,\n fill: '#333333'\n }\n }\n }, {\n markup: [{\n tagName: 'image',\n selector: 'image'\n }, {\n tagName: 'text',\n selector: 'label'\n }]\n })\n\n Element.define('standard.BorderedImage', {\n attrs: {\n border: {\n refWidth: '100%',\n refHeight: '100%',\n stroke: '#333333',\n strokeWidth: 2\n },\n image: {\n // xlinkHref: '[URL]'\n refWidth: -1,\n refHeight: -1,\n x: 0.5,\n y: 0.5\n },\n label: {\n textVerticalAnchor: 'top',\n textAnchor: 'middle',\n refX: '50%',\n refY: '100%',\n refY2: 10,\n fontSize: 14,\n fill: '#333333'\n }\n }\n }, {\n markup: [{\n tagName: 'image',\n selector: 'image'\n }, {\n tagName: 'rect',\n selector: 'border',\n attributes: {\n fill: 'none'\n }\n }, {\n tagName: 'text',\n selector: 'label'\n }]\n })\n\n Element.define('standard.EmbeddedImage', {\n attrs: {\n body: {\n refWidth: '100%',\n refHeight: '100%',\n stroke: '#333333',\n fill: '#FFFFFF',\n strokeWidth: 2\n },\n image: {\n // xlinkHref: '[URL]'\n refWidth: '30%',\n refHeight: -20,\n x: 10,\n y: 10,\n preserveAspectRatio: 'xMidYMin'\n },\n label: {\n textVerticalAnchor: 'top',\n textAnchor: 'left',\n refX: '30%',\n refX2: 20, // 10 + 10\n refY: 10,\n fontSize: 14,\n fill: '#333333'\n }\n }\n }, {\n markup: [{\n tagName: 'rect',\n selector: 'body'\n }, {\n tagName: 'image',\n selector: 'image'\n }, {\n tagName: 'text',\n selector: 'label'\n }]\n })\n\n Element.define('standard.HeaderedRectangle', {\n attrs: {\n body: {\n refWidth: '100%',\n refHeight: '100%',\n strokeWidth: 2,\n stroke: '#000000',\n fill: '#FFFFFF'\n },\n header: {\n refWidth: '100%',\n height: 30,\n strokeWidth: 2,\n stroke: '#000000',\n fill: '#FFFFFF'\n },\n headerText: {\n textVerticalAnchor: 'middle',\n textAnchor: 'middle',\n refX: '50%',\n refY: 15,\n fontSize: 16,\n fill: '#333333'\n },\n bodyText: {\n textVerticalAnchor: 'middle',\n textAnchor: 'middle',\n refX: '50%',\n refY: '50%',\n refY2: 15,\n fontSize: 14,\n fill: '#333333'\n }\n }\n }, {\n markup: [{\n tagName: 'rect',\n selector: 'body'\n }, {\n tagName: 'rect',\n selector: 'header'\n }, {\n tagName: 'text',\n selector: 'headerText'\n }, {\n tagName: 'text',\n selector: 'bodyText'\n }]\n })\n\n const CYLINDER_TILT = 10\n\n joint.dia.Element.define('standard.Cylinder', {\n attrs: {\n body: {\n lateralArea: CYLINDER_TILT,\n fill: '#FFFFFF',\n stroke: '#333333',\n strokeWidth: 2\n },\n top: {\n refCx: '50%',\n cy: CYLINDER_TILT,\n refRx: '50%',\n ry: CYLINDER_TILT,\n fill: '#FFFFFF',\n stroke: '#333333',\n strokeWidth: 2\n },\n label: {\n textVerticalAnchor: 'middle',\n textAnchor: 'middle',\n refX: '50%',\n refY: '100%',\n refY2: 15,\n fontSize: 14,\n fill: '#333333'\n }\n }\n }, {\n markup: [{\n tagName: 'path',\n selector: 'body'\n }, {\n tagName: 'ellipse',\n selector: 'top'\n }, {\n tagName: 'text',\n selector: 'label'\n }],\n\n topRy: function(t, opt) {\n // getter\n if (t === undefined) return this.attr('body/lateralArea')\n\n // setter\n const isPercentage = util.isPercentage(t)\n\n const bodyAttrs = { lateralArea: t }\n const topAttrs = isPercentage\n ? { refCy: t, refRy: t, cy: null, ry: null }\n : { refCy: null, refRy: null, cy: t, ry: t }\n\n return this.attr({ body: bodyAttrs, top: topAttrs }, opt)\n }\n\n }, {\n attributes: {\n lateralArea: {\n set: function(t, refBBox) {\n const isPercentage = util.isPercentage(t)\n if (isPercentage) t = parseFloat(t) / 100\n\n const {x} = refBBox\n const {y} = refBBox\n const w = refBBox.width\n const h = refBBox.height\n\n // curve control point variables\n const rx = w / 2\n const ry = isPercentage ? (h * t) : t\n\n const kappa = V.KAPPA\n const cx = kappa * rx\n const cy = kappa * (isPercentage ? (h * t) : t)\n\n // shape variables\n const xLeft = x\n const xCenter = x + (w / 2)\n const xRight = x + w\n\n const ySideTop = y + ry\n const yCurveTop = ySideTop - ry\n const ySideBottom = y + h - ry\n const yCurveBottom = y + h\n\n // return calculated shape\n const data = [\n 'M', xLeft, ySideTop,\n 'L', xLeft, ySideBottom,\n 'C', x, (ySideBottom + cy), (xCenter - cx), yCurveBottom, xCenter, yCurveBottom,\n 'C', (xCenter + cx), yCurveBottom, xRight, (ySideBottom + cy), xRight, ySideBottom,\n 'L', xRight, ySideTop,\n 'C', xRight, (ySideTop - cy), (xCenter + cx), yCurveTop, xCenter, yCurveTop,\n 'C', (xCenter - cx), yCurveTop, xLeft, (ySideTop - cy), xLeft, ySideTop,\n 'Z'\n ]\n return { d: data.join(' ') }\n }\n }\n }\n })\n\n const foLabelMarkup = {\n tagName: 'foreignObject',\n selector: 'foreignObject',\n attributes: {\n overflow: 'hidden'\n },\n children: [{\n tagName: 'div',\n namespaceURI: 'http://www.w3.org/1999/xhtml',\n selector: 'label',\n style: {\n width: '100%',\n height: '100%',\n position: 'static',\n backgroundColor: 'transparent',\n textAlign: 'center',\n margin: 0,\n padding: '0px 5px',\n boxSizing: 'border-box',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center'\n }\n }]\n }\n\n const svgLabelMarkup = {\n tagName: 'text',\n selector: 'label',\n attributes: {\n 'text-anchor': 'middle'\n }\n }\n\n const labelMarkup = (env.test('svgforeignobject')) ? foLabelMarkup : svgLabelMarkup\n\n Element.define('standard.TextBlock', {\n attrs: {\n body: {\n refWidth: '100%',\n refHeight: '100%',\n stroke: '#333333',\n fill: '#ffffff',\n strokeWidth: 2\n },\n foreignObject: {\n refWidth: '100%',\n refHeight: '100%'\n },\n label: {\n style: {\n fontSize: 14\n }\n }\n }\n }, {\n markup: [{\n tagName: 'rect',\n selector: 'body'\n }, labelMarkup]\n }, {\n attributes: {\n text: {\n set: function(text, refBBox, node, attrs) {\n if (node instanceof HTMLElement) {\n node.textContent = text\n } else {\n // No foreign object\n const style = attrs.style || {}\n const wrapValue = { text: text, width: -5, height: '100%' }\n const wrapAttrs = util.assign({ textVerticalAnchor: 'middle' }, style)\n dia.attributes.textWrap.set.call(this, wrapValue, refBBox, node, wrapAttrs)\n return { fill: style.color || null }\n }\n },\n position: function(text, refBBox, node) {\n // No foreign object\n if (node instanceof SVGElement) return refBBox.center()\n }\n }\n }\n })\n\n // LINKS\n\n const {Link} = dia\n\n Link.define('standard.Link', {\n attrs: {\n line: {\n connection: true,\n stroke: '#333333',\n strokeWidth: 2,\n strokeLinejoin: 'round',\n targetMarker: {\n type: 'path',\n d: 'M 10 -5 0 0 10 5 z'\n }\n },\n wrapper: {\n connection: true,\n strokeWidth: 10,\n strokeLinejoin: 'round'\n }\n }\n }, {\n markup: [{\n tagName: 'path',\n selector: 'wrapper',\n attributes: {\n fill: 'none',\n cursor: 'pointer',\n stroke: 'transparent'\n }\n }, {\n tagName: 'path',\n selector: 'line',\n attributes: {\n fill: 'none',\n 'pointer-events': 'none'\n }\n }]\n })\n\n Link.define('standard.DoubleLink', {\n attrs: {\n line: {\n connection: true,\n stroke: '#DDDDDD',\n strokeWidth: 4,\n strokeLinejoin: 'round',\n targetMarker: {\n type: 'path',\n stroke: '#000000',\n d: 'M 10 -3 10 -10 -2 0 10 10 10 3'\n }\n },\n outline: {\n connection: true,\n stroke: '#000000',\n strokeWidth: 6,\n strokeLinejoin: 'round'\n }\n }\n }, {\n markup: [{\n tagName: 'path',\n selector: 'outline',\n attributes: {\n fill: 'none'\n }\n }, {\n tagName: 'path',\n selector: 'line',\n attributes: {\n fill: 'none'\n }\n }]\n })\n\n Link.define('standard.ShadowLink', {\n attrs: {\n line: {\n connection: true,\n stroke: '#FF0000',\n strokeWidth: 20,\n strokeLinejoin: 'round',\n targetMarker: {\n type: 'path',\n stroke: 'none',\n d: 'M 0 -10 -10 0 0 10 z'\n },\n sourceMarker: {\n type: 'path',\n stroke: 'none',\n d: 'M -10 -10 0 0 -10 10 0 10 0 -10 z'\n }\n },\n shadow: {\n connection: true,\n refX: 3,\n refY: 6,\n stroke: '#000000',\n strokeOpacity: 0.2,\n strokeWidth: 20,\n strokeLinejoin: 'round',\n targetMarker: {\n type: 'path',\n d: 'M 0 -10 -10 0 0 10 z',\n stroke: 'none'\n },\n sourceMarker: {\n type: 'path',\n stroke: 'none',\n d: 'M -10 -10 0 0 -10 10 0 10 0 -10 z'\n }\n }\n }\n }, {\n markup: [{\n tagName: 'path',\n selector: 'shadow',\n attributes: {\n fill: 'none'\n }\n }, {\n tagName: 'path',\n selector: 'line',\n attributes: {\n fill: 'none'\n }\n }]\n })\n\n\n})(joint.dia, joint.util, joint.env, V)\n\njoint.routers.manhattan = (function(g, joint, util) {\n\n \n\n var config = {\n\n // size of the step to find a route (the grid of the manhattan pathfinder)\n step: 10,\n\n // the number of route finding loops that cause the router to abort\n // returns fallback route instead\n maximumLoops: 2000,\n\n // the number of decimal places to round floating point coordinates\n precision: 10,\n\n // maximum change of direction\n maxAllowedDirectionChange: 90,\n\n // should the router use perpendicular linkView option?\n // does not connect anchor of element but rather a point close-by that is orthogonal\n // this looks much better\n perpendicular: true,\n\n // should the source and/or target not be considered as obstacles?\n excludeEnds: [], // 'source', 'target'\n\n // should certain types of elements not be considered as obstacles?\n excludeTypes: ['basic.Text'],\n\n // possible starting directions from an element\n startDirections: ['top', 'right', 'bottom', 'left'],\n\n // possible ending directions to an element\n endDirections: ['top', 'right', 'bottom', 'left'],\n\n // specify the directions used above and what they mean\n directionMap: {\n top: { x: 0, y: -1 },\n right: { x: 1, y: 0 },\n bottom: { x: 0, y: 1 },\n left: { x: -1, y: 0 }\n },\n\n // cost of an orthogonal step\n cost: function() {\n\n return this.step\n },\n\n // an array of directions to find next points on the route\n // different from start/end directions\n directions: function() {\n\n const {step} = this\n const cost = this.cost()\n\n return [\n { offsetX: step , offsetY: 0 , cost: cost },\n { offsetX: 0 , offsetY: step , cost: cost },\n { offsetX: -step , offsetY: 0 , cost: cost },\n { offsetX: 0 , offsetY: -step , cost: cost }\n ]\n },\n\n // a penalty received for direction change\n penalties: function() {\n\n return {\n 0: 0,\n 45: this.step / 2,\n 90: this.step / 2\n }\n },\n\n // padding applied on the element bounding boxes\n paddingBox: function() {\n\n const {step} = this\n\n return {\n x: -step,\n y: -step,\n width: 2 * step,\n height: 2 * step\n }\n },\n\n // a router to use when the manhattan router fails\n // (one of the partial routes returns null)\n fallbackRouter: function(vertices, opt, linkView) {\n\n if (!util.isFunction(joint.routers.orthogonal)) {\n throw new Error('Manhattan requires the orthogonal router as default fallback.')\n }\n\n return joint.routers.orthogonal(vertices, util.assign({}, config, opt), linkView)\n },\n\n /* Deprecated */\n // a simple route used in situations when main routing method fails\n // (exceed max number of loop iterations, inaccessible)\n fallbackRoute: function(from, to, opt) {\n\n return null // null result will trigger the fallbackRouter\n\n // left for reference:\n /*// Find an orthogonal route ignoring obstacles.\n\n var point = ((opt.previousDirAngle || 0) % 180 === 0)\n ? new g.Point(from.x, to.y)\n : new g.Point(to.x, from.y);\n\n return [point];*/\n },\n\n // if a function is provided, it's used to route the link while dragging an end\n // i.e. function(from, to, opt) { return []; }\n draggingRoute: null\n }\n\n // HELPER CLASSES //\n\n // Map of obstacles\n // Helper structure to identify whether a point lies inside an obstacle.\n function ObstacleMap(opt) {\n\n this.map = {}\n this.options = opt\n // tells how to divide the paper when creating the elements map\n this.mapGridSize = 100\n }\n\n ObstacleMap.prototype.build = function(graph, link) {\n\n const opt = this.options\n\n // source or target element could be excluded from set of obstacles\n const excludedEnds = util.toArray(opt.excludeEnds).reduce(function(res, item) {\n\n const end = link.get(item)\n if (end) {\n const cell = graph.getCell(end.id)\n if (cell) {\n res.push(cell)\n }\n }\n\n return res\n }, [])\n\n // Exclude any embedded elements from the source and the target element.\n let excludedAncestors = []\n\n const source = graph.getCell(link.get('source').id)\n if (source) {\n excludedAncestors = util.union(excludedAncestors, source.getAncestors().map(function(cell) { return cell.id }))\n }\n\n const target = graph.getCell(link.get('target').id)\n if (target) {\n excludedAncestors = util.union(excludedAncestors, target.getAncestors().map(function(cell) { return cell.id }))\n }\n\n // Builds a map of all elements for quicker obstacle queries (i.e. is a point contained\n // in any obstacle?) (a simplified grid search).\n // The paper is divided into smaller cells, where each holds information about which\n // elements belong to it. When we query whether a point lies inside an obstacle we\n // don't need to go through all obstacles, we check only those in a particular cell.\n const {mapGridSize} = this\n\n graph.getElements().reduce(function(map, element) {\n\n const isExcludedType = util.toArray(opt.excludeTypes).includes(element.get('type'))\n const isExcludedEnd = excludedEnds.find(function(excluded) { return excluded.id === element.id })\n const isExcludedAncestor = excludedAncestors.includes(element.id)\n\n const isExcluded = isExcludedType || isExcludedEnd || isExcludedAncestor\n if (!isExcluded) {\n const bbox = element.getBBox().moveAndExpand(opt.paddingBox)\n\n const origin = bbox.origin().snapToGrid(mapGridSize)\n const corner = bbox.corner().snapToGrid(mapGridSize)\n\n for (let {x} = origin; x <= corner.x; x += mapGridSize) {\n for (let {y} = origin; y <= corner.y; y += mapGridSize) {\n const gridKey = `${x }@${ y}`\n map[gridKey] = map[gridKey] || []\n map[gridKey].push(bbox)\n }\n }\n }\n\n return map\n }, this.map)\n\n return this\n }\n\n ObstacleMap.prototype.isPointAccessible = function(point) {\n\n const mapKey = point.clone().snapToGrid(this.mapGridSize).toString()\n\n return util.toArray(this.map[mapKey]).every( function(obstacle) {\n return !obstacle.containsPoint(point)\n })\n }\n\n // Sorted Set\n // Set of items sorted by given value.\n function SortedSet() {\n this.items = []\n this.hash = {}\n this.values = {}\n this.OPEN = 1\n this.CLOSE = 2\n }\n\n SortedSet.prototype.add = function(item, value) {\n\n if (this.hash[item]) {\n // item removal\n this.items.splice(this.items.indexOf(item), 1)\n } else {\n this.hash[item] = this.OPEN\n }\n\n this.values[item] = value\n\n const index = joint.util.sortedIndex(this.items, item, function(i) {\n return this.values[i]\n }.bind(this))\n\n this.items.splice(index, 0, item)\n }\n\n SortedSet.prototype.remove = function(item) {\n\n this.hash[item] = this.CLOSE\n }\n\n SortedSet.prototype.isOpen = function(item) {\n\n return this.hash[item] === this.OPEN\n }\n\n SortedSet.prototype.isClose = function(item) {\n\n return this.hash[item] === this.CLOSE\n }\n\n SortedSet.prototype.isEmpty = function() {\n\n return this.items.length === 0\n }\n\n SortedSet.prototype.pop = function() {\n\n const item = this.items.shift()\n this.remove(item)\n return item\n }\n\n // HELPERS //\n\n // return source bbox\n function getSourceBBox(linkView, opt) {\n\n // expand by padding box\n if (opt && opt.paddingBox) return linkView.sourceBBox.clone().moveAndExpand(opt.paddingBox)\n\n return linkView.sourceBBox.clone()\n }\n\n // return target bbox\n function getTargetBBox(linkView, opt) {\n\n // expand by padding box\n if (opt && opt.paddingBox) return linkView.targetBBox.clone().moveAndExpand(opt.paddingBox)\n\n return linkView.targetBBox.clone()\n }\n\n // return source anchor\n function getSourceAnchor(linkView, opt) {\n\n if (linkView.sourceAnchor) return linkView.sourceAnchor\n\n // fallback: center of bbox\n const sourceBBox = getSourceBBox(linkView, opt)\n return sourceBBox.center()\n }\n\n // return target anchor\n function getTargetAnchor(linkView, opt) {\n\n if (linkView.targetAnchor) return linkView.targetAnchor\n\n // fallback: center of bbox\n const targetBBox = getTargetBBox(linkView, opt)\n return targetBBox.center() // default\n }\n\n // returns a direction index from start point to end point\n // corrects for grid deformation between start and end\n function getDirectionAngle(start, end, numDirections, grid, opt) {\n\n const quadrant = 360 / numDirections\n const angleTheta = start.theta(fixAngleEnd(start, end, grid, opt))\n const normalizedAngle = g.normalizeAngle(angleTheta + (quadrant / 2))\n return quadrant * Math.floor(normalizedAngle / quadrant)\n }\n\n // helper function for getDirectionAngle()\n // corrects for grid deformation\n // (if a point is one grid steps away from another in both dimensions,\n // it is considered to be 45 degrees away, even if the real angle is different)\n // this causes visible angle discrepancies if `opt.step` is much larger than `paper.gridSize`\n function fixAngleEnd(start, end, grid, opt) {\n\n const {step} = opt\n\n const diffX = end.x - start.x\n const diffY = end.y - start.y\n\n const gridStepsX = diffX / grid.x\n const gridStepsY = diffY / grid.y\n\n const distanceX = gridStepsX * step\n const distanceY = gridStepsY * step\n\n return new g.Point(start.x + distanceX, start.y + distanceY)\n }\n\n // return the change in direction between two direction angles\n function getDirectionChange(angle1, angle2) {\n\n const directionChange = Math.abs(angle1 - angle2)\n return (directionChange > 180) ? (360 - directionChange) : directionChange\n }\n\n // fix direction offsets according to current grid\n function getGridOffsets(directions, grid, opt) {\n\n const {step} = opt\n\n util.toArray(opt.directions).forEach(function(direction) {\n\n direction.gridOffsetX = (direction.offsetX / step) * grid.x\n direction.gridOffsetY = (direction.offsetY / step) * grid.y\n })\n }\n\n // get grid size in x and y dimensions, adapted to source and target positions\n function getGrid(step, source, target) {\n\n return {\n source: source.clone(),\n x: getGridDimension(target.x - source.x, step),\n y: getGridDimension(target.y - source.y, step)\n }\n }\n\n // helper function for getGrid()\n function getGridDimension(diff, step) {\n\n // return step if diff = 0\n if (!diff) return step\n\n const absDiff = Math.abs(diff)\n const numSteps = Math.round(absDiff / step)\n\n // return absDiff if less than one step apart\n if (!numSteps) return absDiff\n\n // otherwise, return corrected step\n const roundedDiff = numSteps * step\n const remainder = absDiff - roundedDiff\n const stepCorrection = remainder / numSteps\n\n return step + stepCorrection\n }\n\n // return a clone of point snapped to grid\n function snapToGrid(point, grid) {\n\n const {source} = grid\n\n const snappedX = g.snapToGrid(point.x - source.x, grid.x) + source.x\n const snappedY = g.snapToGrid(point.y - source.y, grid.y) + source.y\n\n return new g.Point(snappedX, snappedY)\n }\n\n // round the point to opt.precision\n function round(point, opt) {\n\n if (!point) return point\n\n return point.round(opt.precision)\n }\n\n // return a string representing the point\n // string is rounded to nearest int in both dimensions\n function getKey(point) {\n\n return point.clone().round().toString()\n }\n\n // return a normalized vector from given point\n // used to determine the direction of a difference of two points\n function normalizePoint(point) {\n\n return new g.Point(\n point.x === 0 ? 0 : Math.abs(point.x) / point.x,\n point.y === 0 ? 0 : Math.abs(point.y) / point.y\n )\n }\n\n // PATHFINDING //\n\n // reconstructs a route by concatenating points with their parents\n function reconstructRoute(parents, points, tailPoint, from, to, opt) {\n\n const route = []\n\n let prevDiff = normalizePoint(to.difference(tailPoint))\n\n let currentKey = getKey(tailPoint)\n let parent = parents[currentKey]\n\n let point\n while (parent) {\n\n point = round(points[currentKey], opt)\n\n const diff = normalizePoint(point.difference(round(parent.clone(), opt)))\n if (!diff.equals(prevDiff)) {\n route.unshift(point)\n prevDiff = diff\n }\n\n currentKey = getKey(parent)\n parent = parents[currentKey]\n }\n\n const leadPoint = round(points[currentKey], opt)\n\n const fromDiff = normalizePoint(leadPoint.difference(from))\n if (!fromDiff.equals(prevDiff)) {\n route.unshift(leadPoint)\n }\n\n return route\n }\n\n // heuristic method to determine the distance between two points\n function estimateCost(from, endPoints) {\n\n let min = Infinity\n\n for (let i = 0, len = endPoints.length; i < len; i++) {\n const cost = from.manhattanDistance(endPoints[i])\n if (cost < min) min = cost\n }\n\n return min\n }\n\n // find points around the bbox taking given directions into account\n // lines are drawn from anchor in given directions, intersections recorded\n // if anchor is outside bbox, only those directions that intersect get a rect point\n // the anchor itself is returned as rect point (representing some directions)\n // (since those directions are unobstructed by the bbox)\n function getRectPoints(anchor, bbox, directionList, grid, opt) {\n\n const {directionMap} = opt\n\n const snappedAnchor = round(snapToGrid(anchor, grid), opt)\n const snappedCenter = round(snapToGrid(bbox.center(), grid), opt)\n const anchorCenterVector = snappedAnchor.difference(snappedCenter)\n\n const keys = util.isObject(directionMap) ? Object.keys(directionMap) : []\n const dirList = util.toArray(directionList)\n const rectPoints = keys.reduce(function(res, key) {\n\n if (dirList.includes(key)) {\n const direction = directionMap[key]\n\n // create a line that is guaranteed to intersect the bbox if bbox is in the direction\n // even if anchor lies outside of bbox\n const endpoint = new g.Point(\n snappedAnchor.x + direction.x * (Math.abs(anchorCenterVector.x) + bbox.width),\n snappedAnchor.y + direction.y * (Math.abs(anchorCenterVector.y) + bbox.height)\n )\n const intersectionLine = new g.Line(anchor, endpoint)\n\n // get the farther intersection, in case there are two\n // (that happens if anchor lies next to bbox)\n const intersections = intersectionLine.intersect(bbox) || []\n const numIntersections = intersections.length\n let farthestIntersectionDistance\n let farthestIntersection = null\n for (let i = 0; i < numIntersections; i++) {\n const currentIntersection = intersections[i]\n const distance = snappedAnchor.squaredDistance(currentIntersection)\n if (farthestIntersectionDistance === undefined || (distance > farthestIntersectionDistance)) {\n farthestIntersectionDistance = distance\n farthestIntersection = snapToGrid(currentIntersection, grid)\n }\n }\n const point = round(farthestIntersection, opt)\n\n // if an intersection was found in this direction, it is our rectPoint\n if (point) {\n // if the rectPoint lies inside the bbox, offset it by one more step\n if (bbox.containsPoint(point)) {\n round(point.offset(direction.x * grid.x, direction.y * grid.y), opt)\n }\n\n // then add the point to the result array\n res.push(point)\n }\n }\n\n return res\n }, [])\n\n // if anchor lies outside of bbox, add it to the array of points\n if (!bbox.containsPoint(snappedAnchor)) rectPoints.push(snappedAnchor)\n\n return rectPoints\n }\n\n // finds the route between two points/rectangles (`from`, `to`) implementing A* algorithm\n // rectangles get rect points assigned by getRectPoints()\n function findRoute(from, to, map, opt) {\n\n // Get grid for this route.\n\n let sourceAnchor; let targetAnchor\n\n if (from instanceof g.Rect) { // `from` is sourceBBox\n sourceAnchor = getSourceAnchor(this, opt).clone()\n } else {\n sourceAnchor = from.clone()\n }\n\n if (to instanceof g.Rect) { // `to` is targetBBox\n targetAnchor = getTargetAnchor(this, opt).clone()\n } else {\n targetAnchor = to.clone()\n }\n\n const grid = getGrid(opt.step, sourceAnchor, targetAnchor)\n\n // Get pathfinding points.\n\n let start; let end\n let startPoints; let endPoints\n\n // set of points we start pathfinding from\n if (from instanceof g.Rect) { // `from` is sourceBBox\n start = round(snapToGrid(sourceAnchor, grid), opt)\n startPoints = getRectPoints(start, from, opt.startDirections, grid, opt)\n\n } else {\n start = round(snapToGrid(sourceAnchor, grid), opt)\n startPoints = [start]\n }\n\n // set of points we want the pathfinding to finish at\n if (to instanceof g.Rect) { // `to` is targetBBox\n end = round(snapToGrid(targetAnchor, grid), opt)\n endPoints = getRectPoints(targetAnchor, to, opt.endDirections, grid, opt)\n\n } else {\n end = round(snapToGrid(targetAnchor, grid), opt)\n endPoints = [end]\n }\n\n // take into account only accessible rect points (those not under obstacles)\n startPoints = startPoints.filter(map.isPointAccessible, map)\n endPoints = endPoints.filter(map.isPointAccessible, map)\n\n // Check that there is an accessible route point on both sides.\n // Otherwise, use fallbackRoute().\n if (startPoints.length > 0 && endPoints.length > 0) {\n\n // The set of tentative points to be evaluated, initially containing the start points.\n // Rounded to nearest integer for simplicity.\n const openSet = new SortedSet()\n // Keeps reference to actual points for given elements of the open set.\n const points = {}\n // Keeps reference to a point that is immediate predecessor of given element.\n const parents = {}\n // Cost from start to a point along best known path.\n const costs = {}\n\n for (var i = 0, n = startPoints.length; i < n; i++) {\n const point = startPoints[i]\n\n const key = getKey(point)\n openSet.add(key, estimateCost(point, endPoints))\n points[key] = point\n costs[key] = 0\n }\n\n const previousRouteDirectionAngle = opt.previousDirectionAngle // undefined for first route\n const isPathBeginning = (previousRouteDirectionAngle === undefined)\n\n // directions\n let direction; let directionChange\n const {directions} = opt\n getGridOffsets(directions, grid, opt)\n\n const numDirections = directions.length\n\n const endPointsKeys = util.toArray(endPoints).reduce(function(res, endPoint) {\n\n const key = getKey(endPoint)\n res.push(key)\n return res\n }, [])\n\n // main route finding loop\n let loopsRemaining = opt.maximumLoops\n while (!openSet.isEmpty() && loopsRemaining > 0) {\n\n // remove current from the open list\n const currentKey = openSet.pop()\n const currentPoint = points[currentKey]\n const currentParent = parents[currentKey]\n const currentCost = costs[currentKey]\n\n const isRouteBeginning = (currentParent === undefined) // undefined for route starts\n const isStart = currentPoint.equals(start) // (is source anchor or `from` point) = can leave in any direction\n\n var previousDirectionAngle\n if (!isRouteBeginning) previousDirectionAngle = getDirectionAngle(currentParent, currentPoint, numDirections, grid, opt) // a vertex on the route\n else if (!isPathBeginning) previousDirectionAngle = previousRouteDirectionAngle // beginning of route on the path\n else if (!isStart) previousDirectionAngle = getDirectionAngle(start, currentPoint, numDirections, grid, opt) // beginning of path, start rect point\n else previousDirectionAngle = null // beginning of path, source anchor or `from` point\n\n // check if we reached any endpoint\n if (endPointsKeys.indexOf(currentKey) >= 0) {\n opt.previousDirectionAngle = previousDirectionAngle\n return reconstructRoute(parents, points, currentPoint, start, end, opt)\n }\n\n // go over all possible directions and find neighbors\n for (i = 0; i < numDirections; i++) {\n direction = directions[i]\n\n const directionAngle = direction.angle\n directionChange = getDirectionChange(previousDirectionAngle, directionAngle)\n\n // if the direction changed rapidly, don't use this point\n // any direction is allowed for starting points\n if (!(isPathBeginning && isStart) && directionChange > opt.maxAllowedDirectionChange) continue\n\n const neighborPoint = currentPoint.clone().offset(direction.gridOffsetX, direction.gridOffsetY)\n const neighborKey = getKey(neighborPoint)\n\n // Closed points from the openSet were already evaluated.\n if (openSet.isClose(neighborKey) || !map.isPointAccessible(neighborPoint)) continue\n\n // We can only enter end points at an acceptable angle.\n if (endPointsKeys.indexOf(neighborKey) >= 0) { // neighbor is an end point\n round(neighborPoint, opt) // remove rounding errors\n\n const isNeighborEnd = neighborPoint.equals(end) // (is target anchor or `to` point) = can be entered in any direction\n\n if (!isNeighborEnd) {\n const endDirectionAngle = getDirectionAngle(neighborPoint, end, numDirections, grid, opt)\n const endDirectionChange = getDirectionChange(directionAngle, endDirectionAngle)\n\n if (endDirectionChange > opt.maxAllowedDirectionChange) continue\n }\n }\n\n // The current direction is ok.\n\n const neighborCost = direction.cost\n const neighborPenalty = isStart ? 0 : opt.penalties[directionChange] // no penalties for start point\n const costFromStart = currentCost + neighborCost + neighborPenalty\n\n if (!openSet.isOpen(neighborKey) || (costFromStart < costs[neighborKey])) {\n // neighbor point has not been processed yet\n // or the cost of the path from start is lower than previously calculated\n\n points[neighborKey] = neighborPoint\n parents[neighborKey] = currentPoint\n costs[neighborKey] = costFromStart\n openSet.add(neighborKey, costFromStart + estimateCost(neighborPoint, endPoints))\n }\n }\n\n loopsRemaining--\n }\n }\n\n // no route found (`to` point either wasn't accessible or finding route took\n // way too much calculation)\n return opt.fallbackRoute.call(this, start, end, opt)\n }\n\n // resolve some of the options\n function resolveOptions(opt) {\n\n opt.directions = util.result(opt, 'directions')\n opt.penalties = util.result(opt, 'penalties')\n opt.paddingBox = util.result(opt, 'paddingBox')\n\n util.toArray(opt.directions).forEach(function(direction) {\n\n const point1 = new g.Point(0, 0)\n const point2 = new g.Point(direction.offsetX, direction.offsetY)\n\n direction.angle = g.normalizeAngle(point1.theta(point2))\n })\n }\n\n // initialization of the route finding\n function router(vertices, opt, linkView) {\n\n resolveOptions(opt)\n\n // enable/disable linkView perpendicular option\n linkView.options.perpendicular = !!opt.perpendicular\n\n const sourceBBox = getSourceBBox(linkView, opt)\n const targetBBox = getTargetBBox(linkView, opt)\n\n const sourceAnchor = getSourceAnchor(linkView, opt)\n //var targetAnchor = getTargetAnchor(linkView, opt);\n\n // pathfinding\n const map = (new ObstacleMap(opt)).build(linkView.paper.model, linkView.model)\n const oldVertices = util.toArray(vertices).map(g.Point)\n const newVertices = []\n let tailPoint = sourceAnchor // the origin of first route's grid, does not need snapping\n\n // find a route by concatenating all partial routes (routes need to pass through vertices)\n // source -> vertex[1] -> ... -> vertex[n] -> target\n for (let i = 0, len = oldVertices.length; i <= len; i++) {\n\n let partialRoute = null\n\n const from = to || sourceBBox\n var to = oldVertices[i]\n\n if (!to) {\n // this is the last iteration\n // we ran through all vertices in oldVertices\n // 'to' is not a vertex.\n\n to = targetBBox\n\n // If the target is a point (i.e. it's not an element), we\n // should use dragging route instead of main routing method if it has been provided.\n const isEndingAtPoint = !linkView.model.get('source').id || !linkView.model.get('target').id\n\n if (isEndingAtPoint && util.isFunction(opt.draggingRoute)) {\n // Make sure we are passing points only (not rects).\n const dragFrom = (from === sourceBBox) ? sourceAnchor : from\n const dragTo = to.origin()\n\n partialRoute = opt.draggingRoute.call(linkView, dragFrom, dragTo, opt)\n }\n }\n\n // if partial route has not been calculated yet use the main routing method to find one\n partialRoute = partialRoute || findRoute.call(linkView, from, to, map, opt)\n\n if (partialRoute === null) { // the partial route cannot be found\n return opt.fallbackRouter(vertices, opt, linkView)\n }\n\n const leadPoint = partialRoute[0]\n\n // remove the first point if the previous partial route had the same point as last\n if (leadPoint && leadPoint.equals(tailPoint)) partialRoute.shift()\n\n // save tailPoint for next iteration\n tailPoint = partialRoute[partialRoute.length - 1] || tailPoint\n\n Array.prototype.push.apply(newVertices, partialRoute)\n }\n\n return newVertices\n }\n\n // public function\n return function(vertices, opt, linkView) {\n\n return router(vertices, util.assign({}, config, opt), linkView)\n }\n\n})(g, joint, joint.util)\n\njoint.routers.metro = (function(util) {\n\n const config = {\n\n maxAllowedDirectionChange: 45,\n\n // cost of a diagonal step\n diagonalCost: function() {\n\n const {step} = this\n return Math.ceil(Math.sqrt(step * step << 1))\n },\n\n // an array of directions to find next points on the route\n // different from start/end directions\n directions: function() {\n\n const {step} = this\n const cost = this.cost()\n const diagonalCost = this.diagonalCost()\n\n return [\n { offsetX: step , offsetY: 0 , cost: cost },\n { offsetX: step , offsetY: step , cost: diagonalCost },\n { offsetX: 0 , offsetY: step , cost: cost },\n { offsetX: -step , offsetY: step , cost: diagonalCost },\n { offsetX: -step , offsetY: 0 , cost: cost },\n { offsetX: -step , offsetY: -step , cost: diagonalCost },\n { offsetX: 0 , offsetY: -step , cost: cost },\n { offsetX: step , offsetY: -step , cost: diagonalCost }\n ]\n },\n\n // a simple route used in situations when main routing method fails\n // (exceed max number of loop iterations, inaccessible)\n fallbackRoute: function(from, to, opt) {\n\n // Find a route which breaks by 45 degrees ignoring all obstacles.\n\n const theta = from.theta(to)\n\n const route = []\n\n let a = { x: to.x, y: from.y }\n let b = { x: from.x, y: to.y }\n\n if (theta % 180 > 90) {\n const t = a\n a = b\n b = t\n }\n\n const p1 = (theta % 90) < 45 ? a : b\n const l1 = new g.Line(from, p1)\n\n const alpha = 90 * Math.ceil(theta / 90)\n\n const p2 = g.Point.fromPolar(l1.squaredLength(), g.toRad(alpha + 135), p1)\n const l2 = new g.Line(to, p2)\n\n const intersectionPoint = l1.intersection(l2)\n const point = intersectionPoint || to\n\n const directionFrom = intersectionPoint ? point : from\n\n const quadrant = 360 / opt.directions.length\n const angleTheta = directionFrom.theta(to)\n const normalizedAngle = g.normalizeAngle(angleTheta + (quadrant / 2))\n const directionAngle = quadrant * Math.floor(normalizedAngle / quadrant)\n\n opt.previousDirectionAngle = directionAngle\n\n if (point) route.push(point.round())\n route.push(to)\n\n return route\n }\n }\n\n // public function\n return function(vertices, opt, linkView) {\n\n if (!util.isFunction(joint.routers.manhattan)) {\n throw new Error('Metro requires the manhattan router.')\n }\n\n return joint.routers.manhattan(vertices, util.assign({}, config, opt), linkView)\n }\n\n})(joint.util)\n\n// Does not make any changes to vertices.\n// Returns the arguments that are passed to it, unchanged.\njoint.routers.normal = function(vertices, opt, linkView) {\n\n return vertices\n}\n\n// Routes the link always to/from a certain side\n//\n// Arguments:\n// padding ... gap between the element and the first vertex. :: Default 40.\n// side ... 'left' | 'right' | 'top' | 'bottom' :: Default 'bottom'.\n//\njoint.routers.oneSide = function(vertices, opt, linkView) {\n\n const side = opt.side || 'bottom'\n const padding = opt.padding || 40\n\n // LinkView contains cached source an target bboxes.\n // Note that those are Geometry rectangle objects.\n const {sourceBBox} = linkView\n const {targetBBox} = linkView\n const sourcePoint = sourceBBox.center()\n const targetPoint = targetBBox.center()\n\n let coordinate; let dimension; let direction\n\n switch (side) {\n case 'bottom':\n direction = 1\n coordinate = 'y'\n dimension = 'height'\n break\n case 'top':\n direction = -1\n coordinate = 'y'\n dimension = 'height'\n break\n case 'left':\n direction = -1\n coordinate = 'x'\n dimension = 'width'\n break\n case 'right':\n direction = 1\n coordinate = 'x'\n dimension = 'width'\n break\n default:\n throw new Error('Router: invalid side')\n }\n\n // move the points from the center of the element to outside of it.\n sourcePoint[coordinate] += direction * (sourceBBox[dimension] / 2 + padding)\n targetPoint[coordinate] += direction * (targetBBox[dimension] / 2 + padding)\n\n // make link orthogonal (at least the first and last vertex).\n if (direction * (sourcePoint[coordinate] - targetPoint[coordinate]) > 0) {\n targetPoint[coordinate] = sourcePoint[coordinate]\n } else {\n sourcePoint[coordinate] = targetPoint[coordinate]\n }\n\n return [sourcePoint].concat(vertices, targetPoint)\n}\n\njoint.routers.orthogonal = (function(util) {\n\n // bearing -> opposite bearing\n const opposites = {\n N: 'S',\n S: 'N',\n E: 'W',\n W: 'E'\n }\n\n // bearing -> radians\n const radians = {\n N: -Math.PI / 2 * 3,\n S: -Math.PI / 2,\n E: 0,\n W: Math.PI\n }\n\n // HELPERS //\n\n // returns a point `p` where lines p,p1 and p,p2 are perpendicular and p is not contained\n // in the given box\n function freeJoin(p1, p2, bbox) {\n\n let p = new g.Point(p1.x, p2.y)\n if (bbox.containsPoint(p)) p = new g.Point(p2.x, p1.y)\n // kept for reference\n // if (bbox.containsPoint(p)) p = null;\n\n return p\n }\n\n // returns either width or height of a bbox based on the given bearing\n function getBBoxSize(bbox, bearing) {\n\n return bbox[(bearing === 'W' || bearing === 'E') ? 'width' : 'height']\n }\n\n // simple bearing method (calculates only orthogonal cardinals)\n function getBearing(from, to) {\n\n if (from.x === to.x) return (from.y > to.y) ? 'N' : 'S'\n if (from.y === to.y) return (from.x > to.x) ? 'W' : 'E'\n return null\n }\n\n // transform point to a rect\n function getPointBox(p) {\n\n return new g.Rect(p.x, p.y, 0, 0)\n }\n\n // return source bbox\n function getSourceBBox(linkView, opt) {\n\n const padding = (opt && opt.elementPadding) || 20\n return linkView.sourceBBox.clone().inflate(padding)\n }\n\n // return target bbox\n function getTargetBBox(linkView, opt) {\n\n const padding = (opt && opt.elementPadding) || 20\n return linkView.targetBBox.clone().inflate(padding)\n }\n\n // return source anchor\n function getSourceAnchor(linkView, opt) {\n\n if (linkView.sourceAnchor) return linkView.sourceAnchor\n\n // fallback: center of bbox\n const sourceBBox = getSourceBBox(linkView, opt)\n return sourceBBox.center()\n }\n\n // return target anchor\n function getTargetAnchor(linkView, opt) {\n\n if (linkView.targetAnchor) return linkView.targetAnchor\n\n // fallback: center of bbox\n const targetBBox = getTargetBBox(linkView, opt)\n return targetBBox.center() // default\n }\n\n // PARTIAL ROUTERS //\n\n function vertexVertex(from, to, bearing) {\n\n const p1 = new g.Point(from.x, to.y)\n const p2 = new g.Point(to.x, from.y)\n const d1 = getBearing(from, p1)\n const d2 = getBearing(from, p2)\n const opposite = opposites[bearing]\n\n const p = (d1 === bearing || (d1 !== opposite && (d2 === opposite || d2 !== bearing))) ? p1 : p2\n\n return { points: [p], direction: getBearing(p, to) }\n }\n\n function elementVertex(from, to, fromBBox) {\n\n const p = freeJoin(from, to, fromBBox)\n\n return { points: [p], direction: getBearing(p, to) }\n }\n\n function vertexElement(from, to, toBBox, bearing) {\n\n const route = {}\n\n const points = [new g.Point(from.x, to.y), new g.Point(to.x, from.y)]\n const freePoints = points.filter(function(pt) { return !toBBox.containsPoint(pt) })\n const freeBearingPoints = freePoints.filter(function(pt) { return getBearing(pt, from) !== bearing })\n\n let p\n\n if (freeBearingPoints.length > 0) {\n // Try to pick a point which bears the same direction as the previous segment.\n\n p = freeBearingPoints.filter(function(pt) { return getBearing(from, pt) === bearing }).pop()\n p = p || freeBearingPoints[0]\n\n route.points = [p]\n route.direction = getBearing(p, to)\n\n } else {\n // Here we found only points which are either contained in the element or they would create\n // a link segment going in opposite direction from the previous one.\n // We take the point inside element and move it outside the element in the direction the\n // route is going. Now we can join this point with the current end (using freeJoin).\n\n p = util.difference(points, freePoints)[0]\n\n const p2 = (new g.Point(to)).move(p, -getBBoxSize(toBBox, bearing) / 2)\n const p1 = freeJoin(p2, from, toBBox)\n\n route.points = [p1, p2]\n route.direction = getBearing(p2, to)\n }\n\n return route\n }\n\n function elementElement(from, to, fromBBox, toBBox) {\n\n let route = elementVertex(to, from, toBBox)\n const p1 = route.points[0]\n\n if (fromBBox.containsPoint(p1)) {\n\n route = elementVertex(from, to, fromBBox)\n const p2 = route.points[0]\n\n if (toBBox.containsPoint(p2)) {\n\n const fromBorder = (new g.Point(from)).move(p2, -getBBoxSize(fromBBox, getBearing(from, p2)) / 2)\n const toBorder = (new g.Point(to)).move(p1, -getBBoxSize(toBBox, getBearing(to, p1)) / 2)\n const mid = (new g.Line(fromBorder, toBorder)).midpoint()\n\n const startRoute = elementVertex(from, mid, fromBBox)\n const endRoute = vertexVertex(mid, to, startRoute.direction)\n\n route.points = [startRoute.points[0], endRoute.points[0]]\n route.direction = endRoute.direction\n }\n }\n\n return route\n }\n\n // Finds route for situations where one element is inside the other.\n // Typically the route is directed outside the outer element first and\n // then back towards the inner element.\n function insideElement(from, to, fromBBox, toBBox, bearing) {\n\n const route = {}\n const boundary = fromBBox.union(toBBox).inflate(1)\n\n // start from the point which is closer to the boundary\n const reversed = boundary.center().distance(to) > boundary.center().distance(from)\n const start = reversed ? to : from\n const end = reversed ? from : to\n\n let p1; let p2; let p3\n\n if (bearing) {\n // Points on circle with radius equals 'W + H` are always outside the rectangle\n // with width W and height H if the center of that circle is the center of that rectangle.\n p1 = g.Point.fromPolar(boundary.width + boundary.height, radians[bearing], start)\n p1 = boundary.pointNearestToPoint(p1).move(p1, -1)\n\n } else {\n p1 = boundary.pointNearestToPoint(start).move(start, 1)\n }\n\n p2 = freeJoin(p1, end, boundary)\n\n if (p1.round().equals(p2.round())) {\n p2 = g.Point.fromPolar(boundary.width + boundary.height, g.toRad(p1.theta(start)) + Math.PI / 2, end)\n p2 = boundary.pointNearestToPoint(p2).move(end, 1).round()\n p3 = freeJoin(p1, p2, boundary)\n route.points = reversed ? [p2, p3, p1] : [p1, p3, p2]\n\n } else {\n route.points = reversed ? [p2, p1] : [p1, p2]\n }\n\n route.direction = reversed ? getBearing(p1, to) : getBearing(p2, to)\n\n return route\n }\n\n // MAIN ROUTER //\n\n // Return points through which a connection needs to be drawn in order to obtain an orthogonal link\n // routing from source to target going through `vertices`.\n function router(vertices, opt, linkView) {\n\n const padding = opt.elementPadding || 20\n\n let sourceBBox = getSourceBBox(linkView, opt)\n let targetBBox = getTargetBBox(linkView, opt)\n\n const sourceAnchor = getSourceAnchor(linkView, opt)\n const targetAnchor = getTargetAnchor(linkView, opt)\n\n // if anchor lies outside of bbox, the bbox expands to include it\n sourceBBox = sourceBBox.union(getPointBox(sourceAnchor))\n targetBBox = targetBBox.union(getPointBox(targetAnchor))\n\n vertices = util.toArray(vertices).map(g.Point)\n vertices.unshift(sourceAnchor)\n vertices.push(targetAnchor)\n\n let bearing // bearing of previous route segment\n\n const orthogonalVertices = [] // the array of found orthogonal vertices to be returned\n for (let i = 0, max = vertices.length - 1; i < max; i++) {\n\n let route = null\n\n const from = vertices[i]\n const to = vertices[i + 1]\n\n const isOrthogonal = !!getBearing(from, to)\n\n if (i === 0) { // source\n\n if (i + 1 === max) { // route source -> target\n\n // Expand one of the elements by 1px to detect situations when the two\n // elements are positioned next to each other with no gap in between.\n if (sourceBBox.intersect(targetBBox.clone().inflate(1))) {\n route = insideElement(from, to, sourceBBox, targetBBox)\n\n } else if (!isOrthogonal) {\n route = elementElement(from, to, sourceBBox, targetBBox)\n }\n\n } else { // route source -> vertex\n\n if (sourceBBox.containsPoint(to)) {\n route = insideElement(from, to, sourceBBox, getPointBox(to).inflate(padding))\n\n } else if (!isOrthogonal) {\n route = elementVertex(from, to, sourceBBox)\n }\n }\n\n } else if (i + 1 === max) { // route vertex -> target\n\n // prevent overlaps with previous line segment\n const isOrthogonalLoop = isOrthogonal && getBearing(to, from) === bearing\n\n if (targetBBox.containsPoint(from) || isOrthogonalLoop) {\n route = insideElement(from, to, getPointBox(from).inflate(padding), targetBBox, bearing)\n\n } else if (!isOrthogonal) {\n route = vertexElement(from, to, targetBBox, bearing)\n }\n\n } else if (!isOrthogonal) { // route vertex -> vertex\n route = vertexVertex(from, to, bearing)\n }\n\n // applicable to all routes:\n\n // set bearing for next iteration\n if (route) {\n Array.prototype.push.apply(orthogonalVertices, route.points)\n bearing = route.direction\n\n } else {\n // orthogonal route and not looped\n bearing = getBearing(from, to)\n }\n\n // push `to` point to identified orthogonal vertices array\n if (i + 1 < max) {\n orthogonalVertices.push(to)\n }\n }\n\n return orthogonalVertices\n }\n\n return router\n\n})(joint.util)\n\njoint.connectors.normal = function(sourcePoint, targetPoint, route, opt) {\n\n const raw = opt && opt.raw\n const points = [sourcePoint].concat(route).concat([targetPoint])\n\n const polyline = new g.Polyline(points)\n const path = new g.Path(polyline)\n\n return (raw) ? path : path.serialize()\n}\n\njoint.connectors.rounded = function(sourcePoint, targetPoint, route, opt) {\n\n opt || (opt = {})\n\n const offset = opt.radius || 10\n const {raw} = opt\n const path = new g.Path()\n let segment\n\n segment = g.Path.createSegment('M', sourcePoint)\n path.appendSegment(segment)\n\n const _13 = 1 / 3\n const _23 = 2 / 3\n\n let curr\n let prev; let next\n let prevDistance; let nextDistance\n let startMove; let endMove\n let roundedStart; let roundedEnd\n let control1; let control2\n\n for (let index = 0, n = route.length; index < n; index++) {\n\n curr = new g.Point(route[index])\n\n prev = route[index - 1] || sourcePoint\n next = route[index + 1] || targetPoint\n\n prevDistance = nextDistance || (curr.distance(prev) / 2)\n nextDistance = curr.distance(next) / 2\n\n startMove = -Math.min(offset, prevDistance)\n endMove = -Math.min(offset, nextDistance)\n\n roundedStart = curr.clone().move(prev, startMove).round()\n roundedEnd = curr.clone().move(next, endMove).round()\n\n control1 = new g.Point((_13 * roundedStart.x) + (_23 * curr.x), (_23 * curr.y) + (_13 * roundedStart.y))\n control2 = new g.Point((_13 * roundedEnd.x) + (_23 * curr.x), (_23 * curr.y) + (_13 * roundedEnd.y))\n\n segment = g.Path.createSegment('L', roundedStart)\n path.appendSegment(segment)\n\n segment = g.Path.createSegment('C', control1, control2, roundedEnd)\n path.appendSegment(segment)\n }\n\n segment = g.Path.createSegment('L', targetPoint)\n path.appendSegment(segment)\n\n return (raw) ? path : path.serialize()\n}\n\njoint.connectors.smooth = function(sourcePoint, targetPoint, route, opt) {\n\n const raw = opt && opt.raw\n let path\n\n if (route && route.length !== 0) {\n\n const points = [sourcePoint].concat(route).concat([targetPoint])\n const curves = g.Curve.throughPoints(points)\n\n path = new g.Path(curves)\n\n } else {\n // if we have no route, use a default cubic bezier curve\n // cubic bezier requires two control points\n // the control points have `x` midway between source and target\n // this produces an S-like curve\n\n path = new g.Path()\n\n let segment\n\n segment = g.Path.createSegment('M', sourcePoint)\n path.appendSegment(segment)\n\n if ((Math.abs(sourcePoint.x - targetPoint.x)) >= (Math.abs(sourcePoint.y - targetPoint.y))) {\n const controlPointX = (sourcePoint.x + targetPoint.x) / 2\n\n segment = g.Path.createSegment('C', controlPointX, sourcePoint.y, controlPointX, targetPoint.y, targetPoint.x, targetPoint.y)\n path.appendSegment(segment)\n\n } else {\n const controlPointY = (sourcePoint.y + targetPoint.y) / 2\n\n segment = g.Path.createSegment('C', sourcePoint.x, controlPointY, targetPoint.x, controlPointY, targetPoint.x, targetPoint.y)\n path.appendSegment(segment)\n\n }\n }\n\n return (raw) ? path : path.serialize()\n}\n\njoint.connectors.jumpover = (function(g, util) {\n\n // default size of jump if not specified in options\n const JUMP_SIZE = 5\n\n // available jump types\n // first one taken as default\n const JUMP_TYPES = ['arc', 'gap', 'cubic']\n\n // takes care of math. error for case when jump is too close to end of line\n const CLOSE_PROXIMITY_PADDING = 1\n\n // list of connector types not to jump over.\n const IGNORED_CONNECTORS = ['smooth']\n\n /**\n * Transform start/end and route into series of lines\n * @param {g.point} sourcePoint start point\n * @param {g.point} targetPoint end point\n * @param {g.point[]} route optional list of route\n * @return {g.line[]} [description]\n */\n function createLines(sourcePoint, targetPoint, route) {\n // make a flattened array of all points\n const points = [].concat(sourcePoint, route, targetPoint)\n return points.reduce(function(resultLines, point, idx) {\n // if there is a next point, make a line with it\n const nextPoint = points[idx + 1]\n if (nextPoint != null) {\n resultLines[idx] = g.line(point, nextPoint)\n }\n return resultLines\n }, [])\n }\n\n function setupUpdating(jumpOverLinkView) {\n let updateList = jumpOverLinkView.paper._jumpOverUpdateList\n\n // first time setup for this paper\n if (updateList == null) {\n updateList = jumpOverLinkView.paper._jumpOverUpdateList = []\n jumpOverLinkView.paper.on('cell:pointerup', updateJumpOver)\n jumpOverLinkView.paper.model.on('reset', function() {\n updateList = jumpOverLinkView.paper._jumpOverUpdateList = []\n })\n }\n\n // add this link to a list so it can be updated when some other link is updated\n if (updateList.indexOf(jumpOverLinkView) < 0) {\n updateList.push(jumpOverLinkView)\n\n // watch for change of connector type or removal of link itself\n // to remove the link from a list of jump over connectors\n jumpOverLinkView.listenToOnce(jumpOverLinkView.model, 'change:connector remove', function() {\n updateList.splice(updateList.indexOf(jumpOverLinkView), 1)\n })\n }\n }\n\n /**\n * Handler for a batch:stop event to force\n * update of all registered links with jump over connector\n * @param {object} batchEvent optional object with info about batch\n */\n function updateJumpOver() {\n const updateList = this._jumpOverUpdateList\n for (let i = 0; i < updateList.length; i++) {\n updateList[i].update()\n }\n }\n\n /**\n * Utility function to collect all intersection poinst of a single\n * line against group of other lines.\n * @param {g.line} line where to find points\n * @param {g.line[]} crossCheckLines lines to cross\n * @return {g.point[]} list of intersection points\n */\n function findLineIntersections(line, crossCheckLines) {\n return util.toArray(crossCheckLines).reduce(function(res, crossCheckLine) {\n const intersection = line.intersection(crossCheckLine)\n if (intersection) {\n res.push(intersection)\n }\n return res\n }, [])\n }\n\n /**\n * Sorting function for list of points by their distance.\n * @param {g.point} p1 first point\n * @param {g.point} p2 second point\n * @return {number} squared distance between points\n */\n function sortPoints(p1, p2) {\n return g.line(p1, p2).squaredLength()\n }\n\n /**\n * Split input line into multiple based on intersection points.\n * @param {g.line} line input line to split\n * @param {g.point[]} intersections poinst where to split the line\n * @param {number} jumpSize the size of jump arc (length empty spot on a line)\n * @return {g.line[]} list of lines being split\n */\n function createJumps(line, intersections, jumpSize) {\n return intersections.reduce(function(resultLines, point, idx) {\n // skipping points that were merged with the previous line\n // to make bigger arc over multiple lines that are close to each other\n if (point.skip === true) {\n return resultLines\n }\n\n // always grab the last line from buffer and modify it\n const lastLine = resultLines.pop() || line\n\n // calculate start and end of jump by moving by a given size of jump\n const jumpStart = g.point(point).move(lastLine.start, -(jumpSize))\n let jumpEnd = g.point(point).move(lastLine.start, +(jumpSize))\n\n // now try to look at the next intersection point\n const nextPoint = intersections[idx + 1]\n if (nextPoint != null) {\n const distance = jumpEnd.distance(nextPoint)\n if (distance <= jumpSize) {\n // next point is close enough, move the jump end by this\n // difference and mark the next point to be skipped\n jumpEnd = nextPoint.move(lastLine.start, distance)\n nextPoint.skip = true\n }\n } else {\n // this block is inside of `else` as an optimization so the distance is\n // not calculated when we know there are no other intersection points\n const endDistance = jumpStart.distance(lastLine.end)\n // if the end is too close to possible jump, draw remaining line instead of a jump\n if (endDistance < jumpSize * 2 + CLOSE_PROXIMITY_PADDING) {\n resultLines.push(lastLine)\n return resultLines\n }\n }\n\n const startDistance = jumpEnd.distance(lastLine.start)\n if (startDistance < jumpSize * 2 + CLOSE_PROXIMITY_PADDING) {\n // if the start of line is too close to jump, draw that line instead of a jump\n resultLines.push(lastLine)\n return resultLines\n }\n\n // finally create a jump line\n const jumpLine = g.line(jumpStart, jumpEnd)\n // it's just simple line but with a `isJump` property\n jumpLine.isJump = true\n\n resultLines.push(\n g.line(lastLine.start, jumpStart),\n jumpLine,\n g.line(jumpEnd, lastLine.end)\n )\n return resultLines\n }, [])\n }\n\n /**\n * Assemble `D` attribute of a SVG path by iterating given lines.\n * @param {g.line[]} lines source lines to use\n * @param {number} jumpSize the size of jump arc (length empty spot on a line)\n * @return {string}\n */\n function buildPath(lines, jumpSize, jumpType) {\n\n const path = new g.Path()\n let segment\n\n // first move to the start of a first line\n segment = g.Path.createSegment('M', lines[0].start)\n path.appendSegment(segment)\n\n // make a paths from lines\n joint.util.toArray(lines).forEach(function(line, index) {\n\n if (line.isJump) {\n let angle; let diff\n\n let control1; let control2\n\n if (jumpType === 'arc') { // approximates semicircle with 2 curves\n angle = -90\n // determine rotation of arc based on difference between points\n diff = line.start.difference(line.end)\n // make sure the arc always points up (or right)\n var xAxisRotate = Number((diff.x < 0) || (diff.x === 0 && diff.y < 0))\n if (xAxisRotate) angle += 180\n\n const midpoint = line.midpoint()\n const centerLine = new g.Line(midpoint, line.end).rotate(midpoint, angle)\n\n let halfLine\n\n // first half\n halfLine = new g.Line(line.start, midpoint)\n\n control1 = halfLine.pointAt(2 / 3).rotate(line.start, angle)\n control2 = centerLine.pointAt(1 / 3).rotate(centerLine.end, -angle)\n\n segment = g.Path.createSegment('C', control1, control2, centerLine.end)\n path.appendSegment(segment)\n\n // second half\n halfLine = new g.Line(midpoint, line.end)\n\n control1 = centerLine.pointAt(1 / 3).rotate(centerLine.end, angle)\n control2 = halfLine.pointAt(1 / 3).rotate(line.end, -angle)\n\n segment = g.Path.createSegment('C', control1, control2, line.end)\n path.appendSegment(segment)\n\n } else if (jumpType === 'gap') {\n segment = g.Path.createSegment('M', line.end)\n path.appendSegment(segment)\n\n } else if (jumpType === 'cubic') { // approximates semicircle with 1 curve\n angle = line.start.theta(line.end)\n\n const xOffset = jumpSize * 0.6\n let yOffset = jumpSize * 1.35\n\n // determine rotation of arc based on difference between points\n diff = line.start.difference(line.end)\n // make sure the arc always points up (or right)\n xAxisRotate = Number((diff.x < 0) || (diff.x === 0 && diff.y < 0))\n if (xAxisRotate) yOffset *= -1\n\n control1 = g.Point(line.start.x + xOffset, line.start.y + yOffset).rotate(line.start, angle)\n control2 = g.Point(line.end.x - xOffset, line.end.y + yOffset).rotate(line.end, angle)\n\n segment = g.Path.createSegment('C', control1, control2, line.end)\n path.appendSegment(segment)\n }\n\n } else {\n segment = g.Path.createSegment('L', line.end)\n path.appendSegment(segment)\n }\n })\n\n return path\n }\n\n /**\n * Actual connector function that will be run on every update.\n * @param {g.point} sourcePoint start point of this link\n * @param {g.point} targetPoint end point of this link\n * @param {g.point[]} route of this link\n * @param {object} opt options\n * @property {number} size optional size of a jump arc\n * @return {string} created `D` attribute of SVG path\n */\n return function(sourcePoint, targetPoint, route, opt) { // eslint-disable-line max-params\n\n setupUpdating(this)\n\n const {raw} = opt\n const jumpSize = opt.size || JUMP_SIZE\n let jumpType = opt.jump && (`${ opt.jump}`).toLowerCase()\n const ignoreConnectors = opt.ignoreConnectors || IGNORED_CONNECTORS\n\n // grab the first jump type as a default if specified one is invalid\n if (JUMP_TYPES.indexOf(jumpType) === -1) {\n jumpType = JUMP_TYPES[0]\n }\n\n const {paper} = this\n const graph = paper.model\n const allLinks = graph.getLinks()\n\n // there is just one link, draw it directly\n if (allLinks.length === 1) {\n return buildPath(\n createLines(sourcePoint, targetPoint, route),\n jumpSize, jumpType\n )\n }\n\n const thisModel = this.model\n const thisIndex = allLinks.indexOf(thisModel)\n const defaultConnector = paper.options.defaultConnector || {}\n\n // not all links are meant to be jumped over.\n const links = allLinks.filter(function(link, idx) {\n\n const connector = link.get('connector') || defaultConnector\n\n // avoid jumping over links with connector type listed in `ignored connectors`.\n if (util.toArray(ignoreConnectors).includes(connector.name)) {\n return false\n }\n // filter out links that are above this one and have the same connector type\n // otherwise there would double hoops for each intersection\n if (idx > thisIndex) {\n return connector.name !== 'jumpover'\n }\n return true\n })\n\n // find views for all links\n const linkViews = links.map(function(link) {\n return paper.findViewByModel(link)\n })\n\n // create lines for this link\n const thisLines = createLines(\n sourcePoint,\n targetPoint,\n route\n )\n\n // create lines for all other links\n const linkLines = linkViews.map(function(linkView) {\n if (linkView == null) {\n return []\n }\n if (linkView === this) {\n return thisLines\n }\n return createLines(\n linkView.sourcePoint,\n linkView.targetPoint,\n linkView.route\n )\n }, this)\n\n // transform lines for this link by splitting with jump lines at\n // points of intersection with other links\n const jumpingLines = thisLines.reduce(function(resultLines, thisLine) {\n // iterate all links and grab the intersections with this line\n // these are then sorted by distance so the line can be split more easily\n\n const intersections = links.reduce(function(res, link, i) {\n // don't intersection with itself\n if (link !== thisModel) {\n\n const lineIntersections = findLineIntersections(thisLine, linkLines[i])\n res.push.apply(res, lineIntersections)\n }\n return res\n }, []).sort(function(a, b) {\n return sortPoints(thisLine.start, a) - sortPoints(thisLine.start, b)\n })\n\n if (intersections.length > 0) {\n // split the line based on found intersection points\n resultLines.push.apply(resultLines, createJumps(thisLine, intersections, jumpSize))\n } else {\n // without any intersection the line goes uninterrupted\n resultLines.push(thisLine)\n }\n return resultLines\n }, [])\n\n const path = buildPath(jumpingLines, jumpSize, jumpType)\n return (raw) ? path : path.serialize()\n }\n}(g, joint.util));\n\n(function(g, joint, util) {\n\n function portTransformAttrs(point, angle, opt) {\n\n const trans = point.toJSON()\n\n trans.angle = angle || 0\n\n return joint.util.defaults({}, opt, trans)\n }\n\n function lineLayout(ports, p1, p2) {\n return ports.map(function(port, index, ports) {\n const p = this.pointAt(((index + 0.5) / ports.length))\n // `dx`,`dy` per port offset option\n if (port.dx || port.dy) {\n p.offset(port.dx || 0, port.dy || 0)\n }\n\n return portTransformAttrs(p.round(), 0, port)\n }, g.line(p1, p2))\n }\n\n function ellipseLayout(ports, elBBox, startAngle, stepFn) {\n\n const center = elBBox.center()\n const ratio = elBBox.width / elBBox.height\n const p1 = elBBox.topMiddle()\n\n const ellipse = g.Ellipse.fromRect(elBBox)\n\n return ports.map(function(port, index, ports) {\n\n const angle = startAngle + stepFn(index, ports.length)\n const p2 = p1.clone()\n .rotate(center, -angle)\n .scale(ratio, 1, center)\n\n const theta = port.compensateRotation ? -ellipse.tangentTheta(p2) : 0\n\n // `dx`,`dy` per port offset option\n if (port.dx || port.dy) {\n p2.offset(port.dx || 0, port.dy || 0)\n }\n\n // `dr` delta radius option\n if (port.dr) {\n p2.move(center, port.dr)\n }\n\n return portTransformAttrs(p2.round(), theta, port)\n })\n }\n\n // Creates a point stored in arguments\n function argPoint(bbox, args) {\n\n let {x} = args\n if (util.isString(x)) {\n x = parseFloat(x) / 100 * bbox.width\n }\n\n let {y} = args\n if (util.isString(y)) {\n y = parseFloat(y) / 100 * bbox.height\n }\n\n return g.point(x || 0, y || 0)\n }\n\n joint.layout.Port = {\n\n /**\n * @param {Array} ports\n * @param {g.Rect} elBBox\n * @param {Object=} opt opt Group options\n * @returns {Array}\n */\n absolute: function(ports, elBBox, opt) {\n //TODO v.talas angle\n return ports.map(argPoint.bind(null, elBBox))\n },\n\n /**\n * @param {Array} ports\n * @param {g.Rect} elBBox\n * @param {Object=} opt opt Group options\n * @returns {Array}\n */\n fn: function(ports, elBBox, opt) {\n return opt.fn(ports, elBBox, opt)\n },\n\n /**\n * @param {Array} ports\n * @param {g.Rect} elBBox\n * @param {Object=} opt opt Group options\n * @returns {Array}\n */\n line: function(ports, elBBox, opt) {\n\n const start = argPoint(elBBox, opt.start || elBBox.origin())\n const end = argPoint(elBBox, opt.end || elBBox.corner())\n\n return lineLayout(ports, start, end)\n },\n\n /**\n * @param {Array} ports\n * @param {g.Rect} elBBox\n * @param {Object=} opt opt Group options\n * @returns {Array}\n */\n left: function(ports, elBBox, opt) {\n return lineLayout(ports, elBBox.origin(), elBBox.bottomLeft())\n },\n\n /**\n * @param {Array} ports\n * @param {g.Rect} elBBox\n * @param {Object=} opt opt Group options\n * @returns {Array}\n */\n right: function(ports, elBBox, opt) {\n return lineLayout(ports, elBBox.topRight(), elBBox.corner())\n },\n\n /**\n * @param {Array} ports\n * @param {g.Rect} elBBox\n * @param {Object=} opt opt Group options\n * @returns {Array}\n */\n top: function(ports, elBBox, opt) {\n return lineLayout(ports, elBBox.origin(), elBBox.topRight())\n },\n\n /**\n * @param {Array} ports\n * @param {g.Rect} elBBox\n * @param {Object=} opt opt Group options\n * @returns {Array}\n */\n bottom: function(ports, elBBox, opt) {\n return lineLayout(ports, elBBox.bottomLeft(), elBBox.corner())\n },\n\n /**\n * @param {Array} ports\n * @param {g.Rect} elBBox\n * @param {Object=} opt Group options\n * @returns {Array}\n */\n ellipseSpread: function(ports, elBBox, opt) {\n\n const startAngle = opt.startAngle || 0\n const stepAngle = opt.step || 360 / ports.length\n\n return ellipseLayout(ports, elBBox, startAngle, function(index) {\n return index * stepAngle\n })\n },\n\n /**\n * @param {Array} ports\n * @param {g.Rect} elBBox\n * @param {Object=} opt Group options\n * @returns {Array}\n */\n ellipse: function(ports, elBBox, opt) {\n\n const startAngle = opt.startAngle || 0\n const stepAngle = opt.step || 20\n\n return ellipseLayout(ports, elBBox, startAngle, function(index, count) {\n return (index + 0.5 - count / 2) * stepAngle\n })\n }\n }\n\n})(g, joint, joint.util);\n\n(function(g, joint, util) {\n\n function labelAttributes(opt1, opt2) {\n\n return util.defaultsDeep({}, opt1, opt2, {\n x: 0,\n y: 0,\n angle: 0,\n attrs: {\n '.': {\n y: '0',\n 'text-anchor': 'start'\n }\n }\n })\n\n }\n\n function outsideLayout(portPosition, elBBox, autoOrient, opt) {\n\n opt = util.defaults({}, opt, { offset: 15 })\n const angle = elBBox.center().theta(portPosition)\n const x = getBBoxAngles(elBBox)\n\n let tx; let ty; let y; let textAnchor\n const {offset} = opt\n let orientAngle = 0\n\n if (angle < x[1] || angle > x[2]) {\n y = '.3em'\n tx = offset\n ty = 0\n textAnchor = 'start'\n } else if (angle < x[0]) {\n y = '0'\n tx = 0\n ty = -offset\n if (autoOrient) {\n orientAngle = -90\n textAnchor = 'start'\n } else {\n textAnchor = 'middle'\n }\n } else if (angle < x[3]) {\n y = '.3em'\n tx = -offset\n ty = 0\n textAnchor = 'end'\n } else {\n y = '.6em'\n tx = 0\n ty = offset\n if (autoOrient) {\n orientAngle = 90\n textAnchor = 'start'\n } else {\n textAnchor = 'middle'\n }\n }\n\n const {round} = Math\n return labelAttributes({\n x: round(tx),\n y: round(ty),\n angle: orientAngle,\n attrs: {\n '.': {\n y: y,\n 'text-anchor': textAnchor\n }\n }\n })\n }\n\n function getBBoxAngles(elBBox) {\n\n const center = elBBox.center()\n\n const tl = center.theta(elBBox.origin())\n const bl = center.theta(elBBox.bottomLeft())\n const br = center.theta(elBBox.corner())\n const tr = center.theta(elBBox.topRight())\n\n return [tl, tr, br, bl]\n }\n\n function insideLayout(portPosition, elBBox, autoOrient, opt) {\n\n const angle = elBBox.center().theta(portPosition)\n opt = util.defaults({}, opt, { offset: 15 })\n\n let tx; let ty; let y; let textAnchor\n const {offset} = opt\n let orientAngle = 0\n\n const bBoxAngles = getBBoxAngles(elBBox)\n\n if (angle < bBoxAngles[1] || angle > bBoxAngles[2]) {\n y = '.3em'\n tx = -offset\n ty = 0\n textAnchor = 'end'\n } else if (angle < bBoxAngles[0]) {\n y = '.6em'\n tx = 0\n ty = offset\n if (autoOrient) {\n orientAngle = 90\n textAnchor = 'start'\n } else {\n textAnchor = 'middle'\n }\n } else if (angle < bBoxAngles[3]) {\n y = '.3em'\n tx = offset\n ty = 0\n textAnchor = 'start'\n } else {\n y = '0em'\n tx = 0\n ty = -offset\n if (autoOrient) {\n orientAngle = -90\n textAnchor = 'start'\n } else {\n textAnchor = 'middle'\n }\n }\n\n const {round} = Math\n return labelAttributes({\n x: round(tx),\n y: round(ty),\n angle: orientAngle,\n attrs: {\n '.': {\n y: y,\n 'text-anchor': textAnchor\n }\n }\n })\n }\n\n function radialLayout(portCenterOffset, autoOrient, opt) {\n\n opt = util.defaults({}, opt, { offset: 20 })\n\n const origin = g.point(0, 0)\n const angle = -portCenterOffset.theta(origin)\n let orientAngle = angle\n const offset = portCenterOffset.clone()\n .move(origin, opt.offset)\n .difference(portCenterOffset)\n .round()\n\n let y = '.3em'\n let textAnchor\n\n if ((angle + 90) % 180 === 0) {\n textAnchor = autoOrient ? 'end' : 'middle'\n if (!autoOrient && angle === -270) {\n y = '0em'\n }\n } else if (angle > -270 && angle < -90) {\n textAnchor = 'start'\n orientAngle = angle - 180\n } else {\n textAnchor = 'end'\n }\n\n const {round} = Math\n return labelAttributes({\n x: round(offset.x),\n y: round(offset.y),\n angle: autoOrient ? orientAngle : 0,\n attrs: {\n '.': {\n y: y,\n 'text-anchor': textAnchor\n }\n }\n })\n }\n\n joint.layout.PortLabel = {\n\n manual: function(portPosition, elBBox, opt) {\n return labelAttributes(opt, elBBox)\n },\n\n left: function(portPosition, elBBox, opt) {\n return labelAttributes(opt, { x: -15, attrs: { '.': { y: '.3em', 'text-anchor': 'end' }}})\n },\n\n right: function(portPosition, elBBox, opt) {\n return labelAttributes(opt, { x: 15, attrs: { '.': { y: '.3em', 'text-anchor': 'start' }}})\n },\n\n top: function(portPosition, elBBox, opt) {\n return labelAttributes(opt, { y: -15, attrs: { '.': { 'text-anchor': 'middle' }}})\n },\n\n bottom: function(portPosition, elBBox, opt) {\n return labelAttributes(opt, { y: 15, attrs: { '.': { y: '.6em', 'text-anchor': 'middle' }}})\n },\n\n outsideOriented: function(portPosition, elBBox, opt) {\n return outsideLayout(portPosition, elBBox, true, opt)\n },\n\n outside: function(portPosition, elBBox, opt) {\n return outsideLayout(portPosition, elBBox, false, opt)\n },\n\n insideOriented: function(portPosition, elBBox, opt) {\n return insideLayout(portPosition, elBBox, true, opt)\n },\n\n inside: function(portPosition, elBBox, opt) {\n return insideLayout(portPosition, elBBox, false, opt)\n },\n\n radial: function(portPosition, elBBox, opt) {\n return radialLayout(portPosition.difference(elBBox.center()), false, opt)\n },\n\n radialOriented: function(portPosition, elBBox, opt) {\n return radialLayout(portPosition.difference(elBBox.center()), true, opt)\n }\n }\n\n})(g, joint, joint.util)\n\njoint.highlighters.addClass = {\n\n className: joint.util.addClassNamePrefix('highlighted'),\n\n /**\n * @param {joint.dia.CellView} cellView\n * @param {Element} magnetEl\n * @param {object=} opt\n */\n highlight: function(cellView, magnetEl, opt) {\n\n const options = opt || {}\n const className = options.className || this.className\n V(magnetEl).addClass(className)\n },\n\n /**\n * @param {joint.dia.CellView} cellView\n * @param {Element} magnetEl\n * @param {object=} opt\n */\n unhighlight: function(cellView, magnetEl, opt) {\n\n const options = opt || {}\n const className = options.className || this.className\n V(magnetEl).removeClass(className)\n }\n}\n\njoint.highlighters.opacity = {\n\n /**\n * @param {joint.dia.CellView} cellView\n * @param {Element} magnetEl\n */\n highlight: function(cellView, magnetEl) {\n\n V(magnetEl).addClass(joint.util.addClassNamePrefix('highlight-opacity'))\n },\n\n /**\n * @param {joint.dia.CellView} cellView\n * @param {Element} magnetEl\n */\n unhighlight: function(cellView, magnetEl) {\n\n V(magnetEl).removeClass(joint.util.addClassNamePrefix('highlight-opacity'))\n }\n}\n\njoint.highlighters.stroke = {\n\n defaultOptions: {\n padding: 3,\n rx: 0,\n ry: 0,\n attrs: {\n 'stroke-width': 3,\n stroke: '#FEB663'\n }\n },\n\n _views: {},\n\n getHighlighterId: function(magnetEl, opt) {\n\n return magnetEl.id + JSON.stringify(opt)\n },\n\n removeHighlighter: function(id) {\n if (this._views[id]) {\n this._views[id].remove()\n this._views[id] = null\n }\n },\n\n /**\n * @param {joint.dia.CellView} cellView\n * @param {Element} magnetEl\n * @param {object=} opt\n */\n highlight: function(cellView, magnetEl, opt) {\n\n const id = this.getHighlighterId(magnetEl, opt)\n\n // Only highlight once.\n if (this._views[id]) return\n\n const options = joint.util.defaults(opt || {}, this.defaultOptions)\n\n const magnetVel = V(magnetEl)\n let magnetBBox\n\n try {\n\n var pathData = magnetVel.convertToPathData()\n\n } catch (error) {\n\n // Failed to get path data from magnet element.\n // Draw a rectangle around the entire cell view instead.\n magnetBBox = magnetVel.bbox(true/* without transforms */)\n pathData = V.rectToPath(joint.util.assign({}, options, magnetBBox))\n }\n\n const highlightVel = V('path').attr({\n d: pathData,\n 'pointer-events': 'none',\n 'vector-effect': 'non-scaling-stroke',\n fill: 'none'\n }).attr(options.attrs)\n\n let highlightMatrix = magnetVel.getTransformToElement(cellView.el)\n\n // Add padding to the highlight element.\n const {padding} = options\n if (padding) {\n\n magnetBBox || (magnetBBox = magnetVel.bbox(true))\n\n const cx = magnetBBox.x + (magnetBBox.width / 2)\n const cy = magnetBBox.y + (magnetBBox.height / 2)\n\n magnetBBox = V.transformRect(magnetBBox, highlightMatrix)\n\n const width = Math.max(magnetBBox.width, 1)\n const height = Math.max(magnetBBox.height, 1)\n const sx = (width + padding) / width\n const sy = (height + padding) / height\n\n const paddingMatrix = V.createSVGMatrix({\n a: sx,\n b: 0,\n c: 0,\n d: sy,\n e: cx - sx * cx,\n f: cy - sy * cy\n })\n\n highlightMatrix = highlightMatrix.multiply(paddingMatrix)\n }\n\n highlightVel.transform(highlightMatrix)\n\n // joint.mvc.View will handle the theme class name and joint class name prefix.\n const highlightView = this._views[id] = new joint.mvc.View({\n svgElement: true,\n className: 'highlight-stroke',\n el: highlightVel.node\n })\n\n // Remove the highlight view when the cell is removed from the graph.\n const removeHandler = this.removeHighlighter.bind(this, id)\n const cell = cellView.model\n highlightView.listenTo(cell, 'remove', removeHandler)\n highlightView.listenTo(cell.graph, 'reset', removeHandler)\n\n cellView.vel.append(highlightVel)\n },\n\n /**\n * @param {joint.dia.CellView} cellView\n * @param {Element} magnetEl\n * @param {object=} opt\n */\n unhighlight: function(cellView, magnetEl, opt) {\n\n this.removeHighlighter(this.getHighlighterId(magnetEl, opt))\n }\n};\n\n(function(joint, util) {\n\n function bboxWrapper(method) {\n\n return function(view, magnet, ref, opt) {\n\n const rotate = !!opt.rotate\n const bbox = (rotate) ? view.getNodeUnrotatedBBox(magnet) : view.getNodeBBox(magnet)\n const anchor = bbox[method]()\n\n let {dx} = opt\n if (dx) {\n const dxPercentage = util.isPercentage(dx)\n dx = parseFloat(dx)\n if (isFinite(dx)) {\n if (dxPercentage) {\n dx /= 100\n dx *= bbox.width\n }\n anchor.x += dx\n }\n }\n\n let {dy} = opt\n if (dy) {\n const dyPercentage = util.isPercentage(dy)\n dy = parseFloat(dy)\n if (isFinite(dy)) {\n if (dyPercentage) {\n dy /= 100\n dy *= bbox.height\n }\n anchor.y += dy\n }\n }\n\n return (rotate) ? anchor.rotate(view.model.getBBox().center(), -view.model.angle()) : anchor\n }\n }\n\n function resolveRefAsBBoxCenter(fn) {\n\n return function(view, magnet, ref, opt) {\n\n if (ref instanceof Element) {\n const refView = this.paper.findView(ref)\n const refPoint = (refView)\n ? refView.getNodeBBox(ref).center()\n : new g.Point()\n\n return fn.call(this, view, magnet, refPoint, opt)\n }\n\n return fn.apply(this, arguments)\n }\n }\n\n function perpendicular(view, magnet, refPoint, opt) {\n\n const angle = view.model.angle()\n const bbox = view.getNodeBBox(magnet)\n const anchor = bbox.center()\n const topLeft = bbox.origin()\n const bottomRight = bbox.corner()\n\n let {padding} = opt\n if (!isFinite(padding)) padding = 0\n\n if ((topLeft.y + padding) <= refPoint.y && refPoint.y <= (bottomRight.y - padding)) {\n const dy = (refPoint.y - anchor.y)\n anchor.x += (angle === 0 || angle === 180) ? 0 : dy * 1 / Math.tan(g.toRad(angle))\n anchor.y += dy\n } else if ((topLeft.x + padding) <= refPoint.x && refPoint.x <= (bottomRight.x - padding)) {\n const dx = (refPoint.x - anchor.x)\n anchor.y += (angle === 90 || angle === 270) ? 0 : dx * Math.tan(g.toRad(angle))\n anchor.x += dx\n }\n\n return anchor\n }\n\n function midSide(view, magnet, refPoint, opt) {\n\n const rotate = !!opt.rotate\n let bbox; let angle; let center\n if (rotate) {\n bbox = view.getNodeUnrotatedBBox(magnet)\n center = view.model.getBBox().center()\n angle = view.model.angle()\n } else {\n bbox = view.getNodeBBox(magnet)\n }\n\n const {padding} = opt\n if (isFinite(padding)) bbox.inflate(padding)\n\n if (rotate) refPoint.rotate(center, angle)\n\n const side = bbox.sideNearestToPoint(refPoint)\n let anchor\n switch (side) {\n case 'left': anchor = bbox.leftMiddle(); break\n case 'right': anchor = bbox.rightMiddle(); break\n case 'top': anchor = bbox.topMiddle(); break\n case 'bottom': anchor = bbox.bottomMiddle(); break\n }\n\n return (rotate) ? anchor.rotate(center, -angle) : anchor\n }\n\n // Can find anchor from model, when there is no selector or the link end\n // is connected to a port\n function modelCenter(view, magnet) {\n\n const {model} = view\n const bbox = model.getBBox()\n const center = bbox.center()\n const angle = model.angle()\n\n const portId = view.findAttribute('port', magnet)\n if (portId) {\n const portGroup = model.portProp(portId, 'group')\n const portsPositions = model.getPortsPositions(portGroup)\n const anchor = new g.Point(portsPositions[portId]).offset(bbox.origin())\n anchor.rotate(center, -angle)\n return anchor\n }\n\n return center\n }\n\n joint.anchors = {\n center: bboxWrapper('center'),\n top: bboxWrapper('topMiddle'),\n bottom: bboxWrapper('bottomMiddle'),\n left: bboxWrapper('leftMiddle'),\n right: bboxWrapper('rightMiddle'),\n topLeft: bboxWrapper('origin'),\n topRight: bboxWrapper('topRight'),\n bottomLeft: bboxWrapper('bottomLeft'),\n bottomRight: bboxWrapper('corner'),\n perpendicular: resolveRefAsBBoxCenter(perpendicular),\n midSide: resolveRefAsBBoxCenter(midSide),\n modelCenter: modelCenter\n }\n\n})(joint, joint.util);\n\n(function(joint, util, g, V) {\n\n function closestIntersection(intersections, refPoint) {\n\n if (intersections.length === 1) return intersections[0]\n return util.sortBy(intersections, function(i) { return i.squaredDistance(refPoint) })[0]\n }\n\n function offset(p1, p2, offset) {\n\n if (!isFinite(offset)) return p1\n const length = p1.distance(p2)\n if (offset === 0 && length > 0) return p1\n return p1.move(p2, -Math.min(offset, length - 1))\n }\n\n function stroke(magnet) {\n\n const stroke = magnet.getAttribute('stroke-width')\n if (stroke === null) return 0\n return parseFloat(stroke) || 0\n }\n\n // Connection Points\n\n function anchor(line, view, magnet, opt) {\n\n return offset(line.end, line.start, opt.offset)\n }\n\n function bboxIntersection(line, view, magnet, opt) {\n\n const bbox = view.getNodeBBox(magnet)\n if (opt.stroke) bbox.inflate(stroke(magnet) / 2)\n const intersections = line.intersect(bbox)\n const cp = (intersections)\n ? closestIntersection(intersections, line.start)\n : line.end\n return offset(cp, line.start, opt.offset)\n }\n\n function rectangleIntersection(line, view, magnet, opt) {\n\n const angle = view.model.angle()\n if (angle === 0) {\n return bboxIntersection(line, view, magnet, opt)\n }\n\n const bboxWORotation = view.getNodeUnrotatedBBox(magnet)\n if (opt.stroke) bboxWORotation.inflate(stroke(magnet) / 2)\n const center = bboxWORotation.center()\n const lineWORotation = line.clone().rotate(center, angle)\n const intersections = lineWORotation.setLength(1e6).intersect(bboxWORotation)\n const cp = (intersections)\n ? closestIntersection(intersections, lineWORotation.start).rotate(center, -angle)\n : line.end\n return offset(cp, line.start, opt.offset)\n }\n\n const BNDR_SUBDIVISIONS = 'segmentSubdivisons'\n const BNDR_SHAPE_BBOX = 'shapeBBox'\n\n function boundaryIntersection(line, view, magnet, opt) {\n\n let node; let intersection\n const {selector} = opt\n const anchor = line.end\n\n if (typeof selector === 'string') {\n node = view.findBySelector(selector)[0]\n } else if (Array.isArray(selector)) {\n node = util.getByPath(magnet, selector)\n } else {\n // Find the closest non-group descendant\n node = magnet\n do {\n const tagName = node.tagName.toUpperCase()\n if (tagName === 'G') {\n node = node.firstChild\n } else if (tagName === 'TITLE') {\n node = node.nextSibling\n } else break\n } while (node)\n }\n\n if (!(node instanceof Element)) return anchor\n\n const localShape = view.getNodeShape(node)\n const magnetMatrix = view.getNodeMatrix(node)\n const translateMatrix = view.getRootTranslateMatrix()\n const rotateMatrix = view.getRootRotateMatrix()\n const targetMatrix = translateMatrix.multiply(rotateMatrix).multiply(magnetMatrix)\n const localMatrix = targetMatrix.inverse()\n const localLine = V.transformLine(line, localMatrix)\n const localRef = localLine.start.clone()\n const data = view.getNodeData(node)\n\n if (opt.insideout === false) {\n if (!data[BNDR_SHAPE_BBOX]) data[BNDR_SHAPE_BBOX] = localShape.bbox()\n const localBBox = data[BNDR_SHAPE_BBOX]\n if (localBBox.containsPoint(localRef)) return anchor\n }\n\n // Caching segment subdivisions for paths\n let pathOpt\n if (localShape instanceof g.Path) {\n const precision = opt.precision || 2\n if (!data[BNDR_SUBDIVISIONS]) data[BNDR_SUBDIVISIONS] = localShape.getSegmentSubdivisions({ precision: precision })\n pathOpt = {\n precision: precision,\n segmentSubdivisions: data[BNDR_SUBDIVISIONS]\n }\n }\n\n if (opt.extrapolate === true) localLine.setLength(1e6)\n\n intersection = localLine.intersect(localShape, pathOpt)\n if (intersection) {\n // More than one intersection\n if (V.isArray(intersection)) intersection = closestIntersection(intersection, localRef)\n } else if (opt.sticky === true) {\n // No intersection, find the closest point instead\n if (localShape instanceof g.Rect) {\n intersection = localShape.pointNearestToPoint(localRef)\n } else if (localShape instanceof g.Ellipse) {\n intersection = localShape.intersectionWithLineFromCenterToPoint(localRef)\n } else {\n intersection = localShape.closestPoint(localRef, pathOpt)\n }\n }\n\n const cp = (intersection) ? V.transformPoint(intersection, targetMatrix) : anchor\n let cpOffset = opt.offset || 0\n if (opt.stroke) cpOffset += stroke(node) / 2\n\n return offset(cp, line.start, cpOffset)\n }\n\n joint.connectionPoints = {\n anchor: anchor,\n bbox: bboxIntersection,\n rectangle: rectangleIntersection,\n boundary: boundaryIntersection\n }\n\n})(joint, joint.util, g, V);\n\n(function(joint, util) {\n\n function abs2rel(value, max) {\n\n if (max === 0) return '0%'\n return `${Math.round(value / max * 100) }%`\n }\n\n function pin(relative) {\n\n return function(end, view, magnet, coords) {\n\n const angle = view.model.angle()\n const bbox = view.getNodeUnrotatedBBox(magnet)\n const origin = view.model.getBBox().center()\n coords.rotate(origin, angle)\n let dx = coords.x - bbox.x\n let dy = coords.y - bbox.y\n\n if (relative) {\n dx = abs2rel(dx, bbox.width)\n dy = abs2rel(dy, bbox.height)\n }\n\n end.anchor = {\n name: 'topLeft',\n args: {\n dx: dx,\n dy: dy,\n rotate: true\n }\n }\n\n return end\n }\n }\n\n joint.connectionStrategies = {\n useDefaults: util.noop,\n pinAbsolute: pin(false),\n pinRelative: pin(true)\n }\n\n})(joint, joint.util);\n\n(function(joint, util, V, g) {\n\n function getAnchor(coords, view, magnet) {\n // take advantage of an existing logic inside of the\n // pin relative connection strategy\n const end = joint.connectionStrategies.pinRelative.call(\n this.paper,\n {},\n view,\n magnet,\n coords,\n this.model\n )\n return end.anchor\n }\n\n function snapAnchor(coords, view, magnet, type, relatedView, toolView) {\n const {snapRadius} = toolView.options\n const isSource = (type === 'source')\n const refIndex = (isSource ? 0 : -1)\n const ref = this.model.vertex(refIndex) || this.getEndAnchor(isSource ? 'target' : 'source')\n if (ref) {\n if (Math.abs(ref.x - coords.x) < snapRadius) coords.x = ref.x\n if (Math.abs(ref.y - coords.y) < snapRadius) coords.y = ref.y\n }\n return coords\n }\n\n const {ToolView} = joint.dia\n\n // Vertex Handles\n const VertexHandle = joint.mvc.View.extend({\n tagName: 'circle',\n svgElement: true,\n className: 'marker-vertex',\n events: {\n mousedown: 'onPointerDown',\n touchstart: 'onPointerDown',\n dblclick: 'onDoubleClick'\n },\n documentEvents: {\n mousemove: 'onPointerMove',\n touchmove: 'onPointerMove',\n mouseup: 'onPointerUp',\n touchend: 'onPointerUp'\n },\n attributes: {\n r: 6,\n fill: '#33334F',\n stroke: '#FFFFFF',\n 'stroke-width': 2,\n cursor: 'move'\n },\n position: function(x, y) {\n this.vel.attr({ cx: x, cy: y })\n },\n onPointerDown: function(evt) {\n evt.stopPropagation()\n this.options.paper.undelegateEvents()\n this.delegateDocumentEvents(null, evt.data)\n this.trigger('will-change')\n },\n onPointerMove: function(evt) {\n this.trigger('changing', this, evt)\n },\n onDoubleClick: function(evt) {\n this.trigger('remove', this, evt)\n },\n onPointerUp: function(evt) {\n this.trigger('changed', this, evt)\n this.undelegateDocumentEvents()\n this.options.paper.delegateEvents()\n }\n })\n\n const Vertices = ToolView.extend({\n name: 'vertices',\n options: {\n handleClass: VertexHandle,\n snapRadius: 20,\n redundancyRemoval: true,\n vertexAdding: true,\n },\n children: [{\n tagName: 'path',\n selector: 'connection',\n className: 'joint-vertices-path',\n attributes: {\n fill: 'none',\n stroke: 'transparent',\n 'stroke-width': 10,\n cursor: 'cell'\n }\n }],\n handles: null,\n events: {\n 'mousedown .joint-vertices-path': 'onPathPointerDown'\n },\n onRender: function() {\n this.resetHandles()\n if (this.options.vertexAdding) {\n this.renderChildren()\n this.updatePath()\n }\n const {relatedView} = this\n const vertices = relatedView.model.vertices()\n for (let i = 0, n = vertices.length; i < n; i++) {\n const vertex = vertices[i]\n const handle = new (this.options.handleClass)({ index: i, paper: this.paper })\n handle.render()\n handle.position(vertex.x, vertex.y)\n this.simulateRelatedView(handle.el)\n handle.vel.appendTo(this.el)\n this.handles.push(handle)\n this.startHandleListening(handle)\n }\n return this\n },\n update: function() {\n this.render()\n return this\n },\n updatePath: function() {\n const {connection} = this.childNodes\n if (connection) connection.setAttribute('d', this.relatedView.getConnection().serialize())\n },\n startHandleListening: function(handle) {\n const {relatedView} = this\n if (relatedView.can('vertexMove')) {\n this.listenTo(handle, 'will-change', this.onHandleWillChange)\n this.listenTo(handle, 'changing', this.onHandleChanging)\n this.listenTo(handle, 'changed', this.onHandleChanged)\n }\n if (relatedView.can('vertexRemove')) {\n this.listenTo(handle, 'remove', this.onHandleRemove)\n }\n },\n resetHandles: function() {\n const {handles} = this\n this.handles = []\n this.stopListening()\n if (!Array.isArray(handles)) return\n for (let i = 0, n = handles.length; i < n; i++) {\n handles[i].remove()\n }\n },\n getNeighborPoints: function(index) {\n const linkView = this.relatedView\n const vertices = linkView.model.vertices()\n const prev = (index > 0) ? vertices[index - 1] : linkView.sourceAnchor\n const next = (index < vertices.length - 1) ? vertices[index + 1] : linkView.targetAnchor\n return {\n prev: new g.Point(prev),\n next: new g.Point(next)\n }\n },\n onHandleWillChange: function(handle, evt) {\n this.focus()\n this.relatedView.model.startBatch('vertex-move', { ui: true, tool: this.cid })\n },\n onHandleChanging: function(handle, evt) {\n const {relatedView} = this\n const {paper} = relatedView\n const {index} = handle.options\n const vertex = paper.snapToGrid(evt.clientX, evt.clientY).toJSON()\n this.snapVertex(vertex, index)\n relatedView.model.vertex(index, vertex, { ui: true, tool: this.cid })\n handle.position(vertex.x, vertex.y)\n },\n snapVertex: function(vertex, index) {\n const {snapRadius} = this.options\n if (snapRadius > 0) {\n const neighbors = this.getNeighborPoints(index)\n const {prev} = neighbors\n const {next} = neighbors\n if (Math.abs(vertex.x - prev.x) < snapRadius) {\n vertex.x = prev.x\n } else if (Math.abs(vertex.x - next.x) < snapRadius) {\n vertex.x = next.x\n }\n if (Math.abs(vertex.y - prev.y) < snapRadius) {\n vertex.y = neighbors.prev.y\n } else if (Math.abs(vertex.y - next.y) < snapRadius) {\n vertex.y = next.y\n }\n }\n },\n onHandleChanged: function(handle, evt) {\n if (this.options.vertexAdding) this.updatePath()\n if (!this.options.redundancyRemoval) return\n const linkView = this.relatedView\n const verticesRemoved = linkView.removeRedundantLinearVertices({ ui: true, tool: this.cid })\n if (verticesRemoved) this.render()\n this.blur()\n linkView.model.stopBatch('vertex-move', { ui: true, tool: this.cid })\n if (this.eventData(evt).vertexAdded) {\n linkView.model.stopBatch('vertex-add', { ui: true, tool: this.cid })\n }\n },\n onHandleRemove: function(handle) {\n const {index} = handle.options\n this.relatedView.model.removeVertex(index, { ui: true })\n },\n onPathPointerDown: function(evt) {\n evt.stopPropagation()\n const vertex = this.paper.snapToGrid(evt.clientX, evt.clientY).toJSON()\n const {relatedView} = this\n relatedView.model.startBatch('vertex-add', { ui: true, tool: this.cid })\n const index = relatedView.getVertexIndex(vertex.x, vertex.y)\n this.snapVertex(vertex, index)\n relatedView.model.insertVertex(index, vertex, { ui: true, tool: this.cid })\n this.render()\n const handle = this.handles[index]\n this.eventData(evt, { vertexAdded: true })\n handle.onPointerDown(evt)\n },\n onRemove: function() {\n this.resetHandles()\n }\n }, {\n VertexHandle: VertexHandle // keep as class property\n })\n\n const SegmentHandle = joint.mvc.View.extend({\n tagName: 'g',\n svgElement: true,\n className: 'marker-segment',\n events: {\n mousedown: 'onPointerDown',\n touchstart: 'onPointerDown'\n },\n documentEvents: {\n mousemove: 'onPointerMove',\n touchmove: 'onPointerMove',\n mouseup: 'onPointerUp',\n touchend: 'onPointerUp'\n },\n children: [{\n tagName: 'line',\n selector: 'line',\n attributes: {\n stroke: '#33334F',\n 'stroke-width': 2,\n fill: 'none',\n 'pointer-events': 'none'\n }\n }, {\n tagName: 'rect',\n selector: 'handle',\n attributes: {\n width: 20,\n height: 8,\n x: -10,\n y: -4,\n rx: 4,\n ry: 4,\n fill: '#33334F',\n stroke: '#FFFFFF',\n 'stroke-width': 2\n }\n }],\n onRender: function() {\n this.renderChildren()\n },\n position: function(x, y, angle, view) {\n\n const matrix = V.createSVGMatrix().translate(x, y).rotate(angle)\n const {handle} = this.childNodes\n handle.setAttribute('transform', V.matrixToTransformString(matrix))\n handle.setAttribute('cursor', (angle % 180 === 0) ? 'row-resize' : 'col-resize')\n\n const viewPoint = view.getClosestPoint(new g.Point(x, y))\n const {line} = this.childNodes\n line.setAttribute('x1', x)\n line.setAttribute('y1', y)\n line.setAttribute('x2', viewPoint.x)\n line.setAttribute('y2', viewPoint.y)\n },\n onPointerDown: function(evt) {\n this.trigger('change:start', this, evt)\n evt.stopPropagation()\n this.options.paper.undelegateEvents()\n this.delegateDocumentEvents(null, evt.data)\n },\n onPointerMove: function(evt) {\n this.trigger('changing', this, evt)\n },\n onPointerUp: function(evt) {\n this.undelegateDocumentEvents()\n this.options.paper.delegateEvents()\n this.trigger('change:end', this, evt)\n },\n show: function() {\n this.el.style.display = ''\n },\n hide: function() {\n this.el.style.display = 'none'\n }\n })\n\n const Segments = ToolView.extend({\n name: 'segments',\n precision: .5,\n options: {\n handleClass: SegmentHandle,\n segmentLengthThreshold: 40,\n redundancyRemoval: true,\n anchor: getAnchor,\n snapRadius: 10,\n snapHandle: true\n },\n handles: null,\n onRender: function() {\n this.resetHandles()\n const {relatedView} = this\n const vertices = relatedView.model.vertices()\n vertices.unshift(relatedView.sourcePoint)\n vertices.push(relatedView.targetPoint)\n for (let i = 0, n = vertices.length; i < n - 1; i++) {\n const vertex = vertices[i]\n const nextVertex = vertices[i + 1]\n const handle = this.renderHandle(vertex, nextVertex)\n this.simulateRelatedView(handle.el)\n this.handles.push(handle)\n handle.options.index = i\n }\n return this\n },\n renderHandle: function(vertex, nextVertex) {\n const handle = new (this.options.handleClass)({ paper: this.paper })\n handle.render()\n this.updateHandle(handle, vertex, nextVertex)\n handle.vel.appendTo(this.el)\n this.startHandleListening(handle)\n return handle\n },\n update: function() {\n this.render()\n return this\n },\n startHandleListening: function(handle) {\n this.listenTo(handle, 'change:start', this.onHandleChangeStart)\n this.listenTo(handle, 'changing', this.onHandleChanging)\n this.listenTo(handle, 'change:end', this.onHandleChangeEnd)\n },\n resetHandles: function() {\n const {handles} = this\n this.handles = []\n this.stopListening()\n if (!Array.isArray(handles)) return\n for (let i = 0, n = handles.length; i < n; i++) {\n handles[i].remove()\n }\n },\n shiftHandleIndexes: function(value) {\n const {handles} = this\n for (let i = 0, n = handles.length; i < n; i++) handles[i].options.index += value\n },\n resetAnchor: function(type, anchor) {\n const relatedModel = this.relatedView.model\n if (anchor) {\n relatedModel.prop([type, 'anchor'], anchor, {\n rewrite: true,\n ui: true,\n tool: this.cid\n })\n } else {\n relatedModel.removeProp([type, 'anchor'], {\n ui: true,\n tool: this.cid\n })\n }\n },\n snapHandle: function(handle, position, data) {\n\n const {index} = handle.options\n const linkView = this.relatedView\n const link = linkView.model\n const vertices = link.vertices()\n const {axis} = handle.options\n const prev = vertices[index - 2] || data.sourceAnchor\n const next = vertices[index + 1] || data.targetAnchor\n const {snapRadius} = this.options\n if (Math.abs(position[axis] - prev[axis]) < snapRadius) {\n position[axis] = prev[axis]\n } else if (Math.abs(position[axis] - next[axis]) < snapRadius) {\n position[axis] = next[axis]\n }\n return position\n },\n\n onHandleChanging: function(handle, evt) {\n\n const data = this.eventData(evt)\n const {relatedView} = this\n const {paper} = relatedView\n const index = handle.options.index - 1\n const coords = paper.snapToGrid(evt.clientX, evt.clientY)\n const position = this.snapHandle(handle, coords.clone(), data)\n const {axis} = handle.options\n const offset = (this.options.snapHandle) ? 0 : (coords[axis] - position[axis])\n const link = relatedView.model\n const vertices = util.cloneDeep(link.vertices())\n let vertex = vertices[index]\n let nextVertex = vertices[index + 1]\n let anchorFn = this.options.anchor\n if (typeof anchorFn !== 'function') anchorFn = null\n\n // First Segment\n const {sourceView} = relatedView\n const {sourceBBox} = relatedView\n let changeSourceAnchor = false\n let deleteSourceAnchor = false\n if (!vertex) {\n vertex = relatedView.sourceAnchor.toJSON()\n vertex[axis] = position[axis]\n if (sourceBBox.containsPoint(vertex)) {\n vertex[axis] = position[axis]\n changeSourceAnchor = true\n } else {\n // we left the area of the source magnet for the first time\n vertices.unshift(vertex)\n this.shiftHandleIndexes(1)\n deleteSourceAnchor = true\n }\n } else if (index === 0) {\n if (sourceBBox.containsPoint(vertex)) {\n vertices.shift()\n this.shiftHandleIndexes(-1)\n changeSourceAnchor = true\n } else {\n vertex[axis] = position[axis]\n deleteSourceAnchor = true\n }\n } else {\n vertex[axis] = position[axis]\n }\n\n if (anchorFn && sourceView) {\n if (changeSourceAnchor) {\n const sourceAnchorPosition = data.sourceAnchor.clone()\n sourceAnchorPosition[axis] = position[axis]\n const sourceAnchor = anchorFn.call(relatedView, sourceAnchorPosition, sourceView, relatedView.sourceMagnet || sourceView.el, 'source', relatedView)\n this.resetAnchor('source', sourceAnchor)\n }\n if (deleteSourceAnchor) {\n this.resetAnchor('source', data.sourceAnchorDef)\n }\n }\n\n // Last segment\n const {targetView} = relatedView\n const {targetBBox} = relatedView\n let changeTargetAnchor = false\n let deleteTargetAnchor = false\n if (!nextVertex) {\n nextVertex = relatedView.targetAnchor.toJSON()\n nextVertex[axis] = position[axis]\n if (targetBBox.containsPoint(nextVertex)) {\n changeTargetAnchor = true\n } else {\n // we left the area of the target magnet for the first time\n vertices.push(nextVertex)\n deleteTargetAnchor = true\n }\n } else if (index === vertices.length - 2) {\n if (targetBBox.containsPoint(nextVertex)) {\n vertices.pop()\n changeTargetAnchor = true\n } else {\n nextVertex[axis] = position[axis]\n deleteTargetAnchor = true\n }\n } else {\n nextVertex[axis] = position[axis]\n }\n\n if (anchorFn && targetView) {\n if (changeTargetAnchor) {\n const targetAnchorPosition = data.targetAnchor.clone()\n targetAnchorPosition[axis] = position[axis]\n const targetAnchor = anchorFn.call(relatedView, targetAnchorPosition, targetView, relatedView.targetMagnet || targetView.el, 'target', relatedView)\n this.resetAnchor('target', targetAnchor)\n }\n if (deleteTargetAnchor) {\n this.resetAnchor('target', data.targetAnchorDef)\n }\n }\n\n link.vertices(vertices, { ui: true, tool: this.cid })\n this.updateHandle(handle, vertex, nextVertex, offset)\n },\n onHandleChangeStart: function(handle, evt) {\n const {index} = handle.options\n const {handles} = this\n if (!Array.isArray(handles)) return\n for (let i = 0, n = handles.length; i < n; i++) {\n if (i !== index) handles[i].hide()\n }\n this.focus()\n const {relatedView} = this\n const relatedModel = relatedView.model\n this.eventData(evt, {\n sourceAnchor: relatedView.sourceAnchor.clone(),\n targetAnchor: relatedView.targetAnchor.clone(),\n sourceAnchorDef: util.clone(relatedModel.prop(['source', 'anchor'])),\n targetAnchorDef: util.clone(relatedModel.prop(['target', 'anchor']))\n })\n relatedView.model.startBatch('segment-move', { ui: true, tool: this.cid })\n },\n onHandleChangeEnd: function(handle) {\n const linkView = this.relatedView\n if (this.options.redundancyRemoval) {\n linkView.removeRedundantLinearVertices({ ui: true, tool: this.cid })\n }\n this.render()\n this.blur()\n linkView.model.stopBatch('segment-move', { ui: true, tool: this.cid })\n },\n updateHandle: function(handle, vertex, nextVertex, offset) {\n const vertical = Math.abs(vertex.x - nextVertex.x) < this.precision\n const horizontal = Math.abs(vertex.y - nextVertex.y) < this.precision\n if (vertical || horizontal) {\n const segmentLine = new g.Line(vertex, nextVertex)\n const length = segmentLine.length()\n if (length < this.options.segmentLengthThreshold) {\n handle.hide()\n } else {\n const position = segmentLine.midpoint()\n const axis = (vertical) ? 'x' : 'y'\n position[axis] += offset || 0\n const angle = segmentLine.vector().vectorAngle(new g.Point(1, 0))\n handle.position(position.x, position.y, angle, this.relatedView)\n handle.show()\n handle.options.axis = axis\n }\n } else {\n handle.hide()\n }\n },\n onRemove: function() {\n this.resetHandles()\n }\n }, {\n SegmentHandle: SegmentHandle // keep as class property\n })\n\n // End Markers\n const Arrowhead = ToolView.extend({\n tagName: 'path',\n xAxisVector: new g.Point(1, 0),\n events: {\n mousedown: 'onPointerDown',\n touchstart: 'onPointerDown'\n },\n documentEvents: {\n mousemove: 'onPointerMove',\n touchmove: 'onPointerMove',\n mouseup: 'onPointerUp',\n touchend: 'onPointerUp'\n },\n onRender: function() {\n this.update()\n },\n update: function() {\n const {ratio} = this\n const view = this.relatedView\n const tangent = view.getTangentAtRatio(ratio)\n let position; let angle\n if (tangent) {\n position = tangent.start\n angle = tangent.vector().vectorAngle(this.xAxisVector) || 0\n } else {\n position = view.getPointAtRatio(ratio)\n angle = 0\n }\n const matrix = V.createSVGMatrix().translate(position.x, position.y).rotate(angle)\n this.vel.transform(matrix, { absolute: true })\n return this\n },\n onPointerDown: function(evt) {\n evt.stopPropagation()\n const {relatedView} = this\n relatedView.model.startBatch('arrowhead-move', { ui: true, tool: this.cid })\n if (relatedView.can('arrowheadMove')) {\n relatedView.startArrowheadMove(this.arrowheadType)\n this.delegateDocumentEvents()\n relatedView.paper.undelegateEvents()\n }\n this.focus()\n this.el.style.pointerEvents = 'none'\n },\n onPointerMove: function(evt) {\n const coords = this.paper.snapToGrid(evt.clientX, evt.clientY)\n this.relatedView.pointermove(evt, coords.x, coords.y)\n },\n onPointerUp: function(evt) {\n this.undelegateDocumentEvents()\n const {relatedView} = this\n const {paper} = relatedView\n const coords = paper.snapToGrid(evt.clientX, evt.clientY)\n relatedView.pointerup(evt, coords.x, coords.y)\n paper.delegateEvents()\n this.blur()\n this.el.style.pointerEvents = ''\n relatedView.model.stopBatch('arrowhead-move', { ui: true, tool: this.cid })\n }\n })\n\n const TargetArrowhead = Arrowhead.extend({\n name: 'target-arrowhead',\n ratio: 1,\n arrowheadType: 'target',\n attributes: {\n d: 'M -10 -8 10 0 -10 8 Z',\n fill: '#33334F',\n stroke: '#FFFFFF',\n 'stroke-width': 2,\n cursor: 'move',\n class: 'target-arrowhead'\n }\n })\n\n const SourceArrowhead = Arrowhead.extend({\n name: 'source-arrowhead',\n ratio: 0,\n arrowheadType: 'source',\n attributes: {\n d: 'M 10 -8 -10 0 10 8 Z',\n fill: '#33334F',\n stroke: '#FFFFFF',\n 'stroke-width': 2,\n cursor: 'move',\n class: 'source-arrowhead'\n }\n })\n\n const Button = ToolView.extend({\n name: 'button',\n events: {\n mousedown: 'onPointerDown',\n touchstart: 'onPointerDown'\n },\n options: {\n distance: 0,\n offset: 0,\n rotate: false\n },\n onRender: function() {\n this.renderChildren(this.options.markup)\n this.update()\n },\n update: function() {\n let tangent; let position; let angle\n const distance = this.options.distance || 0\n if (util.isPercentage(distance)) {\n tangent = this.relatedView.getTangentAtRatio(parseFloat(distance) / 100)\n } else {\n tangent = this.relatedView.getTangentAtLength(distance)\n }\n if (tangent) {\n position = tangent.start\n angle = tangent.vector().vectorAngle(new g.Point(1,0)) || 0\n } else {\n position = this.relatedView.getConnection().start\n angle = 0\n }\n let matrix = V.createSVGMatrix()\n .translate(position.x, position.y)\n .rotate(angle)\n .translate(0, this.options.offset || 0)\n if (!this.options.rotate) matrix = matrix.rotate(-angle)\n this.vel.transform(matrix, { absolute: true })\n return this\n },\n onPointerDown: function(evt) {\n evt.stopPropagation()\n const actionFn = this.options.action\n if (typeof actionFn === 'function') {\n actionFn.call(this.relatedView, evt, this.relatedView)\n }\n }\n })\n\n\n const Remove = Button.extend({\n children: [{\n tagName: 'circle',\n selector: 'button',\n attributes: {\n r: 7,\n fill: '#FF1D00',\n cursor: 'pointer'\n }\n }, {\n tagName: 'path',\n selector: 'icon',\n attributes: {\n d: 'M -3 -3 3 3 M -3 3 3 -3',\n fill: 'none',\n stroke: '#FFFFFF',\n 'stroke-width': 2,\n 'pointer-events': 'none'\n }\n }],\n options: {\n distance: 60,\n offset: 0,\n action: function(evt) {\n this.model.remove({ ui: true, tool: this.cid })\n }\n }\n })\n\n const Boundary = ToolView.extend({\n name: 'boundary',\n tagName: 'rect',\n options: {\n padding: 10\n },\n attributes: {\n fill: 'none',\n stroke: '#33334F',\n 'stroke-width': .5,\n 'stroke-dasharray': '5, 5',\n 'pointer-events': 'none'\n },\n onRender: function() {\n this.update()\n },\n update: function() {\n let {padding} = this.options\n if (!isFinite(padding)) padding = 0\n const bbox = this.relatedView.getConnection().bbox().inflate(padding)\n this.vel.attr(bbox.toJSON())\n return this\n }\n })\n\n const Anchor = ToolView.extend({\n tagName: 'g',\n type: null,\n children: [{\n tagName: 'circle',\n selector: 'anchor',\n attributes: {\n cursor: 'pointer'\n }\n }, {\n tagName: 'rect',\n selector: 'area',\n attributes: {\n 'pointer-events': 'none',\n fill: 'none',\n stroke: '#33334F',\n 'stroke-dasharray': '2,4',\n rx: 5,\n ry: 5\n }\n }],\n events: {\n mousedown: 'onPointerDown',\n touchstart: 'onPointerDown',\n dblclick: 'onPointerDblClick'\n },\n documentEvents: {\n mousemove: 'onPointerMove',\n touchmove: 'onPointerMove',\n mouseup: 'onPointerUp',\n touchend: 'onPointerUp'\n },\n options: {\n snap: snapAnchor,\n anchor: getAnchor,\n customAnchorAttributes: {\n 'stroke-width': 4,\n stroke: '#33334F',\n fill: '#FFFFFF',\n r: 5\n },\n defaultAnchorAttributes: {\n 'stroke-width': 2,\n stroke: '#FFFFFF',\n fill: '#33334F',\n r: 6\n },\n areaPadding: 6,\n snapRadius: 10,\n restrictArea: true,\n redundancyRemoval: true\n },\n onRender: function() {\n this.renderChildren()\n this.toggleArea(false)\n this.update()\n },\n update: function() {\n const {type} = this\n const {relatedView} = this\n const view = relatedView.getEndView(type)\n if (view) {\n this.updateAnchor()\n this.updateArea()\n this.el.style.display = ''\n } else {\n this.el.style.display = 'none'\n }\n return this\n },\n updateAnchor: function() {\n const {childNodes} = this\n if (!childNodes) return\n const anchorNode = childNodes.anchor\n if (!anchorNode) return\n const {relatedView} = this\n const {type} = this\n const position = relatedView.getEndAnchor(type)\n const {options} = this\n const customAnchor = relatedView.model.prop([type, 'anchor'])\n anchorNode.setAttribute('transform', `translate(${ position.x },${ position.y })`)\n const anchorAttributes = (customAnchor) ? options.customAnchorAttributes : options.defaultAnchorAttributes\n for (const attrName in anchorAttributes) {\n anchorNode.setAttribute(attrName, anchorAttributes[attrName])\n }\n },\n updateArea: function() {\n const {childNodes} = this\n if (!childNodes) return\n const areaNode = childNodes.area\n if (!areaNode) return\n const {relatedView} = this\n const {type} = this\n const view = relatedView.getEndView(type)\n const magnet = relatedView.getEndMagnet(type)\n let padding = this.options.areaPadding\n if (!isFinite(padding)) padding = 0\n const bbox = view.getNodeUnrotatedBBox(magnet).inflate(padding)\n const angle = view.model.angle()\n areaNode.setAttribute('x', -bbox.width / 2)\n areaNode.setAttribute('y', -bbox.height / 2)\n areaNode.setAttribute('width', bbox.width)\n areaNode.setAttribute('height', bbox.height)\n const origin = view.model.getBBox().center()\n const center = bbox.center().rotate(origin, -angle)\n areaNode.setAttribute('transform', `translate(${ center.x },${ center.y }) rotate(${ angle })`)\n },\n toggleArea: function(visible) {\n this.childNodes.area.style.display = (visible) ? '' : 'none'\n },\n onPointerDown: function(evt) {\n evt.stopPropagation()\n this.paper.undelegateEvents()\n this.delegateDocumentEvents()\n this.focus()\n this.toggleArea(this.options.restrictArea)\n this.relatedView.model.startBatch('anchor-move', { ui: true, tool: this.cid })\n },\n resetAnchor: function(anchor) {\n const {type} = this\n const relatedModel = this.relatedView.model\n if (anchor) {\n relatedModel.prop([type, 'anchor'], anchor, {\n rewrite: true,\n ui: true,\n tool: this.cid\n })\n } else {\n relatedModel.removeProp([type, 'anchor'], {\n ui: true,\n tool: this.cid\n })\n }\n },\n onPointerMove: function(evt) {\n\n const {relatedView} = this\n const {type} = this\n const view = relatedView.getEndView(type)\n const magnet = relatedView.getEndMagnet(type)\n\n let coords = this.paper.clientToLocalPoint(evt.clientX, evt.clientY)\n const snapFn = this.options.snap\n if (typeof snapFn === 'function') {\n coords = snapFn.call(relatedView, coords, view, magnet, type, relatedView, this)\n coords = new g.Point(coords)\n }\n\n if (this.options.restrictArea) {\n // snap coords within node bbox\n const bbox = view.getNodeUnrotatedBBox(magnet)\n const angle = view.model.angle()\n const origin = view.model.getBBox().center()\n const rotatedCoords = coords.clone().rotate(origin, angle)\n if (!bbox.containsPoint(rotatedCoords)) {\n coords = bbox.pointNearestToPoint(rotatedCoords).rotate(origin, -angle)\n }\n }\n\n let anchor\n const anchorFn = this.options.anchor\n if (typeof anchorFn === 'function') {\n anchor = anchorFn.call(relatedView, coords, view, magnet, type, relatedView)\n }\n\n this.resetAnchor(anchor)\n this.update()\n },\n\n onPointerUp: function(evt) {\n this.paper.delegateEvents()\n this.undelegateDocumentEvents()\n this.blur()\n this.toggleArea(false)\n const linkView = this.relatedView\n if (this.options.redundancyRemoval) linkView.removeRedundantLinearVertices({ ui: true, tool: this.cid })\n linkView.model.stopBatch('anchor-move', { ui: true, tool: this.cid })\n },\n\n onPointerDblClick: function() {\n this.resetAnchor()\n this.update()\n }\n })\n\n const SourceAnchor = Anchor.extend({\n name: 'source-anchor',\n type: 'source'\n })\n\n const TargetAnchor = Anchor.extend({\n name: 'target-anchor',\n type: 'target'\n })\n\n // Export\n joint.linkTools = {\n Vertices: Vertices,\n Segments: Segments,\n SourceArrowhead: SourceArrowhead,\n TargetArrowhead: TargetArrowhead,\n SourceAnchor: SourceAnchor,\n TargetAnchor: TargetAnchor,\n Button: Button,\n Remove: Remove,\n Boundary: Boundary\n }\n\n})(joint, joint.util, V, g)\n\njoint.dia.Element.define('erd.Entity', {\n size: { width: 150, height: 60 },\n attrs: {\n '.outer': {\n fill: '#2ECC71', stroke: '#27AE60', 'stroke-width': 2,\n points: '100,0 100,60 0,60 0,0'\n },\n '.inner': {\n fill: '#2ECC71', stroke: '#27AE60', 'stroke-width': 2,\n points: '95,5 95,55 5,55 5,5',\n display: 'none'\n },\n text: {\n text: 'Entity',\n 'font-family': 'Arial', 'font-size': 14,\n 'ref-x': .5, 'ref-y': .5,\n 'y-alignment': 'middle', 'text-anchor': 'middle'\n }\n }\n}, {\n markup: '',\n})\n\n\njoint.shapes.erd.Entity.define('erd.WeakEntity', {\n attrs: {\n '.inner': { display: 'auto' },\n text: { text: 'Weak Entity' }\n }\n})\n\njoint.dia.Element.define('erd.Relationship', {\n size: { width: 80, height: 80 },\n attrs: {\n '.outer': {\n fill: '#3498DB', stroke: '#2980B9', 'stroke-width': 2,\n points: '40,0 80,40 40,80 0,40'\n },\n '.inner': {\n fill: '#3498DB', stroke: '#2980B9', 'stroke-width': 2,\n points: '40,5 75,40 40,75 5,40',\n display: 'none'\n },\n text: {\n text: 'Relationship',\n 'font-family': 'Arial', 'font-size': 12,\n 'ref-x': .5, 'ref-y': .5,\n 'y-alignment': 'middle', 'text-anchor': 'middle'\n }\n }\n}, {\n markup: '',\n})\n\njoint.shapes.erd.Relationship.define('erd.IdentifyingRelationship', {\n attrs: {\n '.inner': { display: 'auto' },\n text: { text: 'Identifying' }\n }\n})\n\njoint.dia.Element.define('erd.Attribute', {\n size: { width: 100, height: 50 },\n attrs: {\n ellipse: {\n transform: 'translate(50, 25)'\n },\n '.outer': {\n stroke: '#D35400', 'stroke-width': 2,\n cx: 0, cy: 0, rx: 50, ry: 25,\n fill: '#E67E22'\n },\n '.inner': {\n stroke: '#D35400', 'stroke-width': 2,\n cx: 0, cy: 0, rx: 45, ry: 20,\n fill: '#E67E22', display: 'none'\n },\n text: {\n 'font-family': 'Arial', 'font-size': 14,\n 'ref-x': .5, 'ref-y': .5,\n 'y-alignment': 'middle', 'text-anchor': 'middle'\n }\n }\n}, {\n markup: '',\n})\n\njoint.shapes.erd.Attribute.define('erd.Multivalued', {\n attrs: {\n '.inner': { display: 'block' },\n text: { text: 'multivalued' }\n }\n})\n\njoint.shapes.erd.Attribute.define('erd.Derived', {\n attrs: {\n '.outer': { 'stroke-dasharray': '3,5' },\n text: { text: 'derived' }\n }\n})\n\njoint.shapes.erd.Attribute.define('erd.Key', {\n attrs: {\n ellipse: { 'stroke-width': 4 },\n text: { text: 'key', 'font-weight': '800', 'text-decoration': 'underline' }\n }\n})\n\njoint.shapes.erd.Attribute.define('erd.Normal', {\n attrs: { text: { text: 'Normal' }}\n})\n\njoint.dia.Element.define('erd.ISA', {\n type: 'erd.ISA',\n size: { width: 100, height: 50 },\n attrs: {\n polygon: {\n points: '0,0 50,50 100,0',\n fill: '#F1C40F', stroke: '#F39C12', 'stroke-width': 2\n },\n text: {\n text: 'ISA', 'font-size': 18,\n 'ref-x': .5, 'ref-y': .3,\n 'y-alignment': 'middle', 'text-anchor': 'middle'\n }\n }\n}, {\n markup: '',\n})\n\njoint.dia.Link.define('erd.Line', {}, {\n cardinality: function(value) {\n this.set('labels', [{ position: -20, attrs: { text: { dy: -8, text: value }}}])\n }\n})\n\njoint.shapes.basic.Circle.define('fsa.State', {\n attrs: {\n circle: { 'stroke-width': 3 },\n text: { 'font-weight': '800' }\n }\n})\n\njoint.dia.Element.define('fsa.StartState', {\n size: { width: 20, height: 20 },\n attrs: {\n circle: {\n transform: 'translate(10, 10)',\n r: 10,\n fill: '#000000'\n }\n }\n}, {\n markup: '',\n})\n\njoint.dia.Element.define('fsa.EndState', {\n size: { width: 20, height: 20 },\n attrs: {\n '.outer': {\n transform: 'translate(10, 10)',\n r: 10,\n fill: '#ffffff',\n stroke: '#000000'\n },\n\n '.inner': {\n transform: 'translate(10, 10)',\n r: 6,\n fill: '#000000'\n }\n }\n}, {\n markup: '',\n})\n\njoint.dia.Link.define('fsa.Arrow', {\n attrs: { '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z' }},\n smooth: true\n})\n\njoint.dia.Element.define('org.Member', {\n size: { width: 180, height: 70 },\n attrs: {\n rect: { width: 170, height: 60 },\n\n '.card': {\n fill: '#FFFFFF', stroke: '#000000', 'stroke-width': 2,\n 'pointer-events': 'visiblePainted', rx: 10, ry: 10\n },\n\n image: {\n width: 48, height: 48,\n ref: '.card', 'ref-x': 10, 'ref-y': 5\n },\n\n '.rank': {\n 'text-decoration': 'underline',\n ref: '.card', 'ref-x': 0.9, 'ref-y': 0.2,\n 'font-family': 'Courier New', 'font-size': 14,\n 'text-anchor': 'end'\n },\n\n '.name': {\n 'font-weight': '800',\n ref: '.card', 'ref-x': 0.9, 'ref-y': 0.6,\n 'font-family': 'Courier New', 'font-size': 14,\n 'text-anchor': 'end'\n }\n }\n}, {\n markup: '',\n})\n\njoint.dia.Link.define('org.Arrow', {\n source: { selector: '.card' }, target: { selector: '.card' },\n attrs: { '.connection': { stroke: '#585858', 'stroke-width': 3 }},\n z: -1\n})\n\njoint.shapes.basic.Generic.define('chess.KingWhite', {\n size: { width: 42, height: 38 }\n}, {\n markup: ' '\n})\n\njoint.shapes.basic.Generic.define('chess.KingBlack', {\n size: { width: 42, height: 38 }\n}, {\n markup: ' '\n})\n\njoint.shapes.basic.Generic.define('chess.QueenWhite', {\n size: { width: 42, height: 38 }\n}, {\n markup: ' '\n})\n\njoint.shapes.basic.Generic.define('chess.QueenBlack', {\n size: { width: 42, height: 38 }\n}, {\n markup: ' '\n})\n\njoint.shapes.basic.Generic.define('chess.RookWhite', {\n size: { width: 32, height: 34 }\n}, {\n markup: ' '\n})\n\njoint.shapes.basic.Generic.define('chess.RookBlack', {\n size: { width: 32, height: 34 }\n}, {\n markup: ' '\n})\n\njoint.shapes.basic.Generic.define('chess.BishopWhite', {\n size: { width: 38, height: 38 }\n}, {\n markup: ' '\n})\n\njoint.shapes.basic.Generic.define('chess.BishopBlack', {\n size: { width: 38, height: 38 }\n}, {\n markup: ' '\n})\n\njoint.shapes.basic.Generic.define('chess.KnightWhite', {\n size: { width: 38, height: 37 }\n}, {\n markup: ' '\n})\n\njoint.shapes.basic.Generic.define('chess.KnightBlack', {\n size: { width: 38, height: 37 }\n}, {\n markup: ' '\n})\n\njoint.shapes.basic.Generic.define('chess.PawnWhite', {\n size: { width: 28, height: 33 }\n}, {\n markup: ''\n})\n\njoint.shapes.basic.Generic.define('chess.PawnBlack', {\n size: { width: 28, height: 33 }\n}, {\n markup: ''\n})\n\njoint.shapes.basic.Generic.define('pn.Place', {\n size: { width: 50, height: 50 },\n attrs: {\n '.root': {\n r: 25,\n fill: '#ffffff',\n stroke: '#000000',\n transform: 'translate(25, 25)'\n },\n '.label': {\n 'text-anchor': 'middle',\n 'ref-x': .5,\n 'ref-y': -20,\n ref: '.root',\n fill: '#000000',\n 'font-size': 12\n },\n '.tokens > circle': {\n fill: '#000000',\n r: 5\n },\n '.tokens.one > circle': { transform: 'translate(25, 25)' },\n\n '.tokens.two > circle:nth-child(1)': { transform: 'translate(19, 25)' },\n '.tokens.two > circle:nth-child(2)': { transform: 'translate(31, 25)' },\n\n '.tokens.three > circle:nth-child(1)': { transform: 'translate(18, 29)' },\n '.tokens.three > circle:nth-child(2)': { transform: 'translate(25, 19)' },\n '.tokens.three > circle:nth-child(3)': { transform: 'translate(32, 29)' },\n\n '.tokens.alot > text': {\n transform: 'translate(25, 18)',\n 'text-anchor': 'middle',\n fill: '#000000'\n }\n }\n}, {\n markup: '',\n})\n\njoint.shapes.pn.PlaceView = joint.dia.ElementView.extend({}, {\n\n initialize: function() {\n\n joint.dia.ElementView.prototype.initialize.apply(this, arguments)\n\n this.model.on('change:tokens', function() {\n\n this.renderTokens()\n this.update()\n\n }, this)\n },\n\n render: function() {\n\n joint.dia.ElementView.prototype.render.apply(this, arguments)\n\n this.renderTokens()\n this.update()\n },\n\n renderTokens: function() {\n\n const $tokens = this.$('.tokens').empty()\n $tokens[0].className.baseVal = 'tokens'\n\n const tokens = this.model.get('tokens')\n\n if (!tokens) return\n\n switch (tokens) {\n\n case 1:\n $tokens[0].className.baseVal += ' one'\n $tokens.append(V('').node)\n break\n\n case 2:\n $tokens[0].className.baseVal += ' two'\n $tokens.append(V('').node, V('').node)\n break\n\n case 3:\n $tokens[0].className.baseVal += ' three'\n $tokens.append(V('').node, V('').node, V('').node)\n break\n\n default:\n $tokens[0].className.baseVal += ' alot'\n $tokens.append(V('').text(`${tokens }`).node)\n break\n }\n }\n})\n\njoint.shapes.basic.Generic.define('pn.Transition', {\n size: { width: 12, height: 50 },\n attrs: {\n rect: {\n width: 12,\n height: 50,\n fill: '#000000',\n stroke: '#000000'\n },\n '.label': {\n 'text-anchor': 'middle',\n 'ref-x': .5,\n 'ref-y': -20,\n ref: 'rect',\n fill: '#000000',\n 'font-size': 12\n }\n }\n}, {\n markup: '',\n})\n\njoint.dia.Link.define('pn.Link', {\n attrs: { '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z' }}\n})\n\n/**\n * @deprecated use the port api insteac\n */\njoint.shapes.basic.Generic.define('devs.Model', {\n inPorts: [],\n outPorts: [],\n size: {\n width: 80,\n height: 80\n },\n attrs: {\n '.': {\n magnet: false\n },\n '.label': {\n text: 'Model',\n 'ref-x': .5,\n 'ref-y': 10,\n 'font-size': 18,\n 'text-anchor': 'middle',\n fill: '#000'\n },\n '.body': {\n 'ref-width': '100%',\n 'ref-height': '100%',\n stroke: '#000'\n }\n },\n ports: {\n groups: {\n in: {\n position: {\n name: 'left'\n },\n attrs: {\n '.port-label': {\n fill: '#000'\n },\n '.port-body': {\n fill: '#fff',\n stroke: '#000',\n r: 10,\n magnet: true\n }\n },\n label: {\n position: {\n name: 'left',\n args: {\n y: 10\n }\n }\n }\n },\n out: {\n position: {\n name: 'right'\n },\n attrs: {\n '.port-label': {\n fill: '#000'\n },\n '.port-body': {\n fill: '#fff',\n stroke: '#000',\n r: 10,\n magnet: true\n }\n },\n label: {\n position: {\n name: 'right',\n args: {\n y: 10\n }\n }\n }\n }\n }\n }\n}, {\n markup: '',\n portMarkup: '',\n portLabelMarkup: '',\n\n initialize: function() {\n\n joint.shapes.basic.Generic.prototype.initialize.apply(this, arguments)\n\n this.on('change:inPorts change:outPorts', this.updatePortItems, this)\n this.updatePortItems()\n },\n\n updatePortItems: function(model, changed, opt) {\n\n // Make sure all ports are unique.\n const inPorts = joint.util.uniq(this.get('inPorts'))\n const outPorts = joint.util.difference(joint.util.uniq(this.get('outPorts')), inPorts)\n\n const inPortItems = this.createPortItems('in', inPorts)\n const outPortItems = this.createPortItems('out', outPorts)\n\n this.prop('ports/items', inPortItems.concat(outPortItems), joint.util.assign({ rewrite: true }, opt))\n },\n\n createPortItem: function(group, port) {\n\n return {\n id: port,\n group: group,\n attrs: {\n '.port-label': {\n text: port\n }\n }\n }\n },\n\n createPortItems: function(group, ports) {\n\n return joint.util.toArray(ports).map(this.createPortItem.bind(this, group))\n },\n\n _addGroupPort: function(port, group, opt) {\n\n const ports = this.get(group)\n return this.set(group, Array.isArray(ports) ? ports.concat(port) : [port], opt)\n },\n\n addOutPort: function(port, opt) {\n\n return this._addGroupPort(port, 'outPorts', opt)\n },\n\n addInPort: function(port, opt) {\n\n return this._addGroupPort(port, 'inPorts', opt)\n },\n\n _removeGroupPort: function(port, group, opt) {\n\n return this.set(group, joint.util.without(this.get(group), port), opt)\n },\n\n removeOutPort: function(port, opt) {\n\n return this._removeGroupPort(port, 'outPorts', opt)\n },\n\n removeInPort: function(port, opt) {\n\n return this._removeGroupPort(port, 'inPorts', opt)\n },\n\n _changeGroup: function(group, properties, opt) {\n\n return this.prop(`ports/groups/${ group}`, joint.util.isObject(properties) ? properties : {}, opt)\n },\n\n changeInGroup: function(properties, opt) {\n\n return this._changeGroup('in', properties, opt)\n },\n\n changeOutGroup: function(properties, opt) {\n\n return this._changeGroup('out', properties, opt)\n }\n})\n\njoint.shapes.devs.Model.define('devs.Atomic', {\n size: {\n width: 80,\n height: 80\n },\n attrs: {\n '.label': {\n text: 'Atomic'\n }\n }\n})\n\njoint.shapes.devs.Model.define('devs.Coupled', {\n size: {\n width: 200,\n height: 300\n },\n attrs: {\n '.label': {\n text: 'Coupled'\n }\n }\n})\n\njoint.dia.Link.define('devs.Link', {\n attrs: {\n '.connection': {\n 'stroke-width': 2\n }\n }\n})\n\njoint.shapes.basic.Generic.define('uml.Class', {\n attrs: {\n rect: { width: 200 },\n\n '.uml-class-name-rect': { stroke: 'black', 'stroke-width': 2, fill: '#3498db' },\n '.uml-class-attrs-rect': { stroke: 'black', 'stroke-width': 2, fill: '#2980b9' },\n '.uml-class-methods-rect': { stroke: 'black', 'stroke-width': 2, fill: '#2980b9' },\n\n '.uml-class-name-text': {\n ref: '.uml-class-name-rect',\n 'ref-y': .5,\n 'ref-x': .5,\n 'text-anchor': 'middle',\n 'y-alignment': 'middle',\n 'font-weight': 'bold',\n fill: 'black',\n 'font-size': 12,\n 'font-family': 'Times New Roman'\n },\n '.uml-class-attrs-text': {\n ref: '.uml-class-attrs-rect', 'ref-y': 5, 'ref-x': 5,\n fill: 'black', 'font-size': 12, 'font-family': 'Times New Roman'\n },\n '.uml-class-methods-text': {\n ref: '.uml-class-methods-rect', 'ref-y': 5, 'ref-x': 5,\n fill: 'black', 'font-size': 12, 'font-family': 'Times New Roman'\n }\n },\n\n name: [],\n attributes: [],\n methods: []\n}, {\n markup: [\n '',\n '',\n '',\n '',\n '',\n ''\n ].join(''),\n\n initialize: function() {\n\n this.on('change:name change:attributes change:methods', function() {\n this.updateRectangles()\n this.trigger('uml-update')\n }, this)\n\n this.updateRectangles()\n\n joint.shapes.basic.Generic.prototype.initialize.apply(this, arguments)\n },\n\n getClassName: function() {\n return this.get('name')\n },\n\n updateRectangles: function() {\n\n const attrs = this.get('attrs')\n\n const rects = [\n { type: 'name', text: this.getClassName() },\n { type: 'attrs', text: this.get('attributes') },\n { type: 'methods', text: this.get('methods') }\n ]\n\n let offsetY = 0\n\n rects.forEach(function(rect) {\n\n const lines = Array.isArray(rect.text) ? rect.text : [rect.text]\n const rectHeight = lines.length * 20 + 20\n\n attrs[`.uml-class-${ rect.type }-text`].text = lines.join('\\n')\n attrs[`.uml-class-${ rect.type }-rect`].height = rectHeight\n attrs[`.uml-class-${ rect.type }-rect`].transform = `translate(0,${ offsetY })`\n\n offsetY += rectHeight\n })\n }\n\n})\n\njoint.shapes.uml.ClassView = joint.dia.ElementView.extend({\n\n initialize: function() {\n\n joint.dia.ElementView.prototype.initialize.apply(this, arguments)\n\n this.listenTo(this.model, 'uml-update', function() {\n this.update()\n this.resize()\n })\n }\n})\n\njoint.shapes.uml.Class.define('uml.Abstract', {\n attrs: {\n '.uml-class-name-rect': { fill: '#e74c3c' },\n '.uml-class-attrs-rect': { fill: '#c0392b' },\n '.uml-class-methods-rect': { fill: '#c0392b' }\n }\n}, {\n\n getClassName: function() {\n return ['<>', this.get('name')]\n }\n\n})\njoint.shapes.uml.AbstractView = joint.shapes.uml.ClassView\n\njoint.shapes.uml.Class.define('uml.Interface', {\n attrs: {\n '.uml-class-name-rect': { fill: '#f1c40f' },\n '.uml-class-attrs-rect': { fill: '#f39c12' },\n '.uml-class-methods-rect': { fill: '#f39c12' }\n }\n}, {\n getClassName: function() {\n return ['<>', this.get('name')]\n }\n})\njoint.shapes.uml.InterfaceView = joint.shapes.uml.ClassView\n\njoint.dia.Link.define('uml.Generalization', {\n attrs: { '.marker-target': { d: 'M 20 0 L 0 10 L 20 20 z', fill: 'white' }}\n})\n\njoint.dia.Link.define('uml.Implementation', {\n attrs: {\n '.marker-target': { d: 'M 20 0 L 0 10 L 20 20 z', fill: 'white' },\n '.connection': { 'stroke-dasharray': '3,3' }\n }\n})\n\njoint.dia.Link.define('uml.Aggregation', {\n attrs: { '.marker-target': { d: 'M 40 10 L 20 20 L 0 10 L 20 0 z', fill: 'white' }}\n})\n\njoint.dia.Link.define('uml.Composition', {\n attrs: { '.marker-target': { d: 'M 40 10 L 20 20 L 0 10 L 20 0 z', fill: 'black' }}\n})\n\njoint.dia.Link.define('uml.Association')\n\n// Statechart\n\njoint.shapes.basic.Generic.define('uml.State', {\n attrs: {\n '.uml-state-body': {\n width: 200, height: 200, rx: 10, ry: 10,\n fill: '#ecf0f1', stroke: '#bdc3c7', 'stroke-width': 3\n },\n '.uml-state-separator': {\n stroke: '#bdc3c7', 'stroke-width': 2\n },\n '.uml-state-name': {\n ref: '.uml-state-body', 'ref-x': .5, 'ref-y': 5, 'text-anchor': 'middle',\n fill: '#000000', 'font-family': 'Courier New', 'font-size': 14\n },\n '.uml-state-events': {\n ref: '.uml-state-separator', 'ref-x': 5, 'ref-y': 5,\n fill: '#000000', 'font-family': 'Courier New', 'font-size': 14\n }\n },\n\n name: 'State',\n events: []\n\n}, {\n markup: [\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n ''\n ].join(''),\n\n initialize: function() {\n\n this.on({\n 'change:name': this.updateName,\n 'change:events': this.updateEvents,\n 'change:size': this.updatePath\n }, this)\n\n this.updateName()\n this.updateEvents()\n this.updatePath()\n\n joint.shapes.basic.Generic.prototype.initialize.apply(this, arguments)\n },\n\n updateName: function() {\n\n this.attr('.uml-state-name/text', this.get('name'))\n },\n\n updateEvents: function() {\n\n this.attr('.uml-state-events/text', this.get('events').join('\\n'))\n },\n\n updatePath: function() {\n\n const d = `M 0 20 L ${ this.get('size').width } 20`\n\n // We are using `silent: true` here because updatePath() is meant to be called\n // on resize and there's no need to to update the element twice (`change:size`\n // triggers also an update).\n this.attr('.uml-state-separator/d', d, { silent: true })\n }\n})\n\njoint.shapes.basic.Circle.define('uml.StartState', {\n type: 'uml.StartState',\n attrs: { circle: { fill: '#34495e', stroke: '#2c3e50', 'stroke-width': 2, rx: 1 }}\n})\n\njoint.shapes.basic.Generic.define('uml.EndState', {\n size: { width: 20, height: 20 },\n attrs: {\n 'circle.outer': {\n transform: 'translate(10, 10)',\n r: 10,\n fill: '#ffffff',\n stroke: '#2c3e50'\n },\n\n 'circle.inner': {\n transform: 'translate(10, 10)',\n r: 6,\n fill: '#34495e'\n }\n }\n}, {\n markup: '',\n})\n\njoint.dia.Link.define('uml.Transition', {\n attrs: {\n '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z', fill: '#34495e', stroke: '#2c3e50' },\n '.connection': { stroke: '#2c3e50' }\n }\n})\n\njoint.shapes.basic.Generic.define('logic.Gate', {\n size: { width: 80, height: 40 },\n attrs: {\n '.': { magnet: false },\n '.body': { width: 100, height: 50 },\n circle: { r: 7, stroke: 'black', fill: 'transparent', 'stroke-width': 2 }\n }\n}, {\n operation: function() {\n return true\n }\n})\n\njoint.shapes.logic.Gate.define('logic.IO', {\n size: { width: 60, height: 30 },\n attrs: {\n '.body': { fill: 'white', stroke: 'black', 'stroke-width': 2 },\n '.wire': { ref: '.body', 'ref-y': .5, stroke: 'black' },\n text: {\n fill: 'black',\n ref: '.body', 'ref-x': .5, 'ref-y': .5, 'y-alignment': 'middle',\n 'text-anchor': 'middle',\n 'font-weight': 'bold',\n 'font-variant': 'small-caps',\n 'text-transform': 'capitalize',\n 'font-size': '14px'\n }\n }\n}, {\n markup: '',\n})\n\njoint.shapes.logic.IO.define('logic.Input', {\n attrs: {\n '.wire': { 'ref-dx': 0, d: 'M 0 0 L 23 0' },\n circle: { ref: '.body', 'ref-dx': 30, 'ref-y': 0.5, magnet: true, class: 'output', port: 'out' },\n text: { text: 'input' }\n }\n})\n\njoint.shapes.logic.IO.define('logic.Output', {\n attrs: {\n '.wire': { 'ref-x': 0, d: 'M 0 0 L -23 0' },\n circle: { ref: '.body', 'ref-x': -30, 'ref-y': 0.5, magnet: 'passive', class: 'input', port: 'in' },\n text: { text: 'output' }\n }\n})\n\njoint.shapes.logic.Gate.define('logic.Gate11', {\n attrs: {\n '.input': { ref: '.body', 'ref-x': -2, 'ref-y': 0.5, magnet: 'passive', port: 'in' },\n '.output': { ref: '.body', 'ref-dx': 2, 'ref-y': 0.5, magnet: true, port: 'out' }\n }\n}, {\n markup: '',\n})\n\njoint.shapes.logic.Gate.define('logic.Gate21', {\n attrs: {\n '.input1': { ref: '.body', 'ref-x': -2, 'ref-y': 0.3, magnet: 'passive', port: 'in1' },\n '.input2': { ref: '.body', 'ref-x': -2, 'ref-y': 0.7, magnet: 'passive', port: 'in2' },\n '.output': { ref: '.body', 'ref-dx': 2, 'ref-y': 0.5, magnet: true, port: 'out' }\n }\n}, {\n markup: '',\n})\n\njoint.shapes.logic.Gate11.define('logic.Repeater', {\n attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PVCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjgiCiAgICAgaW5rc2NhcGU6Y3g9Ijg0LjY4NTM1MiIKICAgICBpbmtzY2FwZTpjeT0iMTUuMjg4NjI4IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzIuMTU2OTEsMjUgTCA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAyOS4wNDM0NzgsMjUgTCA1LjA0MzQ3ODEsMjUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSAyOC45Njg3NSwyLjU5Mzc1IEwgMjguOTY4NzUsNSBMIDI4Ljk2ODc1LDQ1IEwgMjguOTY4NzUsNDcuNDA2MjUgTCAzMS4xMjUsNDYuMzQzNzUgTCA3Mi4xNTYyNSwyNi4zNDM3NSBMIDcyLjE1NjI1LDIzLjY1NjI1IEwgMzEuMTI1LDMuNjU2MjUgTCAyOC45Njg3NSwyLjU5Mzc1IHogTSAzMS45Njg3NSw3LjQwNjI1IEwgNjguMDkzNzUsMjUgTCAzMS45Njg3NSw0Mi41OTM3NSBMIDMxLjk2ODc1LDcuNDA2MjUgeiIKICAgICAgIGlkPSJwYXRoMjYzOCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjY2NjYyIgLz4KICA8L2c+Cjwvc3ZnPgo=' }}\n}, {\n operation: function(input) {\n return input\n }\n})\n\njoint.shapes.logic.Gate11.define('logic.Not', {\n attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PVCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjgiCiAgICAgaW5rc2NhcGU6Y3g9Ijg0LjY4NTM1MiIKICAgICBpbmtzY2FwZTpjeT0iMTUuMjg4NjI4IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzkuMTU2OTEsMjUgTCA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAyOS4wNDM0NzgsMjUgTCA1LjA0MzQ3ODEsMjUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSAyOC45Njg3NSwyLjU5Mzc1IEwgMjguOTY4NzUsNSBMIDI4Ljk2ODc1LDQ1IEwgMjguOTY4NzUsNDcuNDA2MjUgTCAzMS4xMjUsNDYuMzQzNzUgTCA3Mi4xNTYyNSwyNi4zNDM3NSBMIDcyLjE1NjI1LDIzLjY1NjI1IEwgMzEuMTI1LDMuNjU2MjUgTCAyOC45Njg3NSwyLjU5Mzc1IHogTSAzMS45Njg3NSw3LjQwNjI1IEwgNjguMDkzNzUsMjUgTCAzMS45Njg3NSw0Mi41OTM3NSBMIDMxLjk2ODc1LDcuNDA2MjUgeiIKICAgICAgIGlkPSJwYXRoMjYzOCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjY2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDI2NzEiCiAgICAgICBzb2RpcG9kaTpjeD0iNzYiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA4MCwyNSBBIDQsNCAwIDEgMSA3MiwyNSBBIDQsNCAwIDEgMSA4MCwyNSB6IgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEsMCkiIC8+CiAgPC9nPgo8L3N2Zz4K' }}\n}, {\n operation: function(input) {\n return !input\n }\n})\n\njoint.shapes.logic.Gate21.define('logic.Or', {\n attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik9SIEFOU0kuc3ZnIgogICBpbmtzY2FwZTpvdXRwdXRfZXh0ZW5zaW9uPSJvcmcuaW5rc2NhcGUub3V0cHV0LnN2Zy5pbmtzY2FwZSI+CiAgPGRlZnMKICAgICBpZD0iZGVmczQiPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjUwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjI1IDogMTAgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjcxNCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iMSA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMC41IDogMC4zMzMzMzMzMyA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyODA2IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyODE5IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjM3Mi4wNDcyNCA6IDM1MC43ODczOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI3NDQuMDk0NDggOiA1MjYuMTgxMDkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzc3IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49Ijc1IDogNDAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iMTUwIDogNjAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDYwIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTMyNzUiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNTAgOiAzMy4zMzMzMzMgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iMTAwIDogNTAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDUwIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTU1MzMiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzIgOiAyMS4zMzMzMzMgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNjQgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMzIgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjU1NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDE2LjY2NjY2NyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDI1IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAyNSA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6em9vbT0iNCIKICAgICBpbmtzY2FwZTpjeD0iMTEzLjAwMDM5IgogICAgIGlua3NjYXBlOmN5PSIxMi44OTM3MzEiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4IgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImcyNTYwIgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpbmtzY2FwZTpncmlkLWJib3g9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1wb2ludHM9InRydWUiCiAgICAgZ3JpZHRvbGVyYW5jZT0iMTAwMDAiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxMzk5IgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9Ijg3NCIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iMzciCiAgICAgaW5rc2NhcGU6d2luZG93LXk9Ii00IgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Im0gNzAsMjUgYyAyMCwwIDI1LDAgMjUsMCIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMSwxNSA1LDE1IgogICAgICAgaWQ9InBhdGgzMDYxIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzIsMzUgNSwzNSIKICAgICAgIGlkPSJwYXRoMzk0NCIgLz4KICAgIDxnCiAgICAgICBpZD0iZzI1NjAiCiAgICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI2LjUsLTM5LjUpIj4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjQwNjI1LDQ0LjUgTCAtMC40MDYyNSw0Ni45Mzc1IEMgLTAuNDA2MjUsNDYuOTM3NSA1LjI1LDUzLjkzNzU0OSA1LjI1LDY0LjUgQyA1LjI1LDc1LjA2MjQ1MSAtMC40MDYyNSw4Mi4wNjI1IC0wLjQwNjI1LDgyLjA2MjUgTCAtMi40MDYyNSw4NC41IEwgMC43NSw4NC41IEwgMTQuNzUsODQuNSBDIDE3LjE1ODA3Niw4NC41MDAwMDEgMjIuNDM5Njk5LDg0LjUyNDUxNCAyOC4zNzUsODIuMDkzNzUgQyAzNC4zMTAzMDEsNzkuNjYyOTg2IDQwLjkxMTUzNiw3NC43NTA0ODQgNDYuMDYyNSw2NS4yMTg3NSBMIDQ0Ljc1LDY0LjUgTCA0Ni4wNjI1LDYzLjc4MTI1IEMgMzUuNzU5Mzg3LDQ0LjcxNTU5IDE5LjUwNjU3NCw0NC41IDE0Ljc1LDQ0LjUgTCAwLjc1LDQ0LjUgTCAtMi40MDYyNSw0NC41IHogTSAzLjQ2ODc1LDQ3LjUgTCAxNC43NSw0Ny41IEMgMTkuNDM0MTczLDQ3LjUgMzMuMDM2ODUsNDcuMzY5NzkzIDQyLjcxODc1LDY0LjUgQyAzNy45NTE5NjQsNzIuOTI5MDc1IDMyLjE5NzQ2OSw3Ny4xODM5MSAyNyw3OS4zMTI1IEMgMjEuNjM5MzM5LDgxLjUwNzkyNCAxNy4xNTgwNzUsODEuNTAwMDAxIDE0Ljc1LDgxLjUgTCAzLjUsODEuNSBDIDUuMzczNTg4NCw3OC4zOTE1NjYgOC4yNSw3Mi40NTA2NSA4LjI1LDY0LjUgQyA4LjI1LDU2LjUyNjY0NiA1LjM0MTQ2ODYsNTAuNTk5ODE1IDMuNDY4NzUsNDcuNSB6IgogICAgICAgICBpZD0icGF0aDQ5NzMiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NzY2NjY3NjY2NjY2NjY2NzY2NzYyIgLz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo=' }}\n}, {\n operation: function(input1, input2) {\n return input1 || input2\n }\n})\n\njoint.shapes.logic.Gate21.define('logic.And', {\n attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IkFORCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI4IgogICAgIGlua3NjYXBlOmN4PSI1Ni42OTgzNDgiCiAgICAgaW5rc2NhcGU6Y3k9IjI1LjMyNjg5OSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDcwLDI1IGMgMjAsMCAyNSwwIDI1LDAiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEsMTUgNSwxNSIKICAgICAgIGlkPSJwYXRoMzA2MSIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMyLDM1IDUsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTptZWRpdW07Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDt0ZXh0LWluZGVudDowO3RleHQtYWxpZ246c3RhcnQ7dGV4dC1kZWNvcmF0aW9uOm5vbmU7bGluZS1oZWlnaHQ6bm9ybWFsO2xldHRlci1zcGFjaW5nOm5vcm1hbDt3b3JkLXNwYWNpbmc6bm9ybWFsO3RleHQtdHJhbnNmb3JtOm5vbmU7ZGlyZWN0aW9uOmx0cjtibG9jay1wcm9ncmVzc2lvbjp0Yjt3cml0aW5nLW1vZGU6bHItdGI7dGV4dC1hbmNob3I6c3RhcnQ7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDozO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGU7Zm9udC1mYW1pbHk6Qml0c3RyZWFtIFZlcmEgU2FuczstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOkJpdHN0cmVhbSBWZXJhIFNhbnMiCiAgICAgICBkPSJNIDMwLDUgTCAzMCw2LjQyODU3MTQgTCAzMCw0My41NzE0MjkgTCAzMCw0NSBMIDMxLjQyODU3MSw0NSBMIDUwLjQ3NjE5LDQ1IEMgNjEuNzQ0MDk4LDQ1IDcwLjQ3NjE5LDM1Ljk5OTk1NSA3MC40NzYxOSwyNSBDIDcwLjQ3NjE5LDE0LjAwMDA0NSA2MS43NDQwOTksNS4wMDAwMDAyIDUwLjQ3NjE5LDUgQyA1MC40NzYxOSw1IDUwLjQ3NjE5LDUgMzEuNDI4NTcxLDUgTCAzMCw1IHogTSAzMi44NTcxNDMsNy44NTcxNDI5IEMgNDAuODM0MjY0LDcuODU3MTQyOSA0NS45MTgzNjgsNy44NTcxNDI5IDQ4LjA5NTIzOCw3Ljg1NzE0MjkgQyA0OS4yODU3MTQsNy44NTcxNDI5IDQ5Ljg4MDk1Miw3Ljg1NzE0MjkgNTAuMTc4NTcxLDcuODU3MTQyOSBDIDUwLjMyNzM4MSw3Ljg1NzE0MjkgNTAuNDA5MjI3LDcuODU3MTQyOSA1MC40NDY0MjksNy44NTcxNDI5IEMgNTAuNDY1MDI5LDcuODU3MTQyOSA1MC40NzE1NDMsNy44NTcxNDI5IDUwLjQ3NjE5LDcuODU3MTQyOSBDIDYwLjIzNjg1Myw3Ljg1NzE0MyA2Ny4xNDI4NTcsMTUuNDk3MDk4IDY3LjE0Mjg1NywyNSBDIDY3LjE0Mjg1NywzNC41MDI5MDIgNTkuNzYwNjYyLDQyLjE0Mjg1NyA1MCw0Mi4xNDI4NTcgTCAzMi44NTcxNDMsNDIuMTQyODU3IEwgMzIuODU3MTQzLDcuODU3MTQyOSB6IgogICAgICAgaWQ9InBhdGgyODg0IgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NzY2NjY3Nzc3NzY2NjIiAvPgogIDwvZz4KPC9zdmc+Cg==' }}\n\n}, {\n operation: function(input1, input2) {\n return input1 && input2\n }\n})\n\njoint.shapes.logic.Gate21.define('logic.Nor', {\n attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PUiBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjEiCiAgICAgaW5rc2NhcGU6Y3g9Ijc4LjY3NzY0NCIKICAgICBpbmtzY2FwZTpjeT0iMjIuMTAyMzQ0IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjM3IgogICAgIGlua3NjYXBlOndpbmRvdy15PSItNCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDc5LDI1IEMgOTksMjUgOTUsMjUgOTUsMjUiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEsMTUgNSwxNSIKICAgICAgIGlkPSJwYXRoMzA2MSIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMyLDM1IDUsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8ZwogICAgICAgaWQ9ImcyNTYwIgogICAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNi41LC0zOS41KSI+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi40MDYyNSw0NC41IEwgLTAuNDA2MjUsNDYuOTM3NSBDIC0wLjQwNjI1LDQ2LjkzNzUgNS4yNSw1My45Mzc1NDkgNS4yNSw2NC41IEMgNS4yNSw3NS4wNjI0NTEgLTAuNDA2MjUsODIuMDYyNSAtMC40MDYyNSw4Mi4wNjI1IEwgLTIuNDA2MjUsODQuNSBMIDAuNzUsODQuNSBMIDE0Ljc1LDg0LjUgQyAxNy4xNTgwNzYsODQuNTAwMDAxIDIyLjQzOTY5OSw4NC41MjQ1MTQgMjguMzc1LDgyLjA5Mzc1IEMgMzQuMzEwMzAxLDc5LjY2Mjk4NiA0MC45MTE1MzYsNzQuNzUwNDg0IDQ2LjA2MjUsNjUuMjE4NzUgTCA0NC43NSw2NC41IEwgNDYuMDYyNSw2My43ODEyNSBDIDM1Ljc1OTM4Nyw0NC43MTU1OSAxOS41MDY1NzQsNDQuNSAxNC43NSw0NC41IEwgMC43NSw0NC41IEwgLTIuNDA2MjUsNDQuNSB6IE0gMy40Njg3NSw0Ny41IEwgMTQuNzUsNDcuNSBDIDE5LjQzNDE3Myw0Ny41IDMzLjAzNjg1LDQ3LjM2OTc5MyA0Mi43MTg3NSw2NC41IEMgMzcuOTUxOTY0LDcyLjkyOTA3NSAzMi4xOTc0NjksNzcuMTgzOTEgMjcsNzkuMzEyNSBDIDIxLjYzOTMzOSw4MS41MDc5MjQgMTcuMTU4MDc1LDgxLjUwMDAwMSAxNC43NSw4MS41IEwgMy41LDgxLjUgQyA1LjM3MzU4ODQsNzguMzkxNTY2IDguMjUsNzIuNDUwNjUgOC4yNSw2NC41IEMgOC4yNSw1Ni41MjY2NDYgNS4zNDE0Njg2LDUwLjU5OTgxNSAzLjQ2ODc1LDQ3LjUgeiIKICAgICAgICAgaWQ9InBhdGg0OTczIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjc2NjY2NzY2NjY2NjY2Njc2Njc2MiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHNvZGlwb2RpOnR5cGU9ImFyYyIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgICBpZD0icGF0aDI2MDQiCiAgICAgICAgIHNvZGlwb2RpOmN4PSI3NSIKICAgICAgICAgc29kaXBvZGk6Y3k9IjI1IgogICAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgICAgc29kaXBvZGk6cnk9IjQiCiAgICAgICAgIGQ9Ik0gNzksMjUgQSA0LDQgMCAxIDEgNzEsMjUgQSA0LDQgMCAxIDEgNzksMjUgeiIKICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTI2LjUsMzkuNSkiIC8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K' }}\n}, {\n operation: function(input1, input2) {\n return !(input1 || input2)\n }\n})\n\njoint.shapes.logic.Gate21.define('logic.Nand', {\n attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5BTkQgQU5TSS5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzNCI+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxMCA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzE0IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIwLjUgOiAwLjMzMzMzMzMzIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MDYiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MTkiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzcyLjA0NzI0IDogMzUwLjc4NzM5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9Ijc0NC4wOTQ0OCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTI2LjE4MTA5IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3NzciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNzUgOiA0MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxNTAgOiA2MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNjAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMzI3NSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI1MCA6IDMzLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxMDAgOiA1MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlNTUzMyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6em9vbT0iMTYiCiAgICAgaW5rc2NhcGU6Y3g9Ijc4LjI4MzMwNyIKICAgICBpbmtzY2FwZTpjeT0iMTYuNDQyODQzIgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzksMjUgQyA5MS44LDI1IDk1LDI1IDk1LDI1IgogICAgICAgaWQ9InBhdGgzMDU5IgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMxLDE1IDUsMTUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMiwzNSA1LDM1IgogICAgICAgaWQ9InBhdGgzOTQ0IiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmb250LXNpemU6bWVkaXVtO2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7dGV4dC1pbmRlbnQ6MDt0ZXh0LWFsaWduOnN0YXJ0O3RleHQtZGVjb3JhdGlvbjpub25lO2xpbmUtaGVpZ2h0Om5vcm1hbDtsZXR0ZXItc3BhY2luZzpub3JtYWw7d29yZC1zcGFjaW5nOm5vcm1hbDt0ZXh0LXRyYW5zZm9ybTpub25lO2RpcmVjdGlvbjpsdHI7YmxvY2stcHJvZ3Jlc3Npb246dGI7d3JpdGluZy1tb2RlOmxyLXRiO3RleHQtYW5jaG9yOnN0YXJ0O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MzttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlO2ZvbnQtZmFtaWx5OkJpdHN0cmVhbSBWZXJhIFNhbnM7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpCaXRzdHJlYW0gVmVyYSBTYW5zIgogICAgICAgZD0iTSAzMCw1IEwgMzAsNi40Mjg1NzE0IEwgMzAsNDMuNTcxNDI5IEwgMzAsNDUgTCAzMS40Mjg1NzEsNDUgTCA1MC40NzYxOSw0NSBDIDYxLjc0NDA5OCw0NSA3MC40NzYxOSwzNS45OTk5NTUgNzAuNDc2MTksMjUgQyA3MC40NzYxOSwxNC4wMDAwNDUgNjEuNzQ0MDk5LDUuMDAwMDAwMiA1MC40NzYxOSw1IEMgNTAuNDc2MTksNSA1MC40NzYxOSw1IDMxLjQyODU3MSw1IEwgMzAsNSB6IE0gMzIuODU3MTQzLDcuODU3MTQyOSBDIDQwLjgzNDI2NCw3Ljg1NzE0MjkgNDUuOTE4MzY4LDcuODU3MTQyOSA0OC4wOTUyMzgsNy44NTcxNDI5IEMgNDkuMjg1NzE0LDcuODU3MTQyOSA0OS44ODA5NTIsNy44NTcxNDI5IDUwLjE3ODU3MSw3Ljg1NzE0MjkgQyA1MC4zMjczODEsNy44NTcxNDI5IDUwLjQwOTIyNyw3Ljg1NzE0MjkgNTAuNDQ2NDI5LDcuODU3MTQyOSBDIDUwLjQ2NTAyOSw3Ljg1NzE0MjkgNTAuNDcxNTQzLDcuODU3MTQyOSA1MC40NzYxOSw3Ljg1NzE0MjkgQyA2MC4yMzY4NTMsNy44NTcxNDMgNjcuMTQyODU3LDE1LjQ5NzA5OCA2Ny4xNDI4NTcsMjUgQyA2Ny4xNDI4NTcsMzQuNTAyOTAyIDU5Ljc2MDY2Miw0Mi4xNDI4NTcgNTAsNDIuMTQyODU3IEwgMzIuODU3MTQzLDQyLjE0Mjg1NyBMIDMyLjg1NzE0Myw3Ljg1NzE0MjkgeiIKICAgICAgIGlkPSJwYXRoMjg4NCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2Njc2NjY2Nzc3Nzc2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDQwMDgiCiAgICAgICBzb2RpcG9kaTpjeD0iNzUiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA3OSwyNSBBIDQsNCAwIDEgMSA3MSwyNSBBIDQsNCAwIDEgMSA3OSwyNSB6IiAvPgogIDwvZz4KPC9zdmc+Cg==' }}\n}, {\n operation: function(input1, input2) {\n return !(input1 && input2)\n }\n})\n\njoint.shapes.logic.Gate21.define('logic.Xor', {\n attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IlhPUiBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjUuNjU2ODU0MiIKICAgICBpbmtzY2FwZTpjeD0iMjUuOTM4MTE2IgogICAgIGlua3NjYXBlOmN5PSIxNy4yMzAwNSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDcwLDI1IGMgMjAsMCAyNSwwIDI1LDAiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzAuMzg1NzE3LDE1IEwgNC45OTk5OTk4LDE1IgogICAgICAgaWQ9InBhdGgzMDYxIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5NzY7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEuMzYyMDkxLDM1IEwgNC45OTk5OTk4LDM1IgogICAgICAgaWQ9InBhdGgzOTQ0IiAvPgogICAgPGcKICAgICAgIGlkPSJnMjU2MCIKICAgICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjYuNSwtMzkuNSkiPgogICAgICA8cGF0aAogICAgICAgICBpZD0icGF0aDM1MTYiCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi4yNSw4MS41MDAwMDUgQyAtMy44NDczNzQsODQuMTQ0NDA1IC00LjUsODQuNTAwMDA1IC00LjUsODQuNTAwMDA1IEwgLTguMTU2MjUsODQuNTAwMDA1IEwgLTYuMTU2MjUsODIuMDYyNTA1IEMgLTYuMTU2MjUsODIuMDYyNTA1IC0wLjUsNzUuMDYyNDUxIC0wLjUsNjQuNSBDIC0wLjUsNTMuOTM3NTQ5IC02LjE1NjI1LDQ2LjkzNzUgLTYuMTU2MjUsNDYuOTM3NSBMIC04LjE1NjI1LDQ0LjUgTCAtNC41LDQ0LjUgQyAtMy43MTg3NSw0NS40Mzc1IC0zLjA3ODEyNSw0Ni4xNTYyNSAtMi4yODEyNSw0Ny41IEMgLTAuNDA4NTMxLDUwLjU5OTgxNSAyLjUsNTYuNTI2NjQ2IDIuNSw2NC41IEMgMi41LDcyLjQ1MDY1IC0wLjM5NjY5Nyw3OC4zNzk0MjUgLTIuMjUsODEuNTAwMDA1IHoiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY3NjY2Njc2MiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi40MDYyNSw0NC41IEwgLTAuNDA2MjUsNDYuOTM3NSBDIC0wLjQwNjI1LDQ2LjkzNzUgNS4yNSw1My45Mzc1NDkgNS4yNSw2NC41IEMgNS4yNSw3NS4wNjI0NTEgLTAuNDA2MjUsODIuMDYyNSAtMC40MDYyNSw4Mi4wNjI1IEwgLTIuNDA2MjUsODQuNSBMIDAuNzUsODQuNSBMIDE0Ljc1LDg0LjUgQyAxNy4xNTgwNzYsODQuNTAwMDAxIDIyLjQzOTY5OSw4NC41MjQ1MTQgMjguMzc1LDgyLjA5Mzc1IEMgMzQuMzEwMzAxLDc5LjY2Mjk4NiA0MC45MTE1MzYsNzQuNzUwNDg0IDQ2LjA2MjUsNjUuMjE4NzUgTCA0NC43NSw2NC41IEwgNDYuMDYyNSw2My43ODEyNSBDIDM1Ljc1OTM4Nyw0NC43MTU1OSAxOS41MDY1NzQsNDQuNSAxNC43NSw0NC41IEwgMC43NSw0NC41IEwgLTIuNDA2MjUsNDQuNSB6IE0gMy40Njg3NSw0Ny41IEwgMTQuNzUsNDcuNSBDIDE5LjQzNDE3Myw0Ny41IDMzLjAzNjg1LDQ3LjM2OTc5MyA0Mi43MTg3NSw2NC41IEMgMzcuOTUxOTY0LDcyLjkyOTA3NSAzMi4xOTc0NjksNzcuMTgzOTEgMjcsNzkuMzEyNSBDIDIxLjYzOTMzOSw4MS41MDc5MjQgMTcuMTU4MDc1LDgxLjUwMDAwMSAxNC43NSw4MS41IEwgMy41LDgxLjUgQyA1LjM3MzU4ODQsNzguMzkxNTY2IDguMjUsNzIuNDUwNjUgOC4yNSw2NC41IEMgOC4yNSw1Ni41MjY2NDYgNS4zNDE0Njg2LDUwLjU5OTgxNSAzLjQ2ODc1LDQ3LjUgeiIKICAgICAgICAgaWQ9InBhdGg0OTczIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjc2NjY2NzY2NjY2NjY2Njc2Njc2MiIC8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K' }}\n}, {\n operation: function(input1, input2) {\n return (!input1 || input2) && (input1 || !input2)\n }\n})\n\njoint.shapes.logic.Gate21.define('logic.Xnor', {\n attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IlhOT1IgQU5TSS5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzNCI+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxMCA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzE0IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIwLjUgOiAwLjMzMzMzMzMzIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MDYiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MTkiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzcyLjA0NzI0IDogMzUwLjc4NzM5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9Ijc0NC4wOTQ0OCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTI2LjE4MTA5IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3NzciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNzUgOiA0MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxNTAgOiA2MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNjAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMzI3NSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI1MCA6IDMzLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxMDAgOiA1MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlNTUzMyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNTU3IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjI1IDogMTYuNjY2NjY3IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjUwIDogMjUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDI1IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI0IgogICAgIGlua3NjYXBlOmN4PSI5NS43MjM2NiIKICAgICBpbmtzY2FwZTpjeT0iLTI2Ljc3NTAyMyIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyLjAwMDAwMDI0O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDc4LjMzMzMzMiwyNSBDIDkxLjY2NjY2NiwyNSA5NSwyNSA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMC4zODU3MTcsMTUgTCA0Ljk5OTk5OTgsMTUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk3NjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMS4zNjIwOTEsMzUgTCA0Ljk5OTk5OTgsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8ZwogICAgICAgaWQ9ImcyNTYwIgogICAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNi41LC0zOS41KSI+CiAgICAgIDxwYXRoCiAgICAgICAgIGlkPSJwYXRoMzUxNiIKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjI1LDgxLjUwMDAwNSBDIC0zLjg0NzM3NCw4NC4xNDQ0MDUgLTQuNSw4NC41MDAwMDUgLTQuNSw4NC41MDAwMDUgTCAtOC4xNTYyNSw4NC41MDAwMDUgTCAtNi4xNTYyNSw4Mi4wNjI1MDUgQyAtNi4xNTYyNSw4Mi4wNjI1MDUgLTAuNSw3NS4wNjI0NTEgLTAuNSw2NC41IEMgLTAuNSw1My45Mzc1NDkgLTYuMTU2MjUsNDYuOTM3NSAtNi4xNTYyNSw0Ni45Mzc1IEwgLTguMTU2MjUsNDQuNSBMIC00LjUsNDQuNSBDIC0zLjcxODc1LDQ1LjQzNzUgLTMuMDc4MTI1LDQ2LjE1NjI1IC0yLjI4MTI1LDQ3LjUgQyAtMC40MDg1MzEsNTAuNTk5ODE1IDIuNSw1Ni41MjY2NDYgMi41LDY0LjUgQyAyLjUsNzIuNDUwNjUgLTAuMzk2Njk3LDc4LjM3OTQyNSAtMi4yNSw4MS41MDAwMDUgeiIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2Njc2NjY2NzYyIgLz4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjQwNjI1LDQ0LjUgTCAtMC40MDYyNSw0Ni45Mzc1IEMgLTAuNDA2MjUsNDYuOTM3NSA1LjI1LDUzLjkzNzU0OSA1LjI1LDY0LjUgQyA1LjI1LDc1LjA2MjQ1MSAtMC40MDYyNSw4Mi4wNjI1IC0wLjQwNjI1LDgyLjA2MjUgTCAtMi40MDYyNSw4NC41IEwgMC43NSw4NC41IEwgMTQuNzUsODQuNSBDIDE3LjE1ODA3Niw4NC41MDAwMDEgMjIuNDM5Njk5LDg0LjUyNDUxNCAyOC4zNzUsODIuMDkzNzUgQyAzNC4zMTAzMDEsNzkuNjYyOTg2IDQwLjkxMTUzNiw3NC43NTA0ODQgNDYuMDYyNSw2NS4yMTg3NSBMIDQ0Ljc1LDY0LjUgTCA0Ni4wNjI1LDYzLjc4MTI1IEMgMzUuNzU5Mzg3LDQ0LjcxNTU5IDE5LjUwNjU3NCw0NC41IDE0Ljc1LDQ0LjUgTCAwLjc1LDQ0LjUgTCAtMi40MDYyNSw0NC41IHogTSAzLjQ2ODc1LDQ3LjUgTCAxNC43NSw0Ny41IEMgMTkuNDM0MTczLDQ3LjUgMzMuMDM2ODUsNDcuMzY5NzkzIDQyLjcxODc1LDY0LjUgQyAzNy45NTE5NjQsNzIuOTI5MDc1IDMyLjE5NzQ2OSw3Ny4xODM5MSAyNyw3OS4zMTI1IEMgMjEuNjM5MzM5LDgxLjUwNzkyNCAxNy4xNTgwNzUsODEuNTAwMDAxIDE0Ljc1LDgxLjUgTCAzLjUsODEuNSBDIDUuMzczNTg4NCw3OC4zOTE1NjYgOC4yNSw3Mi40NTA2NSA4LjI1LDY0LjUgQyA4LjI1LDU2LjUyNjY0NiA1LjM0MTQ2ODYsNTAuNTk5ODE1IDMuNDY4NzUsNDcuNSB6IgogICAgICAgICBpZD0icGF0aDQ5NzMiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NzY2NjY3NjY2NjY2NjY2NzY2NzYyIgLz4KICAgIDwvZz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDM1NTEiCiAgICAgICBzb2RpcG9kaTpjeD0iNzUiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA3OSwyNSBBIDQsNCAwIDEgMSA3MSwyNSBBIDQsNCAwIDEgMSA3OSwyNSB6IiAvPgogIDwvZz4KPC9zdmc+Cg==' }}\n}, {\n operation: function(input1, input2) {\n return (!input1 || !input2) && (input1 || input2)\n }\n})\n\njoint.dia.Link.define('logic.Wire', {\n attrs: {\n '.connection': { 'stroke-width': 2 },\n '.marker-vertex': { r: 7 }\n },\n\n router: { name: 'orthogonal' },\n connector: { name: 'rounded', args: { radius: 10 }}\n}, {\n arrowheadMarkup: [\n '\">',\n '\" r=\"7\"/>',\n ''\n ].join(''),\n\n vertexMarkup: [\n ', <%= y %>)\">',\n '\" r=\"10\" />',\n '',\n '\" d=\"M16,5.333c-7.732,0-14,4.701-14,10.5c0,1.982,0.741,3.833,2.016,5.414L2,25.667l5.613-1.441c2.339,1.317,5.237,2.107,8.387,2.107c7.732,0,14-4.701,14-10.5C30,10.034,23.732,5.333,16,5.333z\" transform=\"translate(5, -33)\"/>',\n '\" transform=\"scale(.8) translate(9.5, -37)\" d=\"M24.778,21.419 19.276,15.917 24.777,10.415 21.949,7.585 16.447,13.087 10.945,7.585 8.117,10.415 13.618,15.917 8.116,21.419 10.946,24.248 16.447,18.746 21.948,24.248z\">',\n 'Remove vertex.',\n '',\n '',\n ''\n ].join('')\n})\n\nif (typeof exports === 'object') {\n\n var graphlib = require('graphlib')\n var dagre = require('dagre')\n}\n\n// In the browser, these variables are set to undefined because of JavaScript hoisting.\n// In that case, should grab them from the window object.\ngraphlib = graphlib || (typeof window !== 'undefined' && window.graphlib)\ndagre = dagre || (typeof window !== 'undefined' && window.dagre)\n\njoint.layout.DirectedGraph = {\n\n exportElement: function(element) {\n\n // The width and height of the element.\n return element.size()\n },\n\n exportLink: function(link) {\n\n const labelSize = link.get('labelSize') || {}\n const edge = {\n // The number of ranks to keep between the source and target of the edge.\n minLen: link.get('minLen') || 1,\n // The weight to assign edges. Higher weight edges are generally\n // made shorter and straighter than lower weight edges.\n weight: link.get('weight') || 1,\n // Where to place the label relative to the edge.\n // l = left, c = center r = right.\n labelpos: link.get('labelPosition') || 'c',\n // How many pixels to move the label away from the edge.\n // Applies only when labelpos is l or r.\n labeloffset: link.get('labelOffset') || 0,\n // The width of the edge label in pixels.\n width: labelSize.width || 0,\n // The height of the edge label in pixels.\n height: labelSize.height || 0\n }\n\n return edge\n },\n\n importElement: function(opt, v, gl) {\n\n const element = this.getCell(v)\n const glNode = gl.node(v)\n\n if (opt.setPosition) {\n opt.setPosition(element, glNode)\n } else {\n element.set('position', {\n x: glNode.x - glNode.width / 2,\n y: glNode.y - glNode.height / 2\n })\n }\n },\n\n importLink: function(opt, edgeObj, gl) {\n\n const link = this.getCell(edgeObj.name)\n const glEdge = gl.edge(edgeObj)\n const points = glEdge.points || []\n\n // check the `setLinkVertices` here for backwards compatibility\n if (opt.setVertices || opt.setLinkVertices) {\n if (joint.util.isFunction(opt.setVertices)) {\n opt.setVertices(link, points)\n } else {\n // Remove the first and last point from points array.\n // Those are source/target element connection points\n // ie. they lies on the edge of connected elements.\n link.set('vertices', points.slice(1, points.length - 1))\n }\n }\n\n if (opt.setLabels && ('x' in glEdge) && ('y' in glEdge)) {\n const labelPosition = { x: glEdge.x, y: glEdge.y }\n if (joint.util.isFunction(opt.setLabels)) {\n opt.setLabels(link, labelPosition, points)\n } else {\n // Convert the absolute label position to a relative position\n // towards the closest point on the edge\n const polyline = g.Polyline(points)\n const length = polyline.closestPointLength(labelPosition)\n const closestPoint = polyline.pointAtLength(length)\n const distance = length / polyline.length()\n link.label(0, {\n position: {\n distance: distance,\n offset: g.Point(labelPosition).difference(closestPoint).toJSON()\n }\n })\n }\n }\n },\n\n layout: function(graphOrCells, opt) {\n\n let graph\n\n if (graphOrCells instanceof joint.dia.Graph) {\n graph = graphOrCells\n } else {\n // Reset cells in dry mode so the graph reference is not stored on the cells.\n // `sort: false` to prevent elements to change their order based on the z-index\n graph = (new joint.dia.Graph()).resetCells(graphOrCells, { dry: true, sort: false })\n }\n\n // This is not needed anymore.\n graphOrCells = null\n\n opt = joint.util.defaults(opt || {}, {\n resizeClusters: true,\n clusterPadding: 10,\n exportElement: this.exportElement,\n exportLink: this.exportLink\n })\n\n // create a graphlib.Graph that represents the joint.dia.Graph\n const glGraph = graph.toGraphLib({\n directed: true,\n // We are about to use edge naming feature.\n multigraph: true,\n // We are able to layout graphs with embeds.\n compound: true,\n setNodeLabel: opt.exportElement,\n setEdgeLabel: opt.exportLink,\n setEdgeName: function(link) {\n // Graphlib edges have no ids. We use edge name property\n // to store and retrieve ids instead.\n return link.id\n }\n })\n\n const glLabel = {}\n const marginX = opt.marginX || 0\n const marginY = opt.marginY || 0\n\n // Dagre layout accepts options as lower case.\n // Direction for rank nodes. Can be TB, BT, LR, or RL\n if (opt.rankDir) glLabel.rankdir = opt.rankDir\n // Alignment for rank nodes. Can be UL, UR, DL, or DR\n if (opt.align) glLabel.align = opt.align\n // Number of pixels that separate nodes horizontally in the layout.\n if (opt.nodeSep) glLabel.nodesep = opt.nodeSep\n // Number of pixels that separate edges horizontally in the layout.\n if (opt.edgeSep) glLabel.edgesep = opt.edgeSep\n // Number of pixels between each rank in the layout.\n if (opt.rankSep) glLabel.ranksep = opt.rankSep\n // Type of algorithm to assign a rank to each node in the input graph.\n // Possible values: network-simplex, tight-tree or longest-path\n if (opt.ranker) glLabel.ranker = opt.ranker\n // Number of pixels to use as a margin around the left and right of the graph.\n if (marginX) glLabel.marginx = marginX\n // Number of pixels to use as a margin around the top and bottom of the graph.\n if (marginY) glLabel.marginy = marginY\n\n // Set the option object for the graph label.\n glGraph.setGraph(glLabel)\n\n // Executes the layout.\n dagre.layout(glGraph, { debugTiming: !!opt.debugTiming })\n\n // Wrap all graph changes into a batch.\n graph.startBatch('layout')\n\n // Update the graph.\n graph.fromGraphLib(glGraph, {\n importNode: this.importElement.bind(graph, opt),\n importEdge: this.importLink.bind(graph, opt)\n })\n\n if (opt.resizeClusters) {\n // Resize and reposition cluster elements (parents of other elements)\n // to fit their children.\n // 1. filter clusters only\n // 2. map id on cells\n // 3. sort cells by their depth (the deepest first)\n // 4. resize cell to fit their direct children only.\n const clusters = glGraph.nodes()\n .filter(function(v) { return glGraph.children(v).length > 0 })\n .map(graph.getCell.bind(graph))\n .sort(function(aCluster, bCluster) {\n return bCluster.getAncestors().length - aCluster.getAncestors().length\n })\n\n joint.util.invoke(clusters, 'fitEmbeds', { padding: opt.clusterPadding })\n }\n\n graph.stopBatch('layout')\n\n // Width and height of the graph extended by margins.\n const glSize = glGraph.graph()\n // Return the bounding box of the graph after the layout.\n return g.Rect(\n marginX,\n marginY,\n Math.abs(glSize.width - 2 * marginX),\n Math.abs(glSize.height - 2 * marginY)\n )\n },\n\n fromGraphLib: function(glGraph, opt) {\n\n opt = opt || {}\n\n const importNode = opt.importNode || joint.util.noop\n const importEdge = opt.importEdge || joint.util.noop\n const graph = (this instanceof joint.dia.Graph) ? this : new joint.dia.Graph\n\n // Import all nodes.\n glGraph.nodes().forEach(function(node) {\n importNode.call(graph, node, glGraph, graph, opt)\n })\n\n // Import all edges.\n glGraph.edges().forEach(function(edge) {\n importEdge.call(graph, edge, glGraph, graph, opt)\n })\n\n return graph\n },\n\n // Create new graphlib graph from existing JointJS graph.\n toGraphLib: function(graph, opt) {\n\n opt = opt || {}\n\n const glGraphType = joint.util.pick(opt, 'directed', 'compound', 'multigraph')\n const glGraph = new graphlib.Graph(glGraphType)\n const setNodeLabel = opt.setNodeLabel || joint.util.noop\n const setEdgeLabel = opt.setEdgeLabel || joint.util.noop\n const setEdgeName = opt.setEdgeName || joint.util.noop\n const collection = graph.get('cells')\n\n for (let i = 0, n = collection.length; i < n; i++) {\n\n const cell = collection.at(i)\n if (cell.isLink()) {\n\n const source = cell.get('source')\n const target = cell.get('target')\n\n // Links that end at a point are ignored.\n if (!source.id || !target.id) break\n\n // Note that if we are creating a multigraph we can name the edges. If\n // we try to name edges on a non-multigraph an exception is thrown.\n glGraph.setEdge(source.id, target.id, setEdgeLabel(cell), setEdgeName(cell))\n\n } else {\n\n glGraph.setNode(cell.id, setNodeLabel(cell))\n\n // For the compound graphs we have to take embeds into account.\n if (glGraph.isCompound() && cell.has('parent')) {\n const parentId = cell.get('parent')\n if (collection.has(parentId)) {\n // Make sure the parent cell is included in the graph (this can\n // happen when the layout is run on part of the graph only).\n glGraph.setParent(cell.id, parentId)\n }\n }\n }\n }\n\n return glGraph\n }\n}\n\njoint.dia.Graph.prototype.toGraphLib = function(opt) {\n\n return joint.layout.DirectedGraph.toGraphLib(this, opt)\n}\n\njoint.dia.Graph.prototype.fromGraphLib = function(glGraph, opt) {\n\n return joint.layout.DirectedGraph.fromGraphLib.call(this, glGraph, opt)\n};\n\n/**\n * Copyright 2015 The Chromium Authors. All rights reserved.\n *\n * The Chromium Authors can be found at\n * http://src.chromium.org/svn/trunk/src/AUTHORS\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n * * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n * * Neither the name of Google Inc. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// SVGPathSeg API polyfill\n// https://github.com/progers/pathseg\n//\n// This is a drop-in replacement for the SVGPathSeg and SVGPathSegList APIs that were removed from\n// SVG2 (https://lists.w3.org/Archives/Public/www-svg/2015Jun/0044.html), including the latest spec\n// changes which were implemented in Firefox 43 and Chrome 46.\n\n(function() { \n if (typeof window !== 'undefined' && !(\"SVGPathSeg\" in window)) {\n // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSeg\n window.SVGPathSeg = function(type, typeAsLetter, owningPathSegList) {\n this.pathSegType = type\n this.pathSegTypeAsLetter = typeAsLetter\n this._owningPathSegList = owningPathSegList\n }\n\n window.SVGPathSeg.prototype.classname = \"SVGPathSeg\"\n\n window.SVGPathSeg.PATHSEG_UNKNOWN = 0\n window.SVGPathSeg.PATHSEG_CLOSEPATH = 1\n window.SVGPathSeg.PATHSEG_MOVETO_ABS = 2\n window.SVGPathSeg.PATHSEG_MOVETO_REL = 3\n window.SVGPathSeg.PATHSEG_LINETO_ABS = 4\n window.SVGPathSeg.PATHSEG_LINETO_REL = 5\n window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS = 6\n window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL = 7\n window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS = 8\n window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL = 9\n window.SVGPathSeg.PATHSEG_ARC_ABS = 10\n window.SVGPathSeg.PATHSEG_ARC_REL = 11\n window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS = 12\n window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL = 13\n window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS = 14\n window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL = 15\n window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS = 16\n window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL = 17\n window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS = 18\n window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL = 19\n\n // Notify owning PathSegList on any changes so they can be synchronized back to the path element.\n window.SVGPathSeg.prototype._segmentChanged = function() {\n if (this._owningPathSegList)\n this._owningPathSegList.segmentChanged(this)\n }\n\n window.SVGPathSegClosePath = function(owningPathSegList) {\n window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CLOSEPATH, \"z\", owningPathSegList)\n }\n window.SVGPathSegClosePath.prototype = Object.create(window.SVGPathSeg.prototype)\n window.SVGPathSegClosePath.prototype.toString = function() { return \"[object SVGPathSegClosePath]\" }\n window.SVGPathSegClosePath.prototype._asPathString = function() { return this.pathSegTypeAsLetter }\n window.SVGPathSegClosePath.prototype.clone = function() { return new window.SVGPathSegClosePath(undefined) }\n\n window.SVGPathSegMovetoAbs = function(owningPathSegList, x, y) {\n window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_ABS, \"M\", owningPathSegList)\n this._x = x\n this._y = y\n }\n window.SVGPathSegMovetoAbs.prototype = Object.create(window.SVGPathSeg.prototype)\n window.SVGPathSegMovetoAbs.prototype.toString = function() { return \"[object SVGPathSegMovetoAbs]\" }\n window.SVGPathSegMovetoAbs.prototype._asPathString = function() { return `${this.pathSegTypeAsLetter } ${ this._x } ${ this._y}` }\n window.SVGPathSegMovetoAbs.prototype.clone = function() { return new window.SVGPathSegMovetoAbs(undefined, this._x, this._y) }\n Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, \"x\", { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, \"y\", { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged() }, enumerable: true })\n\n window.SVGPathSegMovetoRel = function(owningPathSegList, x, y) {\n window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_REL, \"m\", owningPathSegList)\n this._x = x\n this._y = y\n }\n window.SVGPathSegMovetoRel.prototype = Object.create(window.SVGPathSeg.prototype)\n window.SVGPathSegMovetoRel.prototype.toString = function() { return \"[object SVGPathSegMovetoRel]\" }\n window.SVGPathSegMovetoRel.prototype._asPathString = function() { return `${this.pathSegTypeAsLetter } ${ this._x } ${ this._y}` }\n window.SVGPathSegMovetoRel.prototype.clone = function() { return new window.SVGPathSegMovetoRel(undefined, this._x, this._y) }\n Object.defineProperty(window.SVGPathSegMovetoRel.prototype, \"x\", { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegMovetoRel.prototype, \"y\", { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged() }, enumerable: true })\n\n window.SVGPathSegLinetoAbs = function(owningPathSegList, x, y) {\n window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_ABS, \"L\", owningPathSegList)\n this._x = x\n this._y = y\n }\n window.SVGPathSegLinetoAbs.prototype = Object.create(window.SVGPathSeg.prototype)\n window.SVGPathSegLinetoAbs.prototype.toString = function() { return \"[object SVGPathSegLinetoAbs]\" }\n window.SVGPathSegLinetoAbs.prototype._asPathString = function() { return `${this.pathSegTypeAsLetter } ${ this._x } ${ this._y}` }\n window.SVGPathSegLinetoAbs.prototype.clone = function() { return new window.SVGPathSegLinetoAbs(undefined, this._x, this._y) }\n Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, \"x\", { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, \"y\", { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged() }, enumerable: true })\n\n window.SVGPathSegLinetoRel = function(owningPathSegList, x, y) {\n window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_REL, \"l\", owningPathSegList)\n this._x = x\n this._y = y\n }\n window.SVGPathSegLinetoRel.prototype = Object.create(window.SVGPathSeg.prototype)\n window.SVGPathSegLinetoRel.prototype.toString = function() { return \"[object SVGPathSegLinetoRel]\" }\n window.SVGPathSegLinetoRel.prototype._asPathString = function() { return `${this.pathSegTypeAsLetter } ${ this._x } ${ this._y}` }\n window.SVGPathSegLinetoRel.prototype.clone = function() { return new window.SVGPathSegLinetoRel(undefined, this._x, this._y) }\n Object.defineProperty(window.SVGPathSegLinetoRel.prototype, \"x\", { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegLinetoRel.prototype, \"y\", { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged() }, enumerable: true })\n\n window.SVGPathSegCurvetoCubicAbs = function(owningPathSegList, x, y, x1, y1, x2, y2) {\n window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS, \"C\", owningPathSegList)\n this._x = x\n this._y = y\n this._x1 = x1\n this._y1 = y1\n this._x2 = x2\n this._y2 = y2\n }\n window.SVGPathSegCurvetoCubicAbs.prototype = Object.create(window.SVGPathSeg.prototype)\n window.SVGPathSegCurvetoCubicAbs.prototype.toString = function() { return \"[object SVGPathSegCurvetoCubicAbs]\" }\n window.SVGPathSegCurvetoCubicAbs.prototype._asPathString = function() { return `${this.pathSegTypeAsLetter } ${ this._x1 } ${ this._y1 } ${ this._x2 } ${ this._y2 } ${ this._x } ${ this._y}` }\n window.SVGPathSegCurvetoCubicAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicAbs(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2) }\n Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, \"x\", { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, \"y\", { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, \"x1\", { get: function() { return this._x1 }, set: function(x1) { this._x1 = x1; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, \"y1\", { get: function() { return this._y1 }, set: function(y1) { this._y1 = y1; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, \"x2\", { get: function() { return this._x2 }, set: function(x2) { this._x2 = x2; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, \"y2\", { get: function() { return this._y2 }, set: function(y2) { this._y2 = y2; this._segmentChanged() }, enumerable: true })\n\n window.SVGPathSegCurvetoCubicRel = function(owningPathSegList, x, y, x1, y1, x2, y2) {\n window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL, \"c\", owningPathSegList)\n this._x = x\n this._y = y\n this._x1 = x1\n this._y1 = y1\n this._x2 = x2\n this._y2 = y2\n }\n window.SVGPathSegCurvetoCubicRel.prototype = Object.create(window.SVGPathSeg.prototype)\n window.SVGPathSegCurvetoCubicRel.prototype.toString = function() { return \"[object SVGPathSegCurvetoCubicRel]\" }\n window.SVGPathSegCurvetoCubicRel.prototype._asPathString = function() { return `${this.pathSegTypeAsLetter } ${ this._x1 } ${ this._y1 } ${ this._x2 } ${ this._y2 } ${ this._x } ${ this._y}` }\n window.SVGPathSegCurvetoCubicRel.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicRel(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2) }\n Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, \"x\", { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, \"y\", { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, \"x1\", { get: function() { return this._x1 }, set: function(x1) { this._x1 = x1; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, \"y1\", { get: function() { return this._y1 }, set: function(y1) { this._y1 = y1; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, \"x2\", { get: function() { return this._x2 }, set: function(x2) { this._x2 = x2; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, \"y2\", { get: function() { return this._y2 }, set: function(y2) { this._y2 = y2; this._segmentChanged() }, enumerable: true })\n\n window.SVGPathSegCurvetoQuadraticAbs = function(owningPathSegList, x, y, x1, y1) {\n window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS, \"Q\", owningPathSegList)\n this._x = x\n this._y = y\n this._x1 = x1\n this._y1 = y1\n }\n window.SVGPathSegCurvetoQuadraticAbs.prototype = Object.create(window.SVGPathSeg.prototype)\n window.SVGPathSegCurvetoQuadraticAbs.prototype.toString = function() { return \"[object SVGPathSegCurvetoQuadraticAbs]\" }\n window.SVGPathSegCurvetoQuadraticAbs.prototype._asPathString = function() { return `${this.pathSegTypeAsLetter } ${ this._x1 } ${ this._y1 } ${ this._x } ${ this._y}` }\n window.SVGPathSegCurvetoQuadraticAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticAbs(undefined, this._x, this._y, this._x1, this._y1) }\n Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, \"x\", { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, \"y\", { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, \"x1\", { get: function() { return this._x1 }, set: function(x1) { this._x1 = x1; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, \"y1\", { get: function() { return this._y1 }, set: function(y1) { this._y1 = y1; this._segmentChanged() }, enumerable: true })\n\n window.SVGPathSegCurvetoQuadraticRel = function(owningPathSegList, x, y, x1, y1) {\n window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL, \"q\", owningPathSegList)\n this._x = x\n this._y = y\n this._x1 = x1\n this._y1 = y1\n }\n window.SVGPathSegCurvetoQuadraticRel.prototype = Object.create(window.SVGPathSeg.prototype)\n window.SVGPathSegCurvetoQuadraticRel.prototype.toString = function() { return \"[object SVGPathSegCurvetoQuadraticRel]\" }\n window.SVGPathSegCurvetoQuadraticRel.prototype._asPathString = function() { return `${this.pathSegTypeAsLetter } ${ this._x1 } ${ this._y1 } ${ this._x } ${ this._y}` }\n window.SVGPathSegCurvetoQuadraticRel.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticRel(undefined, this._x, this._y, this._x1, this._y1) }\n Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, \"x\", { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, \"y\", { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, \"x1\", { get: function() { return this._x1 }, set: function(x1) { this._x1 = x1; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, \"y1\", { get: function() { return this._y1 }, set: function(y1) { this._y1 = y1; this._segmentChanged() }, enumerable: true })\n\n window.SVGPathSegArcAbs = function(owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) {\n window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_ABS, \"A\", owningPathSegList)\n this._x = x\n this._y = y\n this._r1 = r1\n this._r2 = r2\n this._angle = angle\n this._largeArcFlag = largeArcFlag\n this._sweepFlag = sweepFlag\n }\n window.SVGPathSegArcAbs.prototype = Object.create(window.SVGPathSeg.prototype)\n window.SVGPathSegArcAbs.prototype.toString = function() { return \"[object SVGPathSegArcAbs]\" }\n window.SVGPathSegArcAbs.prototype._asPathString = function() { return `${this.pathSegTypeAsLetter } ${ this._r1 } ${ this._r2 } ${ this._angle } ${ this._largeArcFlag ? \"1\" : \"0\" } ${ this._sweepFlag ? \"1\" : \"0\" } ${ this._x } ${ this._y}` }\n window.SVGPathSegArcAbs.prototype.clone = function() { return new window.SVGPathSegArcAbs(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag) }\n Object.defineProperty(window.SVGPathSegArcAbs.prototype, \"x\", { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegArcAbs.prototype, \"y\", { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegArcAbs.prototype, \"r1\", { get: function() { return this._r1 }, set: function(r1) { this._r1 = r1; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegArcAbs.prototype, \"r2\", { get: function() { return this._r2 }, set: function(r2) { this._r2 = r2; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegArcAbs.prototype, \"angle\", { get: function() { return this._angle }, set: function(angle) { this._angle = angle; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegArcAbs.prototype, \"largeArcFlag\", { get: function() { return this._largeArcFlag }, set: function(largeArcFlag) { this._largeArcFlag = largeArcFlag; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegArcAbs.prototype, \"sweepFlag\", { get: function() { return this._sweepFlag }, set: function(sweepFlag) { this._sweepFlag = sweepFlag; this._segmentChanged() }, enumerable: true })\n\n window.SVGPathSegArcRel = function(owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) {\n window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_REL, \"a\", owningPathSegList)\n this._x = x\n this._y = y\n this._r1 = r1\n this._r2 = r2\n this._angle = angle\n this._largeArcFlag = largeArcFlag\n this._sweepFlag = sweepFlag\n }\n window.SVGPathSegArcRel.prototype = Object.create(window.SVGPathSeg.prototype)\n window.SVGPathSegArcRel.prototype.toString = function() { return \"[object SVGPathSegArcRel]\" }\n window.SVGPathSegArcRel.prototype._asPathString = function() { return `${this.pathSegTypeAsLetter } ${ this._r1 } ${ this._r2 } ${ this._angle } ${ this._largeArcFlag ? \"1\" : \"0\" } ${ this._sweepFlag ? \"1\" : \"0\" } ${ this._x } ${ this._y}` }\n window.SVGPathSegArcRel.prototype.clone = function() { return new window.SVGPathSegArcRel(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag) }\n Object.defineProperty(window.SVGPathSegArcRel.prototype, \"x\", { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegArcRel.prototype, \"y\", { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegArcRel.prototype, \"r1\", { get: function() { return this._r1 }, set: function(r1) { this._r1 = r1; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegArcRel.prototype, \"r2\", { get: function() { return this._r2 }, set: function(r2) { this._r2 = r2; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegArcRel.prototype, \"angle\", { get: function() { return this._angle }, set: function(angle) { this._angle = angle; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegArcRel.prototype, \"largeArcFlag\", { get: function() { return this._largeArcFlag }, set: function(largeArcFlag) { this._largeArcFlag = largeArcFlag; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegArcRel.prototype, \"sweepFlag\", { get: function() { return this._sweepFlag }, set: function(sweepFlag) { this._sweepFlag = sweepFlag; this._segmentChanged() }, enumerable: true })\n\n window.SVGPathSegLinetoHorizontalAbs = function(owningPathSegList, x) {\n window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS, \"H\", owningPathSegList)\n this._x = x\n }\n window.SVGPathSegLinetoHorizontalAbs.prototype = Object.create(window.SVGPathSeg.prototype)\n window.SVGPathSegLinetoHorizontalAbs.prototype.toString = function() { return \"[object SVGPathSegLinetoHorizontalAbs]\" }\n window.SVGPathSegLinetoHorizontalAbs.prototype._asPathString = function() { return `${this.pathSegTypeAsLetter } ${ this._x}` }\n window.SVGPathSegLinetoHorizontalAbs.prototype.clone = function() { return new window.SVGPathSegLinetoHorizontalAbs(undefined, this._x) }\n Object.defineProperty(window.SVGPathSegLinetoHorizontalAbs.prototype, \"x\", { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged() }, enumerable: true })\n\n window.SVGPathSegLinetoHorizontalRel = function(owningPathSegList, x) {\n window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL, \"h\", owningPathSegList)\n this._x = x\n }\n window.SVGPathSegLinetoHorizontalRel.prototype = Object.create(window.SVGPathSeg.prototype)\n window.SVGPathSegLinetoHorizontalRel.prototype.toString = function() { return \"[object SVGPathSegLinetoHorizontalRel]\" }\n window.SVGPathSegLinetoHorizontalRel.prototype._asPathString = function() { return `${this.pathSegTypeAsLetter } ${ this._x}` }\n window.SVGPathSegLinetoHorizontalRel.prototype.clone = function() { return new window.SVGPathSegLinetoHorizontalRel(undefined, this._x) }\n Object.defineProperty(window.SVGPathSegLinetoHorizontalRel.prototype, \"x\", { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged() }, enumerable: true })\n\n window.SVGPathSegLinetoVerticalAbs = function(owningPathSegList, y) {\n window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS, \"V\", owningPathSegList)\n this._y = y\n }\n window.SVGPathSegLinetoVerticalAbs.prototype = Object.create(window.SVGPathSeg.prototype)\n window.SVGPathSegLinetoVerticalAbs.prototype.toString = function() { return \"[object SVGPathSegLinetoVerticalAbs]\" }\n window.SVGPathSegLinetoVerticalAbs.prototype._asPathString = function() { return `${this.pathSegTypeAsLetter } ${ this._y}` }\n window.SVGPathSegLinetoVerticalAbs.prototype.clone = function() { return new window.SVGPathSegLinetoVerticalAbs(undefined, this._y) }\n Object.defineProperty(window.SVGPathSegLinetoVerticalAbs.prototype, \"y\", { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged() }, enumerable: true })\n\n window.SVGPathSegLinetoVerticalRel = function(owningPathSegList, y) {\n window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL, \"v\", owningPathSegList)\n this._y = y\n }\n window.SVGPathSegLinetoVerticalRel.prototype = Object.create(window.SVGPathSeg.prototype)\n window.SVGPathSegLinetoVerticalRel.prototype.toString = function() { return \"[object SVGPathSegLinetoVerticalRel]\" }\n window.SVGPathSegLinetoVerticalRel.prototype._asPathString = function() { return `${this.pathSegTypeAsLetter } ${ this._y}` }\n window.SVGPathSegLinetoVerticalRel.prototype.clone = function() { return new window.SVGPathSegLinetoVerticalRel(undefined, this._y) }\n Object.defineProperty(window.SVGPathSegLinetoVerticalRel.prototype, \"y\", { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged() }, enumerable: true })\n\n window.SVGPathSegCurvetoCubicSmoothAbs = function(owningPathSegList, x, y, x2, y2) {\n window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS, \"S\", owningPathSegList)\n this._x = x\n this._y = y\n this._x2 = x2\n this._y2 = y2\n }\n window.SVGPathSegCurvetoCubicSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype)\n window.SVGPathSegCurvetoCubicSmoothAbs.prototype.toString = function() { return \"[object SVGPathSegCurvetoCubicSmoothAbs]\" }\n window.SVGPathSegCurvetoCubicSmoothAbs.prototype._asPathString = function() { return `${this.pathSegTypeAsLetter } ${ this._x2 } ${ this._y2 } ${ this._x } ${ this._y}` }\n window.SVGPathSegCurvetoCubicSmoothAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, this._x, this._y, this._x2, this._y2) }\n Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, \"x\", { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, \"y\", { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, \"x2\", { get: function() { return this._x2 }, set: function(x2) { this._x2 = x2; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, \"y2\", { get: function() { return this._y2 }, set: function(y2) { this._y2 = y2; this._segmentChanged() }, enumerable: true })\n\n window.SVGPathSegCurvetoCubicSmoothRel = function(owningPathSegList, x, y, x2, y2) {\n window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL, \"s\", owningPathSegList)\n this._x = x\n this._y = y\n this._x2 = x2\n this._y2 = y2\n }\n window.SVGPathSegCurvetoCubicSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype)\n window.SVGPathSegCurvetoCubicSmoothRel.prototype.toString = function() { return \"[object SVGPathSegCurvetoCubicSmoothRel]\" }\n window.SVGPathSegCurvetoCubicSmoothRel.prototype._asPathString = function() { return `${this.pathSegTypeAsLetter } ${ this._x2 } ${ this._y2 } ${ this._x } ${ this._y}` }\n window.SVGPathSegCurvetoCubicSmoothRel.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, this._x, this._y, this._x2, this._y2) }\n Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, \"x\", { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, \"y\", { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, \"x2\", { get: function() { return this._x2 }, set: function(x2) { this._x2 = x2; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, \"y2\", { get: function() { return this._y2 }, set: function(y2) { this._y2 = y2; this._segmentChanged() }, enumerable: true })\n\n window.SVGPathSegCurvetoQuadraticSmoothAbs = function(owningPathSegList, x, y) {\n window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS, \"T\", owningPathSegList)\n this._x = x\n this._y = y\n }\n window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype)\n window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.toString = function() { return \"[object SVGPathSegCurvetoQuadraticSmoothAbs]\" }\n window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype._asPathString = function() { return `${this.pathSegTypeAsLetter } ${ this._x } ${ this._y}` }\n window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, this._x, this._y) }\n Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, \"x\", { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, \"y\", { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged() }, enumerable: true })\n\n window.SVGPathSegCurvetoQuadraticSmoothRel = function(owningPathSegList, x, y) {\n window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, \"t\", owningPathSegList)\n this._x = x\n this._y = y\n }\n window.SVGPathSegCurvetoQuadraticSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype)\n window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.toString = function() { return \"[object SVGPathSegCurvetoQuadraticSmoothRel]\" }\n window.SVGPathSegCurvetoQuadraticSmoothRel.prototype._asPathString = function() { return `${this.pathSegTypeAsLetter } ${ this._x } ${ this._y}` }\n window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, this._x, this._y) }\n Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, \"x\", { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged() }, enumerable: true })\n Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, \"y\", { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged() }, enumerable: true })\n\n // Add createSVGPathSeg* functions to window.SVGPathElement.\n // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-Interfacewindow.SVGPathElement.\n window.SVGPathElement.prototype.createSVGPathSegClosePath = function() { return new window.SVGPathSegClosePath(undefined) }\n window.SVGPathElement.prototype.createSVGPathSegMovetoAbs = function(x, y) { return new window.SVGPathSegMovetoAbs(undefined, x, y) }\n window.SVGPathElement.prototype.createSVGPathSegMovetoRel = function(x, y) { return new window.SVGPathSegMovetoRel(undefined, x, y) }\n window.SVGPathElement.prototype.createSVGPathSegLinetoAbs = function(x, y) { return new window.SVGPathSegLinetoAbs(undefined, x, y) }\n window.SVGPathElement.prototype.createSVGPathSegLinetoRel = function(x, y) { return new window.SVGPathSegLinetoRel(undefined, x, y) }\n window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicAbs = function(x, y, x1, y1, x2, y2) { return new window.SVGPathSegCurvetoCubicAbs(undefined, x, y, x1, y1, x2, y2) }\n window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicRel = function(x, y, x1, y1, x2, y2) { return new window.SVGPathSegCurvetoCubicRel(undefined, x, y, x1, y1, x2, y2) }\n window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticAbs = function(x, y, x1, y1) { return new window.SVGPathSegCurvetoQuadraticAbs(undefined, x, y, x1, y1) }\n window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticRel = function(x, y, x1, y1) { return new window.SVGPathSegCurvetoQuadraticRel(undefined, x, y, x1, y1) }\n window.SVGPathElement.prototype.createSVGPathSegArcAbs = function(x, y, r1, r2, angle, largeArcFlag, sweepFlag) { return new window.SVGPathSegArcAbs(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag) }\n window.SVGPathElement.prototype.createSVGPathSegArcRel = function(x, y, r1, r2, angle, largeArcFlag, sweepFlag) { return new window.SVGPathSegArcRel(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag) }\n window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalAbs = function(x) { return new window.SVGPathSegLinetoHorizontalAbs(undefined, x) }\n window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalRel = function(x) { return new window.SVGPathSegLinetoHorizontalRel(undefined, x) }\n window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalAbs = function(y) { return new window.SVGPathSegLinetoVerticalAbs(undefined, y) }\n window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalRel = function(y) { return new window.SVGPathSegLinetoVerticalRel(undefined, y) }\n window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothAbs = function(x, y, x2, y2) { return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, x, y, x2, y2) }\n window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothRel = function(x, y, x2, y2) { return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, x, y, x2, y2) }\n window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothAbs = function(x, y) { return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, x, y) }\n window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothRel = function(x, y) { return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, x, y) }\n }\n\n if (typeof window !== 'undefined' && !(\"SVGPathSegList\" in window)) {\n // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSegList\n window.SVGPathSegList = function(pathElement) {\n this._pathElement = pathElement\n this._list = this._parsePath(this._pathElement.getAttribute(\"d\"))\n\n // Use a MutationObserver to catch changes to the path's \"d\" attribute.\n this._mutationObserverConfig = { attributes: true, attributeFilter: [\"d\"] }\n this._pathElementMutationObserver = new MutationObserver(this._updateListFromPathMutations.bind(this))\n this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig)\n }\n\n window.SVGPathSegList.prototype.classname = \"SVGPathSegList\"\n\n Object.defineProperty(window.SVGPathSegList.prototype, \"numberOfItems\", {\n get: function() {\n this._checkPathSynchronizedToList()\n return this._list.length\n },\n enumerable: true\n })\n\n // Add the pathSegList accessors to window.SVGPathElement.\n // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGAnimatedPathData\n Object.defineProperty(window.SVGPathElement.prototype, \"pathSegList\", {\n get: function() {\n if (!this._pathSegList)\n this._pathSegList = new window.SVGPathSegList(this)\n return this._pathSegList\n },\n enumerable: true\n })\n // FIXME: The following are not implemented and simply return window.SVGPathElement.pathSegList.\n Object.defineProperty(window.SVGPathElement.prototype, \"normalizedPathSegList\", { get: function() { return this.pathSegList }, enumerable: true })\n Object.defineProperty(window.SVGPathElement.prototype, \"animatedPathSegList\", { get: function() { return this.pathSegList }, enumerable: true })\n Object.defineProperty(window.SVGPathElement.prototype, \"animatedNormalizedPathSegList\", { get: function() { return this.pathSegList }, enumerable: true })\n\n // Process any pending mutations to the path element and update the list as needed.\n // This should be the first call of all public functions and is needed because\n // MutationObservers are not synchronous so we can have pending asynchronous mutations.\n window.SVGPathSegList.prototype._checkPathSynchronizedToList = function() {\n this._updateListFromPathMutations(this._pathElementMutationObserver.takeRecords())\n }\n\n window.SVGPathSegList.prototype._updateListFromPathMutations = function(mutationRecords) {\n if (!this._pathElement)\n return\n let hasPathMutations = false\n mutationRecords.forEach(function(record) {\n if (record.attributeName == \"d\")\n hasPathMutations = true\n })\n if (hasPathMutations)\n this._list = this._parsePath(this._pathElement.getAttribute(\"d\"))\n }\n\n // Serialize the list and update the path's 'd' attribute.\n window.SVGPathSegList.prototype._writeListToPath = function() {\n this._pathElementMutationObserver.disconnect()\n this._pathElement.setAttribute(\"d\", window.SVGPathSegList._pathSegArrayAsString(this._list))\n this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig)\n }\n\n // When a path segment changes the list needs to be synchronized back to the path element.\n window.SVGPathSegList.prototype.segmentChanged = function(pathSeg) {\n this._writeListToPath()\n }\n\n window.SVGPathSegList.prototype.clear = function() {\n this._checkPathSynchronizedToList()\n\n this._list.forEach(function(pathSeg) {\n pathSeg._owningPathSegList = null\n })\n this._list = []\n this._writeListToPath()\n }\n\n window.SVGPathSegList.prototype.initialize = function(newItem) {\n this._checkPathSynchronizedToList()\n\n this._list = [newItem]\n newItem._owningPathSegList = this\n this._writeListToPath()\n return newItem\n }\n\n window.SVGPathSegList.prototype._checkValidIndex = function(index) {\n if (isNaN(index) || index < 0 || index >= this.numberOfItems)\n throw \"INDEX_SIZE_ERR\"\n }\n\n window.SVGPathSegList.prototype.getItem = function(index) {\n this._checkPathSynchronizedToList()\n\n this._checkValidIndex(index)\n return this._list[index]\n }\n\n window.SVGPathSegList.prototype.insertItemBefore = function(newItem, index) {\n this._checkPathSynchronizedToList()\n\n // Spec: If the index is greater than or equal to numberOfItems, then the new item is appended to the end of the list.\n if (index > this.numberOfItems)\n index = this.numberOfItems\n if (newItem._owningPathSegList) {\n // SVG2 spec says to make a copy.\n newItem = newItem.clone()\n }\n this._list.splice(index, 0, newItem)\n newItem._owningPathSegList = this\n this._writeListToPath()\n return newItem\n }\n\n window.SVGPathSegList.prototype.replaceItem = function(newItem, index) {\n this._checkPathSynchronizedToList()\n\n if (newItem._owningPathSegList) {\n // SVG2 spec says to make a copy.\n newItem = newItem.clone()\n }\n this._checkValidIndex(index)\n this._list[index] = newItem\n newItem._owningPathSegList = this\n this._writeListToPath()\n return newItem\n }\n\n window.SVGPathSegList.prototype.removeItem = function(index) {\n this._checkPathSynchronizedToList()\n\n this._checkValidIndex(index)\n const item = this._list[index]\n this._list.splice(index, 1)\n this._writeListToPath()\n return item\n }\n\n window.SVGPathSegList.prototype.appendItem = function(newItem) {\n this._checkPathSynchronizedToList()\n\n if (newItem._owningPathSegList) {\n // SVG2 spec says to make a copy.\n newItem = newItem.clone()\n }\n this._list.push(newItem)\n newItem._owningPathSegList = this\n // TODO: Optimize this to just append to the existing attribute.\n this._writeListToPath()\n return newItem\n }\n\n window.SVGPathSegList._pathSegArrayAsString = function(pathSegArray) {\n let string = \"\"\n let first = true\n pathSegArray.forEach(function(pathSeg) {\n if (first) {\n first = false\n string += pathSeg._asPathString()\n } else {\n string += ` ${ pathSeg._asPathString()}`\n }\n })\n return string\n }\n\n // This closely follows SVGPathParser::parsePath from Source/core/svg/SVGPathParser.cpp.\n window.SVGPathSegList.prototype._parsePath = function(string) {\n if (!string || string.length == 0)\n return []\n\n const owningPathSegList = this\n\n const Builder = function() {\n this.pathSegList = []\n }\n\n Builder.prototype.appendSegment = function(pathSeg) {\n this.pathSegList.push(pathSeg)\n }\n\n const Source = function(string) {\n this._string = string\n this._currentIndex = 0\n this._endIndex = this._string.length\n this._previousCommand = window.SVGPathSeg.PATHSEG_UNKNOWN\n\n this._skipOptionalSpaces()\n }\n\n Source.prototype._isCurrentSpace = function() {\n const character = this._string[this._currentIndex]\n return character <= \" \" && (character == \" \" || character == \"\\n\" || character == \"\\t\" || character == \"\\r\" || character == \"\\f\")\n }\n\n Source.prototype._skipOptionalSpaces = function() {\n while (this._currentIndex < this._endIndex && this._isCurrentSpace())\n this._currentIndex++\n return this._currentIndex < this._endIndex\n }\n\n Source.prototype._skipOptionalSpacesOrDelimiter = function() {\n if (this._currentIndex < this._endIndex && !this._isCurrentSpace() && this._string.charAt(this._currentIndex) != \",\")\n return false\n if (this._skipOptionalSpaces()) {\n if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == \",\") {\n this._currentIndex++\n this._skipOptionalSpaces()\n }\n }\n return this._currentIndex < this._endIndex\n }\n\n Source.prototype.hasMoreData = function() {\n return this._currentIndex < this._endIndex\n }\n\n Source.prototype.peekSegmentType = function() {\n const lookahead = this._string[this._currentIndex]\n return this._pathSegTypeFromChar(lookahead)\n }\n\n Source.prototype._pathSegTypeFromChar = function(lookahead) {\n switch (lookahead) {\n case \"Z\":\n case \"z\":\n return window.SVGPathSeg.PATHSEG_CLOSEPATH\n case \"M\":\n return window.SVGPathSeg.PATHSEG_MOVETO_ABS\n case \"m\":\n return window.SVGPathSeg.PATHSEG_MOVETO_REL\n case \"L\":\n return window.SVGPathSeg.PATHSEG_LINETO_ABS\n case \"l\":\n return window.SVGPathSeg.PATHSEG_LINETO_REL\n case \"C\":\n return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS\n case \"c\":\n return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL\n case \"Q\":\n return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS\n case \"q\":\n return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL\n case \"A\":\n return window.SVGPathSeg.PATHSEG_ARC_ABS\n case \"a\":\n return window.SVGPathSeg.PATHSEG_ARC_REL\n case \"H\":\n return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS\n case \"h\":\n return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL\n case \"V\":\n return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS\n case \"v\":\n return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL\n case \"S\":\n return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS\n case \"s\":\n return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL\n case \"T\":\n return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS\n case \"t\":\n return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL\n default:\n return window.SVGPathSeg.PATHSEG_UNKNOWN\n }\n }\n\n Source.prototype._nextCommandHelper = function(lookahead, previousCommand) {\n // Check for remaining coordinates in the current command.\n if ((lookahead == \"+\" || lookahead == \"-\" || lookahead == \".\" || (lookahead >= \"0\" && lookahead <= \"9\")) && previousCommand != window.SVGPathSeg.PATHSEG_CLOSEPATH) {\n if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_ABS)\n return window.SVGPathSeg.PATHSEG_LINETO_ABS\n if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_REL)\n return window.SVGPathSeg.PATHSEG_LINETO_REL\n return previousCommand\n }\n return window.SVGPathSeg.PATHSEG_UNKNOWN\n }\n\n Source.prototype.initialCommandIsMoveTo = function() {\n // If the path is empty it is still valid, so return true.\n if (!this.hasMoreData())\n return true\n const command = this.peekSegmentType()\n // Path must start with moveTo.\n return command == window.SVGPathSeg.PATHSEG_MOVETO_ABS || command == window.SVGPathSeg.PATHSEG_MOVETO_REL\n }\n\n // Parse a number from an SVG path. This very closely follows genericParseNumber(...) from Source/core/svg/SVGParserUtilities.cpp.\n // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-PathDataBNF\n Source.prototype._parseNumber = function() {\n let exponent = 0\n let integer = 0\n let frac = 1\n let decimal = 0\n let sign = 1\n let expsign = 1\n\n const startIndex = this._currentIndex\n\n this._skipOptionalSpaces()\n\n // Read the sign.\n if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == \"+\")\n this._currentIndex++\n else if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == \"-\") {\n this._currentIndex++\n sign = -1\n }\n\n if (this._currentIndex == this._endIndex || ((this._string.charAt(this._currentIndex) < \"0\" || this._string.charAt(this._currentIndex) > \"9\") && this._string.charAt(this._currentIndex) != \".\"))\n // The first character of a number must be one of [0-9+-.].\n return undefined\n\n // Read the integer part, build right-to-left.\n const startIntPartIndex = this._currentIndex\n while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= \"0\" && this._string.charAt(this._currentIndex) <= \"9\")\n this._currentIndex++ // Advance to first non-digit.\n\n if (this._currentIndex != startIntPartIndex) {\n let scanIntPartIndex = this._currentIndex - 1\n let multiplier = 1\n while (scanIntPartIndex >= startIntPartIndex) {\n integer += multiplier * (this._string.charAt(scanIntPartIndex--) - \"0\")\n multiplier *= 10\n }\n }\n\n // Read the decimals.\n if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == \".\") {\n this._currentIndex++\n\n // There must be a least one digit following the .\n if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < \"0\" || this._string.charAt(this._currentIndex) > \"9\")\n return undefined\n while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= \"0\" && this._string.charAt(this._currentIndex) <= \"9\") {\n frac *= 10\n decimal += (this._string.charAt(this._currentIndex) - \"0\") / frac\n this._currentIndex += 1\n }\n }\n\n // Read the exponent part.\n if (this._currentIndex != startIndex && this._currentIndex + 1 < this._endIndex && (this._string.charAt(this._currentIndex) == \"e\" || this._string.charAt(this._currentIndex) == \"E\") && (this._string.charAt(this._currentIndex + 1) != \"x\" && this._string.charAt(this._currentIndex + 1) != \"m\")) {\n this._currentIndex++\n\n // Read the sign of the exponent.\n if (this._string.charAt(this._currentIndex) == \"+\") {\n this._currentIndex++\n } else if (this._string.charAt(this._currentIndex) == \"-\") {\n this._currentIndex++\n expsign = -1\n }\n\n // There must be an exponent.\n if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < \"0\" || this._string.charAt(this._currentIndex) > \"9\")\n return undefined\n\n while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= \"0\" && this._string.charAt(this._currentIndex) <= \"9\") {\n exponent *= 10\n exponent += (this._string.charAt(this._currentIndex) - \"0\")\n this._currentIndex++\n }\n }\n\n let number = integer + decimal\n number *= sign\n\n if (exponent)\n number *= Math.pow(10, expsign * exponent)\n\n if (startIndex == this._currentIndex)\n return undefined\n\n this._skipOptionalSpacesOrDelimiter()\n\n return number\n }\n\n Source.prototype._parseArcFlag = function() {\n if (this._currentIndex >= this._endIndex)\n return undefined\n let flag = false\n const flagChar = this._string.charAt(this._currentIndex++)\n if (flagChar == \"0\")\n flag = false\n else if (flagChar == \"1\")\n flag = true\n else\n return undefined\n\n this._skipOptionalSpacesOrDelimiter()\n return flag\n }\n\n Source.prototype.parseSegment = function() {\n const lookahead = this._string[this._currentIndex]\n let command = this._pathSegTypeFromChar(lookahead)\n if (command == window.SVGPathSeg.PATHSEG_UNKNOWN) {\n // Possibly an implicit command. Not allowed if this is the first command.\n if (this._previousCommand == window.SVGPathSeg.PATHSEG_UNKNOWN)\n return null\n command = this._nextCommandHelper(lookahead, this._previousCommand)\n if (command == window.SVGPathSeg.PATHSEG_UNKNOWN)\n return null\n } else {\n this._currentIndex++\n }\n\n this._previousCommand = command\n\n switch (command) {\n case window.SVGPathSeg.PATHSEG_MOVETO_REL:\n return new window.SVGPathSegMovetoRel(owningPathSegList, this._parseNumber(), this._parseNumber())\n case window.SVGPathSeg.PATHSEG_MOVETO_ABS:\n return new window.SVGPathSegMovetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber())\n case window.SVGPathSeg.PATHSEG_LINETO_REL:\n return new window.SVGPathSegLinetoRel(owningPathSegList, this._parseNumber(), this._parseNumber())\n case window.SVGPathSeg.PATHSEG_LINETO_ABS:\n return new window.SVGPathSegLinetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber())\n case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL:\n return new window.SVGPathSegLinetoHorizontalRel(owningPathSegList, this._parseNumber())\n case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS:\n return new window.SVGPathSegLinetoHorizontalAbs(owningPathSegList, this._parseNumber())\n case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL:\n return new window.SVGPathSegLinetoVerticalRel(owningPathSegList, this._parseNumber())\n case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS:\n return new window.SVGPathSegLinetoVerticalAbs(owningPathSegList, this._parseNumber())\n case window.SVGPathSeg.PATHSEG_CLOSEPATH:\n this._skipOptionalSpaces()\n return new window.SVGPathSegClosePath(owningPathSegList)\n case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL:\n var points = {x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}\n return new window.SVGPathSegCurvetoCubicRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2)\n case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS:\n var points = {x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}\n return new window.SVGPathSegCurvetoCubicAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2)\n case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:\n var points = {x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}\n return new window.SVGPathSegCurvetoCubicSmoothRel(owningPathSegList, points.x, points.y, points.x2, points.y2)\n case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:\n var points = {x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}\n return new window.SVGPathSegCurvetoCubicSmoothAbs(owningPathSegList, points.x, points.y, points.x2, points.y2)\n case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL:\n var points = {x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}\n return new window.SVGPathSegCurvetoQuadraticRel(owningPathSegList, points.x, points.y, points.x1, points.y1)\n case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:\n var points = {x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}\n return new window.SVGPathSegCurvetoQuadraticAbs(owningPathSegList, points.x, points.y, points.x1, points.y1)\n case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:\n return new window.SVGPathSegCurvetoQuadraticSmoothRel(owningPathSegList, this._parseNumber(), this._parseNumber())\n case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:\n return new window.SVGPathSegCurvetoQuadraticSmoothAbs(owningPathSegList, this._parseNumber(), this._parseNumber())\n case window.SVGPathSeg.PATHSEG_ARC_REL:\n var points = {x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber()}\n return new window.SVGPathSegArcRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep)\n case window.SVGPathSeg.PATHSEG_ARC_ABS:\n var points = {x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber()}\n return new window.SVGPathSegArcAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep)\n default:\n throw \"Unknown path seg type.\"\n }\n }\n\n const builder = new Builder()\n const source = new Source(string)\n\n if (!source.initialCommandIsMoveTo())\n return []\n while (source.hasMoreData()) {\n const pathSeg = source.parseSegment()\n if (!pathSeg)\n return []\n builder.appendSegment(pathSeg)\n }\n\n return builder.pathSegList\n }\n }\n}())\n\n// JointJS library (http://jointjs.com)\n// (c) 2011-2014 client IO. http://client.io.\n\njoint.shapes.chart = {}\n\njoint.shapes.chart.Plot = joint.shapes.basic.Generic.extend({\n\n markup: [\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n ''\n ].join(''),\n tickMarkup: '',\n pointMarkup: '',\n barMarkup: '',\n markingMarkup: '',\n serieMarkup: '',\n legendItemMarkup: '',\n\n defaults: joint.util.deepSupplement({\n\n type: 'chart.Plot',\n attrs: {\n '.data path': { fill: 'none', stroke: 'black' },\n '.data .bars rect': { fill: 'none', stroke: 'black' },\n '.background rect': { fill: 'white', stroke: '#e5e5e5', opacity: 1 },\n '.background text': { fill: 'black', text: 'No data available.', ref: '.', 'ref-x': .5, 'ref-y': .5, 'text-anchor': 'middle', 'y-alignment': 'middle', display: 'none' },\n '.foreground > rect': { fill: 'white', stroke: '#e5e5e5', opacity: 0, 'pointer-events': 'none' },\n '.foreground .caption': { fill: 'black', text: '', ref: '.foreground > rect', 'ref-x': .5, 'ref-y': 10, 'text-anchor': 'middle', 'y-alignment': 'middle', 'font-size': 14 },\n '.foreground .subcaption': { fill: 'black', text: '', ref: '.foreground > rect', 'ref-x': .5, 'ref-y': 23, 'text-anchor': 'middle', 'y-alignment': 'middle', 'font-size': 10 },\n '.point': { display: 'inline-block' },\n '.point circle': { r: 2, stroke: 'black', fill: 'black', opacity: .3 },\n '.point text': { fill: 'black', 'font-size': 8, 'text-anchor': 'middle', display: 'none' },\n '.axis path': { fill: 'none', stroke: 'black' },\n '.axis .tick': { fill: 'none', stroke: 'black' },\n '.y-axis .tick line': { fill: 'none', stroke: 'black', x2: 2, y2: 0, opacity: 1 },\n '.x-axis .tick line': { fill: 'none', stroke: 'black', x2: 0, y2: -3, opacity: 1 },\n '.y-axis .tick text': { fill: 'black', stroke: 'none', 'font-size': 10, 'text-anchor': 'end' },\n '.x-axis .tick text': { fill: 'black', stroke: 'none', 'font-size': 10, 'text-anchor': 'middle' },\n '.y-axis .tick text > tspan': { dy: '-.5em', x: -5 },\n '.x-axis .tick text > tspan': { dy: '.5em', x: 0 },\n '.axis .markings': { fill: 'black', stroke: 'none', 'fill-opacity': 1 },\n '.axis .markings text': { fill: 'black', 'text-anchor': 'end', 'font-size': 10, dy: -5, dx: -5 },\n '.guideline': { 'pointer-events': 'none', display: 'none' },\n '.x-guideline': { stroke: 'black', visibility: 'hidden' },\n '.y-guideline': { stroke: 'black', visibility: 'hidden' },\n '.legend': { 'ref-x': 10, 'ref-y': 10 },\n '.legend-item text': { fill: 'black', transform: 'translate(14, 0)', 'font-size': 11 },\n '.legend-item circle': { r: 5, transform: 'translate(5,5)' },\n '.legend-item': { cursor: 'pointer' },\n '.legend-item.disabled circle': { fill: 'gray' },\n '.legend-item.disabled text': { opacity: .5 }\n }\n\n }, joint.shapes.basic.Generic.prototype.defaults),\n\n legendPosition: function(pos, opt) {\n\n opt = opt || {}\n\n this.trigger('batch:start');\n\n // Clean up previous attributes first. Do it silently so that we don't unncessarilly trigger updates.\n [\n '.legend/ref-x',\n '.legend/ref-y',\n '.legend/ref-dx',\n '.legend/ref-dy',\n '.legend/x-alignment',\n '.legend/y-alignment'\n ].forEach(function(item) {\n this.removeAttr(item, { silent: true })\n }, this)\n\n const padding = opt.padding || 10\n\n const attrs = {\n\n n: { '.legend': { 'ref-x': .5, 'x-alignment': -.5, 'ref-y': padding }},\n ne: { '.legend': { 'ref-dx': -padding, 'x-alignment': -.999, 'ref-y': padding }},\n e: { '.legend': { 'ref-dx': -padding, 'x-alignment': -.999, 'ref-y': .5, 'y-alignment': -.5 }},\n se: { '.legend': { 'ref-dx': -padding, 'ref-dy': -padding, 'x-alignment': -.999, 'y-alignment': -.999 }},\n s: { '.legend': { 'ref-x': .5, 'ref-dy': -padding, 'x-alignment': -.5, 'y-alignment': -.999 }},\n sw: { '.legend': { 'ref-x': padding, 'ref-dy': -padding, 'y-alignment': -.999 }},\n w: { '.legend': { 'ref-x': padding, 'ref-y': .5, 'y-alignment': -.5 }},\n nw: { '.legend': { 'ref-x': padding, 'ref-y': padding }},\n nnw: { '.legend': { 'ref-x': padding, 'ref-y': -padding, 'y-alignment': -.999 }},\n nn: { '.legend': { 'ref-x': .5, 'ref-y': -padding, 'x-alignment': -.5, 'y-alignment': -.999 }},\n nne: { '.legend': { 'ref-dx': -padding, 'ref-y': -padding, 'x-alignment': -.999, 'y-alignment': -.999 }},\n nnee: { '.legend': { 'ref-dx': padding, 'ref-y': -padding, 'y-alignment': -.999 }},\n nee: { '.legend': { 'ref-y': padding, 'ref-dx': padding }},\n ee: { '.legend': { 'ref-dx': padding, 'ref-y': .5, 'y-alignment': -.5 }},\n see: { '.legend': { 'ref-dx': padding, 'ref-dy': -padding, 'y-alignment': -.999 }},\n ssee: { '.legend': { 'ref-dx': padding, 'ref-dy': padding }},\n sse: { '.legend': { 'ref-dx': -padding, 'ref-dy': padding, 'x-alignment': -.999 }},\n ss: { '.legend': { 'ref-x': .5, 'ref-dy': padding, 'x-alignment': -.5 }},\n ssw: { '.legend': { 'ref-x': padding, 'ref-dy': padding }},\n ssww: { '.legend': { 'ref-x': -padding, 'ref-dy': padding, 'x-alignment': -.999 }},\n sww: { '.legend': { 'ref-x': -padding, 'ref-dy': -padding, 'x-alignment': -.999, 'y-alignment': -.999 }},\n ww: { '.legend': { 'ref-x': -padding, 'ref-y': .5, 'x-alignment': -.999, 'y-alignment': -.5 }},\n nww: { '.legend': { 'ref-x': -padding, 'ref-y': padding, 'x-alignment': -.999 }},\n nnww: { '.legend': { 'ref-x': -padding, 'ref-y': -padding, 'x-alignment': -.999, 'y-alignment': -.999 }}\n }\n\n if (attrs[pos]) {\n this.attr(attrs[pos])\n }\n\n this.trigger('batch:stop')\n },\n\n // Add point `p` as the last point to the serie identified by `serieName`. If `opt.maxLen` is set and\n // the number of points in the serie is higher than `maxLen`, shift the data in the serie.\n addPoint: function(p, serieName, opt) {\n\n opt = opt || {}\n\n let series = this.get('series')\n\n const serieIndex = joint.util.toArray(series).findIndex(function(item) {\n return item.name === serieName\n })\n if (serieIndex === -1) {\n throw new Error(`Serie ${ serieName } was not found.`)\n }\n\n // Clone the serie so that the normal Backbone mechanism for `set()` and `prev()` works as expected.\n const serie = joint.util.cloneDeep(series[serieIndex])\n serie.data.push(p)\n\n if (Number.isFinite(opt.maxLen) && serie.data.length > opt.maxLen) {\n\n serie.data.shift()\n }\n\n // Again, slice the array so that we don't alter the `series` array currently set.\n series = series.slice()\n series[serieIndex] = serie\n\n this.set('series', series, opt)\n },\n\n // Return the last point in the serie identified by `serieName`.\n lastPoint: function(serieName) {\n\n const serie = joint.util.toArray(this.get('series')).find(function(item) {\n return item && item.name === serieName\n }).data\n\n return serie[serie.length - 1]\n },\n\n // Return the first point in the serie identified by `serieName`.\n firstPoint: function(serieName) {\n\n return joint.util.toArray(this.get('series')).find(function(item) {\n return item && item.name === serieName\n }).data[0]\n }\n})\n\njoint.shapes.chart.PlotView = joint.dia.ElementView.extend({\n\n events: {\n\n mousemove: 'onMouseMove',\n mouseout: 'onMouseOut'\n },\n\n initialize: function() {\n\n joint.dia.ElementView.prototype.initialize.apply(this, arguments)\n\n this.listenTo(this.model, 'change:series change:interpolate change:padding change:canvas change:markings change:axis', function() { this.update() })\n\n this.on('cell:pointerdown', this.onPointerDown, this)\n\n // A list of disabled serie names. This is used when toggling series via the legend\n // or programmatically.\n this._disabledSeries = []\n },\n\n renderMarkup: function() {\n\n joint.dia.ElementView.prototype.renderMarkup.apply(this, arguments)\n\n // Cache important elements for faster access.\n this.elDataClipPath = this.$('.clip')[0]\n this.elDataClipPathRect = this.elDataClipPath.firstChild\n this.elBackgroundRect = this.$('.background rect')[0]\n this.elBackgroundText = this.$('.background text')[0]\n this.elForeground = this.$('.foreground')[0]\n this.elForegroundRect = this.$('.foreground rect')[0]\n this.elDataSeries = this.$('.data .series')[0]\n this.elYAxisPath = this.$('.y-axis path')[0]\n this.elYAxisTicks = this.$('.y-axis .ticks')[0]\n this.elXAxisPath = this.$('.x-axis path')[0]\n this.elXAxisTicks = this.$('.x-axis .ticks')[0]\n this.elMarkings = this.$('.axis .markings')[0]\n this.elXGuideline = this.$('.x-guideline')[0]\n this.elYGuideline = this.$('.y-guideline')[0]\n this.elLegend = this.$('.legend')[0]\n this.elLegendItems = this.$('.legend-items')[0]\n\n // An SVG element for repeatable elements. This will be used as an original for future clones.\n this.elTick = V(this.model.tickMarkup)\n this.elMarking = V(this.model.markingMarkup)\n this.elLegendItem = V(this.model.legendItemMarkup)\n this.elPoint = V(this.model.pointMarkup)\n this.elBar = V(this.model.barMarkup)\n this.elSerie = V(this.model.serieMarkup)\n\n // Create clip region for the chart area and for the markings as they could also be out\n // of the clip region.\n this.elDataClipPath.id = `clip_${ this.cid}`\n V(this.$('.data')[0]).attr('clip-path', `url(#${ this.elDataClipPath.id })`)\n V(this.elMarkings).attr('clip-path', `url(#${ this.elDataClipPath.id })`)\n },\n\n update: function() {\n\n const series = this.filterSeries()\n\n // Get statistics about the series.\n this.calculateStats(series)\n\n const size = this.model.get('size')\n const {width} = size\n const {height} = size\n\n // Chart area.\n this.canvas = joint.util.assign({\n x: 0, y: 0,\n width: width, height: height\n }, this.model.get('canvas'))\n\n // Padding. In theory, padding is not necessary as one can always set the canvas area\n // directly. However, it is much more convenient to be able to set a padding only for\n // a specific dimension(s) (top/right/bottom/left) and let the canvas alone.\n // Note that it is always advisable to set `padding` for bar charts, otherwise\n // some of the bars (or their parts - depending on the `align` option) won't be visible.\n let padding\n const defaultPadding = { top: 0, right: 0, bottom: 0, left: 0 }\n const modelPadding = this.model.get('padding')\n if (joint.util.isObject(modelPadding)) {\n\n padding = joint.util.assign({}, defaultPadding, modelPadding)\n\n } else if (modelPadding !== undefined) {\n // The padding is assumed to be a number. In this case, compensate for the right/bottom coordinates\n // automatically. So that if e.g. `padding` is `10`, the chart area is moved `10px` from the left\n // and the width becomes `2*10px` less so that there is also `10px` padding from the right.\n padding = { top: modelPadding, right: 2 * modelPadding, bottom: 2 * modelPadding, left: modelPadding }\n\n } else {\n\n padding = defaultPadding\n }\n\n this.canvas = g.rect(this.canvas).moveAndExpand(g.rect(padding.left, padding.top, -padding.right, -padding.bottom))\n\n const viewRect = { x: 0, y: 0, width: width, height: height }\n\n V(this.elDataClipPathRect).attr(viewRect)\n V(this.elBackgroundRect).attr(viewRect)\n V(this.elForegroundRect).attr(viewRect)\n\n\n this.updateAxis()\n this.updateMarkings()\n\n if (this.isEmpty()) {\n // No data available.\n // Show the \"No data available\" label that is hidden by default.\n $(this.elBackgroundText).show()\n\n } else {\n\n $(this.elBackgroundText).hide()\n }\n\n this.updateSeries(series)\n\n this.updateLegend()\n\n // Apply attrs.\n joint.dia.ElementView.prototype.update.apply(this, arguments)\n },\n\n calculateStats: function(series) {\n\n series = series || this.model.get('series')\n\n const xValues = []\n const yValues = []\n // `xMap` maps x values to an array of series these x values appear in and the corresponding\n // y values. This is useful when we want to, for a given x value, retriev all the corresponding\n // y values from all the series this x value appeared in (especially useful in tooltips).\n const xMap = {}\n const yMap = {}\n\n const bySerie = {}\n\n joint.util.toArray(series).forEach(function(serie, idx) {\n\n const stats = bySerie[serie.name || idx] || (bySerie[serie.name || idx] = {})\n\n // Initial assumptions.\n stats.decreasingX = true\n stats.decreasingY = true\n stats.nonDecreasingX = true\n stats.nonDecreasingY = true\n\n let prev // Previous data point.\n\n joint.util.forIn(serie.data, function(dp) {\n\n stats.minX = stats.minX === undefined ? dp.x : Math.min(stats.minX, dp.x)\n stats.maxX = stats.maxX === undefined ? dp.x : Math.max(stats.maxX, dp.x)\n stats.minY = stats.minY === undefined ? dp.y : Math.min(stats.minY, dp.y)\n stats.maxY = stats.maxY === undefined ? dp.y : Math.max(stats.maxY, dp.y)\n\n if (prev) {\n stats.decreasingX = stats.decreasingX && dp.x < prev.x\n stats.decreasingY = stats.decreasingY && dp.y < prev.y\n stats.nonDecreasingX = stats.nonDecreasingX && dp.x >= prev.x\n stats.nonDecreasingY = stats.nonDecreasingY && dp.y >= prev.y\n }\n\n if (!xValues.includes(dp.x)) xValues.push(dp.x)\n if (!yValues.includes(dp.y)) yValues.push(dp.y);\n\n (xMap[dp.x] || (xMap[dp.x] = [])).push({ serie: serie, x: dp.x, y: dp.y });\n (yMap[dp.y] || (yMap[dp.y] = [])).push({ serie: serie, x: dp.x, y: dp.y })\n\n prev = dp\n })\n })\n\n const axis = this.model.get('axis') || {}\n const xAxis = axis['x-axis'] || {}\n const yAxis = axis['y-axis'] || {}\n\n this.stats = {\n minX: xAxis.min === undefined ? xValues.reduce(function(min, item) {\n return item < min ? item : min\n }, Infinity) : xAxis.min,\n maxX: xAxis.max === undefined ? xValues.reduce(function(max, item) {\n return item > max ? item : max\n }, -Infinity) : xAxis.max,\n minY: yAxis.min === undefined ? yValues.reduce(function(min, item) {\n return item < min ? item : min\n }, Infinity) : yAxis.min,\n maxY: yAxis.max === undefined ? yValues.reduce(function(max, item) {\n return item > max ? item : max\n }, -Infinity) : yAxis.max,\n bySerie: bySerie,\n xValues: xValues,\n yValues: yValues,\n xMap: xMap,\n yMap: yMap\n }\n },\n\n isEmpty: function() {\n\n return !this.stats.xValues.length\n },\n\n updateSeries: function(series) {\n\n series = series || this.model.get('series')\n\n // Remove all the previously rendered series.\n this.elDataSeries.textContent = ''\n\n if (this.isEmpty()) return\n\n const xDomain = [this.stats.minX, this.stats.maxX]\n const yDomain = [this.stats.minY, this.stats.maxY]\n const xRange = [this.canvas.x, this.canvas.x + this.canvas.width]\n // Note how the `yRange` is inverted. This is because we render points from bottom to top.\n const yRange = [this.canvas.y + this.canvas.height, this.canvas.y]\n\n const attrs = this.model.get('attrs')\n\n joint.util.toArray(series).forEach(function(serie, i) {\n\n const points = serie.data\n const transformedPoints = []\n\n const elSerie = this.elSerie.clone().attr('class', serie.name || (`serie-${ i}`))\n V(this.elDataSeries).append(elSerie)\n\n joint.util.forIn(points, function(p) {\n\n // Transform the data point to the chart area.\n const x = g.scale.linear(xDomain, xRange, p.x)\n const y = g.scale.linear(yDomain, yRange, p.y)\n\n transformedPoints.push({ x: x, y: y })\n\n // Set position of the point element circle and label.\n // A little optimization: do not render the points if they're not turned on.\n if (attrs['.point'] && attrs['.point'].display !== 'none') {\n\n this.renderPoint(p, serie)\n }\n\n if (serie.bars) {\n\n this.renderBar(p, serie)\n }\n\n }.bind(this))\n\n // Clip the serie path in order to clip the helper continuation of the path\n // that is used to close the path for filling. (see `fixPathForFill()`).\n const elSeriePathClip = elSerie.findOne('.serie-clip')\n\n const size = this.model.get('size')\n const stats = this.stats.bySerie[serie.name || i]\n const minX = g.scale.linear(xDomain, xRange, stats.minX)\n const maxX = g.scale.linear(xDomain, xRange, stats.maxX)\n\n const elSeriePathClipRect = elSeriePathClip.findOne('rect')\n elSeriePathClipRect.attr(g.rect(minX, 0, maxX - minX, size.height))\n\n if (!serie.bars) {\n // Bars were already rendered for each data point.\n\n const elSeriePath = elSerie.findOne('path')\n elSeriePath.attr({\n d: this.seriePathData(transformedPoints, serie, i),\n 'clip-path': `url(#${ elSeriePathClip.node.id })`\n })\n }\n\n }, this)\n },\n\n seriePathClipData: function(points, serie) {\n\n const padding = 10\n const size = this.model.get('size')\n const firstPoint = points[0]\n const d = ['M', firstPoint.x, firstPoint.y, 'V', size.height + padding]\n return d.join(' ')\n },\n\n renderBar: function(p, serie) {\n\n const xDomain = [this.stats.minX, this.stats.maxX]\n const yDomain = [this.stats.minY, this.stats.maxY]\n const xRange = [this.canvas.x, this.canvas.x + this.canvas.width]\n // Note how the `yRange` is inverted. This is because we render points from bottom to top.\n const yRange = [this.canvas.y + this.canvas.height, this.canvas.y]\n\n // Transform the data point to the chart area.\n const x = g.scale.linear(xDomain, xRange, p.x)\n const y = g.scale.linear(yDomain, yRange, p.y)\n\n const definedBarWidth = serie.bars.barWidth || .8\n const barWidth = definedBarWidth > 1 ? definedBarWidth : (this.canvas.width / (this.stats.maxX - this.stats.minX)) * definedBarWidth\n const barHeight = g.scale.linear(yDomain, yRange, 0) - y\n\n // `rx` values can be defined either directly in the data point for a specific bar\n // or on the `serie` object for all the bars.\n const topRx = p['top-rx'] || serie.bars['top-rx']\n const topRy = p['top-ry'] || serie.bars['top-ry']\n const bottomRx = p['bottom-rx'] || serie.bars['bottom-rx']\n const bottomRy = p['bottom-ry'] || serie.bars['bottom-ry']\n\n // Alignment of the bar against the x coordinate. `'left'` is the default.\n const barX = ({\n\n left: x,\n middle: x - barWidth / 2,\n right: x - barWidth\n\n })[serie.bars.align || 'middle']\n\n const elBar = this.elBar.clone()\n elBar.attr({\n 'data-serie': serie.name,\n 'data-x': p.x,\n 'data-y': p.y,\n d: V.rectToPath({ x: barX, y: y, width: barWidth, height: barHeight, 'top-rx': topRx, 'top-ry': topRy, 'bottom-rx': bottomRx, 'bottom-ry': bottomRy })\n })\n\n const serieSelector = serie.name || (`serie-${ this.model.get('series').indexOf(serie)}`)\n V(this.elDataSeries).findOne(`.${ serieSelector } .bars`).append(elBar)\n\n return elBar.node\n },\n\n renderPoint: function(p, serie) {\n\n const xDomain = [this.stats.minX, this.stats.maxX]\n const yDomain = [this.stats.minY, this.stats.maxY]\n const xRange = [this.canvas.x, this.canvas.x + this.canvas.width]\n // Note how the `yRange` is inverted. This is because we render points from bottom to top.\n const yRange = [this.canvas.y + this.canvas.height, this.canvas.y]\n\n // Transform the data point to the chart area.\n const x = g.scale.linear(xDomain, xRange, p.x)\n const y = g.scale.linear(yDomain, yRange, p.y)\n\n const elPoint = this.elPoint.clone()\n elPoint.attr({\n 'data-serie': serie.name,\n 'data-x': p.x,\n 'data-y': p.y\n })\n elPoint.findOne('circle').attr({ cx: x, cy: y })\n elPoint.findOne('text').attr({ x: x, dy: y }).text(this.pointLabel(p, serie))\n\n const serieSelector = serie.name || (`serie-${ this.model.get('series').indexOf(serie)}`)\n V(this.elDataSeries).findOne(`.${ serieSelector } .points`).append(elPoint)\n\n return elPoint.node\n },\n\n // Construct an SVG path for the data points. Use interpolation if desired.\n seriePathData: function(points, serie, idx) {\n\n const interpolate = serie.interpolate === undefined ? this.model.get('interpolate') : serie.interpolate\n let d\n let i\n let p\n const pointsLength = points.length\n\n switch (interpolate) {\n\n case 'bezier':\n d = new g.Path(g.Curve.throughPoints(points)).serialize()\n break\n\n case 'step':\n p = points[0]\n d = ['M', p.x, p.y]\n for (i = 1; i < pointsLength; i++) {\n d.push('H', (p.x + points[i].x) / 2, 'V', points[i].y)\n p = points[i]\n }\n break\n\n case 'stepBefore':\n d = ['M', points[0].x, points[0].y]\n for (i = 1; i < pointsLength; i++) {\n d.push('V', points[i].y, 'H', points[i].x)\n }\n break\n\n case 'stepAfter':\n d = ['M', points[0].x, points[0].y]\n for (i = 1; i < pointsLength; i++) {\n d.push('H', points[i].x, 'V', points[i].y)\n }\n break\n\n default: // linear\n d = ['M']\n for (i = 0; i < pointsLength; i++) {\n d.push(points[i].x, points[i].y)\n }\n break\n }\n\n d = this.fixPathForFill(d, points, serie, idx)\n\n return d.join(' ')\n },\n\n fixPathForFill: function(d, points, serie, idx) {\n\n // Nothing needs to be fixed for empty points list.\n if (points.length === 0) return d\n\n // If the serie is not non-decreasing (it is an arbitrary function), it is assumed that\n // the author knows what he's doing and handles proper fill himself.\n const stats = this.stats.bySerie[serie.name || idx]\n if (!stats.nonDecreasingX) return d\n\n const padding = 10\n const size = this.model.get('size')\n const firstPoint = points[0]\n const lastPoint = points[points.length - 1]\n\n // Start a subpath at the level of the last point of the serie but below the visible chart area\n // (i.e. below the clipped region) and continue that path to the level of the first point circling\n // around the bottom of the clipping region. This makes sure that even though the serie\n // has its last x point before another serie in the chart and fill is set on the serie SVG path element\n // the fill will properly fill the region below the serie path. This is nicely visible\n // if you comment out the setting of the `clip-path` attribute on the `.data` group.\n const dPrefix = ['M', lastPoint.x, size.height + padding, 'H', firstPoint.x - padding, 'V', firstPoint.y]\n // The first path command is assumed to be `'M'`. Due to the trick\n // to fully support a proper fill, there is already preceeding a subpath.\n // Therefore, we want to continue that subpath with a line command.\n d[0] = 'L'\n\n return dPrefix.concat(d)\n },\n\n updateAxis: function() {\n\n const size = this.model.get('size')\n const {width} = size\n const {height} = size\n\n const axis = this.model.get('axis')\n\n // The canvas could be larger or smaller than the actual height of the chart view.\n // This can happen when the chart is zoomed. However, we still want to render the\n // axis along the height of the chart view (not continueing it below or above).\n // The `canvasHeighRatio` helps us adjust the range to which we map the values\n // from the axis domain.\n const canvasHeightRatio = this.canvas.height / height\n\n // Axis line.\n V(this.elYAxisPath).attr('d', ['M', 0, 0, 'L', 0, height].join(' '))\n V(this.elXAxisPath).attr('d', ['M', 0, height, 'L', width, height].join(' '))\n\n // Clean up old ticks.\n this.elXAxisTicks.textContent = ''\n this.elYAxisTicks.textContent = ''\n\n if (this.isEmpty()) return\n\n const xDomain = [this.stats.minX, this.stats.maxX]\n const yDomain = [this.stats.minY, this.stats.maxY]\n const xRange = [this.canvas.x, this.canvas.x + this.canvas.width]\n const yRange = [0, this.canvas.height]\n const yDomainSpan = yDomain[1] - yDomain[0]\n const yAxis = axis && axis['y-axis'] || {}\n const xAxis = axis && axis['x-axis'] || {}\n\n // Number of ticks. If the length of the domain interval is zero, show at least one tick.\n const ticks = (yDomainSpan) > 0 ? (yAxis.ticks - 1 || 10) : 0\n // A tick step. We must scale the tick step down by the `canvasHeightRatio`.\n // The tick step becomes smaller if the canvas is larger than the chart view and bigger otherwise.\n const tickStep = (yDomainSpan / ticks) / canvasHeightRatio\n\n // Render ticks.\n // Start at `yDomain[0]` and step by `tickStep` `ticks` number of times.\n let y = yDomain[0]\n\n for (let i = 0; i < ticks + 1; i++) {\n\n const ty = g.scale.linear(yDomain, yRange, y)\n\n const elTick = this.elTick.clone()\n elTick.translate(0, ty)\n V(this.elYAxisTicks).append(elTick)\n\n // Invert the `y` value according to the domain as we're moving from top to bottom but\n // the axis ticks are labeled from bottom to top.\n let tickValue = yDomain[1] - (y - yDomain[0])\n // The `tickValue` is shifted by the `canvas.y` offset but first we must scale this offset\n // back to the domain of the axis.\n tickValue += g.scale.linear(yRange, yDomain, this.canvas.y) - yDomain[0]\n\n elTick.findOne('text').text(this.tickLabel(tickValue, yAxis))\n\n y += tickStep\n }\n this.stats.xValues.forEach(function(x, i) {\n\n // Show only every `tickStep` value. Default is to show all the `x` values.\n if (i % (xAxis.tickStep || 1) !== 0) return\n\n const tx = g.scale.linear(xDomain, xRange, x)\n\n // Do not show ticks that are outside the chart view area.\n if (tx > width) return\n\n const elTick = this.elTick.clone()\n elTick.translate(tx, height)\n V(this.elXAxisTicks).append(elTick)\n\n elTick.findOne('text').text(this.tickLabel(x, xAxis))\n\n }, this)\n },\n\n tickLabel: function(value, opt) {\n\n if (joint.util.isFunction(opt.tickFormat)) {\n\n return opt.tickFormat(value)\n }\n\n const formatSpecifier = opt.tickFormat || '.1f'\n const label = joint.util.format.number(formatSpecifier, value)\n return label + (joint.util.isFunction(opt.tickSuffix) ? opt.tickSuffix(value) : (opt.tickSuffix || ''))\n },\n\n pointLabel: function(p, opt) {\n\n if (joint.util.isFunction(opt.pointFormat)) {\n\n return opt.pointFormat(p)\n }\n\n const formatSpecifier = opt.pointFormat || '.1f'\n const label = joint.util.format.number(formatSpecifier, p.y)\n return label + (opt.pointSuffix || '')\n },\n\n updateMarkings: function() {\n\n // Clean up old markings.\n this.elMarkings.textContent = ''\n\n const markings = this.model.get('markings')\n // No need to continue if there are no markings.\n if (!markings || markings.length === 0) return\n\n const size = this.model.get('size')\n const {width} = size\n const {height} = size\n\n const xDomain = [this.stats.minX, this.stats.maxX]\n const yDomain = [this.stats.minY, this.stats.maxY]\n let xRange = [this.canvas.x, this.canvas.x + this.canvas.width]\n let yRange = [this.canvas.y, this.canvas.y + this.canvas.height]\n\n function firstDefined(a, b) {\n return a === undefined ? b : a\n }\n\n joint.util.toArray(markings).forEach(function(marking, i) {\n\n // Start and end of the marking. The following adjustments makes it\n // easier to define the marking. It does not really matter if the `end`\n // is before `start`. Also, if the only thing defined is `start.y`, the marking\n // will be a single line starting at that `y` position crossing the whole chart.\n const start = marking.start || marking.end\n const end = marking.end || marking.start\n\n const startX = Math.min(firstDefined(start.x, this.stats.minX), firstDefined(end.x, this.stats.minX))\n const endX = Math.max(firstDefined(start.x, this.stats.maxX), firstDefined(end.x, this.stats.maxX))\n const startY = Math.min(firstDefined(start.y, this.stats.minY), firstDefined(end.y, this.stats.minY))\n const endY = Math.max(firstDefined(start.y, this.stats.maxY), firstDefined(end.y, this.stats.maxY))\n\n // Scale `start` and `end` to use for translating the marking rectangle.\n\n // If the marking is a trendline, i.e. one of the coordinates is missing,\n // (in other words the marking is not an area), we want such line to\n // cover the whole view regardless of the canvas area. The reasoning behind\n // this is that, for example, if we have a bar chart and some of the bars\n // are rendered after the canvas area and we can compensate for this by\n // setting a padding on the canvas area, we still want the trendlines\n // to be rendered for those bars.\n const isTrendLineX = start.x === undefined || end.x === undefined\n const isTrendLineY = start.y === undefined || end.y === undefined\n\n if (isTrendLineX) xRange = [0, width]\n if (isTrendLineY) yRange = [0, height]\n\n const startTx = g.scale.linear(xDomain, xRange, startX)\n const endTx = g.scale.linear(xDomain, xRange, endX)\n const startTy = g.scale.linear(yDomain, yRange, startY)\n const endTy = g.scale.linear(yDomain, yRange, endY)\n\n // Marking position and dimensions.\n const mx = startTx\n const my = yRange[1] - endTy + yRange[0]\n let mw = endTx - startTx\n let mh = endTy - startTy\n\n // Limit the marking to the bounding box of the canvas.\n //if (mx + mw > this.canvas.width + this.canvas.x) mw = this.canvas.width + this.canvas.x - mx;\n //if (my + mh > this.canvas.height + this.canvas.y) mh = this.canvas.height + this.canvas.y - my;\n\n // Make sure we give the marking a positive width and height, otherwise it's not visible at all.\n mw = Math.max(mw, 1)\n mh = Math.max(mh, 1)\n\n // Render the marking.\n const elMarking = this.elMarking.clone()\n elMarking.findOne('rect').attr({ x: mx, y: my, width: mw, height: mh })\n elMarking.findOne('text').text(marking.label || '').attr({ x: mx + mw, y: my })\n const className = `${elMarking.attr('class') } ${ marking.name || (`marking-${ i}`)}`\n elMarking.attr(joint.util.assign({ class: className }, marking.attrs))\n V(this.elMarkings).append(elMarking)\n\n }, this)\n },\n\n updateLegend: function() {\n\n const series = this.model.get('series')\n\n this.elLegendItems.textContent = ''\n\n joint.util.toArray(series).forEach(function(serie, i) {\n\n // Give the outside world the ability to decide whether a legend item should be shown or not.\n if (joint.util.isFunction(serie.showLegend) && !serie.showLegend(serie, this.stats.bySerie[serie.name || i])) {\n\n return\n\n } else if (serie.showLegend === false) {\n\n return\n }\n\n const elLegendItem = this.elLegendItem.clone()\n if (this._disabledSeries.includes(serie.name)) {\n elLegendItem.addClass('disabled')\n }\n elLegendItem.attr('data-serie', serie.name)\n elLegendItem.findOne('circle').attr({ fill: this.getSerieColor(serie.name) })\n elLegendItem.findOne('text').text(serie.label || serie.name)\n elLegendItem.translate(0, i * (serie.legendLabelLineHeight || 16))\n V(this.elLegendItems).append(elLegendItem)\n\n }, this)\n },\n\n getSerieColor: function(serieName) {\n\n const attrs = this.model.get('attrs')\n\n const serieAttrs = Object.keys(attrs).find(function(selector) {\n return selector.includes(serieName)\n })\n\n return serieAttrs ? attrs[serieAttrs].stroke || attrs[serieAttrs].fill : 'black'\n },\n\n hideSerie: function(serieName) {\n\n if (!this._disabledSeries.includes(serieName)) {\n this._disabledSeries.push(serieName)\n }\n\n const series = this.filterSeries()\n this.update(series)\n },\n\n showSerie: function(serieName) {\n\n this._disabledSeries = joint.util.without(this._disabledSeries, serieName)\n\n const series = this.filterSeries()\n this.update(series)\n },\n\n filterSeries: function(series) {\n\n series = series || this.model.get('series')\n\n series = joint.util.toArray(series).filter(function(serie) {\n return !this._disabledSeries.includes(serie.name)\n }, this)\n\n return series\n },\n\n // Interaction.\n // ------------\n\n onPointerDown: function(evt, x, y) {\n\n const elLegendItem = $(evt.target).closest('.legend-item')[0]\n if (elLegendItem) {\n\n V(elLegendItem).toggleClass('disabled')\n\n if (V(elLegendItem).hasClass('disabled')) {\n\n this.hideSerie(V(elLegendItem).attr('data-serie'))\n\n } else {\n\n this.showSerie(V(elLegendItem).attr('data-serie'))\n }\n }\n },\n\n onMouseMove: function(evt) {\n\n this.showGuidelines(evt.clientX, evt.clientY, evt)\n },\n\n onMouseOut: function(evt) {\n\n this.hideGuidelines()\n this.trigger('mouseout', evt)\n },\n\n showGuidelines: function(clientX, clientY, evt) {\n\n const angle = this.model.get('angle')\n const bbox = this.model.getBBox()\n const localPoint = g.point(V(this.paper.viewport).toLocalPoint(clientX, clientY)).rotate(bbox.center(), angle)\n\n if (g.rect(bbox).containsPoint(localPoint)) {\n\n const size = this.model.get('size')\n\n const x = localPoint.x - bbox.x\n const y = localPoint.y - bbox.y\n\n V(this.elXGuideline).attr({ x1: x, y1: 0, x2: x, y2: size.height, visibility: 'visible' })\n V(this.elYGuideline).attr({ x1: 0, y1: y, x2: size.width, y2: y, visibility: 'visible' })\n\n const dataX = g.scale.linear([this.canvas.x, this.canvas.x + this.canvas.width], [this.stats.minX, this.stats.maxX], x)\n const dataY = g.scale.linear([this.canvas.y, this.canvas.y + this.canvas.height], [this.stats.minY, this.stats.maxY], y)\n\n const dataPoint = { x: dataX, y: this.stats.minY + this.stats.maxY - dataY }\n const clientPoint = { x: clientX, y: clientY }\n const closestPoints = this.closestPoints(dataX)\n\n this.trigger('mouseover', dataPoint, clientPoint, closestPoints, evt)\n }\n },\n\n // Return the closest points for a given `x` value. The returned array contains objects\n // with `x` and `y` values and a `serie` object this `x` value appeared in.\n closestPoints: function(x) {\n\n const xValuesIndex = joint.util.sortedIndex(this.stats.xValues, x)\n\n const xValue = this.stats.xValues[xValuesIndex]\n const xValueBefore = this.stats.xValues[xValuesIndex - 1]\n\n const xClosest = xValueBefore === undefined ? xValue : (Math.abs(x - xValue) < Math.abs(x - xValueBefore) ? xValue : xValueBefore)\n\n return this.stats.xMap[xClosest]\n },\n\n hideGuidelines: function() {\n\n V(this.elXGuideline).attr('visibility', 'hidden')\n V(this.elYGuideline).attr('visibility', 'hidden')\n }\n})\n\njoint.shapes.chart.Pie = joint.shapes.basic.Generic.extend({\n\n markup: [\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n ''\n ].join(''),\n\n sliceMarkup: '',\n sliceFillMarkup: '',\n sliceBorderMarkup: '',\n sliceInnerLabelMarkup: '',\n legendSerieMarkup: '',\n legendSliceMarkup: '',\n\n defaults: joint.util.deepSupplement({\n\n type: 'chart.Pie',\n size: { width: 200, height: 200 },\n\n // work only on first (or alone) serie\n pieHole: 0,\n\n // serieDefaults.startAngle: pie is draw clockwise from est (right)\n serieDefaults: {\n startAngle: 0,\n degree: 360,\n label: null,\n showLegend: true,\n labelLineHeight: 6\n },\n\n // onClickEffect/onHoverEffect: effect on click/mouseOver (see this.effectOnSlice for a list and option, ex. onHoverEffect: {type: 'enlarge', scale: 1.05})\n sliceDefaults: {\n innerLabel: '{percentage:.0f}%',\n innerLabelMargin: 6,\n legendLabel: '{label}: {value}',\n legendLabelLineHeight: 6,\n legendLabelMargin: 14,\n offset: 0,\n onClickEffect: { type: 'offset', offset: 20 },\n onHoverEffect: null\n },\n\n series: [],\n\n attrs: {\n '.background > rect': { opacity: 0 },\n '.background > text': { fill: 'black', text: 'No data available.', ref: '.background > rect', 'ref-x': .5, 'ref-y': .5, 'text-anchor': 'middle', 'y-alignment': 'middle', display: 'none' },\n '.foreground > rect': { fill: 'white', stroke: '#e5e5e5', opacity: 0, 'pointer-events': 'none' },\n '.foreground .caption': { fill: 'black', text: '', ref: '.foreground > rect', 'ref-x': 2, 'ref-y': 6, 'text-anchor': 'start', 'y-alignment': 'middle', 'font-size': 14 },\n '.foreground .subcaption': { fill: 'black', text: '', ref: '.foreground > rect', 'ref-x': 2, 'ref-y': 18, 'text-anchor': 'start', 'y-alignment': 'middle', 'font-size': 10 },\n '.data': { ref: '.background', 'ref-x': .5, 'ref-y': .5 },\n '.slice': { cursor: 'pointer' },\n '.slice > .slice-fill': { stroke: '#ffffff', 'stroke-width': 1, 'fill-opacity': 1 },\n '.slice.hover > .slice-fill': { 'fill-opacity': .8 },\n '.slice > .slice-border': { 'stroke-width': 6, 'stroke-opacity': .4, 'fill-opacity': 1, fill: 'none', display: 'none' },\n '.slice.hover > .slice-border': { display: 'block' },\n '.slice > .slice-inner-label': { 'text-anchor': 'middle', 'font-size': '12', stroke: 'none', 'stroke-width': '0', fill: '#ffffff' },\n '.slice > .slice-inner-label > tspan': { dy: '-.5em' },\n '.legend': { 'ref-dx': 20, 'ref-y': 5 },\n '.legend-serie text': { fill: 'grey', transform: 'translate(2, 0)', 'font-size': 13 },\n '.legend-slice': { cursor: 'pointer' },\n '.legend-slice text': { 'font-weight': 'normal', fill: 'black', 'font-size': 11 },\n '.legend-slice.hover text': { 'font-weight': 'bold' },\n '.legend-slice circle': { r: 5, transform: 'translate(5,5)' }\n }\n\n }, joint.shapes.basic.Generic.prototype.defaults),\n\n addSlice: function(slice, serieIndex, opt) {\n\n opt = opt || {}\n serieIndex = serieIndex || 0\n\n let series = this.get('series')\n\n // If serie is undefinied (first slice added to serie)\n if (series[serieIndex] === undefined) series[serieIndex] = { data: [] }\n\n // Clone the serie so that the normal Backbone mechanism for `set()` and `prev()` works as expected.\n const serie = joint.util.cloneDeep(series[serieIndex])\n serie.data.push(slice)\n\n // Again, slice the array so that we don't alter the `series` array currently set.\n series = series.slice()\n series[serieIndex] = serie\n\n // If it's a new serie (first slice added)\n opt = serie.data.length > 1 ? joint.util.assign(opt, { changedSerieIndex: serieIndex }) : opt\n\n // Set in opt the serieIndex that change for update only serieIndex on view (could it be better?)\n this.set('series', series, opt)\n },\n\n editSlice: function(slice, sliceIndex, serieIndex, opt) {\n\n opt = opt || {}\n serieIndex = serieIndex || 0\n\n let series = this.get('series')\n\n if (series[serieIndex] === undefined || series[serieIndex].data[sliceIndex] === undefined) {\n throw new Error(`Slice ${ sliceIndex } on serie ${ serieIndex } was not found.`)\n }\n\n // Clone the serie so that the normal Backbone mechanism for `set()` and `prev()` works as expected.\n const serie = joint.util.cloneDeep(series[serieIndex])\n serie.data[sliceIndex] = joint.util.assign(serie.data[sliceIndex], slice)\n\n // Again, slice the array so that we don't alter the `series` array currently set.\n series = series.slice()\n series[serieIndex] = serie\n\n this.set('series', series, joint.util.assign(opt, { changedSerieIndex: serieIndex }))\n }\n})\n\njoint.shapes.chart.PieView = joint.dia.ElementView.extend({\n\n events: {\n 'mouseover .slice': 'onMouseOverSlice',\n 'mouseout .slice': 'onMouseOverSlice',\n 'mousemove .slice': 'onMouseMoveSlice',\n 'mouseover .legend-slice': 'onEventLegendItem',\n 'mouseout .legend-slice': 'onEventLegendItem'\n },\n\n initialize: function() {\n\n joint.dia.ElementView.prototype.initialize.apply(this, arguments)\n\n // Little optimization that update only the serie changed\n this.listenTo(this.model, 'change:series change:serieDefaults change:sliceDefaults change:pieHole',\n function(model, attr, opt) {\n this.update(null, null, null, opt.changedSerieIndex)\n })\n\n this.on('cell:pointerclick', this.onClickSlice, this)\n this.on('cell:pointerclick', this.onEventLegendItem, this)\n },\n\n renderMarkup: function() {\n\n joint.dia.ElementView.prototype.renderMarkup.apply(this, arguments)\n\n // Cache important elements for faster access.\n this.elBackgroundRect = this.$('.background rect')[0]\n this.elBackgroundText = this.$('.background text')[0]\n\n this.elForegroundRect = this.$('.foreground rect')[0]\n\n this.elLegendItems = this.$('.legend-items')[0]\n\n this.elPie = this.$('.data')[0]\n\n // An SVG element for repeatable elements. This will be used as an original for future clones.\n this.elSlice = V(this.model.sliceMarkup)\n this.elSliceFill = V(this.model.sliceFillMarkup)\n this.elSliceBorder = V(this.model.sliceBorderMarkup)\n this.elSliceInnerLabel = V(this.model.sliceInnerLabelMarkup)\n\n this.elLegendSerie = V(this.model.legendSerieMarkup)\n this.elLegendSlice = V(this.model.legendSliceMarkup)\n },\n\n update: function(cell, renderingOnlyAttrs, opt, serieIndex) {\n\n const series = this.calculateSeries(serieIndex)\n\n if (serieIndex in series) {\n // Remove only the serieIndex for which is request update\n $(this.elPie).find(`.serie-${ serieIndex}`).remove()\n } else {\n // Remove all the previously rendered series.\n $(this.elPie).empty()\n }\n\n const size = this.model.get('size')\n V(this.elBackgroundRect).attr(size)\n V(this.elForegroundRect).attr(size)\n\n if (!series.length) {\n // No data available.\n // Show the \"No data available\" label that is hidden by default.\n $(this.elBackgroundText).show()\n } else {\n $(this.elBackgroundText).hide()\n }\n\n joint.util.toArray(series).forEach(function(serie, index) {\n // Use serieIndex for update only the requested serie\n if (serieIndex !== undefined && serieIndex !== index) return\n\n joint.util.forIn(serie.data, this.updateSlice.bind(this))\n }, this)\n\n this.updateLegend()\n\n // Apply attrs.\n joint.dia.ElementView.prototype.update.apply(this, arguments)\n },\n\n calculateSeries: function(serieIndex) {\n\n const series = joint.util.cloneDeep(this.model.get('series'))\n\n const serieDefaults = this.model.get('serieDefaults')\n const sliceDefaults = this.model.get('sliceDefaults')\n\n // Pie outer radius less margin\n const size = this.model.get('size')\n const radius = Math.min(size.width, size.height) / 2\n\n let pieHole = this.model.get('pieHole')\n pieHole = pieHole > 1 ? pieHole : radius * pieHole\n\n let outerRadius = radius\n const radiusStep = (radius - pieHole) / series.length\n\n this._series = series.map(function(serie, index) {\n\n // Use serieIndex for update only the selected serie\n if (serieIndex !== undefined && serieIndex !== index) return serie\n\n serie = joint.util.defaults(serie, serieDefaults)\n\n let {startAngle} = serie\n\n // Calculate percentage of each slice\n const total = serie.data.reduce(function(sum, slice) {\n return sum + slice.value\n }, 0)\n const circleDividedByTotal = serie.degree / total || 0\n const percentageDividedByTotal = 100 / total\n\n serie.data = serie.data.map(function(slice, sliceIndex) {\n\n // Init default params for all slice (less some attributes valid only for outer slice)\n slice = joint.util.defaults(slice, joint.util.omit(sliceDefaults, 'offset', 'onClickEffect', 'onHoverEffect'))\n\n slice.outerRadius = outerRadius\n slice.innerRadius = outerRadius - radiusStep\n\n // For outer slice\n if (!index) {\n // Init default params for outer slice\n slice = joint.util.defaults(slice, joint.util.pick(sliceDefaults, 'offset', 'onClickEffect', 'onHoverEffect'))\n\n slice.isOuter = true\n slice.offset = slice.offset > 1 ? slice.offset : slice.offset * slice.outerRadius\n slice.onClickEffect.offset = slice.onClickEffect.offset > 1 ? slice.onClickEffect.offset : slice.onClickEffect.offset * slice.outerRadius\n }\n\n slice.serieIndex = index\n slice.sliceIndex = sliceIndex\n slice.innerLabelMargin = (slice.innerLabelMargin < -1 || slice.innerLabelMargin > 1) ? slice.innerLabelMargin : slice.innerLabelMargin * slice.outerRadius\n slice.percentage = slice.value * percentageDividedByTotal\n\n const angle = slice.value * circleDividedByTotal\n\n slice.degree = {\n angle: angle,\n start: startAngle,\n end: angle + startAngle\n }\n\n slice.rad = {\n angle: g.toRad(slice.degree.angle, true),\n start: g.toRad(slice.degree.start, true),\n end: g.toRad(slice.degree.end, true)\n }\n\n slice.middleangle = (slice.rad.start + slice.rad.end) / 2\n\n startAngle = slice.degree.end\n\n return slice\n })\n\n outerRadius -= radiusStep\n\n return serie\n })\n\n return this._series\n },\n\n updateLegend: function() {\n\n const series = this._series\n\n this.elLegendItems.textContent = ''\n\n let xPadding = 0\n const fontSizeLegendSerieText = parseInt(this.model.attr('.legend-serie text/font-size'), 10)\n const fontSizeLegendSliceText = parseInt(this.model.attr('.legend-slice text/font-size'), 10)\n\n joint.util.toArray(series).forEach(function(serie, serieIndex) {\n\n if (!serie.showLegend) return\n\n // Append Serie label\n if (serie.label) {\n const elLegendSerie = this.elLegendSerie.clone()\n\n if (serie.name) elLegendSerie.addClass(serie.name)\n elLegendSerie.attr({ 'data-serie': serieIndex })\n\n elLegendSerie.findOne('text').text(serie.label)\n elLegendSerie.translate(0, xPadding)\n\n V(this.elLegendItems).append(elLegendSerie)\n\n // 1.5 is the proportional space between the legend items (one and half height of item)\n xPadding += (fontSizeLegendSerieText + serie.labelLineHeight)\n }\n\n // Append Slices\n joint.util.forIn(serie.data, function(slice, sliceIndex) {\n\n const elLegendSlice = this.elLegendSlice.clone()\n\n const slicefillColor = this.getSliceFillColor(sliceIndex, serieIndex)\n\n if (slice.name) elLegendSlice.addClass(slice.name)\n elLegendSlice.attr({ 'data-serie': serieIndex, 'data-slice': sliceIndex })\n\n elLegendSlice.findOne('text').text(joint.util.format.string(slice.legendLabel, slice))\n elLegendSlice.findOne('text').translate(slice.legendLabelMargin)\n elLegendSlice.translate(0, xPadding)\n\n // 1.5 is the proportional space between the legend items (one and half height of item)\n xPadding += (fontSizeLegendSliceText + slice.legendLabelLineHeight)\n\n // is a gradient\n if (joint.util.isObject(slicefillColor)) {\n this.applyGradient(elLegendSlice.findOne('circle'), 'fill', slicefillColor)\n } else {\n elLegendSlice.findOne('circle').attr({ fill: slicefillColor })\n }\n\n V(this.elLegendItems).append(elLegendSlice)\n\n }.bind(this))\n }, this)\n },\n\n // `selector` is a CSS selector or `'.'`. `attr` is either a `'fill'` or `'stroke'`.\n // `gradient` must be in the special JointJS gradient format:\n // `{ type: , stops: [ { offset: , color: }, ... ]`.\n // An example is: `{ fill: { type: 'linearGradient', stops: [ { offset: '10%', color: 'green' }, { offset: '50%', color: 'blue' } ] } }`.\n applyGradient: function(selector, attr, gradient) {\n\n const $selected = joint.util.isString(selector) ? this.findBySelector(selector) : $(selector).toArray()\n const gradientId = this.paper.defineGradient(gradient)\n\n $selected.forEach(function(node) {\n V(node).attr(attr, `url(#${ gradientId })`)\n })\n },\n\n updateSlice: function(slice) {\n\n const elSlice = this.elSlice.clone()\n\n // Append slice (at start for use .bbox() later)\n V(this.elPie).append(elSlice)\n\n // RENDER SLICE\n const elSliceFill = this.elSliceFill.clone()\n\n const slicefillColor = this.getSliceFillColor(slice.sliceIndex, slice.serieIndex)\n\n elSliceFill.attr({\n fill: slicefillColor,\n d: V.createSlicePathData(slice.innerRadius, slice.outerRadius, slice.rad.start, slice.rad.end)\n })\n\n elSlice.append(elSliceFill)\n\n // is a gradient\n if (joint.util.isObject(slicefillColor)) {\n\n this.applyGradient(`#${ elSliceFill.attr('id')}`, 'fill', slicefillColor)\n }\n\n // RENDER BORDER\n const elSliceBorder = this.elSliceBorder.clone()\n\n // ...with polar coordinate\n const borderStrokeWidth = parseInt(this.model.attr('.slice > .slice-border/stroke-width'), 10)\n const startPoint = g.point.fromPolar(slice.outerRadius + borderStrokeWidth / 2, -slice.rad.start, g.point(0, 0))\n const endPoint = g.point.fromPolar(slice.outerRadius + borderStrokeWidth / 2, -slice.rad.end, g.point(0, 0))\n\n elSliceBorder.attr({\n stroke: slicefillColor,\n d: this.drawArc(startPoint, endPoint, slice.outerRadius + borderStrokeWidth / 2, slice.rad.start, slice.rad.end)\n })\n\n elSlice.append(elSliceBorder)\n\n // is a gradient\n if (joint.util.isObject(slicefillColor)) {\n\n this.applyGradient(`#${ elSliceBorder.attr('id')}`, 'stroke', slicefillColor)\n }\n\n // RENDER INNER LABEL\n const elSliceInnerLabel = this.elSliceInnerLabel.clone()\n\n // Apply inner label text through template\n elSliceInnerLabel.text(joint.util.format.string(slice.innerLabel, slice))\n\n elSlice.append(elSliceInnerLabel)\n\n // After the append (inserted in DOM) can calculate bbox of element\n const innerLabelBbox = elSliceInnerLabel.bbox()\n\n // Translate label: the gap from the middle of the text (bbox) and the pie border is constant\n const radiusLabel = (slice.outerRadius - innerLabelBbox.width / 2) - slice.innerLabelMargin\n\n elSliceInnerLabel.translate((radiusLabel * Math.cos(-slice.middleangle)),\n (-radiusLabel * Math.sin(-slice.middleangle)))\n\n // Add element data attributes\n elSlice.attr({\n 'data-serie': slice.serieIndex,\n 'data-slice': slice.sliceIndex,\n 'data-value': slice.value\n })\n\n // Add class for styling use\n const nameSerie = this._series[slice.serieIndex].name\n\n if (nameSerie) elSlice.addClass(nameSerie)\n if (slice.name) elSlice.addClass(slice.name)\n\n elSlice.addClass(`serie-${ slice.serieIndex } slice-${ slice.sliceIndex}`)\n\n // Is an outer slice\n if (slice.isOuter) {\n elSlice.addClass('outer')\n\n // Apply init offset for explode some slices\n if (slice.offset) {\n elSlice.addClass('clicked')\n\n this.effectOnSlice(elSlice, slice, { type: 'offset', offset: slice.offset })\n }\n }\n\n return elSlice\n },\n\n getSliceFillColor: function(sliceIndex, serieIndex) {\n\n serieIndex = serieIndex || 0\n\n const attrs = this.model.get('attrs')\n\n // Find if there is customized fill color for selected slice in attrs\n const sliceFillAttr = Object.keys(attrs).find(function(selector) {\n return selector.indexOf(`.serie-${ serieIndex }.slice-${ sliceIndex } > .slice-fill`) > -1\n })\n\n return sliceFillAttr ? attrs[sliceFillAttr].fill : this._series[serieIndex].data[sliceIndex].fill\n },\n\n onMouseMoveSlice: function(event) {\n\n const elSlice = V(event.currentTarget)\n\n const serieIndex = elSlice.attr('data-serie')\n const sliceIndex = elSlice.attr('data-slice')\n\n const slice = this._series[serieIndex].data[sliceIndex]\n\n this.trigger(event.type, slice, event)\n },\n\n mouseOverSlice: function(sliceIndex, serieIndex) {\n\n serieIndex = serieIndex || 0\n\n const elSlice = V(this.$(`.slice[data-serie=\"${ serieIndex }\"][data-slice=\"${ sliceIndex }\"]`)[0])\n\n const slice = this._series[serieIndex].data[sliceIndex]\n\n elSlice.toggleClass('hover')\n\n // Do effect if it is an outer slice and requested\n if (slice.isOuter && !joint.util.isEmpty(slice.onHoverEffect)) {\n this.effectOnSlice(elSlice, slice, slice.onHoverEffect, !elSlice.hasClass('hover'))\n }\n\n // Add class 'hover' also to legend\n const elLegendSlice = V(this.$(`.legend-slice[data-serie=\"${ serieIndex }\"][data-slice=\"${ sliceIndex }\"]`)[0])\n if (elLegendSlice) elLegendSlice.toggleClass('hover')\n\n // Apply only attr style with selector '.slice' or '.legend-slice'\n const attrsForSliceAndLegend = Object.keys(this.model.get('attrs')).filter(function(selector) {\n return (selector.indexOf('.slice') > -1 || selector.indexOf('.legend-slice') > -1)\n })\n\n joint.dia.ElementView.prototype.update.call(this, this.model, joint.util.pick(this.model.get('attrs'), attrsForSliceAndLegend))\n },\n\n onMouseOverSlice: function(event) {\n\n const elSlice = V(event.currentTarget)\n\n const serieIndex = elSlice.attr('data-serie')\n const sliceIndex = elSlice.attr('data-slice')\n\n this.mouseOverSlice(sliceIndex, serieIndex)\n\n const slice = this._series[serieIndex].data[sliceIndex]\n\n this.trigger(event.type, slice, event)\n },\n\n clickSlice: function(sliceIndex, serieIndex) {\n\n serieIndex = serieIndex || 0\n\n const elSlice = V(this.$(`.slice[data-serie=\"${ serieIndex }\"][data-slice=\"${ sliceIndex }\"]`)[0])\n\n const slice = this._series[serieIndex].data[sliceIndex]\n\n if (!slice.isOuter) return\n\n if (!elSlice.hasClass('clicked')) {\n elSlice.addClass('clicked')\n\n // Update the model series => resize and clone preserve offset\n this.model.get('series')[serieIndex].data[sliceIndex].offset = slice.onClickEffect.offset\n\n this.effectOnSlice(elSlice, slice, slice.onClickEffect)\n } else {\n elSlice.removeClass('clicked')\n\n // Update the model series => resize and clone preserve offset\n this.model.get('series')[serieIndex].data[sliceIndex].offset = 0\n\n this.effectOnSlice(elSlice, slice, slice.onClickEffect, true)\n }\n },\n\n onClickSlice: function(event) {\n\n // Only for outer (external slice)\n const elSlice = V($(event.target).closest('.slice.outer')[0])\n\n if (elSlice) {\n\n const serieIndex = elSlice.attr('data-serie')\n const sliceIndex = elSlice.attr('data-slice')\n\n this.clickSlice(sliceIndex, serieIndex)\n\n const slice = this._series[serieIndex].data[sliceIndex]\n\n this.trigger(event.type, slice, event)\n }\n },\n\n onEventLegendItem: function(event) {\n\n const elLegendItem = V($(event.target).closest('.legend-slice')[0])\n\n if (elLegendItem) {\n\n const serieIndex = elLegendItem.attr('data-serie')\n const sliceIndex = elLegendItem.attr('data-slice')\n\n switch (event.type) {\n case 'click':\n this.clickSlice(sliceIndex, serieIndex)\n break\n case 'mouseover':\n case 'mouseout':\n this.mouseOverSlice(sliceIndex, serieIndex)\n break\n }\n }\n },\n\n effectOnSlice: function(elSlice, slice, effect, remove) {\n\n remove = remove || false\n\n switch (effect.type) {\n case 'enlarge':\n if (!remove) elSlice.scale(effect.scale || 1.05)\n else elSlice.scale(1)\n break\n case 'offset':\n if (!remove) elSlice.translate(effect.offset * Math.cos(-slice.middleangle), -effect.offset * Math.sin(-slice.middleangle))\n else elSlice.translate(0, 0, { absolute: true })\n break\n }\n },\n\n svgArcMax: 2 * Math.PI - 1e-6,\n drawArc: function(startPoint, endPoint, radius, startAngle, endAngle) {\n let largeArcFlag = 0\n let sweepFlag = 1\n\n const angle = endAngle - startAngle\n\n if (angle > Math.PI) {\n\n largeArcFlag = 1\n\n if (angle >= this.svgArcMax) {\n largeArcFlag = 0\n sweepFlag = 0\n }\n }\n\n return `M${ startPoint.x },${ startPoint.y } A${ radius },${\n radius } 0 ${ largeArcFlag },${ sweepFlag\n } ${ endPoint.x },${ endPoint.y}`\n }\n\n})\n\n// Knob chart.\n// -----------\n\n// Supports the following properties:\n// `min` and `max` for defining the domain of the `value`.\n// `value` is the final value of the knob.\n// `fill` for the fill color of the knob.\n// Moreover, all of these properties can be arrays in which case the knob\n// displayes more values stacked one on another.\njoint.shapes.chart.Knob = joint.shapes.chart.Pie.extend({\n\n defaults: joint.util.deepSupplement({\n type: 'chart.Knob',\n sliceDefaults: {\n legendLabel: '{value:.0f}',\n outer: { offsetOnClick: 0 }\n },\n pieHole: .7,\n value: 0,\n attrs: {\n '.legend': { 'ref-x': .5, 'ref-y': .5, 'ref-dx': null, 'x-alignment': -.5, 'y-alignment': -.5 },\n '.legend-slice text': { 'font-size': 30 },\n '.legend-slice circle': { display: 'none' },\n '.slice-inner-label': { display: 'none' },\n '.slice-fill': { stroke: 'none' }\n }\n }, joint.shapes.chart.Pie.prototype.defaults),\n\n initialize: function() {\n\n this.set('series', this.getKnobSeries(), { silent: true })\n joint.shapes.chart.Pie.prototype.initialize.apply(this, arguments)\n this.on('change:value change:min change:max change:fill', this.updateKnob, this)\n },\n\n getKnobSeries: function() {\n\n // Create one serie with one slice holding the knob value and color.\n const values = Array.isArray(this.get('value')) ? this.get('value') : [this.get('value')]\n const fills = Array.isArray(this.get('fill')) ? this.get('fill') : [this.get('fill')]\n const mins = Array.isArray(this.get('min')) ? this.get('min') : [this.get('min')]\n const maxs = Array.isArray(this.get('max')) ? this.get('max') : [this.get('max')]\n const series = values.map(function(value, i) {\n\n const min = mins[i] === undefined ? mins[0] : mins[i]\n const max = maxs[i] === undefined ? maxs[0] : maxs[i]\n const fill = fills[i] === undefined ? fills[0] : fills[i]\n return {\n degree: g.scale.linear([min, max], [0, 360], value),\n data: [ { value: value, fill: fill } ],\n showLegend: !(i > 0) // Show legend only for the first serie.\n }\n })\n\n return series\n },\n\n updateKnob: function() {\n\n this.set('series', this.getKnobSeries())\n }\n})\n\njoint.shapes.chart.KnobView = joint.shapes.chart.PieView\n\n\n// Matrix diagram.\njoint.shapes.chart.Matrix = joint.shapes.basic.Generic.extend({\n\n markup: [\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n ''\n ].join(''),\n\n cellMarkup: '',\n labelMarkup: '',\n gridLineMarkup: '',\n\n defaults: joint.util.deepSupplement({\n\n type: 'chart.Matrix',\n\n attrs: {\n '.background rect': { fill: '#eeeeee' },\n '.grid-line': { stroke: 'white', 'stroke-width': 2 },\n '.label': { fill: 'black', 'alignment-baseline': 'middle' },\n '.labels .rows .label': { 'text-anchor': 'end' },\n '.labels .columns .label': { 'text-anchor': 'start' }\n }\n\n }, joint.shapes.basic.Generic.prototype.defaults)\n})\n\njoint.shapes.chart.MatrixView = joint.dia.ElementView.extend({\n\n initialize: function() {\n\n joint.dia.ElementView.prototype.initialize.apply(this, arguments)\n this.listenTo(this.model, 'change:size', function() { this.renderLabels(); this.update() })\n this.listenTo(this.model, 'change:cells', function() { this.renderMarkup(); this.update() })\n },\n\n renderMarkup: function() {\n\n joint.dia.ElementView.prototype.renderMarkup.apply(this, arguments)\n\n this.elCells = this.$('.cells')[0]\n this.elRowLabels = this.$('.labels .rows')[0]\n this.elColumnLabels = this.$('.labels .columns')[0]\n this.elForeground = this.$('.foreground')[0]\n\n this.elCell = V(this.model.cellMarkup)\n this.elGridLine = V(this.model.gridLineMarkup)\n\n const cells = this.model.get('cells') || []\n const size = this.model.get('size')\n\n this.elBackgroundRect = this.$('.background rect')[0]\n V(this.elBackgroundRect).attr(size)\n\n const cellHeight = size.height / cells.length\n const cellWidth = size.width / cells.length\n const elCellsFragment = document.createDocumentFragment()\n this.elCells.textContent = ''\n\n this.elForeground.textContent = ''\n const elGridLinesFragment = document.createDocumentFragment()\n\n // Cells.\n // ------\n\n let row; let j; let elGridLine; let cell; let elCell\n\n for (let i = 0; i < cells.length; i++) {\n\n elGridLine = this.elGridLine.clone()\n elGridLine.addClass('horizontal')\n elGridLine.attr('d', `M 0 ${ i * cellHeight } ${ size.width } ${ i * cellHeight}`)\n elGridLinesFragment.appendChild(elGridLine.node)\n\n row = cells[i]\n for (j = 0; j < row.length; j++) {\n\n if (i === 0) {\n\n elGridLine = this.elGridLine.clone()\n elGridLine.addClass('vertical')\n elGridLine.attr('d', `M ${ j * cellWidth } 0 ${ j * cellWidth } ${ size.height}`)\n elGridLinesFragment.appendChild(elGridLine.node)\n }\n\n cell = row[j]\n if (cell) {\n elCell = this.elCell.clone()\n elCell.attr(joint.util.assign({\n x: j * cellWidth,\n y: i * cellHeight,\n width: cellWidth,\n height: cellHeight\n }, cell))\n\n elCellsFragment.appendChild(elCell.node)\n }\n }\n }\n\n this.elForeground.appendChild(elGridLinesFragment)\n this.elCells.appendChild(elCellsFragment)\n\n this.renderLabels()\n },\n\n renderLabels: function() {\n\n // Labels are outside the scalables groups. Therefore,\n // we must make sure their position stays correct after resize.\n\n this.elLabel = V(this.model.labelMarkup)\n\n const cells = this.model.get('cells') || []\n const labels = this.model.get('labels') || {}\n const rowLabels = labels.rows || []\n const columnLabels = labels.columns || []\n const size = this.model.get('size')\n const cellHeight = size.height / cells.length\n const cellWidth = size.width / cells.length\n let label; let elLabel\n\n this.elRowLabels.textContent = ''\n this.elColumnLabels.textContent = ''\n\n const elRowLabelsFragment = document.createDocumentFragment()\n for (let i = 0; i < rowLabels.length; i++) {\n\n label = labels.rows[i]\n elLabel = this.elLabel.clone()\n elLabel.text(label.text)\n elLabel.attr(joint.util.assign({\n x: -(labels.padding || 5),\n y: i * cellHeight + cellHeight / 2,\n 'text-anchor': 'end',\n 'dominant-baseline': 'central',\n 'font-size': cellHeight,\n 'data-row': i\n }, joint.util.omit(label, 'text')))\n elRowLabelsFragment.appendChild(elLabel.node)\n }\n this.elRowLabels.appendChild(elRowLabelsFragment)\n\n let x; let y\n const elColumnLabelsFragment = document.createDocumentFragment()\n for (let j = 0; j < columnLabels.length; j++) {\n\n label = labels.columns[j]\n elLabel = this.elLabel.clone()\n x = j * cellWidth + cellWidth / 2\n y = -(labels.padding || 5)\n elLabel.attr('x', x)\n elLabel.text(label.text)\n elLabel.attr(joint.util.assign({\n y: y,\n 'text-anchor': 'start',\n 'dominant-baseline': 'central',\n 'font-size': cellWidth,\n 'data-column': j\n }, joint.util.omit(label, 'text')))\n elLabel.rotate(-90, x, y)\n elColumnLabelsFragment.appendChild(elLabel.node)\n }\n this.elColumnLabels.appendChild(elColumnLabelsFragment)\n }\n})\n\njoint.shapes.bpmn = {}\n\njoint.shapes.bpmn.icons = {\n\n none: '',\n\n message: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUxMiA1MTIiIGhlaWdodD0iNTEycHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB3aWR0aD0iNTEycHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxwYXRoIGQ9Ik00NzkuOTk4LDY0SDMyQzE0LjMyOSw2NCwwLDc4LjMxMiwwLDk2djMyMGMwLDE3LjY4OCwxNC4zMjksMzIsMzIsMzJoNDQ3Ljk5OEM0OTcuNjcxLDQ0OCw1MTIsNDMzLjY4OCw1MTIsNDE2Vjk2ICBDNTEyLDc4LjMxMiw0OTcuNjcxLDY0LDQ3OS45OTgsNjR6IE00MTYsMTI4TDI1NiwyNTZMOTYsMTI4SDQxNnogTTQ0OCwzODRINjRWMTYwbDE5MiwxNjBsMTkyLTE2MFYzODR6Ii8+PC9zdmc+',\n\n plus: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDI0IDI0IiBoZWlnaHQ9IjI0cHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjI0cHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxwYXRoIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTIyLjUsMTRIMTR2OC41YzAsMC4yNzYtMC4yMjQsMC41LTAuNSwwLjVoLTRDOS4yMjQsMjMsOSwyMi43NzYsOSwyMi41VjE0SDAuNSAgQzAuMjI0LDE0LDAsMTMuNzc2LDAsMTMuNXYtNEMwLDkuMjI0LDAuMjI0LDksMC41LDlIOVYwLjVDOSwwLjIyNCw5LjIyNCwwLDkuNSwwaDRDMTMuNzc2LDAsMTQsMC4yMjQsMTQsMC41VjloOC41ICBDMjIuNzc2LDksMjMsOS4yMjQsMjMsOS41djRDMjMsMTMuNzc2LDIyLjc3NiwxNCwyMi41LDE0eiIgZmlsbC1ydWxlPSJldmVub2RkIi8+PC9zdmc+',\n\n cross: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDI0IDI0IiBoZWlnaHQ9IjI0cHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjI0cHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxwYXRoIGQ9Ik0yMi4yNDUsNC4wMTVjMC4zMTMsMC4zMTMsMC4zMTMsMC44MjYsMCwxLjEzOWwtNi4yNzYsNi4yN2MtMC4zMTMsMC4zMTItMC4zMTMsMC44MjYsMCwxLjE0bDYuMjczLDYuMjcyICBjMC4zMTMsMC4zMTMsMC4zMTMsMC44MjYsMCwxLjE0bC0yLjI4NSwyLjI3N2MtMC4zMTQsMC4zMTItMC44MjgsMC4zMTItMS4xNDIsMGwtNi4yNzEtNi4yNzFjLTAuMzEzLTAuMzEzLTAuODI4LTAuMzEzLTEuMTQxLDAgIGwtNi4yNzYsNi4yNjdjLTAuMzEzLDAuMzEzLTAuODI4LDAuMzEzLTEuMTQxLDBsLTIuMjgyLTIuMjhjLTAuMzEzLTAuMzEzLTAuMzEzLTAuODI2LDAtMS4xNGw2LjI3OC02LjI2OSAgYzAuMzEzLTAuMzEyLDAuMzEzLTAuODI2LDAtMS4xNEwxLjcwOSw1LjE0N2MtMC4zMTQtMC4zMTMtMC4zMTQtMC44MjcsMC0xLjE0bDIuMjg0LTIuMjc4QzQuMzA4LDEuNDE3LDQuODIxLDEuNDE3LDUuMTM1LDEuNzMgIEwxMS40MDUsOGMwLjMxNCwwLjMxNCwwLjgyOCwwLjMxNCwxLjE0MSwwLjAwMWw2LjI3Ni02LjI2N2MwLjMxMi0wLjMxMiwwLjgyNi0wLjMxMiwxLjE0MSwwTDIyLjI0NSw0LjAxNXoiLz48L3N2Zz4=',\n\n user: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDI0IDI0IiBoZWlnaHQ9IjI0cHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjI0cHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxwYXRoIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTIyLDIwLjk5OGgtMWMwLDAtMSwwLTEtMVYxNy41YzAtMC4yNzctMC4yMjQtMC41LTAuNS0wLjVTMTksMTcuMjIzLDE5LDE3LjUgIGwtMC4wMDgsNC4yOTVjMCwwLjYwOS0yLjAxLDIuMjA1LTYuNDkyLDIuMjA1cy02LjQ5Mi0xLjU5Ni02LjQ5Mi0yLjIwNUw2LDE3LjVDNiwxNy4yMjMsNS43NzYsMTcsNS41LDE3UzUsMTcuMjIzLDUsMTcuNXYyLjQ5OCAgYzAsMS0xLDEtMSwxSDNjMCwwLTEsMC0xLTFWMTUuNzVjMC0yLjkyMiwyLjg5Mi01LjQwMSw2LjkzLTYuMzQxYzAsMCwxLjIzNCwxLjEwNywzLjU3LDEuMTA3czMuNTctMS4xMDcsMy41Ny0xLjEwNyAgYzQuMDM4LDAuOTQsNi45MywzLjQxOSw2LjkzLDYuMzQxdjQuMjQ4QzIzLDIwLjk5OCwyMiwyMC45OTgsMjIsMjAuOTk4eiBNMTIuNDc3LDljLTIuNDg1LDAtNC41LTIuMDE1LTQuNS00LjVTOS45OTEsMCwxMi40NzcsMCAgczQuNSwyLjAxNSw0LjUsNC41UzE0Ljk2Miw5LDEyLjQ3Nyw5eiIgZmlsbC1ydWxlPSJldmVub2RkIi8+PC9zdmc+',\n\n circle: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gULEBE3DEP64QAAAwlJREFUaN7dmktrU0EUx38ZmmBbfEIL2hSjkYKC1EW6EDFudC+404/gE6WKSvGxERQfIH4AX1T9EOKrCrYurVrbgsZWoaBVixDbpC6ci+Fyz9ybZG478cBs7syc+Z+5c86c+c8ksCPrgW1ADtgEbARafG1+AW+AYWAIGADGWUTZAJwHxoD5GssocA7ILiTwLcADoFQHcH8pAfeB7jiBtwO3gLJF4P5S1mO02wa/C5iMEbi/TAI7bYE/Y3m5VLOs+sLAJULqrgKHIxhZBp4DT4FX2jkLGoinq1M7fg7YDmwFVATd14CjFboiy5UIs/QBOAmka/izaeCU1hE2zuVqlZ8IUfgVOAA0WViiTcBBrdM0Zm9UhTuAOYOiRzXOeJh0Ak8M484B+TAlK4BPBiU3gWSMoTqpw6g0fgFYblJww9D5dojT25IEcMeA47rUsdsQLp9FmPmURSNSOqpJS2lzUKd+ocN3IBNx5mz+oXXADwHTXX/jjMFxjy1iwtgrYJoF1lY27BMafozZaaMspYKA7XRlw7f1xt4Y5biA7bXXIGv4TW0OGNCmsQRhzCidlwTJADDlgAFTwAuhLq+AHqHyMe6IhKVHAV1C5ZBDBkhYupThPPreIQNGJTJBGXKLLw4Z8NmQu/Fb8PCkQwakBIxFRWPLvAJmhMpWh4AuFb7PKGBaqFzjkAGrhe/TSjNrQZJ1yAAJy5gCRoTKnEMGSFhGFDBoOBu7IhKWQe8wLRFLHQ6A7zCcFNNK59vvAjoqYK8DBuwTCLBhTUD8Hweahj9S2jjU297VqzrU26BVmi2yEjXRKg1PbHnpqYla7AeWxAi+GbhHHdSit2mYyN2XQQ5kQTJ6Y6qL3PUkCr2+H7v0+jcs0eueRLngGNeKa9mxY73g8JzpEtHusorAQ/7e+e7WUWIl//jSVTrK7QEu6KgW9d7tYr3B44iBWPJfkZZ8pZ4r2VngkC0HywMTLNwN5YSBcKtZWoGzernEBbyox2iJc6Np2KcGfnHisYet1CDouc2yCjbhp07MrD+3+QNxi4JkAscRswAAAABJRU5ErkJggg==',\n\n service: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAB1RJREFUeNrMWltMVFcUHZAIRSAkSFACITRAVbQ/DVhHiRZC2y9jotFgqgmxKqShKdFCggZbEpP2h1Q/oLUg8GH0xy/LKwpDEEFsjKCM8kYkgAhTpggDMzCc7kX2mGF677w80J5khbnnntc+Zz/W2ReNxvsSQLhPWCBYGPhdTdjg7aBCCI/gVSfu+CHBfPz48ba7d+82AocOHfqT6mYIEV6Oua4CfE6YHxsbeyS49Pf3P6C6RYJ2vQTwVTtKHx+fXYRzhM0qTbYR/g4JCQmyVYSFhQXTHyMhUWXMzTzmTo3MorATHxH0eEXAriYrtCmNiop6tby8bLCdgNVqfR0ZGTlC735RaJ/EY+HhGSFhTVSIyg7CIO3mRHNzs44WOYydJuQQ/AjxhJ9hsFqt9qlwKElJSRB8nvAjIYYN+hucDMbCmMHBwZP03E/YLlUAKh8TnoSHh49NT0+vLG5paWk0JyfnPu9cJ8EUGho6cfTo0fbBwcFWRwH6+vpaYcy0yCk26Hb0zc7ObsFYaIOxaY5xqn9M2ClTgGw8wqM4LuzevXu6xMTEnitXrjTOz8/3Chfl7du3Ly5fvtyUkJDQrzRedXV1I2/KWZkCwCDrsMMWi+WV46Sk42+EhwV24VhnNpuHaY43NFct5pRtA7tw9BkZGQ/peVHIL+bDhw8/YrtKXJM4QOVrVNfV1TXKXr2d6mTK8EI+tk4KPrszNjY2mIJTiK+vb5iK+zX29vbqW1paLNRe7N271590PpF+hyq1hzpt3bpVTE5ODlDfFIU5FdfiTRxAkDKVlpY2QI2VVNvOHUIVXjJmUEcC6Rz7keGLffv2CdoMK5/AogzupCZA8aZNmwxkbEMKi18sKSmxqcF1QjohmgF6UYl3LPw7GxoYGBAbN24U+fn5puLi4qWqqqoBGdwJxZ+J2ZeEXF6U9dixYw+VdLi1tbWJF3/aycBn0aa9vb3J1q++vl4EBARAfYRM7oTSwpFzjjAeExMznJ6e/mRkZORfAoA20Mn8Re3KFAaD9zpH2MzP13GK1GcafSl+iIiICGEymd6Nh4BG7SbVNgNj8Zg7nQmwdPLkybahoaG22dnZbntu41i6u7sh7CzhM1fciZCKTUEf9M3KyhLJycnYBCGTO6FYmpqa3HKX5eXlDWysUW5wp0K0LSoqasvLyxP+/v7ixIkTQjZ3WhFAR8UbAdzgTmY/Pz9rfHy8KCgoEKOjo0I2d1pxzxhAr9e3GI3GZ87ogp0KpbriTrdv3+6ghS9XVlYaFxYWxFpxJ5QaltxMeAOOf+DAgQ4VI55mIy53hzuRx7GuNXfSsM5FwJURzhBK3HSjZ/8P3MmrQIYgxUJUcvDKx/OtW7c61ps7ObtSziHiqlEJ0AU+VqgfPI8lOjraajAYVNUGKgg7Kisra4RDwG9bnFAqZMTjcBBI3ziLA0ovQPL0cXFxg6STU84W1NPT8+DSpUtt8DY3btwwqqm2p9zJ1q+wsBAqC7uL9EQA3H+Fu+41NzdXkPELMmQhizvZeSc9bQ5OucAtATiKLsAPK+0KXCKIWW1trSBSJjIzM8EwxYULF4RM7mRf0tLSnrCX/JXwLW9CLPO4VVfKcHAj4kMvldwZ6ojzr5CywMBAsWXLFrFnz54VmjAxMSE84U4KQqziTvYFapqSktIJnkbtXjNvW+DU5ioB8vDIOrlq7Xfu3GmgINNHl47lixcvIiO3QszsuY273ElFgFXcSSVGTBFfew7eBv4GHucoAHz6Y+I1r23UgFRmED6e1QD8XZBOW7zlTk4EgE28rKioaHBnbNgnPJ+vw8UGrO8rcoVzdJ2MqKmp0ZEbC7l58yaSWacJn0LqoKCgcc1/XOg0nHqhBDt6XI+TsXvnNXdycQJpUCHouzOagTkxN9/mrKqZAE7QgloEOtR7zZ1cCFABRqpkxNgIMuKnmIsvQGaOJ9UepzLelzupjJnlzI2mpqZ28KKvcspHy2vY4I0AMrhTDOMLQhXeXbt2zftAJikd7yl3AmZAxXFK3lIJH08lUUo++aBSo+ki7vQBGaHTRBjdwJ7TghGENFqtNoAuPTvUEmFkDxN0Z7ZSkBymvlqltUj5DOQpd/Kk4HbG6pct5fuYp9xJQlk8depUK1/8P5EqgDvcydPV4g6gVMcutNmWd5IlgEvuhA8ixJl63LjU64uKinS4g+AW5niauE+wKn0vUwB3uNMcvM+RI0faQcKU0ioHDx60pVWMtmQWvj9jLFtaBXNwWmWXbBvYThiAO8TO8UIMHHAQaOIIPyEQ7d+/v9NRgN27d3exfv/AhG4D9zVgLIzJ7heJrW3SjdgVd7Jr8zsybfZUATyKU4tXVXKt9Txml2pqUdbXdDXuZPf+OySPkX+1CQBixtzmjEqfQL6tJaqtxU/ix3I9n4JaeUEInZmZGSHasVIxNTU1wx9TulTGNNGf35zN67eOFL6X4Hv+/Hkr3aN14PNEPYL5atgvjde4+1HBy3/PaXb49xwwzD/eZyP/EWAAQ3AUnjpOYHIAAAAASUVORK5CYII='\n}\n\n// Icon Interface\n\njoint.shapes.bpmn.IconInterface = {\n\n initialize: function() {\n\n // In order to be able to use multiple interfaces for one Backbone.model, we need to keep\n // reference to the actual parent class prototype.\n this._parent = (this._parent || this).constructor.__super__\n\n this._parent.initialize.apply(this, arguments)\n\n this.listenTo(this, 'change:icon', this._onIconChange)\n\n this._onIconChange(this, this.get('icon') || 'none')\n },\n\n _onIconChange: function(cell, icon) {\n\n const {icons} = joint.shapes.bpmn\n\n if (joint.util.has(icons, icon)) {\n\n cell.attr('image/xlink:href', icons[icon])\n\n } else {\n\n throw `BPMN: Unknown icon: ${ icon}`\n }\n }\n}\n\n// SubProcess Interface\n\njoint.shapes.bpmn.SubProcessInterface = {\n\n initialize: function() {\n\n // See IconInterface.initialize()\n this._parent = (this._parent || this).constructor.__super__\n\n this._parent.initialize.apply(this, arguments)\n\n this.listenTo(this, 'change:subProcess', this._onSubProcessChange)\n\n this._onSubProcessChange(this, this.get('subProcess') || null)\n },\n\n _onSubProcessChange: function(cell, subProcess) {\n\n cell.attr({\n '.sub-process': {\n visibility: subProcess ? 'visible' : 'hidden',\n 'data-sub-process': subProcess || ''\n }\n })\n }\n}\n\n\n// Task\n\njoint.shapes.bpmn.ActivityView = joint.shapes.basic.TextBlockView\njoint.shapes.bpmn.Activity = joint.shapes.basic.TextBlock.extend({\n\n markup: [\n '',\n '',\n joint.env.test('svgforeignobject') ? '
' : '',\n ''\n ].join(''),\n\n defaults: joint.util.deepSupplement({\n\n size: { width: 100, height: 100 },\n type: 'bpmn.Activity',\n attrs: {\n rect: {\n rx: 8,\n ry: 8,\n width: 100,\n height: 100\n },\n '.body': {\n fill: '#ffffff',\n stroke: '#000000'\n },\n '.inner': {\n transform: 'scale(0.9,0.9) translate(5,5)'\n },\n path: {\n d: 'M 0 0 L 30 0 30 30 0 30 z M 15 4 L 15 26 M 4 15 L 26 15',\n ref: '.inner',\n 'ref-x': 0.5,\n 'ref-dy': -30,\n 'x-alignment': 'middle',\n stroke: '#000000',\n fill: 'transparent'\n },\n image: {\n ref: '.inner',\n 'ref-x': 5,\n width: 20,\n height: 20\n }\n },\n activityType: 'task',\n subProcess: null\n\n }, joint.shapes.basic.TextBlock.prototype.defaults),\n\n initialize: function() {\n\n joint.shapes.basic.TextBlock.prototype.initialize.apply(this, arguments)\n\n this.listenTo(this, 'change:activityType', this.onActivityTypeChange)\n this.listenTo(this, 'change:subProcess', this.onSubProcessChange)\n\n this.onSubProcessChange(this, this.get('subProcess'))\n this.onActivityTypeChange(this, this.get('activityType'))\n },\n\n onActivityTypeChange: function(cell, type) {\n\n switch (type) {\n\n case 'task':\n\n cell.attr({\n '.inner': {\n visibility: 'hidden'\n },\n '.outer': {\n 'stroke-width': 1,\n 'stroke-dasharray': 'none'\n },\n path: {\n ref: '.outer'\n },\n image: {\n ref: '.outer'\n }\n })\n\n break\n\n case 'transaction':\n\n cell.attr({\n '.inner': {\n visibility: 'visible'\n },\n '.outer': {\n 'stroke-width': 1,\n 'stroke-dasharray': 'none'\n },\n path: {\n ref: '.inner'\n },\n image: {\n ref: '.inner'\n }\n })\n\n break\n\n case 'event-sub-process':\n\n cell.attr({\n '.inner': {\n visibility: 'hidden'\n },\n '.outer': {\n 'stroke-width': 1,\n 'stroke-dasharray': '1,2'\n },\n path: {\n ref: '.outer'\n },\n image: {\n ref: '.outer'\n }\n })\n\n break\n\n case 'call-activity':\n\n cell.attr({\n '.inner': {\n visibility: 'hidden'\n },\n '.outer': {\n 'stroke-width': 5,\n 'stroke-dasharray': 'none'\n },\n path: {\n ref: '.outer'\n },\n image: {\n ref: '.outer'\n }\n })\n\n break\n\n default:\n\n throw `BPMN: Unknown Activity Type: ${ type}`\n }\n },\n\n onSubProcessChange: function(cell, subProcess) {\n\n // Although that displaying sub-process icon is implemented in the interface\n // we want also to reposition text and image when sub-process is shown.\n\n if (subProcess) {\n\n cell.attr({\n '.fobj div': {\n style: {\n verticalAlign: 'baseline',\n paddingTop: 10\n }\n },\n image: {\n 'ref-dy': -25,\n 'ref-y': ''\n },\n text: { // IE fallback only\n 'ref-y': 25\n }\n })\n\n } else {\n\n cell.attr({\n '.fobj div': {\n style: {\n verticalAlign: 'middle',\n paddingTop: 0\n }\n },\n image: {\n 'ref-dy': '',\n 'ref-y': 5\n },\n text: { // IE fallback only\n 'ref-y': .5\n }\n })\n }\n }\n\n}).extend(joint.shapes.bpmn.IconInterface).extend(joint.shapes.bpmn.SubProcessInterface)\n\n// Annotation\n\njoint.shapes.bpmn.AnnotationView = joint.shapes.basic.TextBlockView\njoint.shapes.bpmn.Annotation = joint.shapes.basic.TextBlock.extend({\n\n markup: [\n '',\n '',\n joint.env.test('svgforeignobject') ? '
' : '',\n ''\n ].join(''),\n\n defaults: joint.util.deepSupplement({\n\n size: { width: 100, height: 100 },\n type: 'bpmn.Annotation',\n attrs: {\n rect: {\n width: 100,\n height: 100\n },\n '.body': {\n 'fill-opacity': 0.1,\n fill: '#ffffff',\n stroke: 'none'\n },\n '.fobj div': {\n style: {\n textAlign: 'left',\n paddingLeft: 10\n }\n },\n '.stroke': {\n stroke: '#000000',\n fill: 'none',\n 'stroke-width': 3\n }\n\n },\n wingLength: 20\n\n }, joint.shapes.basic.TextBlock.prototype.defaults),\n\n initialize: function() {\n\n joint.shapes.basic.TextBlock.prototype.initialize.apply(this, arguments)\n\n this.listenTo(this, 'change:size', this.onSizeChange)\n\n // calculate dasharray for first time\n this.onSizeChange(this, this.get('size'))\n },\n\n onSizeChange: function(cell, size) {\n\n cell.attr('.stroke', {\n d: cell.getStrokePathData(size.width, size.height, cell.get('wingLength'))\n })\n },\n\n getStrokePathData: function(width, height, wingLength) {\n\n // wing length can't be greater than the element width\n wingLength = Math.min(wingLength, width)\n\n return ['M', wingLength, '0 L 0 0 0', height, wingLength, height].join(' ')\n }\n\n})\n\n// Gateway\n\njoint.shapes.bpmn.Gateway = joint.dia.Element.extend({\n\n markup: '',\n\n defaults: joint.util.deepSupplement({\n\n type: 'bpmn.Gateway',\n size: { width: 80, height: 80 },\n attrs: {\n '.body': {\n points: '40,0 80,40 40,80 0,40',\n fill: '#ffffff',\n stroke: '#000000'\n },\n '.label': {\n text: '',\n ref: '.body',\n 'ref-x': .5,\n 'ref-dy': 20,\n 'y-alignment': 'middle',\n 'x-alignment': 'middle',\n 'font-size': 14,\n 'font-family': 'Arial, helvetica, sans-serif',\n fill: '#000000'\n },\n image: {\n width: 40, height: 40, 'xlink:href': '', transform: 'translate(20,20)'\n }\n }\n\n }, joint.dia.Element.prototype.defaults)\n\n}).extend(joint.shapes.bpmn.IconInterface)\n\n// Events\n\njoint.shapes.bpmn.Event = joint.dia.Element.extend({\n\n markup: '',\n\n defaults: joint.util.deepSupplement({\n\n type: 'bpmn.Event',\n size: { width: 60, height: 60 },\n attrs: {\n '.body': {\n fill: '#ffffff',\n stroke: '#000000'\n },\n '.outer': {\n 'stroke-width': 1, r: 30,\n transform: 'translate(30,30)'\n },\n '.inner': {\n 'stroke-width': 1, r: 26,\n transform: 'translate(30,30)'\n },\n image: {\n width: 40, height: 40, 'xlink:href': '', transform: 'translate(10,10)'\n },\n '.label': {\n text: '',\n fill: '#000000',\n 'font-family': 'Arial', 'font-size': 14,\n ref: '.outer', 'ref-x': .5, 'ref-dy': 20,\n 'x-alignment': 'middle', 'y-alignment': 'middle'\n }\n },\n eventType: 'start'\n\n }, joint.dia.Element.prototype.defaults),\n\n initialize: function() {\n\n joint.dia.Element.prototype.initialize.apply(this, arguments)\n\n this.listenTo(this, 'change:eventType', this.onEventTypeChange)\n\n this.onEventTypeChange(this, this.get('eventType'))\n },\n\n onEventTypeChange: function(cell, type) {\n\n switch (type) {\n\n case 'start':\n\n cell.attr({\n '.inner': {\n visibility: 'hidden'\n },\n '.outer': {\n 'stroke-width': 1\n }\n })\n\n break\n\n case 'end':\n\n cell.attr({\n '.inner': {\n visibility: 'hidden'\n },\n '.outer': {\n 'stroke-width': 5\n }\n })\n\n break\n\n case 'intermediate':\n\n cell.attr({\n '.inner': {\n visibility: 'visible'\n },\n '.outer': {\n 'stroke-width': 1\n }\n })\n\n break\n\n default:\n\n throw `BPMN: Unknown Event Type: ${ type}`\n }\n }\n\n}).extend(joint.shapes.bpmn.IconInterface)\n\n// Pool & Lanes\n\njoint.shapes.bpmn.Pool = joint.dia.Element.extend({\n\n markup: ['',\n '',\n '',\n '',\n '',\n ''].join(''),\n\n laneMarkup: '',\n\n defaults: joint.util.deepSupplement({\n\n type: 'bpmn.Pool',\n size: {\n width: 600,\n height: 300\n },\n attrs: {\n '.body': {\n fill: '#ffffff',\n stroke: '#000000',\n width: 500,\n height: 200,\n 'pointer-events': 'stroke'\n },\n '.header': {\n fill: '#ffffff',\n stroke: '#000000',\n width: 20,\n ref: '.body',\n 'ref-height': 1,\n 'pointer-events': 'visiblePainted'\n },\n '.label': {\n fill: '#000000',\n transform: 'rotate(-90)',\n ref: '.header',\n 'ref-x': 10,\n 'ref-y': .5,\n 'font-family': 'Arial',\n 'font-size': 14,\n 'x-alignment': 'middle',\n 'text-anchor': 'middle'\n },\n '.lane-body': {\n fill: '#ffffff',\n stroke: '#000000',\n 'pointer-events': 'stroke'\n },\n '.lane-header': {\n fill: '#ffffff',\n stroke: '#000000',\n 'pointer-events': 'visiblePainted'\n },\n '.lane-label': {\n fill: '#000000',\n transform: 'rotate(-90)',\n 'text-anchor': 'middle',\n 'font-family': 'Arial',\n 'font-size': 13\n },\n '.blackbox-wrap': {\n ref: '.body',\n 'ref-width': 1,\n 'ref-height': 1\n },\n '.blackbox-label': {\n text: 'Black Box',\n 'text-anchor': 'middle',\n transform: 'translate(0,-7)'\n },\n '.blackbox-label > tspan': {\n dx: '50%',\n dy: '50%'\n }\n }\n\n }, joint.dia.Element.prototype.defaults)\n})\n\njoint.shapes.bpmn.PoolView = joint.dia.ElementView.extend({\n\n options: {\n headerWidth: 20\n },\n\n initialize: function() {\n\n this.listenTo(this.model, 'change:lanes', function(cell, lanes) {\n\n this.renderLanes(lanes)\n })\n\n joint.dia.ElementView.prototype.initialize.apply(this, arguments)\n },\n\n update: function() {\n\n if (this.lanesAttrs === undefined) {\n // This is the first time update. We render the lanes.\n return this.renderLanes(this.model.get('lanes'))\n }\n\n return joint.dia.ElementView.prototype.update.call(this, this.model,\n // always update everything including the lanes attributes\n joint.util.merge({}, this.model.get('attrs'), this.lanesAttrs || {})\n )\n },\n\n renderMarkup: function() {\n\n joint.dia.ElementView.prototype.renderMarkup.apply(this, arguments)\n\n // a holder for all the lanes\n this.$lanes = this.$('.lanes')\n\n // An SVG element for the lane\n this.laneMarkup = V(this.model.laneMarkup)\n },\n\n renderLanes: function(lanes) {\n\n lanes = lanes || {}\n\n // index keeps track on how many lanes we created\n this.index = 0\n\n const headerWidth = lanes.headerWidth === undefined ? this.options.headerWidth : lanes.headerWidth\n this.lanesAttrs = {\n '.header': { width: headerWidth },\n '.label': { text: lanes.label || '' }\n }\n\n this.$lanes.empty()\n\n if (lanes.sublanes) {\n\n // recursion start\n this.renderSublanes(lanes.sublanes, headerWidth, 0, 1, 'lanes')\n }\n\n // We don't want the lanes attributes to be stored on model.\n // That's why we are using renderingOnlyAttrs parameter in ElementView.update\n this.update(this.model, joint.util.merge({}, this.model.get('attrs'), this.lanesAttrs))\n },\n\n calculateRatios: function(lanes, ratio) {\n\n let definedRatio = 0\n const calculated = []\n const ratios = []\n\n for (let i = 0, n = lanes.length; i < n; i++) {\n const lane = lanes[i]\n const laneRatio = lane.ratio\n if (Number.isFinite(laneRatio)) {\n definedRatio += laneRatio / 10\n ratios[i] = laneRatio / 10 / ratio\n } else {\n calculated.push(i)\n }\n }\n\n const calculatedRatio = Math.max(1 - definedRatio, 0) * ratio / calculated.length\n for (let j = 0, m = calculated.length; j < m; j++) {\n ratios[calculated[j]] = calculatedRatio\n }\n\n return ratios\n },\n\n renderSublanes: function(lanes, prevX, prevY, prevRatio, prevPath) {\n\n const defaultHeaderWidth = this.options.headerWidth\n const path = `${prevPath }/sublanes/`\n const ratios = this.calculateRatios(lanes, prevRatio)\n\n joint.util.toArray(lanes).reduce(function(ratioSum, lane, index) {\n\n let className = `lane${ this.index}`\n const bodySelector = `.${ className } .lane-body`\n const headerSelector = `.${ className } .lane-header`\n const labelSelector = `.${ className } .lane-label`\n\n if (lane.name) {\n // add custom css class if specified\n className += ` ${ lane.name}`\n }\n\n // append a new lane to the pool\n const lanePath = path + index\n const svgLane = this.laneMarkup.clone()\n .addClass(className)\n .attr({\n 'data-lane-path': lanePath,\n 'data-lane-index': this.index\n })\n\n this.$lanes.append(svgLane.node)\n\n const ratio = ratios[index]\n const headerWidth = lane.headerWidth === undefined ? defaultHeaderWidth : lane.headerWidth\n const y = prevY + ratioSum\n\n this.lanesAttrs[bodySelector] = {\n ref: '.body',\n 'ref-height': ratio,\n 'ref-width': -prevX,\n 'ref-x': prevX,\n 'ref-y': y\n }\n\n this.lanesAttrs[headerSelector] = {\n width: headerWidth,\n ref: '.body',\n 'ref-height': ratio,\n 'ref-x': prevX,\n 'ref-y': y\n }\n\n this.lanesAttrs[labelSelector] = {\n text: lane.label,\n ref: headerSelector,\n 'ref-x': 10,\n 'ref-y': .5,\n 'x-alignment': 'middle'\n }\n\n this.index++\n\n if (lane.sublanes) {\n\n // recursively render any child lanes\n this.renderSublanes(lane.sublanes, prevX + headerWidth, y, ratio, lanePath)\n }\n\n return ratioSum + ratio\n\n }.bind(this), 0)\n }\n})\n\n// Group\n\njoint.shapes.bpmn.Group = joint.dia.Element.extend({\n\n markup: '',\n\n defaults: joint.util.deepSupplement({\n\n type: 'bpmn.Group',\n size: {\n width: 200,\n height: 200\n },\n attrs: {\n '.body': {\n width: 200,\n height: 200,\n stroke: '#000000',\n 'stroke-dasharray': '6,6',\n 'stroke-width': 2,\n fill: 'transparent',\n rx: 15,\n ry: 15,\n 'pointer-events': 'stroke'\n },\n '.label-rect': {\n ref: '.body',\n 'ref-width': 0.6,\n 'ref-x': 0.4,\n 'ref-y': -30,\n height: 25,\n fill: '#ffffff',\n stroke: '#000000'\n },\n '.label-group': {\n ref: '.label-rect',\n 'ref-x': 0,\n 'ref-y': 0\n },\n '.label-wrap': {\n ref: '.label-rect',\n 'ref-width': 1,\n 'ref-height': 1\n },\n '.label': {\n text: '',\n x: '50%',\n y: '1.3em',\n 'text-anchor': 'middle',\n 'font-family': 'Arial',\n 'font-size': 14,\n fill: '#000000'\n }\n }\n\n }, joint.dia.Element.prototype.defaults)\n})\n\n// Data Object\n\njoint.shapes.bpmn.DataObject = joint.dia.Element.extend({\n\n markup: '',\n\n defaults: joint.util.deepSupplement({\n\n type: 'bpmn.DataObject',\n size: {\n width: 60,\n height: 80\n },\n attrs: {\n '.body': {\n points: '20,0 60,0 60,80 0,80 0,20 20,0 20,20 0,20',\n stroke: '#000000',\n fill: '#ffffff'\n },\n '.label': {\n ref: '.body',\n 'ref-x': .5,\n 'ref-dy': 5,\n text: '',\n 'text-anchor': 'middle'\n }\n }\n\n }, joint.dia.Element.prototype.defaults)\n})\n\njoint.shapes.bpmn.Conversation = joint.dia.Element.extend({\n\n markup: '',\n\n defaults: joint.util.deepSupplement({\n\n type: 'bpmn.Conversation',\n size: {\n width: 100,\n height: 100\n },\n attrs: {\n '.body': {\n points: '25,0 75,0 100,50 75,100 25,100 0,50',\n stroke: '#000000',\n fill: '#ffffff'\n },\n '.label': {\n text: '',\n ref: '.body',\n 'ref-x': .5,\n 'ref-dy': 5,\n 'text-anchor': 'middle'\n },\n path: {\n d: 'M 0 0 L 30 0 30 30 0 30 z M 15 4 L 15 26 M 4 15 L 26 15',\n ref: '.body',\n 'ref-x': 0.5,\n 'ref-dy': -30,\n 'x-alignment': 'middle',\n fill: '#ffffff',\n stroke: '#000000',\n 'fill-opacity': 0\n }\n },\n\n conversationType: 'conversation'\n\n }, joint.dia.Element.prototype.defaults),\n\n initialize: function() {\n\n joint.dia.Element.prototype.initialize.apply(this, arguments)\n\n this.listenTo(this, 'change:conversationType', this.onConversationTypeChange)\n\n this.onConversationTypeChange(this, this.get('conversationType'))\n },\n\n onConversationTypeChange: function(cell, type) {\n\n switch (type) {\n\n case 'conversation':\n\n cell.attr('polygon/stroke-width', 1)\n\n break\n\n case 'call-conversation':\n\n cell.attr('polygon/stroke-width', 4)\n\n break\n\n default:\n\n throw `BPMN: Unknown Conversation Type: ${ type}`\n }\n }\n\n}).extend(joint.shapes.bpmn.SubProcessInterface)\n\n// Choreograpy\n\njoint.shapes.bpmn.Choreography = joint.shapes.basic.TextBlock.extend({\n /*\n markup: '',\n*/\n markup: [\n '',\n '',\n joint.env.test('svgforeignobject') ? '
' : '',\n '',\n ''\n ].join(''),\n\n participantMarkup: '',\n\n defaults: joint.util.deepSupplement({\n\n type: 'bpmn.Choreography',\n size: {\n width: 60,\n height: 80\n },\n attrs: {\n // HACK (TODO!): as we don't have indexes on attributes yet (to determine the order\n // of applying attributes) we have to reintroduce the rect attribute again\n // so it changes the \"order\" how the attributes are processed.\n // It's necessary to apply all `rect` attributes before we apply '.body' attributes,\n // otherwise our stroke and fill will be overwritten.\n rect: {},\n '.body': {\n width: 60,\n height: 80,\n stroke: '#000000',\n fill: '#ffffff'\n },\n '.label': {\n ref: '.body',\n 'ref-x': .5,\n 'ref-dy': 5,\n text: '',\n 'text-anchor': 'middle'\n },\n '.participant-rect': {\n stroke: '#000000',\n fill: '#aaaaaa',\n ref: '.body',\n 'ref-width': 1\n },\n '.participant-label': {\n 'text-anchor': 'middle',\n ref: '.participant_0 .participant-rect',\n 'ref-x': .5,\n 'ref-y': .5,\n 'y-alignment': 'middle'\n },\n '.sub-process': {\n d: 'M 0 0 L 30 0 30 30 0 30 z M 15 4 L 15 26 M 4 15 L 26 15',\n ref: '.body',\n 'ref-x': 0.5,\n 'ref-dy': -30,\n 'x-alignment': 'middle',\n fill: 'transparent',\n stroke: '#000000'\n }\n },\n\n participants: [],\n initiatingParticipant: 0 // index (number) or participant name (string)\n\n }, joint.shapes.basic.TextBlock.prototype.defaults)\n\n}).extend(joint.shapes.bpmn.SubProcessInterface)\n\njoint.shapes.bpmn.ChoreographyView = joint.shapes.basic.TextBlockView.extend({\n\n options: {\n participantHeight: 20\n },\n\n initialize: function() {\n\n this.listenTo(this.model, 'change:participants', function(cell, participants) {\n this.renderParticipants(participants)\n })\n\n this.listenTo(this.model, 'change:initiatingParticipant', this.layoutAndUpdate)\n\n joint.shapes.basic.TextBlockView.prototype.initialize.apply(this, arguments)\n\n if (!joint.env.test('svgforeignobject')) {\n // Attach our own handler for content change.\n // The new one will take the participant attributes into account.\n this.stopListening(this.model, 'change:content').listenTo(this.model, 'change:content', function(cell) {\n this.updateContent(cell, this.participantsAttrs)\n })\n }\n },\n\n update: function() {\n\n if (this.participantsAttrs === undefined) {\n // This is the first time update. We render the participants.\n return this.renderParticipants(this.model.get('participants'))\n }\n\n this.layoutAndUpdate()\n\n return this\n },\n\n render: function() {\n // Reset participants cache when `dirty: true` flag used in `attrs()`.\n this.participantsAttrs = undefined\n joint.dia.ElementView.prototype.render.apply(this, arguments)\n },\n\n renderMarkup: function() {\n\n joint.dia.ElementView.prototype.renderMarkup.apply(this, arguments)\n\n // a holder for all the lanes\n this.$participants = this.$('.participants')\n\n // An SVG element for the lane\n this.participantMarkup = V(this.model.participantMarkup)\n },\n\n renderParticipants: function(participants) {\n\n this.$participants.empty()\n this.participantsAttrs = {}\n\n joint.util.toArray(participants).forEach(function(participant, index) {\n\n const className = `participant_${ index}`\n const selector = `.${ className}`\n\n this.participantsAttrs[`${selector } .participant-rect`] = {\n height: this.options.participantHeight\n }\n\n this.participantsAttrs[`${selector } .participant-label`] = {\n text: participant\n }\n\n this.$participants.append(this.participantMarkup.clone().addClass(className).node)\n\n }, this)\n\n this.layoutAndUpdate()\n },\n\n layoutAndUpdate: function() {\n\n const participants = this.model.get('participants') || []\n\n const count = participants.length\n\n const pHeight = this.options.participantHeight\n const eHeight = this.model.get('size').height\n const bHeight = Math.max(0, eHeight - (pHeight * count))\n\n let offsetY = 0\n\n const initiator = this.model.get('initiatingParticipant')\n\n // initiator index\n const i = Math.max(joint.util.isNumber(initiator) ? Math.abs(initiator) : participants.indexOf(initiator), 0)\n\n // body position index\n const b = Math.min(i, count - 2)\n\n joint.util.toArray(participants).forEach(function(participant, index) {\n\n const selector = `.participant_${ index}`\n\n this.participantsAttrs[selector] = {\n transform: `translate(0,${ offsetY })`\n }\n\n this.participantsAttrs[`${selector } .participant-rect`].fill = (i == index)\n ? this.model.attr('.body/fill')\n : this.model.attr('.participant-rect/fill')\n\n this.participantsAttrs[`${selector } .participant-rect`].stroke = (i == index)\n ? this.model.attr('.body/stroke')\n : this.model.attr('.participant-rect/stroke')\n\n offsetY += pHeight + (b == index ? bHeight : 0)\n\n }, this)\n\n // set sub-process icon position\n const sp = count < 2 ? 0 : b - count + 1\n\n this.participantsAttrs['.sub-process'] = {\n 'ref-dy': Math.max(-eHeight, sp * pHeight - 30)\n }\n\n // Change text content position in the middle of the participant body\n const c = count < 2 ? 0 : b + 1\n\n this.participantsAttrs['.fobj div'] = {\n style: {\n height: bHeight,\n paddingTop: pHeight * c\n }\n }\n // Same as above just IE fallback\n this.participantsAttrs['.content'] = {\n 'ref-y': pHeight * c + bHeight / 2\n }\n\n // We don't want the participants attributes to be stored on model.\n // That's why we are using renderingOnlyAttrs parameter in ElementView.update\n const attrs = joint.util.merge({}, this.model.get('attrs'), this.participantsAttrs || {})\n\n // Backwards compatibility\n // The text is set by `.content/html` now (leaving this in the attrs would result\n // in not being able to change content).\n joint.util.unsetByPath(attrs, 'div/html')\n\n joint.shapes.basic.TextBlockView.prototype.update.call(this, this.model, attrs)\n }\n\n})\n\n// Message\n\njoint.shapes.bpmn.Message = joint.dia.Element.extend({\n\n markup: '',\n\n defaults: joint.util.deepSupplement({\n\n type: 'bpmn.Message',\n size: {\n width: 60,\n height: 40\n },\n attrs: {\n '.body': {\n points: '0,0 60,0 60,40 0,40 0,0 60,0 30,20 0,0',\n stroke: '#000000',\n fill: '#ffffff'\n },\n '.label': {\n ref: '.body',\n 'ref-x': .5,\n 'ref-dy': 5,\n text: '',\n 'text-anchor': 'middle'\n }\n }\n\n }, joint.dia.Element.prototype.defaults)\n})\n\n// Sequence Flows\n\njoint.shapes.bpmn.Flow = joint.dia.Link.extend({\n\n defaults: {\n\n type: 'bpmn.Flow',\n\n attrs: {\n\n '.marker-source': {\n d: 'M 0 0'\n },\n '.marker-target': {\n d: 'M 10 0 L 0 5 L 10 10 z',\n fill: '#000000'\n },\n '.connection': {\n 'stroke-dasharray': ' ',\n 'stroke-width': 1\n },\n '.connection-wrap': {\n style: '',\n onMouseOver: '',\n onMouseOut: ''\n }\n },\n\n flowType: 'normal'\n },\n\n initialize: function() {\n\n joint.dia.Link.prototype.initialize.apply(this, arguments)\n\n this.listenTo(this, 'change:flowType', this.onFlowTypeChange)\n\n this.onFlowTypeChange(this, this.get('flowType'))\n },\n\n onFlowTypeChange: function(cell, type) {\n\n let attrs\n\n switch (type) {\n\n case 'default':\n\n attrs = {\n '.marker-source': {\n d: 'M 0 5 L 20 5 M 20 0 L 10 10',\n fill: 'none'\n }\n }\n\n break\n\n case 'conditional':\n\n attrs = {\n '.marker-source': {\n d: 'M 20 8 L 10 0 L 0 8 L 10 16 z',\n fill: '#FFF'\n }\n }\n\n break\n\n case 'normal':\n\n attrs = {}\n\n break\n\n case 'message':\n\n attrs = {\n '.marker-target': {\n fill: '#FFF'\n },\n '.connection': {\n 'stroke-dasharray': '4,4'\n }\n }\n\n break\n\n case 'association':\n\n attrs = {\n '.marker-target': {\n d: 'M 0 0'\n },\n '.connection': {\n 'stroke-dasharray': '4,4'\n }\n }\n\n break\n\n case 'conversation':\n\n // The only way how to achieved 'spaghetti insulation effect' on links is to\n // have the .connection-wrap covering the inner part of the .connection.\n // The outer part of the .connection then looks like two parallel lines.\n attrs = {\n '.marker-target': {\n d: 'M 0 0'\n },\n '.connection': {\n 'stroke-width': '7px'\n },\n '.connection-wrap': {\n // As the css takes priority over the svg attributes, that's only way\n // how to overwrite default jointjs styling.\n style: 'stroke: #fff; stroke-width: 5px; opacity: 1;',\n onMouseOver: 'var s=this.style;s.stroke=\\'#000\\';s.strokeWidth=15;s.opacity=.4',\n onMouseOut: 'var s=this.style;s.stroke=\\'#fff\\';s.strokeWidth=5;s.opacity=1'\n }\n }\n\n break\n\n default:\n\n throw `BPMN: Unknown Flow Type: ${ type}`\n }\n\n cell.attr(joint.util.merge({}, this.defaults.attrs, attrs))\n }\n})\n\n// Command manager implements undo/redo functionality.\n\njoint.dia.CommandManager = Backbone.Model.extend({\n\n defaults: {\n cmdBeforeAdd: null,\n cmdNameRegex: /^(?:add|remove|change:\\w+)$/,\n // List of options names that will passed when a command is applied (redone).\n // e.g ['propertyPath', 'propertyValue']\n // See `dia.Cell.prototype.prop()` for more about the `propertyPath` option.\n applyOptionsList: ['propertyPath'],\n // List of options names that will passed when a command is reverted (undone).\n revertOptionsList: ['propertyPath']\n },\n\n // length of prefix 'change:' in the event name\n PREFIX_LENGTH: 7,\n\n initialize: function(options) {\n\n joint.util.bindAll(this, 'initBatchCommand', 'storeBatchCommand')\n\n this.graph = options.graph\n\n this.reset()\n this.listen()\n },\n\n listen: function() {\n\n this.listenTo(this.graph, 'all', this.addCommand, this)\n\n this.listenTo(this.graph, 'batch:start', this.initBatchCommand, this)\n this.listenTo(this.graph, 'batch:stop', this.storeBatchCommand, this)\n },\n\n createCommand: function(options) {\n\n const cmd = {\n action: undefined,\n data: { id: undefined, type: undefined, previous: {}, next: {}},\n batch: options && options.batch\n }\n\n return cmd\n },\n\n push: function(cmd) {\n\n this.redoStack = []\n\n if (!cmd.batch) {\n this.undoStack.push(cmd)\n this.trigger('add', cmd)\n } else {\n this.lastCmdIndex = Math.max(this.lastCmdIndex, 0)\n // Commands possible thrown away. Someone might be interested.\n this.trigger('batch', cmd)\n }\n },\n\n addCommand: function(cmdName, cell, graph, options) {\n\n // Do not account for changes in `dry` run.\n if (options && options.dry) {\n return\n }\n\n if (!this.get('cmdNameRegex').test(cmdName)) {\n return\n }\n\n if (typeof this.get('cmdBeforeAdd') === 'function' && !this.get('cmdBeforeAdd').apply(this, arguments)) {\n return\n }\n\n let command\n const isGraphCommand = (cell instanceof joint.dia.Graph)\n\n if (this.batchCommand) {\n // set command as the one used last.\n // in most cases we are working with same object, doing same action\n // etc. translate an object piece by piece\n command = this.batchCommand[Math.max(this.lastCmdIndex, 0)]\n\n // Check if we are start working with new object or performing different action with it.\n // Note, that command is uninitialized when lastCmdIndex equals -1. (see 'initBatchCommand()')\n // in that case we are done, command we were looking for is already set\n const isDifferentModel = ((isGraphCommand && !command.graphChange) || command.data.id !== cell.id)\n const isDifferentAction = (command.action !== cmdName)\n if (this.lastCmdIndex >= 0 && (isDifferentModel || isDifferentAction)) {\n\n // trying to find command first, which was performing same action with the object\n // as we are doing with the model now\n const similarCommandIndex = this.batchCommand.findIndex(function(cmd, index) {\n return ((isGraphCommand && cmd.graphChange) || cmd.data.id === cell.id) && cmd.action === cmdName\n })\n\n if (similarCommandIndex < 0 || (cmdName === 'add' || cmdName === 'remove')) {\n // command with such an id and action was not found. Let's create new one.\n // Adding and Removing is always preserve as new command. e.g.\n // (add1, remove1, add2) can not be changed to (remove1, add2) neither (add2, remove1).\n command = this.createCommand({ batch: true })\n } else {\n // move the command to the end of the batch.\n command = this.batchCommand[similarCommandIndex]\n this.batchCommand.splice(similarCommandIndex, 1)\n }\n\n this.lastCmdIndex = this.batchCommand.push(command) - 1\n }\n\n } else {\n\n // single command\n command = this.createCommand({ batch: false })\n }\n\n if (cmdName === 'add' || cmdName === 'remove') {\n\n command.action = cmdName\n command.data.id = cell.id\n command.data.type = cell.attributes.type\n command.data.attributes = joint.util.merge({}, cell.toJSON())\n command.options = options || {}\n\n this.push(command)\n return\n }\n\n // `changedAttribute` holds the attribute name corresponding\n // to the change event triggered on the model.\n const changedAttribute = cmdName.substr(this.PREFIX_LENGTH)\n\n if (!command.batch || !command.action) {\n // Do this only once. Set previous box and action (also serves as a flag so that\n // we don't repeat this branche).\n command.action = cmdName\n command.data.previous[changedAttribute] = joint.util.clone(cell.previous(changedAttribute))\n command.options = options || {}\n if (isGraphCommand) {\n command.graphChange = true\n } else {\n command.data.id = cell.id\n command.data.type = cell.attributes.type\n }\n }\n\n command.data.next[changedAttribute] = joint.util.clone(cell.get(changedAttribute))\n\n this.push(command)\n },\n\n // Batch commands are those that merge certain commands applied in a row (1) and those that\n // hold multiple commands where one action consists of more than one command (2)\n // (1) This is useful for e.g. when the user is dragging an object in the paper which would\n // normally lead to 1px translation commands. Applying undo() on such commands separately is\n // most likely undesirable.\n // (2) e.g When you are removing an element, you don't want all links connected to that element, which\n // are also being removed to be part of different command\n\n initBatchCommand: function() {\n\n if (!this.batchCommand) {\n\n this.batchCommand = [this.createCommand({ batch: true })]\n this.lastCmdIndex = -1\n\n // batch level counts how many times has been initBatchCommand executed.\n // It is useful when we doing an operation recursively.\n this.batchLevel = 0\n\n } else {\n\n // batch command is already active\n this.batchLevel++\n }\n },\n\n storeBatchCommand: function() {\n\n // In order to store batch command it is necesary to run storeBatchCommand as many times as\n // initBatchCommand was executed\n if (this.batchCommand && this.batchLevel <= 0) {\n\n const batchCommand = this.filterBatchCommand(this.batchCommand)\n // checking if there is any valid command in batch\n // for example: calling `initBatchCommand` immediately followed by `storeBatchCommand`\n if (batchCommand.length > 0) {\n\n this.redoStack = []\n\n this.undoStack.push(batchCommand)\n this.trigger('add', batchCommand)\n }\n\n this.batchCommand = null\n this.lastCmdIndex = null\n this.batchLevel = null\n\n } else if (this.batchCommand && this.batchLevel > 0) {\n\n // low down batch command level, but not store it yet\n this.batchLevel--\n }\n },\n\n // Takes batch commands and returns only such commands, which when applied in order change the graph.\n filterBatchCommand: function(batchCommand) {\n\n let commands = batchCommand.slice()\n const filteredCommands = []\n\n while (commands.length > 0) {\n\n const command = commands.shift()\n var {id} = command.data\n\n if (command.action == null || (id == null && !command.graphChange)) {\n continue\n }\n\n if (command.action === 'add') {\n\n var removeIndex = commands.findIndex(function(item) {\n return item.action === 'remove' && item.data && item.data.id === id\n })\n if (removeIndex >= 0) {\n // `add` command followed by `remove` command\n // Lets remove the `remove` command and all other commands related to\n // this cell. Note that no commands can exist after the `remove` command,\n // but some could inbetween `add` and `remove`.\n // e.g. . ADD . CHNG . REM . => . . . .\n commands = commands.filter(function(cmd, index) {\n return index > removeIndex || cmd.data.id !== id\n })\n continue\n }\n\n } else if (command.action === 'remove') {\n\n const addIndex = commands.findIndex(function(item) {\n return item.action === 'add' && item.data && item.data.id == id\n })\n if (addIndex >= 0) {\n // `remove` command followed by `add` command\n // Lets remove only the `add` command. Note that another commands could exist\n // after the `add` command, but not inbetween `remove` and `add`.\n // e.g. . CHNG1 . REM . ADD . CHNG2 . ==> . CHNG1 . . . CHNG2 .\n commands.splice(addIndex, 1)\n continue\n }\n\n } else if (command.action.indexOf('change') === 0) {\n\n if (joint.util.isEqual(command.data.previous, command.data.next)) {\n // This is a command which when applied doesn't actually change anything.\n continue\n }\n }\n\n // This is a valid command.\n filteredCommands.push(command)\n }\n\n return filteredCommands\n },\n\n revertCommand: function(command, opt) {\n\n this.stopListening()\n\n let batchCommand\n if (Array.isArray(command)) {\n batchCommand = this.constructor.sortBatchCommands(command)\n } else {\n batchCommand = [command]\n }\n\n const {graph} = this\n for (let i = batchCommand.length - 1; i >= 0; i--) {\n\n const cmd = batchCommand[i]\n const model = cmd.graphChange ? graph : graph.getCell(cmd.data.id)\n const cmdOpt = joint.util.assign({\n commandManager: this.id || this.cid\n }, opt, joint.util.pick(cmd.options, this.get('revertOptionsList')))\n\n switch (cmd.action) {\n case 'add':\n model.remove(cmdOpt)\n break\n\n case 'remove':\n graph.addCell(cmd.data.attributes, cmdOpt)\n break\n\n default:\n var attribute = cmd.action.substr(this.PREFIX_LENGTH)\n model.set(attribute, cmd.data.previous[attribute], cmdOpt)\n break\n }\n }\n\n this.listen()\n },\n\n applyCommand: function(command, opt) {\n\n this.stopListening()\n\n let batchCommand\n if (Array.isArray(command)) {\n batchCommand = this.constructor.sortBatchCommands(command)\n } else {\n batchCommand = [command]\n }\n\n const {graph} = this\n for (let i = 0; i < batchCommand.length; i++) {\n\n const cmd = batchCommand[i]\n const model = cmd.graphChange ? graph : graph.getCell(cmd.data.id)\n const cmdOpt = joint.util.assign({\n commandManager: this.id || this.cid\n }, opt, joint.util.pick(cmd.options, this.get('applyOptionsList')))\n\n switch (cmd.action) {\n\n case 'add':\n graph.addCell(cmd.data.attributes, cmdOpt)\n break\n\n case 'remove':\n model.remove(cmdOpt)\n break\n\n default:\n var attribute = cmd.action.substr(this.PREFIX_LENGTH)\n model.set(attribute, cmd.data.next[attribute], cmdOpt)\n break\n }\n }\n\n this.listen()\n },\n\n undo: function(opt) {\n\n const command = this.undoStack.pop()\n\n if (command) {\n\n this.revertCommand(command, opt)\n this.redoStack.push(command)\n }\n },\n\n redo: function(opt) {\n\n const command = this.redoStack.pop()\n\n if (command) {\n\n this.applyCommand(command, opt)\n this.undoStack.push(command)\n }\n },\n\n cancel: function(opt) {\n\n if (this.hasUndo()) {\n\n this.revertCommand(this.undoStack.pop(), opt)\n this.redoStack = []\n }\n },\n\n reset: function() {\n\n this.undoStack = []\n this.redoStack = []\n },\n\n hasUndo: function() {\n\n return this.undoStack.length > 0\n },\n\n hasRedo: function() {\n\n return this.redoStack.length > 0\n }\n}, {\n\n sortBatchCommands: function(commands) {\n\n // Note that `filterBatchCommand` method makes sure there is no `add` and `remove`\n // command in the same batch for the same element.\n\n // Here we swap only commands related to the same element, where `change` command\n // is before an `add` command (similary for the `remove` event).\n\n const commandsSorted = []\n for (let index = 0; index < commands.length; index++) {\n\n const command = commands[index]\n let insertPosition = null\n\n if (command.action === 'add') {\n const {id} = command.data\n\n for (let i = 0; i < index; i++) {\n\n if (commands[i].data.id === id) {\n // add command should appear before the first change command\n insertPosition = i - 1\n break\n }\n }\n }\n\n if (insertPosition !== null) {\n commandsSorted.splice(insertPosition, 0, command)\n\n } else {\n commandsSorted.push(command)\n }\n }\n\n return commandsSorted\n }\n\n})\n\njoint.dia.Validator = Backbone.Model.extend({\n\n initialize: function(options) {\n\n this._map = {}\n this._commandManager = options.commandManager\n\n this.listenTo(this._commandManager, 'add', this._onCommand)\n },\n\n defaults: {\n\n // To cancel (= undo + delete from redo stack) a command if is not valid.\n cancelInvalid: true\n },\n\n // iterates throught each command, stops on first invalid command.\n _onCommand: function(command) {\n\n if (Array.isArray(command)) {\n return command.find(function(singleCmd) { return !this._validateCommand(singleCmd) }, this)\n }\n\n return this._validateCommand(command)\n },\n\n // check whether the command is not against any rule\n _validateCommand: function(command) {\n\n // Backbone.model set() and Backbone.collection add() allow to pass an option parameter.\n // That is also kept within the command. It skips validation if requested.\n if (command.options && command.options.validation === false) return true\n\n let handoverErr\n\n joint.util.toArray(this._map[command.action]).forEach(function(route) {\n\n let i = 0\n\n function callbacks(err) {\n\n const fn = route[i++]\n\n try {\n if (fn) {\n fn(err, command, callbacks)\n } else {\n handoverErr = err\n return\n }\n } catch (err) {\n callbacks(err)\n }\n }\n\n callbacks(handoverErr)\n })\n\n if (handoverErr) {\n\n if (this.get('cancelInvalid')) this._commandManager.cancel()\n this.trigger('invalid', handoverErr)\n return false\n }\n\n //command is valid\n return true\n },\n\n\n validate: function(actions) {\n\n const callbacks = Array.prototype.slice.call(arguments, 1)\n\n callbacks.forEach(function(callback) {\n if (joint.util.isFunction(callback)) return\n throw new Error(`${actions } requires callback functions.`)\n })\n\n actions.split(' ').forEach(function(action) {\n (this._map[action] = this._map[action] || []).push(callbacks)\n }, this)\n\n return this\n }\n\n})\n\n// Various utility functions for graph construction.\n// =================================================\n\n// This plugin extends the `joint.dia.Graph` object with additional methods.\n\n\n// Construct a tree from JSON structure of the form:\n// `{ name: 'my label', children: [ { name: 'my label 2', children: [...] }, ...] }`\n// `parent` is the tree object, i.e. the top level node.\n// `opt.children` is the property specifying the children array. `'children'` is the default.\n// If `opt.children` is a function, it will called with the current node as an argument and should return an array of its child nodes.\n// `opt.makeElement` is a function that is passed the current tree node and returns a JointJS element for it.\n// `opt.makeLink` is a function that is passed a parent and child nodes and returns a JointJS link for the edge.\njoint.dia.Graph.prototype.constructTree = function(parent, opt, parentElement, collector) {\n\n collector = collector || []\n\n const children = joint.util.isFunction(opt.children) ? opt.children(parent) : parent[opt.children || 'children']\n\n if (!parentElement) {\n\n parentElement = opt.makeElement(parent)\n collector.push(parentElement)\n }\n\n joint.util.toArray(children).forEach(function(child) {\n\n const childElement = opt.makeElement(child)\n const link = opt.makeLink(parentElement, childElement)\n collector.push(childElement, link)\n\n this.constructTree(child, opt, childElement, collector)\n\n }, this)\n\n return collector\n}\n\n// Returns an array of IDs of nodes on the shortest path between `source` and `target`.\n// `source` and `target` can either be elements or IDs of elemements.\n// `opt.weight` is an optional function returning a distance between two nodes.\n// If `opt.directed` is `true`, the algorithm will take link direction into account.\njoint.dia.Graph.prototype.shortestPath = function(source, target, opt) {\n\n opt = opt || {}\n\n const adjacencyList = {}\n this.getLinks().forEach(function(link) {\n\n const sourceId = link.get('source').id\n const targetId = link.get('target').id\n if (!adjacencyList[sourceId]) {\n adjacencyList[sourceId] = []\n }\n if (!adjacencyList[targetId]) {\n adjacencyList[targetId] = []\n }\n\n adjacencyList[sourceId].push(targetId)\n if (!opt.directed) {\n adjacencyList[targetId].push(sourceId)\n }\n })\n\n const previous = joint.alg.Dijkstra(adjacencyList, source.id || source, opt.weight)\n\n const path = []\n let u = target.id || target\n if (previous[u]) path.push(u)\n while ((u = previous[u])) {\n path.unshift(u)\n }\n return path\n}\n\n// PaperScroller\n// =============\n\n\n// `PaperScroller` wraps the paper root element and implements panning and centering of the paper.\n\n// Example usage:\n\n// var paperScroller = new joint.ui.PaperScroller;\n// var paper = new joint.dia.Paper({ el: paperScroller.el });\n// paperScroller.options.paper = paper;\n// $appElement.append(paperScroller.render().el);\n\n// paperScroller.center();\n// paper.on('blank:pointerdown', paperScroller.startPanning);\n\njoint.ui.PaperScroller = joint.mvc.View.extend({\n\n className: 'paper-scroller',\n\n options: {\n paper: undefined,\n // Default padding makes sure the paper inside the paperScroller is always panable\n // all the way left, right, bottom and top.\n // It also makes sure that there is always at least a fragment of the paper visible.\n // Example usage:\n // padding: 10\n // padding: { left: 20, right: 20 }\n // padding: function() { return 10; }\n padding: function() {\n\n const clientSize = this.getClientSize()\n const minVisibleSize = Math.max(this.options.minVisiblePaperSize, 1) || 1\n const padding = {}\n\n padding.left = padding.right = Math.max(clientSize.width - minVisibleSize, 0)\n padding.top = padding.bottom = Math.max(clientSize.height - minVisibleSize, 0)\n\n return padding\n },\n // Minimal size (px) of the paper that has to stay visible.\n // Used by the default padding method only.\n minVisiblePaperSize: 50,\n autoResizePaper: false,\n baseWidth: undefined,\n baseHeight: undefined,\n contentOptions: undefined,\n cursor: 'default'\n },\n\n // Internal padding storage\n _padding: { left: 0, top: 0 },\n\n init: function() {\n\n joint.util.bindAll(this, 'startPanning', 'stopPanning', 'pan', 'onBackgroundEvent')\n\n const {paper} = this.options\n\n // keep scale values for a quicker access\n const initScale = paper.scale()\n this._sx = initScale.sx\n this._sy = initScale.sy\n\n // if the base paper dimension is not specified use the paper size.\n this.options.baseWidth === undefined && (this.options.baseWidth = paper.options.width)\n this.options.baseHeight === undefined && (this.options.baseHeight = paper.options.height)\n\n this.$background = $('
').addClass('paper-scroller-background')\n .css({ width: paper.options.width, height: paper.options.height })\n .append(paper.el)\n .appendTo(this.el)\n\n this.listenTo(paper, 'scale', this.onScale)\n .listenTo(paper, 'resize', this.onResize)\n .listenTo(paper, 'beforeprint beforeexport', this.storeScrollPosition)\n .listenTo(paper, 'afterprint afterexport', this.restoreScrollPosition)\n\n // automatically resize the paper\n if (this.options.autoResizePaper) {\n this.listenTo(paper.model, 'change add remove reset', this.adjustPaper)\n if (paper.options.async) {\n this.listenTo(paper, 'render:done', this.adjustPaper)\n }\n }\n\n this.delegateBackgroundEvents()\n this.setCursor(this.options.cursor)\n },\n\n lock: function() {\n this.$el.css('overflow', 'hidden')\n return this\n },\n\n unlock: function() {\n this.$el.css('overflow', 'scroll')\n return this\n },\n\n setCursor: function(cursor) {\n\n switch (cursor) {\n case 'grab':\n // Make a special case for the cursor above\n // due to bad support across browsers.\n // It's handled in `layout.css`.\n this.$el.css('cursor', '')\n break\n default:\n this.$el.css('cursor', cursor)\n break\n }\n\n this.$el.attr('data-cursor', cursor)\n this.options.cursor = cursor\n\n return this\n },\n\n // Set up listeners for passing events from outside the paper to the paper\n delegateBackgroundEvents: function(events) {\n\n events || (events = joint.util.result(this.options.paper, 'events'))\n\n const normalizedEvents = this.paperEvents = Object.keys(events || {}).reduce(normalizeEvents.bind(this), {})\n\n Object.keys(normalizedEvents).forEach(delegateBackgroundEvent, this)\n\n function normalizeEvents(res, event) {\n const listener = events[event]\n // skip events with selectors\n if (event.indexOf(' ') === -1) {\n res[event] = joint.util.isFunction(listener) ? listener : this.options.paper[listener]\n }\n return res\n }\n\n function delegateBackgroundEvent(event) {\n // Sending event data with `guarded=false` to prevent events from\n // being guarded by the paper.\n this.delegate(event, { guarded: false }, this.onBackgroundEvent)\n }\n\n return this\n },\n\n // Pass the event outside the paper to the paper.\n onBackgroundEvent: function(evt) {\n\n if (this.$background.is(evt.target)) {\n const listener = this.paperEvents[evt.type]\n if (joint.util.isFunction(listener)) {\n listener.apply(this.options.paper, arguments)\n }\n }\n },\n\n onResize: function() {\n\n // Move scroller so the user sees the same area as before the resizing.\n if (this._center) this.center(this._center.x, this._center.y)\n },\n\n onScale: function(sx, sy, ox, oy) {\n\n this.adjustScale(sx, sy)\n\n // update scale values for a quicker access\n this._sx = sx\n this._sy = sy\n\n // Move scroller to scale origin.\n if (ox || oy) this.center(ox, oy)\n },\n\n storeScrollPosition: function() {\n\n this._scrollLeftBeforePrint = this.el.scrollLeft\n this._scrollTopBeforePrint = this.el.scrollTop\n },\n\n restoreScrollPosition: function() {\n\n // Set the paper element to the scroll position before printing.\n this.el.scrollLeft = this._scrollLeftBeforePrint\n this.el.scrollTop = this._scrollTopBeforePrint\n\n // Clean-up.\n this._scrollLeftBeforePrint = null\n this._scrollTopBeforePrint = null\n },\n\n beforePaperManipulation: function() {\n\n if (joint.env.test('msie') || joint.env.test('msedge')) {\n // IE is trying to show every frame while we manipulate the paper.\n // That makes the viewport kind of jumping while zooming for example.\n // Make the paperScroller invisible fixes this.\n // MSEDGE seems to have a problem with text positions after the animation.\n this.$el.css('visibility', 'hidden')\n }\n },\n\n afterPaperManipulation: function() {\n\n if (joint.env.test('msie') || joint.env.test('msedge')) {\n this.$el.css('visibility', 'visible')\n }\n },\n\n clientToLocalPoint: function(x, y) {\n\n const ctm = this.options.paper.matrix()\n\n x += this.el.scrollLeft - this._padding.left - ctm.e\n x /= ctm.a\n\n\n y += this.el.scrollTop - this._padding.top - ctm.f\n y /= ctm.d\n\n return new g.Point(x, y)\n },\n\n localToBackgroundPoint: function(x, y) {\n\n const localPoint = new g.Point(x, y)\n const ctm = this.options.paper.matrix()\n const padding = this._padding\n return V.transformPoint(localPoint, ctm).offset(padding.left, padding.top)\n },\n\n adjustPaper: function() {\n\n // store the current mid point of visible paper area, so we can center the paper\n // to the same point after the resize\n const clientSize = this.getClientSize()\n this._center = this.clientToLocalPoint(clientSize.width / 2, clientSize.height / 2)\n\n const options = joint.util.assign({\n gridWidth: this.options.baseWidth,\n gridHeight: this.options.baseHeight,\n allowNewOrigin: 'negative'\n }, this.options.contentOptions)\n\n this.options.paper.fitToContent(this.transformContentOptions(options))\n\n return this\n },\n\n adjustScale: function(sx, sy) {\n\n const paperOptions = this.options.paper.options\n const fx = sx / this._sx\n const fy = sy / this._sy\n\n this.options.paper.setOrigin(paperOptions.origin.x * fx, paperOptions.origin.y * fy)\n this.options.paper.setDimensions(paperOptions.width * fx, paperOptions.height * fy)\n },\n\n // Recalculates content options taking the current scale into account.\n transformContentOptions: function(opt) {\n\n const sx = this._sx\n const sy = this._sy\n\n if (opt.gridWidth) opt.gridWidth *= sx\n if (opt.gridHeight) opt.gridHeight *= sy\n if (opt.minWidth) opt.minWidth *= sx\n if (opt.minHeight) opt.minHeight *= sy\n\n if (joint.util.isObject(opt.padding)) {\n opt.padding = {\n left: (opt.padding.left || 0) * sx,\n right: (opt.padding.right || 0) * sx,\n top: (opt.padding.top || 0) * sy,\n bottom: (opt.padding.bottom || 0) * sy\n }\n } else if (joint.util.isNumber(opt.padding)) {\n opt.padding *= sx\n }\n\n return opt\n },\n\n // Adjust the paper position so the point [x,y] (local units) is moved\n // to the center of paperScroller element.\n // If neither `x` nor `y` provided, center to paper center.\n // If `x` or `y` not provided, only center in the dimensions we know.\n // Difference from scroll() is that center() adds padding to paper to\n // make sure x, y will actually be centered.\n center: function(x, y, opt) {\n\n const ctm = this.options.paper.matrix()\n\n // the paper rectangle\n // x1,y1 ---------\n // | |\n // ----------- x2,y2\n const x1 = -ctm.e\n const y1 = -ctm.f\n const x2 = x1 + this.options.paper.options.width\n const y2 = y1 + this.options.paper.options.height\n\n const xIsNumber = joint.util.isNumber(x)\n const yIsNumber = joint.util.isNumber(y)\n\n let localOpt\n\n if (!xIsNumber && !yIsNumber) {\n // no coordinates provided\n\n localOpt = x\n\n // find center of the paper\n x = (x1 + x2) / 2\n y = (y1 + y2) / 2\n\n } else {\n localOpt = opt\n\n // if one of the coords not provided, substitute with middle\n // of visible area in that dimension\n const visibleAreaCenter = this.getVisibleArea().center()\n\n if (xIsNumber) x *= ctm.a // convert x to local\n else x = visibleAreaCenter.x // default\n\n if (yIsNumber) y *= ctm.d // convert y to local\n else y = visibleAreaCenter.y // default\n }\n\n const clientSize = this.getClientSize()\n\n const p = this.getPadding()\n const cx = clientSize.width / 2\n const cy = clientSize.height / 2\n\n // calculate paddings\n const left = cx - p.left - x + x1\n const right = cx - p.right + x - x2\n const top = cy - p.top - y + y1\n const bottom = cy - p.bottom + y - y2\n\n this.addPadding(\n Math.max(left, 0),\n Math.max(right, 0),\n Math.max(top, 0),\n Math.max(bottom, 0)\n )\n\n this.scroll(x, y, localOpt)\n\n return this\n },\n\n // Position the paper so that the center of content (local units) is at\n // the center of client area.\n centerContent: function(opt) {\n\n return this.positionContent('center', opt)\n },\n\n // Position the paper so that the center of element (local units) is at\n // the center of client area.\n centerElement: function(element, opt) {\n\n this.checkElement(element, 'centerElement')\n\n return this.positionElement(element, 'center', opt)\n },\n\n // Position the paper so that the `positionName`-determined point of\n // content is at `positionName`-determined point of client area.\n positionContent: function(positionName, opt) {\n\n const contentArea = this.options.paper.getContentArea() // local units\n return this.positionRect(contentArea, positionName, opt)\n },\n\n // Position the paper so that the `positionName`-determined point of\n // element area is at `positionName`-determined point of client area.\n positionElement: function(element, positionName, opt) {\n\n this.checkElement(element, 'positionElement')\n\n const elementArea = element.getBBox() // local units\n return this.positionRect(elementArea, positionName, opt)\n },\n\n // Position the paper so that the `positionName`-determined point of\n // `rect` is at `positionName`-determined point of client area.\n // For example, to position the paper so that the top-left corner of\n // `rect` is in the top-left corner of client area and 10px away from\n // edges:\n // - `positionRect('top-left', { padding: 10 });`\n positionRect: function(rect, positionName, opt) {\n\n let point\n switch (positionName) {\n case 'center':\n point = rect.center()\n return this.positionPoint(point, '50%', '50%', opt)\n\n case 'top':\n point = rect.topMiddle()\n return this.positionPoint(point, '50%', 0, opt)\n\n case 'top-right':\n point = rect.topRight()\n return this.positionPoint(point, '100%', 0, opt)\n\n case 'right':\n point = rect.rightMiddle()\n return this.positionPoint(point, '100%', '50%', opt)\n\n case 'bottom-right':\n point = rect.bottomRight()\n return this.positionPoint(point, '100%', '100%', opt)\n\n case 'bottom':\n point = rect.bottomMiddle()\n return this.positionPoint(point, '50%', '100%', opt)\n\n case 'bottom-left':\n point = rect.bottomLeft()\n return this.positionPoint(point, 0, '100%', opt)\n\n case 'left':\n point = rect.leftMiddle()\n return this.positionPoint(point, 0, '50%', opt)\n\n case 'top-left':\n point = rect.topLeft()\n return this.positionPoint(point, 0, 0, opt)\n\n default:\n throw new Error(`Provided positionName ('${ positionName }') was not recognized.`)\n }\n },\n\n // Position the paper so that `point` is `x` and `y` away from the (left\n // and top) edges of the client area.\n // Optional padding from edges with `opt.padding`.\n // Optional animation with `opt.animaiton`.\n // Percentages are allowed; they are understood with reference to the area\n // of the client area that is inside padding.\n // Negative values/percentages mean start counting from the other edge of\n // the client area (right and/or bottom).\n positionPoint: function(point, x, y, opt) {\n\n opt = opt || {}\n const padding = joint.util.normalizeSides(opt.padding) // client units\n\n const clientRect = new g.Rect(this.getClientSize())\n const restrictedClientRect = clientRect.clone().moveAndExpand({\n x: padding.left,\n y: padding.top,\n width: -padding.right - padding.left,\n height: -padding.top - padding.bottom\n })\n\n const xIsPercentage = joint.util.isPercentage(x)\n x = parseFloat(x) // ignores the final %\n if (xIsPercentage) x = (x / 100) * Math.max(0, restrictedClientRect.width)\n if (x < 0) x = restrictedClientRect.width + x // if negative, start counting from other edge\n\n const yIsPercentage = joint.util.isPercentage(y)\n y = parseFloat(y) // ignores the final %\n if (yIsPercentage) y = (y / 100) * Math.max(0, restrictedClientRect.height)\n if (y < 0) y = restrictedClientRect.height + y // if negative, start counting from other edge\n\n const target = restrictedClientRect.origin().offset(x, y) // client units\n const center = clientRect.center()\n const centerVector = center.difference(target)\n\n const scale = this.zoom()\n\n const localCenterVector = centerVector.scale(1 / scale, 1 / scale) // local units\n const localCenter = point.clone().offset(localCenterVector)\n return this.center(localCenter.x, localCenter.y, opt)\n },\n\n // Put the point at [x,y] in the paper (local units) to the center of\n // paperScroller window.\n // Less aggresive than center() as it only changes position of scrollbars\n // without adding paddings - it won't actually move view onto the position\n // if there isn't enough room for it!\n // If `x` or `y` is not provided, only scroll in the directions we know.\n // Optionally you can specify `animation` key in option argument\n // to make the scroll animated; object is passed into $.animate\n scroll: function(x, y, opt) {\n\n const ctm = this.options.paper.matrix()\n\n const clientSize = this.getClientSize()\n\n const change = {}\n\n if (joint.util.isNumber(x)) {\n const cx = clientSize.width / 2\n change.scrollLeft = x - cx + ctm.e + (this._padding.left || 0)\n }\n\n if (joint.util.isNumber(y)) {\n const cy = clientSize.height / 2\n change.scrollTop = y - cy + ctm.f + (this._padding.top || 0)\n }\n\n if (opt && opt.animation) this.$el.animate(change, opt.animation)\n else this.$el.prop(change)\n },\n\n // Simple wrapper around scroll method that finds center of paper\n // content and scrolls to it.\n // Accepts same `opt` objects as the scroll() method (`opt.animation`).\n scrollToContent: function(opt) {\n\n const center = this.options.paper.getContentArea().center()\n const sx = this._sx\n const sy = this._sy\n\n center.x *= sx\n center.y *= sy\n\n return this.scroll(center.x, center.y, opt)\n },\n\n // Simple wrapper around scroll method that finds center of specified\n // element and scrolls to it.\n // Accepts same `opt` objects as the scroll() method (`opt.animation`).\n scrollToElement: function(element, opt) {\n\n this.checkElement(element, 'scrollToElement')\n\n const center = element.getBBox().center()\n const sx = this._sx\n const sy = this._sy\n\n center.x *= sx\n center.y *= sy\n\n return this.scroll(center.x, center.y, opt)\n },\n\n // Position the paper inside the paper wrapper and resize the wrapper.\n addPadding: function(left, right, top, bottom) {\n\n const base = this.getPadding()\n\n const padding = this._padding = {\n left: Math.round(base.left + (left || 0)),\n top: Math.round(base.top + (top || 0)),\n bottom: Math.round(base.bottom + (bottom || 0)),\n right: Math.round(base.right + (right || 0))\n }\n\n this.$background.css({\n width: padding.left + this.options.paper.options.width + padding.right,\n height: padding.top + this.options.paper.options.height + padding.bottom\n })\n this.options.paper.$el.css({\n left: padding.left,\n top: padding.top\n })\n\n return this\n },\n\n zoom: function(value, opt) {\n\n if (value === undefined) {\n return this._sx\n }\n\n opt = opt || {}\n\n const clientSize = this.getClientSize()\n\n const center = this.clientToLocalPoint(clientSize.width / 2, clientSize.height / 2)\n let sx = value\n let sy = value\n let ox\n let oy\n\n if (!opt.absolute) {\n sx += this._sx\n sy += this._sy\n }\n\n if (opt.grid) {\n sx = Math.round(sx / opt.grid) * opt.grid\n sy = Math.round(sy / opt.grid) * opt.grid\n }\n\n // check if the new scale won't exceed the given boundaries\n if (opt.max) {\n sx = Math.min(opt.max, sx)\n sy = Math.min(opt.max, sy)\n }\n\n if (opt.min) {\n sx = Math.max(opt.min, sx)\n sy = Math.max(opt.min, sy)\n }\n\n if (opt.ox === undefined || opt.oy === undefined) {\n\n // if the origin is not specified find the center of the paper's visible area.\n ox = center.x\n oy = center.y\n\n } else {\n\n const fsx = sx / this._sx\n const fsy = sy / this._sy\n\n ox = opt.ox - ((opt.ox - center.x) / fsx)\n oy = opt.oy - ((opt.oy - center.y) / fsy)\n }\n\n this.beforePaperManipulation()\n\n this.options.paper.scale(sx, sy)\n this.center(ox, oy)\n\n this.afterPaperManipulation()\n\n return this\n },\n\n zoomToFit: function(opt) {\n\n opt = opt || {}\n\n const {paper} = this.options\n const paperOrigin = joint.util.assign({}, paper.options.origin)\n\n // fitting bbox has exact size of the the PaperScroller\n opt.fittingBBox = opt.fittingBBox || joint.util.assign({}, new g.Point(paperOrigin), {\n width: this.$el.width(),\n height: this.$el.height()\n })\n\n this.beforePaperManipulation()\n\n // scale the vieport\n paper.scaleContentToFit(opt)\n\n // restore original origin\n paper.setOrigin(paperOrigin.x, paperOrigin.y)\n\n this.adjustPaper().centerContent()\n\n this.afterPaperManipulation()\n\n return this\n },\n\n transitionClassName: 'transition-in-progress',\n transitionEventName: 'transitionend.paper-scroller-transition',\n\n transitionToPoint: function(x, y, opt) {\n\n // Allow both `transition(point, options)` and `transition(x, y, options)`\n if (joint.util.isObject(x)) {\n opt = y\n y = x.y\n x = x.x\n }\n\n opt || (opt = {})\n\n const oldScale = this._sx\n const scale = Math.max(opt.scale || oldScale, 1e-6)\n\n const clientSize = this.getClientSize()\n\n const localPoint = new g.Point(x, y)\n const localCenter = this.clientToLocalPoint(clientSize.width / 2, clientSize.height / 2)\n let transform; let transformOrigin\n\n if (oldScale === scale) {\n // Tranlate only\n const translate = localCenter.difference(localPoint).scale(oldScale, oldScale).round()\n transform = `translate(${ translate.x }px,${ translate.y }px)`\n\n } else {\n // Translate and scale concurrently\n const distance = scale / (oldScale - scale) * localPoint.distance(localCenter)\n const localOrigin = localCenter.clone().move(localPoint, distance)\n const origin = this.localToBackgroundPoint(localOrigin).round()\n transform = `scale(${ scale / oldScale })`\n transformOrigin = `${origin.x }px ${ origin.y }px`\n }\n\n this.$el\n .addClass(this.transitionClassName)\n this.$background\n .off(this.transitionEventName)\n .on(this.transitionEventName, function(evt) {\n\n const {paperScroller} = this\n paperScroller.syncTransition(this.scale, { x: this.x, y: this.y })\n // Trigger a callback\n const {onTransitionEnd} = this\n if (joint.util.isFunction(onTransitionEnd)) {\n onTransitionEnd.call(paperScroller, evt)\n }\n }.bind({\n // TransitionEnd handler context\n paperScroller: this,\n scale: scale,\n x: x,\n y: y,\n onTransitionEnd: opt.onTransitionEnd\n }))\n .css({\n transition: 'transform',\n transitionDuration: opt.duration || '1s',\n transitionDelay: opt.delay,\n transitionTimingFunction: opt.timingFunction,\n transformOrigin: transformOrigin,\n transform: transform\n })\n\n return this\n },\n\n syncTransition: function(scale, center) {\n\n this.beforePaperManipulation()\n\n this.options.paper.scale(scale)\n\n this.removeTransition()\n .center(center.x, center.y)\n\n this.afterPaperManipulation()\n\n return this\n },\n\n removeTransition: function() {\n\n this.$el\n .removeClass(this.transitionClassName)\n this.$background\n .off(this.transitionEventName)\n .css({\n transition: '',\n transitionDuration: '',\n transitionDelay: '',\n transitionTimingFunction: '',\n transform: '',\n transformOrigin: ''\n })\n\n return this\n },\n\n transitionToRect: function(rect, opt) {\n\n rect = new g.Rect(rect)\n opt || (opt = {})\n\n const maxScale = opt.maxScale || Infinity\n const minScale = opt.minScale || Number.MIN_VALUE\n const scaleGrid = opt.scaleGrid || null\n const visibility = opt.visibility || 1\n const center = (opt.center) ? new g.Point(opt.center) : rect.center()\n\n const clientSize = this.getClientSize()\n\n const clientWidth = clientSize.width * visibility\n const clientHeight = clientSize.height * visibility\n const clientRect = new g.Rect({\n x: center.x - clientWidth / 2,\n y: center.y - clientHeight / 2,\n width: clientWidth,\n height: clientHeight\n })\n\n // scale the paper so all the corner points are in the viewport.\n let scale = clientRect.maxRectUniformScaleToFit(rect, center)\n scale = Math.min(scale, maxScale)\n if (scaleGrid) {\n scale = Math.floor(scale / scaleGrid) * scaleGrid\n }\n scale = Math.max(minScale, scale)\n\n return this.transitionToPoint(center, joint.util.defaults({ scale: scale }, opt))\n },\n\n startPanning: function(evt) {\n\n evt = joint.util.normalizeEvent(evt)\n\n this._clientX = evt.clientX\n this._clientY = evt.clientY\n\n this.$el.addClass('is-panning')\n this.trigger('pan:start', evt)\n\n $(document.body).on({\n 'mousemove.panning touchmove.panning': this.pan,\n 'mouseup.panning touchend.panning': this.stopPanning\n })\n\n $(window).on('mouseup.panning', this.stopPanning)\n },\n\n pan: function(evt) {\n\n evt = joint.util.normalizeEvent(evt)\n\n const dx = evt.clientX - this._clientX\n const dy = evt.clientY - this._clientY\n\n this.el.scrollTop -= dy\n this.el.scrollLeft -= dx\n\n this._clientX = evt.clientX\n this._clientY = evt.clientY\n },\n\n stopPanning: function(evt) {\n\n $(document.body).off('.panning')\n $(window).off('.panning')\n this.$el.removeClass('is-panning')\n this.trigger('pan:stop', evt)\n },\n\n // Return the client dimensions in pixels as reported by browser.\n // \"What is the size of the window through which the user can see the paper?\"\n getClientSize: function() {\n\n return { width: this.el.clientWidth, height: this.el.clientHeight }\n },\n\n getPadding: function() {\n\n let {padding} = this.options\n if (joint.util.isFunction(padding)) {\n padding = padding.call(this)\n }\n\n return joint.util.normalizeSides(padding)\n },\n\n // Return the dimensions of the visible area in local units.\n // \"What part of the paper can be seen by the user, taking zooming and panning into account?\"\n getVisibleArea: function() {\n\n const ctm = this.options.paper.matrix()\n const clientSize = this.getClientSize() // client units\n\n const area = {\n x: this.el.scrollLeft || 0,\n y: this.el.scrollTop || 0,\n width: clientSize.width,\n height: clientSize.height\n } // client units\n\n const transformedArea = V.transformRect(area, ctm.inverse()) // local units\n\n transformedArea.x -= (this._padding.left || 0) / this._sx\n transformedArea.y -= (this._padding.top || 0) / this._sy\n\n return new g.Rect(transformedArea)\n },\n\n isElementVisible: function(element, opt) {\n\n this.checkElement(element, 'isElementVisible')\n\n opt = opt || {}\n const method = opt.strict ? 'containsRect' : 'intersect'\n return !!this.getVisibleArea()[method](element.getBBox())\n },\n\n isPointVisible: function(point) {\n\n return this.getVisibleArea().containsPoint(point)\n },\n\n // some method require element only because link is missing some tools (eg. bbox)\n checkElement: function(element, methodName) {\n\n if (!(element && element instanceof joint.dia.Element)) {\n throw new TypeError(`ui.PaperScroller.${ methodName }() accepts instance of joint.dia.Element only`)\n }\n },\n\n onRemove: function() {\n\n this.stopPanning()\n }\n\n})\n\njoint.env.addTest('msie', function() {\n const {userAgent} = window.navigator\n return userAgent.indexOf('MSIE') !== -1 || userAgent.indexOf('Trident') !== -1\n})\n\njoint.env.addTest('msedge', function() {\n return /Edge\\/\\d+/.test(window.navigator.userAgent)\n})\n\n// Selection\n// =============\n\n// `Selection` implements selecting group of elements and moving the selected elements in one go.\n// Typically, the selection will be bound to the `Shift` key\n// and selecting/deselecting individual elements to the `Ctrl` key.\n\n// Example usage:\n\n// var graph = new joint.dia.Graph;\n// var paper = new joint.dia.Paper({ model: graph });\n// var selectionItems = new Backbone.Collection;\n// var selection = new joint.ui.Selection({ paper: paper, graph: graph, model: selectionItems });\n\n// // Bulk selecting group of elements by creating a rectangular selection area.\n// paper.on('blank:pointerdown', selection.startSelecting);\n\n// // Selecting individual elements with click and the `Ctrl`/`Command` key.\n// paper.on('cell:pointerup', function(cellView, evt) {\n// if ((evt.ctrlKey || evt.metaKey) && !(cellView.model instanceof joint.dia.Link)) {\n// selectionItems.add(cellView.model);\n// }\n// });\n\n// // Deselecting previously selected elements with click and the `Ctrl`/`Command` key.\n// selection.on('selection-box:pointerdown', function(evt) {\n// if (evt.ctrlKey || evt.metaKey) {\n// var cell = selectionItems.get($(evt.target).data('model'));\n// selectionItems.reset(selectionItems.without(cell));\n// selection.destroySelectionBox(paper.findViewByModel(cell));\n// }\n// });\n\njoint.ui.Selection = joint.mvc.View.extend({\n\n options: {\n paper: undefined,\n graph: undefined,\n boxContent: function(boxElement) {\n return joint.util.template('<%= length %> elements selected.')({\n length: this.model.length\n })\n },\n handles: [{\n name: 'remove',\n position: 'nw',\n events: {\n pointerdown: 'removeElements'\n }\n }, {\n name: 'rotate',\n position: 'sw',\n events: {\n pointerdown: 'startRotating',\n pointermove: 'doRotate',\n pointerup: 'stopBatch'\n }\n }, {\n name: 'resize',\n position: 'se',\n events: {\n pointerdown: 'startResizing',\n pointermove: 'doResize',\n pointerup: 'stopBatch'\n }\n }],\n useModelGeometry: false,\n strictSelection: false,\n rotateAngleGrid: 15,\n allowTranslate: true\n },\n\n className: 'selection',\n\n events: {\n 'mousedown .selection-box': 'onSelectionBoxPointerDown',\n 'touchstart .selection-box': 'onSelectionBoxPointerDown',\n 'mousedown .handle': 'onHandlePointerDown',\n 'touchstart .handle': 'onHandlePointerDown'\n },\n\n /**\n * @private\n */\n init: function() {\n\n // For backwards compatibility:\n if (this.options.model) {\n this.options.collection = this.options.model\n }\n\n const collection = this.collection = this.options.collection || this.collection || new Backbone.Collection\n\n if (!collection.comparator) {\n // Make sure the elements are always sorted from the parents to their childs.\n // That is necessary for translating selected elements.\n collection.comparator = this.constructor.depthComparator\n collection.sort()\n }\n\n // For backwards compatibility:\n this.model = collection\n\n if (this.options.paper) {\n // Allow selection to be initialized with a paper only.\n joint.util.defaults(this.options, { graph: this.options.paper.model })\n } else {\n throw new Error('Selection: paper required')\n }\n\n joint.util.bindAll(this, 'startSelecting', 'stopSelecting', 'adjustSelection', 'pointerup')\n\n $(document.body).on('mousemove.selection touchmove.selection', this.adjustSelection)\n $(document).on('mouseup.selection touchend.selection', this.pointerup)\n\n const {paper} = this.options\n const {graph} = this.options\n\n this.listenTo(graph, 'reset', this.cancelSelection)\n this.listenTo(paper, 'scale translate', this.updateSelectionBoxes)\n this.listenTo(graph, 'remove change', function(cell, opt) {\n // Do not react on changes that happened inside the selection.\n if (opt.selection !== this.cid) {\n this.updateSelectionBoxes()\n }\n })\n\n this.listenTo(collection, 'remove', this.onRemoveElement)\n this.listenTo(collection, 'reset', this.onResetElements)\n this.listenTo(collection, 'add', this.onAddElement)\n\n paper.$el.append(this.$el)\n\n // A counter of existing boxes. We don't want to update selection boxes on\n // each graph change when no selection boxes exist.\n this._boxCount = 0\n\n this.$selectionWrapper = this.createSelectionWrapper()\n\n // Add handles.\n this.handles = []\n joint.util.toArray(this.options.handles).forEach(this.addHandle, this)\n },\n\n /**\n * @private\n */\n cancelSelection: function() {\n\n this.model.reset([], { ui: true })\n },\n\n /**\n * @public\n * @param {object} opt\n * @returns {joint.ui.Selection}\n */\n addHandle: function(opt) {\n\n this.handles.push(opt)\n\n const $handle = $('
', {\n class: `handle ${ opt.position || '' } ${ opt.name || ''}`,\n 'data-action': opt.name\n })\n if (opt.icon) {\n $handle.css('background-image', `url(${ opt.icon })`)\n }\n\n $handle.html(opt.content || '')\n\n // `opt.attrs` allows for setting arbitrary attributes on the generated HTML.\n // This object is of the form: ` : { : , ... }`\n joint.util.setAttributesBySelector($handle, opt.attrs)\n\n this.$selectionWrapper.append($handle)\n\n joint.util.forIn(opt.events, function(method, event) {\n\n if (joint.util.isString(method)) {\n this.on(`action:${ opt.name }:${ event}`, this[method], this)\n } else {\n // Otherwise, it must be a function.\n this.on(`action:${ opt.name }:${ event}`, method)\n }\n\n }.bind(this))\n\n return this\n },\n\n /**\n * @public\n * @param {jQuery.Event} evt\n */\n stopSelecting: function(evt) {\n\n let localPoint\n const {paper} = this.options\n\n switch (this._action) {\n\n case 'selecting':\n\n var offset = this.$el.offset()\n var width = this.$el.width()\n var height = this.$el.height()\n\n // Convert offset coordinates to the local point of the root element viewport.\n localPoint = paper.pageToLocalPoint(offset.left, offset.top)\n\n // Convert width and height to take current viewport scale into account\n var paperScale = paper.scale()\n width /= paperScale.sx\n height /= paperScale.sy\n\n var selectedArea = g.rect(localPoint.x, localPoint.y, width, height)\n var elementViews = this.getElementsInSelectedArea(selectedArea)\n\n var {filter} = this.options\n if (Array.isArray(filter)) {\n\n elementViews = elementViews.filter(function(view) {\n return !filter.includes(view.model) && !filter.includes(view.model.get('type'))\n })\n\n } else if (joint.util.isFunction(filter)) {\n\n elementViews = elementViews.filter(function(view) {\n return !filter(view.model)\n })\n }\n\n var models = elementViews.map(function(view) {\n return view.model\n })\n this.model.reset(models, { ui: true })\n\n break\n\n case 'translating':\n\n this.options.graph.stopBatch('selection-translate')\n localPoint = paper.snapToGrid({ x: evt.clientX, y: evt.clientY })\n this.notify('selection-box:pointerup', evt, localPoint.x, localPoint.y)\n // Everything else is done during the translation.\n break\n\n default:\n // Hide selection if the user clicked somehwere else in the document.\n if (!this._action) {\n this.cancelSelection()\n }\n break\n }\n\n this._action = null\n },\n\n /**\n * @public\n * @param {string} name\n * @returns {joint.ui.Selection}\n */\n removeHandle: function(name) {\n\n const handleIdx = joint.util.toArray(this.handles).findIndex(function(item) {\n return item.name === name\n })\n\n const handle = this.handles[handleIdx]\n if (handle) {\n\n joint.util.forIn(handle.events, function(method, event) {\n this.off(`action:${ name }:${ event}`)\n }.bind(this))\n\n this.$(`.handle.${ name}`).remove()\n\n this.handles.splice(handleIdx, 1)\n }\n\n return this\n },\n\n /**\n * @public\n * @param {jQuery.Event} evt\n */\n startSelecting: function(evt) {\n\n evt = joint.util.normalizeEvent(evt)\n\n this.cancelSelection()\n\n const paperElement = this.options.paper.el\n let offsetX; let offsetY\n\n if (evt.offsetX != null && evt.offsetY != null && $.contains(paperElement, evt.target)) {\n\n offsetX = evt.offsetX\n offsetY = evt.offsetY\n\n } else {\n\n // We do not use `evt.offsetX` and `evt.offsetY` event properties when the event target\n // is not inside the the paper element or properties are not defined (FF).\n\n const paperOffset = $(paperElement).offset()\n const paperScrollLeft = paperElement.scrollLeft\n const paperScrollTop = paperElement.scrollTop\n\n offsetX = evt.clientX - paperOffset.left + window.pageXOffset + paperScrollLeft\n offsetY = evt.clientY - paperOffset.top + window.pageYOffset + paperScrollTop\n }\n\n this.$el.css({ width: 1, height: 1, left: offsetX, top: offsetY })\n this.showLasso()\n\n this._action = 'selecting'\n this._clientX = evt.clientX\n this._clientY = evt.clientY\n this._offsetX = offsetX\n this._offsetY = offsetY\n },\n\n /**\n * @param {string} name\n * @param {Object} opt\n * @returns {joint.ui.Selection}\n */\n changeHandle: function(name, opt) {\n\n const handle = joint.util.toArray(this.handles).find(function(item) {\n return item && item.name === name\n })\n\n if (handle) {\n\n this.removeHandle(name)\n this.addHandle(joint.util.merge({ name: name }, handle, opt))\n }\n\n return this\n },\n\n /**\n * @private\n * @param {jQuery.Event} evt\n */\n onSelectionBoxPointerDown: function(evt) {\n\n evt.stopPropagation()\n evt = joint.util.normalizeEvent(evt)\n\n // Start translating selected elements.\n if (this.options.allowTranslate) {\n this.startTranslatingSelection(evt)\n }\n\n this._activeElementView = this.getCellView(evt.target)\n const localPoint = this.options.paper.snapToGrid({ x: evt.clientX, y: evt.clientY })\n this.notify('selection-box:pointerdown', evt, localPoint.x, localPoint.y)\n },\n\n /**\n * @private\n * @param {jQuery.Event} evt\n */\n startTranslatingSelection: function(evt) {\n\n this._action = 'translating'\n\n this.options.graph.startBatch('selection-translate')\n\n const snappedClientCoords = this.options.paper.snapToGrid({ x: evt.clientX, y: evt.clientY })\n this._snappedClientX = snappedClientCoords.x\n this._snappedClientY = snappedClientCoords.y\n },\n\n /**\n * @private\n * @param {jQuery.Event} evt\n */\n adjustSelection: function(evt) {\n\n evt = joint.util.normalizeEvent(evt)\n\n let dx\n let dy\n\n switch (this._action) {\n\n case 'selecting':\n\n dx = evt.clientX - this._clientX\n dy = evt.clientY - this._clientY\n\n var left = parseInt(this.$el.css('left'), 10)\n var top = parseInt(this.$el.css('top'), 10)\n\n this.$el.css({\n left: dx < 0 ? this._offsetX + dx : left,\n top: dy < 0 ? this._offsetY + dy : top,\n width: Math.abs(dx),\n height: Math.abs(dy)\n })\n break\n\n case 'translating':\n\n var snappedClientCoords = this.options.paper.snapToGrid({ x: evt.clientX, y: evt.clientY })\n var snappedClientX = snappedClientCoords.x\n var snappedClientY = snappedClientCoords.y\n\n dx = snappedClientX - this._snappedClientX\n dy = snappedClientY - this._snappedClientY\n\n // restrict to area\n var restrictedArea = this.options.paper.getRestrictedArea()\n if (restrictedArea) {\n\n const elements = this.model.toArray()\n const selectionBBox = this.options.graph.getCellsBBox(elements)\n\n // restrict movement to ensure that all elements within selection stay inside restricted area\n const minDx = restrictedArea.x - selectionBBox.x\n const minDy = restrictedArea.y - selectionBBox.y\n const maxDx = (restrictedArea.x + restrictedArea.width) - (selectionBBox.x + selectionBBox.width)\n const maxDy = (restrictedArea.y + restrictedArea.height) - (selectionBBox.y + selectionBBox.height)\n\n if (dx < minDx) dx = minDx\n if (dy < minDy) dy = minDy\n\n if (dx > maxDx) dx = maxDx\n if (dy > maxDy) dy = maxDy\n }\n\n if (dx || dy) {\n\n this.translateSelectedElements(dx, dy)\n\n if (!this.boxesUpdated) {\n\n const paperScale = this.options.paper.scale()\n\n // Translate each of the `selection-box` amd `selection-wrapper`.\n this.$el.children('.selection-box').add(this.$selectionWrapper)\n .css({\n left: `+=${ dx * paperScale.sx}`,\n top: `+=${ dy * paperScale.sy}`\n })\n\n } else if (this.model.length > 1) {\n\n // If there is more than one cell in the selection, we need to update\n // the selection boxes again. e.g when the first element went over the\n // edge of the paper, a translate event was triggered, which updated the selection\n // boxes. After that all remaining elements were translated but the selection\n // boxes stayed unchanged.\n this.updateSelectionBoxes()\n }\n\n this._snappedClientX = snappedClientX\n this._snappedClientY = snappedClientY\n }\n\n this.notify('selection-box:pointermove', evt, snappedClientX, snappedClientY)\n break\n\n default:\n if (this._action) {\n this.pointermove(evt)\n }\n break\n }\n\n this.boxesUpdated = false\n },\n\n translateSelectedElements: function(dx, dy) {\n\n // This hash of flags makes sure we're not adjusting vertices of one link twice.\n // This could happen as one link can be an inbound link of one element in the selection\n // and outbound link of another at the same time.\n const processedCells = {}\n\n this.model.each(function(element) {\n\n // TODO: snap to grid.\n if (processedCells[element.id]) return\n\n // Make sure that selection won't update itself when not necessary\n const opt = { selection: this.cid }\n\n // Translate the element itself.\n element.translate(dx, dy, opt)\n\n element.getEmbeddedCells({ deep: true }).forEach(function(embed) {\n processedCells[embed.id] = true\n })\n\n // Translate link vertices as well.\n const connectedLinks = this.options.graph.getConnectedLinks(element)\n\n connectedLinks.forEach(function(link) {\n\n if (processedCells[link.id]) return\n\n link.translate(dx, dy, opt)\n\n processedCells[link.id] = true\n })\n\n }.bind(this))\n },\n\n /**\n * @private\n * @param {string} eventName\n * @param {jQuery.Event} event\n */\n notify: function(eventName, event) {\n\n const args = Array.prototype.slice.call(arguments, 1)\n this.trigger.apply(this, [eventName, this._activeElementView].concat(args))\n },\n\n /**\n * @private\n * @param {g.rect} selectedArea\n * @returns {Object.}\n */\n getElementsInSelectedArea: function(selectedArea) {\n\n const {paper} = this.options\n\n const filterOpt = {\n strict: this.options.strictSelection\n }\n\n if (this.options.useModelGeometry) {\n const models = paper.model.findModelsInArea(selectedArea, filterOpt)\n return models.map(paper.findViewByModel, paper).filter(function(item) {\n return !!item\n })\n }\n\n return paper.findViewsInArea(selectedArea, filterOpt)\n },\n\n /**\n * @private\n * @param {jQuery.Event} evt\n */\n pointerup: function(evt) {\n\n if (!this._action) return\n\n this.triggerAction(this._action, 'pointerup', evt)\n this.stopSelecting(evt)\n\n this._activeElementView = null\n this._action = null\n },\n\n /**\n * @private\n * @param {joint.dia.Element} element\n */\n destroySelectionBox: function(element) {\n\n this.$(`[data-model=\"${ element.get('id') }\"]`).remove()\n\n if (this.$el.children('.selection-box').length === 0) {\n this.hide()\n }\n\n this._boxCount = Math.max(0, this._boxCount - 1)\n },\n\n /**\n * @private\n */\n hide: function() {\n this.$el.removeClass('lasso selected')\n },\n\n /**\n * @private\n */\n showSelected: function() {\n this.$el.addClass('selected')\n },\n\n /**\n * @private\n */\n showLasso: function() {\n this.$el.addClass('lasso')\n },\n\n /**\n * @private\n */\n destroyAllSelectionBoxes: function() {\n\n this.hide()\n this.$el.children('.selection-box').remove()\n this._boxCount = 0\n },\n\n /**\n * @private\n * @param {joint.dia.Element} element\n */\n createSelectionBox: function(element) {\n\n const elementView = element.findView(this.options.paper)\n if (elementView) {\n const viewBBox = elementView.getBBox({ useModelGeometry: this.options.useModelGeometry })\n $('
')\n .addClass('selection-box')\n .attr('data-model', element.get('id'))\n .css({ left: viewBBox.x, top: viewBBox.y, width: viewBBox.width, height: viewBBox.height })\n .appendTo(this.el)\n this.showSelected()\n this._boxCount++\n }\n },\n\n /**\n * @private\n * @returns {jQuery}\n */\n createSelectionWrapper: function() {\n\n const $selectionWrapper = $('
', { class: 'selection-wrapper' })\n const $box = $('
', { class: 'box' })\n $selectionWrapper.append($box)\n $selectionWrapper.attr('data-selection-length', this.model.length)\n this.$el.prepend($selectionWrapper)\n return $selectionWrapper\n },\n\n /**\n * @private\n */\n updateSelectionWrapper: function() {\n\n // Find the position and dimension of the rectangle wrapping\n // all the element views.\n const origin = { x: Infinity, y: Infinity }\n const corner = { x: 0, y: 0 }\n\n this.model.each(function(cell) {\n\n const view = this.options.paper.findViewByModel(cell)\n if (view) {\n const bbox = view.getBBox({ useModelGeometry: this.options.useModelGeometry })\n origin.x = Math.min(origin.x, bbox.x)\n origin.y = Math.min(origin.y, bbox.y)\n corner.x = Math.max(corner.x, bbox.x + bbox.width)\n corner.y = Math.max(corner.y, bbox.y + bbox.height)\n }\n }.bind(this))\n\n this.$selectionWrapper.css({\n left: origin.x,\n top: origin.y,\n width: (corner.x - origin.x),\n height: (corner.y - origin.y)\n }).attr('data-selection-length', this.model.length)\n\n if (joint.util.isFunction(this.options.boxContent)) {\n\n const $box = this.$('.box')\n const content = this.options.boxContent.call(this, $box[0])\n\n // don't append empty content. (the content might had been created inside boxContent()\n if (content) {\n $box.html(content)\n }\n }\n },\n\n /**\n * @private\n */\n updateSelectionBoxes: function() {\n\n if (!this._boxCount) return\n\n this.hide()\n\n const children = this.$el.children('.selection-box')\n for (let i = 0, n = children.length; i < n; i++) {\n const element = children[i]\n\n const removedId = $(element).remove().attr('data-model')\n\n // try to find an element with the same id in the selection collection and\n // find the view for this model.\n const removedModel = this.model.get(removedId)\n\n if (removedModel) {\n // The view doesn't need to exist on the paper anymore as we use this method\n // as a handler for element removal.\n this.createSelectionBox(removedModel)\n }\n }\n this.updateSelectionWrapper()\n\n // When an user drags selection boxes over the edge of the paper and the paper gets resized,\n // we update the selection boxes here (giving them exact position) and we do not want\n // the selection boxes to be shifted again based on the mousemove.\n // See adjustSelection() method.\n this.boxesUpdated = true\n },\n\n /**\n * @private\n */\n onRemove: function() {\n\n $(document.body).off('.selection', this.adjustSelection)\n $(document).off('.selection', this.pointerup)\n },\n\n /**\n * @private\n * @param {jQuery.Event} evt\n */\n onHandlePointerDown: function(evt) {\n\n this._action = $(evt.target).closest('.handle').attr('data-action')\n if (this._action) {\n\n evt.preventDefault()\n evt.stopPropagation()\n evt = joint.util.normalizeEvent(evt)\n\n this._clientX = evt.clientX\n this._clientY = evt.clientY\n this._startClientX = this._clientX\n this._startClientY = this._clientY\n\n this.triggerAction(this._action, 'pointerdown', evt)\n }\n },\n\n /**\n * @private\n * @param {HTMLElement} element\n * @returns {joint.dia.Element}\n */\n getCellView: function(element) {\n\n const cell = this.model.get(element.getAttribute('data-model'))\n return cell && cell.findView(this.options.paper)\n },\n\n /**\n * @private\n * @param {jQuery.Event} evt\n */\n pointermove: function(evt) {\n\n if (!this._action) return\n\n const clientCoords = this.options.paper.snapToGrid({ x: evt.clientX, y: evt.clientY })\n const oldClientCoords = this.options.paper.snapToGrid({ x: this._clientX, y: this._clientY })\n\n const dx = clientCoords.x - oldClientCoords.x\n const dy = clientCoords.y - oldClientCoords.y\n\n this.triggerAction(this._action, 'pointermove', evt, dx, dy, evt.clientX - this._startClientX, evt.clientY - this._startClientY)\n\n this._clientX = evt.clientX\n this._clientY = evt.clientY\n },\n\n /**\n * Trigger an action on the Selection object. `evt` is a DOM event\n * @private\n * @param {string} action\n * @param {string} eventName abstracted JointJS event name (pointerdown, pointermove, pointerup).\n * @param {jQuery.Event} evt\n */\n triggerAction: function(action, eventName, evt) {\n\n const args = Array.prototype.slice.call(arguments, 2)\n args.unshift(`action:${ action }:${ eventName}`)\n this.trigger.apply(this, args)\n },\n\n // Handle actions.\n\n /**\n * @private\n * @param {joint.dia.Element} element\n */\n onRemoveElement: function(element) {\n\n this.destroySelectionBox(element)\n this.updateSelectionWrapper()\n },\n\n /**\n * @private\n * @param {Backbone.Collection.} elements\n */\n onResetElements: function(elements) {\n\n this.destroyAllSelectionBoxes()\n\n elements.each(this.createSelectionBox.bind(this))\n\n this.updateSelectionWrapper()\n },\n\n /**\n * @private\n * @param {joint.dia.Element} element\n */\n onAddElement: function(element) {\n\n this.createSelectionBox(element)\n this.updateSelectionWrapper()\n },\n\n /**\n * @private\n * @param {jQuery.Event} evt\n */\n removeElements: function(evt) {\n\n // Store cells before `cancelSelection()` resets the selection collection.\n const cells = this.collection.toArray()\n this.cancelSelection()\n this.options.graph.removeCells(cells, { selection: this.cid })\n },\n\n /**\n * @private\n * @param {jQuery.Event} evt\n */\n startRotating: function(evt) {\n\n this.options.graph.trigger('batch:start')\n\n const center = this.options.graph.getBBox(this.model.models).center()\n const clientCoords = this.options.paper.snapToGrid({ x: evt.clientX, y: evt.clientY })\n const initialAngles = this.model.toArray().reduce(function(res, element) {\n res[element.id] = g.normalizeAngle(element.get('angle') || 0)\n return res\n }, {})\n\n this._rotation = {\n center: center,\n clientAngle: g.point(clientCoords).theta(center),\n initialAngles: initialAngles\n }\n },\n\n /**\n * @private\n * @param {jQuery.Event} evt\n */\n startResizing: function(evt) {\n\n const {paper} = this.options\n const {graph} = this.options\n const grid = paper.options.gridSize\n const elements = this.model.toArray()\n\n const selectionBBox = graph.getBBox(elements)\n const elBBoxes = joint.util.invoke(elements, 'getBBox')\n const minElWidth = elBBoxes.reduce(function(min, bBox) {\n return bBox.width < min ? bBox.width : min\n }, Infinity)\n const minElHeight = elBBoxes.reduce(function(min, bBox) {\n return bBox.height < min ? bBox.height : min\n }, Infinity)\n\n this._resize = {\n cells: graph.getSubgraph(elements),\n bbox: selectionBBox,\n minWidth: grid * selectionBBox.width / minElWidth,\n minHeight: grid * selectionBBox.height / minElHeight\n }\n\n graph.trigger('batch:start')\n },\n\n /**\n * @param {jQuery.Event} evt\n * @param {number} dx\n * @param {number} dy\n */\n doResize: function(evt, dx, dy) {\n\n const opt = this._resize\n const {bbox} = opt\n const prevWidth = bbox.width\n const prevHeight = bbox.height\n const width = Math.max(prevWidth + dx, opt.minWidth)\n const height = Math.max(prevHeight + dy, opt.minHeight)\n\n if (Math.abs(prevWidth - width) > 1e-3 || Math.abs(prevHeight - height) > 1e-3) {\n\n this.options.graph.resizeCells(width, height, opt.cells, {\n selection: this.cid\n })\n\n // update selection bbox\n bbox.width = width\n bbox.height = height\n\n this.updateSelectionBoxes()\n }\n },\n\n /**\n * @private\n * @param {jQuery.Event} evt\n */\n doRotate: function(evt) {\n\n const rotation = this._rotation\n\n // Calculate an angle between the line starting at mouse coordinates, ending at the centre\n // of rotation and y-axis and deduct the angle from the start of rotation.\n const angleGrid = this.options.rotateAngleGrid\n const clientCoords = this.options.paper.snapToGrid({ x: evt.clientX, y: evt.clientY })\n const theta = rotation.clientAngle - g.point(clientCoords).theta(rotation.center)\n\n if (Math.abs(theta) > 1e-3) {\n\n this.model.each(function(element) {\n const newAngle = g.snapToGrid(rotation.initialAngles[element.id] + theta, angleGrid)\n element.rotate(newAngle, true, rotation.center, { selection: this.cid })\n }, this)\n\n this.updateSelectionBoxes()\n }\n },\n\n /**\n * @private\n */\n stopBatch: function() {\n\n this.options.graph.trigger('batch:stop')\n },\n\n /**\n * @private\n * Return the current action of the Selection.\n * This can be one of:\n * 'translating' | 'selecting' or any custom action.\n * This is especially useful if you want to prevent from something happening\n * while the selection is taking place (e.g. in the 'selecting' state and you are\n * handling the mouseover event).\n * @returns {string}\n */\n getAction: function() {\n\n return this._action\n }\n}, {\n\n depthComparator: function(element) {\n // Where depth is a number of ancestors.\n return element.getAncestors().length\n }\n})\n\n// An alias for backwards compatibility\njoint.ui.SelectionView = joint.ui.Selection\n\n// Implements Clipboard for copy-pasting elements.\n// Note that the clipboard is also able to copy elements and their associated links from one graph\n// and paste them to another.\n\n// Usage:\n\n// var selection = new Backbone.Collection;\n// var graph = new joint.dia.Graph;\n// // ... now something that adds elements to the selection ...\n// var clipboard = new joint.ui.Clipboard;\n// KeyboardJS.on('ctrl + c', function() { clipboard.copyElements(selection, graph); });\n// KeyboardJS.on('ctrl + v', function() { clipboard.pasteCells(graph); });\njoint.ui.Clipboard = Backbone.Collection.extend({\n LOCAL_STORAGE_KEY: 'joint.ui.Clipboard.cells',\n defaults: {\n useLocalStorage: true\n },\n /**\n * @public\n * This function returns the elements and links from the original graph that were copied. This is useful for implements\n * the Cut operation where the original cells should be removed from the graph. `selection` contains\n * elements that should be copied to the clipboard. Note that with these elements, also all the associated\n * links are copied. That's why we also need the `graph` parameter, to find these links.\n *\n * @param {Backbone.Collection} selection\n * @param {joint.dia.Graph} graph\n * @param {Object=} opt Used as a default for settings passed through the `pasteCells` method.\n * @returns {Array.}\n */\n copyElements: function(selection, graph, opt) {\n\n this.options = joint.util.assign({}, this.defaults, opt)\n opt = this.options\n\n const originalElements = selection.toArray()\n const clones = joint.util.sortBy(graph.cloneSubgraph(originalElements, opt), function(cell) {\n return cell.isLink() ? 2 : 1\n })\n\n this.reset(clones)\n\n if (opt.useLocalStorage && window.localStorage) {\n localStorage.setItem(this.LOCAL_STORAGE_KEY, JSON.stringify(this.toJSON()))\n }\n\n return originalElements\n },\n\n /**\n * @public\n * Same logic as per `copyElements`, but elements are removed from the graph\n * @param {Backbone.Collection} selection\n * @param {joint.dia.Graph} graph\n * @param {Object=} opt Used as a default for settings passed through the `pasteCells` method.\n * @returns {Array.} elements removed from graph\n */\n cutElements: function(selection, graph, opt) {\n\n const elementsToRemove = this.copyElements(selection, graph, opt)\n graph.trigger('batch:start', { batchName: 'cut' })\n joint.util.invoke(elementsToRemove, 'remove')\n graph.trigger('batch:stop', { batchName: 'cut' })\n return elementsToRemove\n },\n\n /**\n * @public\n * @param {joint.dia.Graph} graph Where paste to.\n * @param {Object.<{ translate: {dx: number, dy: number}, useLocalStorage: boolean, link: Object}>=} opt\n * If `translate` object with `dx` and `dy` properties is passed, the copied elements will be\n * translated by the specified amount. This is useful for e.g. the 'cut' operation where we'd like to have\n * the pasted elements moved by an offset to see they were pasted to the paper.\n *\n * If `useLocalStorage` is `true`, the copied elements will be saved to the localStorage (if present)\n * making it possible to copy-paste elements between browser tabs or sessions.\n *\n * `link` is attributes that will be set all links before they are added to the `graph`.\n * This is useful for e.g. setting `z: -1` for links in order to always put them to the bottom of the paper.\n * @returns {Array.}\n */\n pasteCells: function(graph, opt) {\n\n //use options passed into copyElements as defaults for paste\n opt = joint.util.defaults(opt || {}, this.options)\n\n if (opt.useLocalStorage && this.isEmpty() && window.localStorage) {\n\n const graphJSON = {\n cells: JSON.parse(localStorage.getItem(this.LOCAL_STORAGE_KEY))\n }\n // Note there is a `{ sort: false }` option passed to make sure\n // the temporary graph does not change the order of cells.\n // i.e. elements must stay before links\n const tmpGraph = new joint.dia.Graph().fromJSON(graphJSON, { sort: false })\n this.reset(tmpGraph.getCells())\n }\n\n //process modification on current data\n const pastedCells = this.map(function(cell) {\n return this.modifyCell(cell, opt)\n }.bind(this))\n\n graph.trigger('batch:start', { batchName: 'paste' })\n graph.addCells(pastedCells)\n graph.trigger('batch:stop', { batchName: 'paste' })\n\n this.copyElements(this, graph)\n\n return pastedCells\n },\n\n /**\n * @public\n */\n clear: function() {\n\n this.options = {}\n this.reset([])\n\n if (window.localStorage) {\n localStorage.removeItem(this.LOCAL_STORAGE_KEY)\n }\n },\n\n /**\n * @private\n * @param {joint.dia.Cell} cell\n * @param {Object} opt\n * @returns {joint.dia.Cell}\n */\n modifyCell: function(cell, opt) {\n\n cell.unset('z')\n if (cell.isLink() && opt.link) {\n cell.set(opt.link)\n }\n\n if (opt.translate) {\n cell.translate(opt.translate.dx || 20, opt.translate.dy || 20)\n }\n\n // It's necessary to unset the collection reference here. Backbone.Collection adds collection\n // attribute to every new model, except if the model already has one. The pasted elements needs to have\n // collection attribute set to the Graph collection (not the Selection collection).\n cell.collection = null\n\n return cell\n }\n});\n\n(function(joint) {\n \n\n /**\n * @constructor\n */\n const LinkHalo = function() {\n this.options = {\n handles: [\n {\n name: 'remove',\n position: 'nw',\n events: { pointerdown: 'removeElement' },\n icon: null\n },\n {\n name: 'direction',\n position: 'se',\n events: { pointerdown: 'directionSwap' },\n icon: null\n }\n ],\n bbox: function(cellView) {\n\n const linkLength = cellView.getConnectionLength() * 0.5\n return cellView.getPointAtLength(linkLength)\n },\n typeCssName: 'type-link',\n tinyThreshold: -1,\n smallThreshold: -1,\n boxContent: false\n }\n }\n\n /**\n * @private\n * Swap direction of the link.\n */\n LinkHalo.prototype.directionSwap = function() {\n const {model} = this.options.cellView\n model.set({ source: model.get('target'), target: model.get('source') }, { halo: this.cid })\n }\n\n /**\n * @constructor\n */\n const ElementHalo = function() {\n this.options = {\n handles: [\n {\n name: 'remove',\n position: 'nw',\n events: { pointerdown: 'removeElement' },\n icon: null\n },\n {\n name: 'resize',\n position: 'se',\n events: { pointerdown: 'startResizing', pointermove: 'doResize', pointerup: 'stopBatch' },\n icon: null\n },\n {\n name: 'clone',\n position: 'n',\n events: { pointerdown: 'startCloning', pointermove: 'doClone', pointerup: 'stopCloning' },\n icon: null\n },\n {\n name: 'link',\n position: 'e',\n events: { pointerdown: 'startLinking', pointermove: 'doLink', pointerup: 'stopLinking' },\n icon: null\n },\n {\n name: 'fork',\n position: 'ne',\n events: { pointerdown: 'startForking', pointermove: 'doFork', pointerup: 'stopForking' },\n icon: null\n },\n {\n name: 'unlink',\n position: 'w',\n events: { pointerdown: 'unlinkElement' },\n icon: null\n },\n {\n name: 'rotate',\n position: 'sw',\n events: { pointerdown: 'startRotating', pointermove: 'doRotate', pointerup: 'stopBatch' },\n icon: null\n }\n ],\n\n /**\n * @param {joint.dia.CellView} cellView\n * @param {joint.ui.Halo} halo\n * @returns {g.rect}\n */\n bbox: function(cellView, halo) {\n return cellView.getBBox({ useModelGeometry: halo.options.useModelGeometry })\n },\n typeCssName: 'type-element',\n tinyThreshold: 40,\n smallThreshold: 80,\n\n // a function returning a html string, which will be used as the halo box content\n boxContent: function(elementView, boxElement) {\n const tmpl = joint.util.template('x: <%= x %>, y: <%= y %>, width: <%= width %>, height: <%= height %>, angle: <%= angle %>')\n const element = elementView.model\n const bbox = element.getBBox()\n\n return tmpl({\n x: Math.floor(bbox.x),\n y: Math.floor(bbox.y),\n width: Math.floor(bbox.width),\n height: Math.floor(bbox.height),\n angle: Math.floor(element.get('angle') || 0)\n })\n },\n\n magnet: function(elementView) {\n // elementView root element by default\n return elementView.el\n },\n\n loopLinkPreferredSide: 'top',\n loopLinkWidth: 40,\n rotateAngleGrid: 15,\n\n // Rest of options are deprecated (better use joint.dia.Paper.options.linkModel)\n linkAttributes: {},\n smoothLinks: undefined\n }\n }\n\n ElementHalo.prototype.startLinking = function(evt, x, y) {\n\n this.startBatch()\n\n const {options} = this\n const {paper} = options\n const {graph} = options\n\n const link = this.createLinkConnectedToSource()\n\n // add link to graph but don't validate\n link.set({\n target: { x: x, y: y }\n }).addTo(graph, {\n validation: false,\n halo: this.cid,\n async: false\n })\n\n const linkView = this._linkView = link.findView(paper)\n linkView.startArrowheadMove('target', { whenNotAllowed: 'remove' })\n }\n\n ElementHalo.prototype.startForking = function(evt, x, y) {\n\n const {options} = this\n const {paper} = options\n const {graph} = options\n\n this.startBatch()\n\n const clone = options.clone(options.cellView.model, { fork: true })\n if (!(clone instanceof joint.dia.Cell)) {\n throw new Error('ui.Halo: option \"clone\" has to return a cell.')\n }\n\n this.centerElementAtCursor(clone, x, y)\n clone.addTo(graph, {\n halo: this.cid,\n async: false\n })\n\n const link = this.createLinkConnectedToSource()\n const cloneView = this._cloneView = clone.findView(paper)\n const targetMagnet = this.getElementMagnet(cloneView, 'target')\n const linkTarget = this.getLinkEnd(cloneView, targetMagnet)\n\n link.set('target', linkTarget).addTo(graph, {\n halo: this.cid,\n async: false\n })\n\n cloneView.pointerdown(evt, x, y)\n }\n\n ElementHalo.prototype.getElementMagnet = function(elementView, endAttribute) {\n const fn = this.options.magnet\n if (joint.util.isFunction(fn)) {\n const magnet = fn.call(this, elementView, endAttribute)\n if (magnet instanceof SVGElement) {\n return magnet\n }\n }\n throw new Error('ui.Halo: magnet() has to return an SVGElement.')\n },\n\n ElementHalo.prototype.getLinkEnd = function(elementView, magnet) {\n const end = { id: elementView.model.id }\n if (magnet !== elementView.el) {\n const port = magnet.getAttribute('port')\n if (port) {\n end.port = port\n } else {\n end.selector = elementView.getSelector(magnet)\n }\n }\n return end\n },\n\n ElementHalo.prototype.createLinkConnectedToSource = function() {\n\n const {options} = this\n const {paper} = options\n const elementView = options.cellView\n const sourceMagnet = this.getElementMagnet(elementView, 'source')\n const linkSource = this.getLinkEnd(elementView, sourceMagnet)\n const link = paper.getDefaultLink(elementView, sourceMagnet).set('source', linkSource)\n\n // Backwards compatibility\n link.attr(options.linkAttributes)\n if (joint.util.isBoolean(options.smoothLinks)) {\n link.set('smooth', options.smoothLinks)\n }\n\n return link\n },\n\n ElementHalo.prototype.startResizing = function(evt) {\n\n this.startBatch()\n\n // determine whether to flip x,y mouse coordinates while resizing or not\n this._flip = [1, 0, 0, 1, 1, 0, 0, 1][Math.floor(g.normalizeAngle(this.options.cellView.model.get('angle')) / 45)]\n }\n\n ElementHalo.prototype.startRotating = function(evt, x, y) {\n\n this.startBatch()\n\n const center = this.options.cellView.model.getBBox().center()\n const angle = g.normalizeAngle(this.options.cellView.model.get('angle'))\n\n this._center = center\n this._rotationStartAngle = angle || 0\n this._clientStartAngle = g.point(x, y).theta(center)\n }\n\n ElementHalo.prototype.doResize = function(evt, x, y, dx, dy) {\n\n const size = this.options.cellView.model.get('size')\n\n const width = Math.max(size.width + ((this._flip ? dx : dy)), 1)\n const height = Math.max(size.height + ((this._flip ? dy : dx)), 1)\n\n this.options.cellView.model.resize(width, height, { absolute: true })\n }\n\n ElementHalo.prototype.doRotate = function(evt, x, y) {\n\n // Calculate an angle between the line starting at mouse coordinates, ending at the centre\n // of rotation and y-axis and deduct the angle from the start of rotation.\n const theta = this._clientStartAngle - g.point(x, y).theta(this._center)\n const newAngle = g.snapToGrid(this._rotationStartAngle + theta, this.options.rotateAngleGrid)\n\n this.options.cellView.model.rotate(newAngle, true)\n }\n\n ElementHalo.prototype.doClone = function(evt, x, y) {\n\n const cloneView = this._cloneView\n if (cloneView) {\n cloneView.pointermove(evt, x, y)\n }\n }\n\n ElementHalo.prototype.startCloning = function(evt, x, y) {\n\n const {options} = this\n\n this.startBatch()\n\n const clone = options.clone(options.cellView.model, { clone: true })\n\n if (!(clone instanceof joint.dia.Cell)) {\n throw new Error('ui.Halo: option \"clone\" has to return a cell.')\n }\n\n this.centerElementAtCursor(clone, x, y)\n clone.addTo(options.graph, { halo: this.cid, async: false })\n\n this._cloneView = clone.findView(options.paper)\n this._cloneView.pointerdown(evt, x, y)\n }\n\n ElementHalo.prototype.centerElementAtCursor = function(element, x, y) {\n\n const center = element.getBBox().center()\n const tx = x - center.x\n const ty = y - center.y\n\n element.translate(tx, ty)\n }\n\n ElementHalo.prototype.doFork = function(evt, x, y) {\n\n const cloneView = this._cloneView\n if (cloneView) {\n cloneView.pointermove(evt, x, y)\n }\n }\n\n ElementHalo.prototype.doLink = function(evt, x, y) {\n\n if (this._linkView) {\n this._linkView.pointermove(evt, x, y)\n }\n }\n\n ElementHalo.prototype.stopLinking = function(evt) {\n\n if (this._linkView) {\n\n this._linkView.pointerup(evt)\n\n if (this._linkView.model.hasLoop()) {\n this.makeLoopLink(this._linkView.model)\n }\n\n this.stopBatch()\n this.triggerAction('link', 'add', this._linkView.model)\n this._linkView = null\n }\n }\n\n ElementHalo.prototype.stopForking = function(evt, x, y) {\n\n const cloneView = this._cloneView\n if (cloneView) {\n cloneView.pointerup(evt, x, y)\n }\n this.stopBatch()\n }\n\n ElementHalo.prototype.stopCloning = function(evt, x, y) {\n\n const cloneView = this._cloneView\n if (cloneView) {\n cloneView.pointerup(evt, x, y)\n }\n this.stopBatch()\n }\n\n ElementHalo.prototype.unlinkElement = function(evt) {\n\n this.startBatch()\n this.options.graph.removeLinks(this.options.cellView.model)\n this.stopBatch()\n }\n\n ElementHalo.prototype.makeLoopLink = function(link) {\n const linkWidth = this.options.loopLinkWidth\n const paperOpt = this.options.paper.options\n const paperRect = g.rect({ x: 0, y: 0, width: paperOpt.width, height: paperOpt.height })\n const bbox = V(this.options.cellView.el).bbox(false, this.options.paper.viewport)\n let p1; let p2\n\n const sides = joint.util.uniq([this.options.loopLinkPreferredSide, 'top', 'bottom', 'left', 'right'])\n const sideFound = sides.find(function(side) {\n\n let centre\n let dx = 0\n let dy = 0\n\n switch (side) {\n\n case 'top':\n centre = g.point(bbox.x + bbox.width / 2, bbox.y - linkWidth)\n dx = linkWidth / 2\n break\n\n case 'bottom':\n centre = g.point(bbox.x + bbox.width / 2, bbox.y + bbox.height + linkWidth)\n dx = linkWidth / 2\n break\n\n case 'left':\n centre = g.point(bbox.x - linkWidth, bbox.y + bbox.height / 2)\n dy = linkWidth / 2\n break\n\n case 'right':\n centre = g.point(bbox.x + bbox.width + linkWidth, bbox.y + bbox.height / 2)\n dy = linkWidth / 2\n break\n }\n\n p1 = g.point(centre).offset(-dx, -dy)\n p2 = g.point(centre).offset(dx, dy)\n\n return paperRect.containsPoint(p1) && paperRect.containsPoint(p2)\n\n }, this)\n\n if (sideFound) link.set('vertices', [p1, p2])\n\n }\n\n joint.ui.Halo = joint.mvc.View.extend({\n\n PIE_INNER_RADIUS: 20,\n PIE_OUTER_RADIUS: 50,\n\n className: 'halo',\n\n events: {\n 'mousedown .handle': 'onHandlePointerDown',\n 'touchstart .handle': 'onHandlePointerDown',\n 'mousedown .pie-toggle': 'onPieTogglePointerDown',\n 'touchstart .pie-toggle': 'onPieTogglePointerDown'\n },\n documentEvents: {\n mousemove: 'pointermove',\n touchmove: 'pointermove',\n mouseup: 'pointerup',\n touchend: 'pointerup'\n },\n options: {\n clearAll: true,\n clearOnBlankPointerdown: true,\n // This option allows you to compute bbox from the model. The view bbox can sometimes return\n // an unwanted result e.g when an element uses SVG filters or clipPaths. Note that downside\n // of computing a bbox is that it takes no relative subelements into account (e.g ports).\n useModelGeometry: false,\n // A function returning a copy of given cell used in cloning and forking.\n // Useful e.g. when you wish to translate the clone after it's created.\n // Note that clone is not in the graph when the function is invoked.\n clone: function(cell, opt) {\n return cell.clone().unset('z')\n },\n // Type of the halo. Determines the look of the halo (esp. positioning of handles).\n type: 'surrounding',\n // Various options for a specific types.\n pieSliceAngle: 45,\n pieStartAngleOffset: 0,\n pieIconSize: 14,\n // Pie toggle buttons. Usually, there is only one but in general, there can be\n // many. Each button can have a position (e ... east, w ... west, s ... south, n ... north)\n // and name. This name is then used when triggering events when the pie toggle button\n // is clicked (pie:open:default / pie:close:default).\n pieToggles: [{ name: 'default', position: 'e' }]\n },\n\n /**\n * @protected\n */\n init: function() {\n\n const {options} = this\n const {cellView} = options\n const cell = cellView.model\n const cellViewAbstract = cell.isLink() ? new LinkHalo() : new ElementHalo()\n\n joint.util.assign(this, joint.util.omit(cellViewAbstract, 'options'))\n\n const {paper} = cellView\n const graph = paper.model\n\n joint.util.defaults(options, cellViewAbstract.options, {\n paper: paper,\n graph: graph\n })\n\n joint.util.bindAll(this, 'render', 'update')\n\n if (options.clearAll) {\n // Clear a previous halo if there was one for the paper.\n this.constructor.clear(paper)\n }\n\n // Update halo when the graph changed.\n this.listenTo(graph, 'reset', this.remove)\n this.listenTo(cell, 'remove', this.remove)\n this.listenTo(paper, 'halo:create', this.remove)\n if (options.clearOnBlankPointerdown) {\n // Hide Halo when the user clicks anywhere in the paper\n this.listenTo(paper, 'blank:pointerdown', this.remove)\n }\n\n this.listenTo(graph, 'all', this.update)\n this.listenTo(paper, 'scale translate', this.update)\n\n // Add all default handles first.\n this.handles = []\n\n joint.util.toArray(options.handles).forEach(this.addHandle, this)\n },\n\n /**\n * @public\n * @returns {joint.ui.Halo}\n */\n render: function() {\n\n const {options} = this\n\n this.$el.empty()\n this.$handles = $('
').addClass('handles').appendTo(this.el)\n this.$box = $('