docs: function init() {
          if (api.initialized) {
              return;
          }
          var testRange;
          var implementsDomRange = false, implementsTextRange = false;
  
          // First, perform basic feature tests
  
          if (isHostMethod(document, "createRange")) {
              testRange = document.createRange();
              if (areHostMethods(testRange, domRangeMethods) && areHostProperties(testRange, domRangeProperties)) {
                  implementsDomRange = true;
              }
          }
  
          var body = getBody(document);
          if (!body || body.nodeName.toLowerCase() != "body") {
              fail("No body element found");
              return;
          }
  
          if (body && isHostMethod(body, "createTextRange")) {
              testRange = body.createTextRange();
              if (isTextRange(testRange)) {
                  implementsTextRange = true;
              }
          }
  
          if (!implementsDomRange && !implementsTextRange) {
              fail("Neither Range nor TextRange are available");
              return;
          }
  
          api.initialized = true;
          api.features = {
              implementsDomRange: implementsDomRange,
              implementsTextRange: implementsTextRange
          };
  
          // Initialize modules
          var module, errorMessage;
          for (var moduleName in modules) {
              if ( (module = modules[moduleName]) instanceof Module ) {
                  module.init(module, api);
              }
          }
  
          // Call init listeners
          for (var i = 0, len = initListeners.length; i < len; ++i) {
              try {
                  initListeners[i](api);
              } catch (ex) {
                  errorMessage = "Rangy init listener threw an exception. Continuing. Detail: " + getErrorDesc(ex);
                  consoleLog(errorMessage);
              }
          }
      }