for reference see 3.3.4 & 3.3.5 in http://unicode.org/reports/tr9/
function _resolveCharType(chars, types, resolvedTypes, index) {
      var cType = types[index],
          wType,
          nType,
          i,
          len;

      switch (cType) {
        case "L":
        case "R":
          _lastArabic = false;
          break;

        case "N":
        case "AN":
          break;

        case "EN":
          if (_lastArabic) {
            cType = "AN";
          }

          break;

        case "AL":
          _lastArabic = true;
          cType = "R";
          break;

        case "WS":
          cType = "N";
          break;

        case "CS":
          if (index < 1 || index + 1 >= types.length || (wType = resolvedTypes[index - 1]) !== "EN" && wType !== "AN" || (nType = types[index + 1]) !== "EN" && nType !== "AN") {
            cType = "N";
          } else if (_lastArabic) {
            nType = "AN";
          }

          cType = nType === wType ? nType : "N";
          break;

        case "ES":
          wType = index > 0 ? resolvedTypes[index - 1] : "B";
          cType = wType === "EN" && index + 1 < types.length && types[index + 1] === "EN" ? "EN" : "N";
          break;

        case "ET":
          if (index > 0 && resolvedTypes[index - 1] === "EN") {
            cType = "EN";
            break;
          } else if (_lastArabic) {
            cType = "N";
            break;
          }

          i = index + 1;
          len = types.length;

          while (i < len && types[i] === "ET") {
            i++;
          }

          if (i < len && types[i] === "EN") {
            cType = "EN";
          } else {
            cType = "N";
          }

          break;

        case "NSM":
          if (_isInVisual && !_isInRtl) {
            //V->L
            len = types.length;
            i = index + 1;

            while (i < len && types[i] === "NSM") {
              i++;
            }

            if (i < len) {
              var c = chars[index];
              var rtlCandidate = c >= 0x0591 && c <= 0x08FF || c === 0xFB1E;
              wType = types[i];

              if (rtlCandidate && (wType === "R" || wType === "AL")) {
                cType = "R";
                break;
              }
            }
          }

          if (index < 1 || (wType = types[index - 1]) === "B") {
            cType = "N";
          } else {
            cType = resolvedTypes[index - 1];
          }

          break;

        case "B":
          _lastArabic = false;
          _hasUbatB = true;
          cType = _dir;
          break;

        case "S":
          _hasUbatS = true;
          cType = "N";
          break;

        case "LRE":
        case "RLE":
        case "LRO":
        case "RLO":
        case "PDF":
          _lastArabic = false;
          break;

        case "BN":
          cType = "N";
          break;
      }

      return cType;
    }