text: window.ls.filter
    .add("avatar", function ($value, element) {
      if (!$value) {
        return "";
      }
  
      let size = element.dataset["size"] || 80;
      let name = $value.name || $value || "";
  
      name = (typeof name !== 'string') ? '--' : name;
  
      return def =
        "/v1/avatars/initials?project=console" +
        "&name=" +
        encodeURIComponent(name) +
        "&width=" +
        size +
        "&height=" +
        size;
    })
    .add("selectedCollection", function ($value, router) {
      return $value === router.params.collectionId ? "selected" : "";
    })
    .add("selectedDocument", function ($value, router) {
      return $value === router.params.documentId ? "selected" : "";
    })
    .add("localeString", function ($value) {
      $value = parseInt($value);
      return !Number.isNaN($value) ? $value.toLocaleString() : "";
    })
    .add("date", function ($value, date) {
      return date.format("Y-m-d", $value);
    })
    .add("dateTime", function ($value, date) {
      return date.format("Y-m-d H:i", $value);
    })
    .add("dateText", function ($value, date) {
      return date.format("d M Y", $value);
    })
    .add("timeSince", function ($value) {
      $value = $value * 1000;
  
      let seconds = Math.floor((Date.now() - $value) / 1000);
      let unit = "second";
      let direction = "ago";
  
      if (seconds < 0) {
        seconds = -seconds;
        direction = "from now";
      }
  
      let value = seconds;
  
      if (seconds >= 31536000) {
        value = Math.floor(seconds / 31536000);
        unit = "year";
      }
      else if (seconds >= 86400) {
        value = Math.floor(seconds / 86400);
        unit = "day";
      }
      else if (seconds >= 3600) {
        value = Math.floor(seconds / 3600);
        unit = "hour";
      }
      else if (seconds >= 60) {
        value = Math.floor(seconds / 60);
        unit = "minute";
      }
  
      if (value != 1) {
        unit = unit + "s";
      }
  
      return value + " " + unit + " " + direction;
    })
    .add("ms2hum", function ($value) {
      let temp = $value;
      const years = Math.floor(temp / 31536000),
        days = Math.floor((temp %= 31536000) / 86400),
        hours = Math.floor((temp %= 86400) / 3600),
        minutes = Math.floor((temp %= 3600) / 60),
        seconds = temp % 60;
  
      if (days || hours || seconds || minutes) {
        return (
          (years ? years + "y " : "") +
          (days ? days + "d " : "") +
          (hours ? hours + "h " : "") +
          (minutes ? minutes + "m " : "") +
          Number.parseFloat(seconds).toFixed(0) +
          "s"
        );
      }
  
      return "< 1s";
    })
    .add("seconds2hum", function ($value) {
  
      var seconds = ($value).toFixed(3);
  
      var minutes = ($value / (60)).toFixed(1);
  
      var hours = ($value / (60 * 60)).toFixed(1);
  
      var days = ($value / (60 * 60 * 24)).toFixed(1);
  
      if (seconds < 60) {
        return seconds + "s";
      } else if (minutes < 60) {
        return minutes + "m";
      } else if (hours < 24) {
        return hours + "h";
      } else {
        return days + "d"
      }
    })
    .add("markdown", function ($value, markdown) {
      return markdown.render($value);
    })
    .add("pageCurrent", function ($value, env) {
      return Math.ceil(parseInt($value || 0) / env.PAGING_LIMIT) + 1;
    })
    .add("pageTotal", function ($value, env) {
      let total = Math.ceil(parseInt($value || 0) / env.PAGING_LIMIT);
      return total ? total : 1;
    })
    .add("humanFileSize", function ($value) {
      if (!$value) {
        return 0;
      }
  
      let thresh = 1000;
  
      if (Math.abs($value) < thresh) {
        return $value;
      }
  
      let units = ["kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
      let u = -1;
  
      do {
        $value /= thresh;
        ++u;
      } while (Math.abs($value) >= thresh && u < units.length - 1);
  
      return $value.toFixed(1);
    })
    .add("humanFileUnit", function ($value) {
      if (!$value) {
        return '';
      }
  
      let thresh = 1000;
  
      if (Math.abs($value) < thresh) {
        return 'B';
      }
  
      let units = ["kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
      let u = -1;
  
      do {
        $value /= thresh;
        ++u;
      } while (Math.abs($value) >= thresh && u < units.length - 1);
  
      return units[u];
    })
    .add("statsTotal", function ($value) {
      if (!$value) {
        return 0;
      }
  
      $value = abbreviate($value, 0, false, false);
  
      return $value ?? "N/A";
    })
    .add("statsGetLast", function ($value) {
      if (!$value || $value.length < 1) {
        return 0;
      }
  
      return $value[$value.length - 1].value;
    })
    .add("statsGetTotal", function ($value) {
      if (!$value || $value.length < 1) {
        return 0;
      }
  
      return $value.reduce(function(value, object) { return value + object.value }, 0);
    })
    .add("isEmpty", function ($value) {
      return (!!$value);
    })
    .add("isEmptyObject", function ($value) {
      return ((Object.keys($value).length === 0 && $value.constructor === Object) || $value.length === 0)
    })
    .add("activeDomainsCount", function ($value) {
      let result = [];
  
      if (Array.isArray($value)) {
        result = $value.filter(function (node) {
          return (node.verification && node.certificateId);
        });
      }
  
      return result.length;
    })
    .add("documentAction", function (container) {
      let collection = container.get('project-collection');
      let document = container.get('project-document');
  
      if (collection && document && !document.$id) {
        return 'database.createDocument';
      }
  
      return 'database.updateDocument';
    })
    .add("documentSuccess", function (container) {
      let document = container.get('project-document');
  
      if (document && !document.$id) {
        return ',redirect';
      }
  
      return '';
    })
    .add("firstElement", function ($value) {
      if ($value && $value[0]) {
        return $value[0];
      }
  
      return $value;
    })
    .add("platformsLimit", function ($value) {
      return $value;
    })
    .add("limit", function ($value) {
      let postfix = ($value.length >= 50) ? '...' : '';
      return $value.substring(0, 50) + postfix;
      ;
    })
    .add("arraySentence", function ($value) {
      if (!Array.isArray($value)) {
        return '';
      }
  
      return $value.join(", ").replace(/,\s([^,]+)$/, ' and $1');
    })
    .add("runtimeName", function ($value, env) {
      if (env && env.RUNTIMES && env.RUNTIMES[$value]) {
        return env.RUNTIMES[$value].name;
      }
  
      return '';
    })
    .add("runtimeLogo", function ($value, env) {
      if (env && env.RUNTIMES && env.RUNTIMES[$value]) {
        return env.RUNTIMES[$value].logo;
      }
  
      return '';
    })
    .add("runtimeVersion", function ($value, env) {
      if (env && env.RUNTIMES && env.RUNTIMES[$value]) {
        return env.RUNTIMES[$value].version;
      }
  
      return '';
    })
    .add("indexAttributes", function ($value) {
      let output = '';
  
      for (let i = 0; i < $value.attributes.length; i++) {
        output += $value.attributes[i] + ' (' + $value.orders[i] + '), '
      }
      return output.slice(0, -2);
    })
    .add("collectionAttributes", function ($value) {
      if (!Array.isArray($value)) {
        return [];
      }
  
      $value.unshift({
        $id: '$id'
      });
  
      return $value;
    })
    .add("documentAttribute", function ($value, attribute) {
      if (attribute.key in $value) {
        return $value[attribute.key];
      }
  
      return null;
    })
    .add("accessProject", function ($value, router) {
      return ($value && $value.hasOwnProperty(router.params.project)) ? $value[router.params.project] : 0;
    })
    .add("first", function ($value) {
      return $value[0].$id;
    })
    .add("last", function ($value) {
      return $value[$value.length - 1].$id;
    })
    ;
  
  function abbreviate(number, maxPlaces, forcePlaces, forceLetter) {
    number = Number(number);
    forceLetter = forceLetter || false;
    if (forceLetter !== false) {
      return annotate(number, maxPlaces, forcePlaces, forceLetter);
    }
    let abbr;
    if (number >= 1e12) {
      abbr = "T";
    } else if (number >= 1e9) {
      abbr = "B";
    } else if (number >= 1e6) {
      abbr = "M";
    } else if (number >= 1e3) {
      abbr = "K";
    } else {
      abbr = "";
    }
    return annotate(number, maxPlaces, forcePlaces, abbr);
  }
  
  function annotate(number, maxPlaces, forcePlaces, abbr) {
    // set places to false to not round
    let rounded = 0;
    switch (abbr) {
      case "T":
        rounded = number / 1e12;
        break;
      case "B":
        rounded = number / 1e9;
        break;
      case "M":
        rounded = number / 1e6;
        break;
      case "K":
        rounded = number / 1e3;
        break;
      case "":
        rounded = number;
        break;
    }
    if (maxPlaces !== false) {
      let test = new RegExp("\\.\\d{" + (maxPlaces + 1) + ",}$");
      if (test.test("" + rounded)) {
        rounded = rounded.toFixed(maxPlaces);
      }
    }
    if (forcePlaces !== false) {
      rounded = Number(rounded).toFixed(forcePlaces);
    }
    return rounded + abbr;
  }
  