"use strict";
function GetJobDirect(eJobDirection) {
    switch (eJobDirection) {
        case JS_TWO_WAY: return "arrow-left-right";
        case JS_LEFT_TO_RIGHT: return "move-right";
        case JS_RIGHT_TO_LEFT: return "move-left";
        default: return "job_error";
    }
}
function GetNumberOfJobDirect(eJobDirection) {
    if (!eJobDirection)
        return JS_UNDEFINDED;
    switch (eJobDirection) {
        case "arrow-left-right": return JS_TWO_WAY;
        case "move-right": return JS_LEFT_TO_RIGHT;
        case "move-left": return JS_RIGHT_TO_LEFT;
        default: return JS_UNDEFINDED;
    }
}
function GetDoneStatus(oJob, sGoodStatusID) {
    if (!oJob)
        return { text: "", class: "" };
    switch (oJob.job_result) {
        case RUN_result_null: return { text: GetLocaleText("JobStatus_Idle"), class: "" };
        case RUN_result_ok: {
            if (oJob.sAttentionMsgA)
                return { text: GetLocaleText("JobStatus_Attention"), class: "attent_cell" };
            return { text: GetLocaleText(sGoodStatusID), class: (sGoodStatusID == "JobStatus_Synced") ? "synced_cell" : "analized_cell" };
        }
        case RUN_result_user_stop: return {
            text: (sGoodStatusID == "JobStatus_Synced") ? GetLocaleText("Str_Sync_Stopped_By_User") : GetLocaleText("Str_Analysis_Stopped_By_User"),
            class: "attent_cell"
        };
        case RUN_result_nonterm_errors: return oJob.sErr ?
            { text: GetLocaleText("JobStatus_ErrorsNonTerm") + ": " + oJob.sErr, class: "non_term_error_cell" } :
            { text: GetLocaleText("Err_AnalyzeFinishedWithErrors"), class: "non_term_error_cell" };
        case RUN_result_term_error: return oJob.sErr ?
            { text: GetLocaleText("JobStatus_ErrorTerm") + " " + oJob.sErr, class: "term_error_cell" } :
            { text: GetLocaleText("JobStatus_ErrorTerm"), class: "term_error_cell" };
        default: return { text: "Unknown", class: "" };
    }
}
function GetJobStatus(oJob) {
    if (!oJob || !oJob.job_run_state)
        return { text: "", class: "" };
    switch (oJob.job_run_state) {
        case "not_analyzed": return { text: GetLocaleText("JobStatus_Idle"), class: "" };
        case "waiting_for_locks": return { text: GetLocaleText("JobStatus_waiting_for_locks"), class: "" };
        case "scheduled_to_run": return { text: GetLocaleText("JobStatus_scheduled_to_run"), class: "" };
        case "analyze_running": return { text: GetLocaleText("JobStatus_AnalyzingWoPercent") + ((oJob.percent_complete !== undefined) ? " " +
                oJob.percent_complete + "%" : ""), class: "analizing_cell" };
        case "analyze_paused": return { text: GetLocaleText("JobStatus_Paused"), class: "analizing_cell" };
        case "analyze_done": {
            const oDoneStatus = GetDoneStatus(oJob, "JobStatus_Analyzed");
            return { text: oDoneStatus.text, class: oDoneStatus.class };
        }
        case "sync_done": {
            const oDoneStatus = GetDoneStatus(oJob, "JobStatus_Synced");
            return { text: oDoneStatus.text, class: oDoneStatus.class };
        }
        case "sync_running": return { text: GetLocaleText("JobStatus_Syncing") + ((oJob.percent_complete !== undefined) ? " " +
                oJob.percent_complete + "%" : ""), class: "syncing_cell" };
        case "sync_paused": return { text: GetLocaleText("JobStatus_Paused"), class: "syncing_cell" };
        case "spop_running": return { text: GetLocaleText("JobStatus_SpOpRunning"), class: "spoping_cell" };
        case "spop_paused": return { text: GetLocaleText("JobStatus_Paused"), class: "spoping_cell" };
        case "spop_done": return { text: GetLocaleText("JobStatus_SpOpDone"), class: "spoping_cell" };
        case "not_connected": return { text: "not connected to runner", class: "not_conn_cell" };
        default: return { text: "unknown", class: "" };
    }
}
function GetJobIcon(oJob) {
    if (!oJob)
        return "";
    if (oJob.aTotals && oJob.aTotals[3]) {
        return "job_error";
    }
    if (oJob.aTotals && oJob.aTotals[2]) {
        return "job_conflicts";
    }
    switch (oJob.oJobStatus.job_run_state) {
        case "not_analyzed": return (oJob.direct == "arrow-left-right") ? "job-sync-init16" : "job-backup-init16";
        case "waiting_for_locks":
        case "scheduled_to_run": return "clock";
        case "analyze_running": return "analyze";
        case "analyze_paused": return "JobPaused";
        case "analyze_done": {
            switch (oJob.oJobStatus.job_result) {
                case RUN_result_ok: {
                    if (oJob.oJobStatus.nItemsConflict > 0)
                        return "job_conflicts";
                    if (oJob.oJobStatus.nItemsChanged > 0)
                        return "changes16";
                    return "job_analyzed_no_changes";
                }
                case RUN_result_nonterm_errors: {
                    return "job_conflicts";
                }
                case RUN_result_user_stop: return "stop16";
                default: return "job_error";
            }
        }
        case "sync_running": return (oJob.direct == "arrow-left-right") ? "syncing16" : "job-backup-init16";
        case "sync_paused": return "JobPaused";
        case "sync_done": {
            switch (oJob.oJobStatus.job_result) {
                case RUN_result_ok: return "synced16";
                case RUN_result_nonterm_errors: return "job_conflicts";
                case RUN_result_user_stop: return "stop16";
                default: return "job_error";
            }
        }
        case "spop_running": return "spop-running16";
        case "spop_paused": return "JobPaused";
        case "spop_done": return "spop-done16";
        case "not_connected": return "job_error";
        default: return "job_error";
    }
}
function GetJobAutoDescription(oJob) {
    if (!oJob)
        return "";
    let sAutoDescription = "";
    if (oJob.atOnFileChange) {
        sAutoDescription += GetLocaleText("Name_DoOnFileChange");
        if (oJob.nOnFileChangeDelay) {
            sAutoDescription += " +" + oJob.nOnFileChangeDelay;
        }
    }
    if (oJob.atOnFolderConnect) {
        if (sAutoDescription != "") {
            sAutoDescription += ", ";
        }
        sAutoDescription += (sAutoDescription ? ", " : "") + GetLocaleText("Name_DoOnFoldersConnect");
    }
    if (oJob.atOnStart) {
        sAutoDescription += sAutoDescription ? ", " : "";
        sAutoDescription += GetLocaleText("Name_DoOnStart");
        if (oJob.nAutoStartDelay) {
            sAutoDescription += " +" + oJob.nAutoStartDelay;
        }
    }
    if (oJob.atOnTimer) {
        sAutoDescription += (sAutoDescription ? ", " : "") + GetLocaleText("Name_DoPeriodically") + " " + oJob.iEveryHour + ":" + oJob.iEveryMinute;
    }
    if (oJob.atOnSchedule && oJob.schedule) {
        sAutoDescription += (sAutoDescription ? ", " : "") + GetLocaleText("Str_DoOnSchedule") + ": " + ShedItemPrint(oJob.schedule);
    }
    if (oJob.atOnLogout) {
        sAutoDescription += (sAutoDescription ? ", " : "") + GetLocaleText("Name_DoBeforeLogout");
    }
    if (oJob.sPreAnalyzeProgram) {
        sAutoDescription += (sAutoDescription ? ", " : "") + "PreAnalyze";
    }
    if (oJob.sPostAnalyzeEmailOrProgram) {
        sAutoDescription += (sAutoDescription ? ", " : "") + "PostAnalyze";
    }
    if (oJob.sPostSyncEmailOrProgram) {
        sAutoDescription += (sAutoDescription ? ", " : "") + "PostSync";
    }
    if (sAutoDescription) {
        if (oJob.bAutoUnattended) {
            sAutoDescription = "UnAtt: " + sAutoDescription;
        }
    }
    return sAutoDescription;
}
function ShedItemPrint(obj) {
    if (!obj)
        return "";
    let s = "";
    if (obj.rMinute && obj.rMinute.RangeList.length != 0)
        s += "Min=" + ShedItemInnerPrint(obj.rMinute.RangeList) + " ";
    if (obj.rHour && obj.rHour.RangeList.length != 0)
        s += "Hour=" + ShedItemInnerPrint(obj.rHour.RangeList) + " ";
    if (obj.rDayOfWeek && obj.rDayOfWeek.RangeList.length != 0)
        s += "DayWeek=" + ShedItemInnerPrint(obj.rDayOfWeek.RangeList) + " ";
    if (obj.rDayOfMonth && obj.rDayOfMonth.RangeList.length != 0)
        s += "DayMon=" + ShedItemInnerPrint(obj.rDayOfMonth.RangeList) + " ";
    if (obj.rMonth && obj.rMonth.RangeList.length != 0)
        s += "Month=" + ShedItemInnerPrint(obj.rMonth.RangeList) + " ";
    return s;
}
function ShedItemInnerPrint(aScheduleBaseElArr) {
    if (!aScheduleBaseElArr || !aScheduleBaseElArr.length)
        return "";
    let sRet = "";
    for (const oShedEl of aScheduleBaseElArr) {
        if (sRet != "") {
            sRet += ", ";
        }
        if (oShedEl.step >= 2) {
            sRet += oShedEl.from + "-" + oShedEl.to + "/" + oShedEl.step;
        }
        else if (oShedEl.step == 1) {
            if (oShedEl.from == oShedEl.to)
                sRet += oShedEl.from + "";
            else
                sRet += oShedEl.from + "-" + oShedEl.to;
        }
    }
    return sRet;
}
function IS_CENTER(act) { return act & _EA_CENTER; }
function IS_LEFT(act) { return act & _EA_LEFT; }
function IS_RIGHT(act) { return act & _EA_RIGHT; }
function GetEffectiveAction(pPair) {
    if (pPair.user_action == EA_USESYSTEM) {
        return pPair.sys_action;
    }
    else {
        return pPair.user_action;
    }
}
function IsAnyFolder(pPair) {
    return pPair.lcur.is_folder || pPair.rcur.is_folder;
}
function IsErrorState(pPair) {
    return pPair.state == ESS_SYNCED_ERR || pPair.state == ESS_ANALYZED_ERR;
}
function DoesExist(part) {
    return part.filled && (part.is_folder || part.is_link || part.is_file);
}
function GetIconIdx(pPair, o) {
    o["l"] = o["c"] = o["r"] = "-";
    if (!pPair)
        return false;
    if (pPair.bExcluded) {
        o.c = "C_Ena_Nop_Fill";
        return true;
    }
    let eff = GetEffectiveAction(pPair);
    let bIsFolder = IsAnyFolder(pPair);
    let bIsError = IsErrorState(pPair);
    if (pPair.state == ESS_SYNCED_OK || pPair.sys_action == EA_NO_COPY) {
        if (bIsFolder) {
            o.c = "C_Ena_Cir";
            o.l = "L_Dis_Dbl_Nofl";
            o.r = "R_Dis_Dbl_Nofl";
        }
        else {
            if (eff == EA_CONFLICT) {
                o.c = "C_Ena_Que_Fil";
            }
            else {
                o.c = "C_Ena_Fill";
            }
        }
        return true;
    }
    let bCenter = IS_CENTER(eff) != 0;
    let bLeft = IS_LEFT(eff) != 0;
    let bRight = IS_RIGHT(eff) != 0;
    let bDefCenter = IS_CENTER(pPair.sys_action) != 0;
    let bDefLeft = IS_LEFT(pPair.sys_action) != 0;
    let bDefRight = IS_RIGHT(pPair.sys_action) != 0;
    if (pPair.state == ESS_SYNCING) {
        o.c = "C_Syncing";
    }
    else if (eff == EA_CONFLICT) {
        o.c = "C_Ena_Que_Fil";
    }
    else if (pPair.state == ESS_SYNCED_ERR || pPair.state == ESS_ANALYZED_ERR) {
        o.c = "C_Ena_Att_Fill";
    }
    else if (pPair.user_action == EA_NO_COPY &&
        (pPair.sys_action == EA_COPY_FILE_RTOL ||
            pPair.sys_action == EA_COPY_FILE_LTOR ||
            pPair.sys_action == EA_CONFLICT ||
            pPair.sys_action == EA_ERROR)) {
        o.c = "C_Ena_Cir";
    }
    else {
        if (bDefCenter) {
            o.c = bCenter ? "C_Ena_Cir_Fill" : "C_Dis_Cir_Fil";
        }
        else {
            o.c = bCenter ? "C_Ena_Cir" : "C_Dis_Cir";
        }
    }
    if (bLeft) {
        if (bIsError) {
            o.l = bIsFolder ? "L_Err_Dbl_Fill" : "L_Err_Arr_Fill";
        }
        else {
            if (eff == EA_COPY_NAME_CASE_RTOL) {
                o.l = "L_Ena_Arr_Aa_Nofl";
            }
            else if (eff == EA_COPY_TIME_ATTR_RTOL) {
                o.l = "L_Ena_Ts_Nofl";
            }
            else {
                let bDel = DoesExist(pPair.lcur) && !DoesExist(pPair.rcur);
                if (bIsFolder) {
                    o.l = bDel ? "L_Del_Dbl_Nofl" : "L_Ena_Dbl_Nofl";
                }
                else {
                    if (bDefLeft) {
                        o.l = bDel ? "L_Del_Arr_Fil" : "L_Ena_Arr_Fil";
                    }
                    else {
                        o.l = bDel ? "L_Del_Arr_Nofl" : "L_Ena_Arr_Fil";
                    }
                }
            }
        }
    }
    else {
        if (bIsFolder) {
            o.l = "L_Dis_Dbl_Nofl";
        }
        else {
            o.l = bDefLeft ? "L_Dis_Arr_Fill" : "L_Dis_Arr_Nofl";
        }
    }
    if (bRight) {
        if (bIsError) {
            o.r = bIsFolder ? "R_Err_Dbl_Fill" : "R_Err_Arr_Fill";
        }
        else {
            if (eff == EA_COPY_NAME_CASE_LTOR) {
                o.r = "R_Ena_Arr_Aa_Nofl";
            }
            else if (eff == EA_COPY_TIME_ATTR_LTOR) {
                o.r = "R_Ena_Ts_Nofl";
            }
            else {
                let bDel = !DoesExist(pPair.lcur) && DoesExist(pPair.rcur);
                if (bIsFolder) {
                    o.r = bDel ? "R_Del_Dbl_Nofl" : "R_Ena_Dbl_Nofl";
                }
                else {
                    if (bDefRight) {
                        o.r = bDel ? "R_Del_Arr_Fil" : "R_Ena_Arr_Fill";
                    }
                    else {
                        o.r = bDel ? "R_Del_Arr_Nofl" : "R_Ena_Arr_Fill";
                    }
                }
            }
        }
    }
    else {
        if (bIsFolder) {
            o.r = "R_Dis_Dbl_Nofl";
        }
        else {
            o.r = bDefRight ? "R_Dis_Arr_Fill" : "R_Dis_Arr_Nofl";
        }
    }
    return true;
}
function GetJobStatusForTree(pair) {
    let sStatus = "";
    if (pair.sys_action == EA_COPY_NAME_CASE_LTOR) {
        sStatus = "* Change Name Case " + pair.rold.fs_name + " -> " + pair.lcur.fs_name;
    }
    else if (pair.sys_action == EA_COPY_NAME_CASE_RTOL) {
        sStatus = "* Change Name Case " + pair.lold.fs_name + " -> " + pair.rcur.fs_name;
    }
    if (pair.error != "") {
        sStatus += ((sStatus == "") ? "" : " ") + pair.error;
    }
    return sStatus;
}
class CFillDateItem {
    constructor() {
        this.text = "";
    }
}
function FillLRDateItem(side) {
    let ret = new CFillDateItem;
    if (DoesExist(side)) {
        if (side.is_folder) {
            ret["text"] = "(" + GetLocaleText("IDS_Str_Folder") + ")";
        }
        else {
            ret["text"] = side.mod_time + "";
        }
    }
    else {
        ret["text"] = "(" + GetLocaleText("IDS_not_present") + ")";
        ret["not_exists"] = true;
    }
    return ret;
}
function IsJobTreeFolder(obj) {
    return obj.data.lcur.is_folder || obj.data.rcur.is_folder || obj.data.lold.is_folder || obj.data.rold.is_folder || !!obj.stat;
}
function GetItemType(obj) {
    if (obj.job_node)
        return "job";
    if (IsJobTreeFolder(obj))
        return "folder";
    return GetFileExtension(obj.name);
}
function GetFileExtension(sItemName) {
    let nDotPos = sItemName.lastIndexOf(".");
    if (nDotPos < 0)
        return ".*";
    else
        return sItemName.slice(nDotPos).toUpperCase();
}
let iColorInd = 0;
function GetIconForGroup(nGroupNum, sGroupName) {
    switch (nGroupNum) {
        case GROUP_TYPE_SYNC: return "job_syncing_red";
        case GROUP_TYPE_BACK: return "job_in_backup_blue";
        default: {
            const nCrc32 = Crc32(sGroupName);
            let r = nCrc32 % 256;
            let g = (nCrc32 >>> 8) % 256;
            let b = (nCrc32 >>> 16) % 256;
            if (r > 250 && g > 250 && b > 250) {
                r -= 40;
                g -= 40;
                b -= 40;
            }
            const color1 = "#" + r.toString(16).padStart(2, "0") + g.toString(16).padStart(2, "0") + b.toString(16).padStart(2, "0");
            return GenerateCircle(color1, 18);
        }
    }
}
function GenerateCircle(color1, h, color2) {
    let stroke = "";
    if (color2) {
        stroke = ` stroke="${color2}" stroke-width="2"`;
    }
    return `<svg width="${h}" height="${h}" viewBox="0 0 ${h} ${h}" fill="none" xmlns="http://www.w3.org/2000/svg">
				<circle cx="8" cy="8" r="8" fill="${color1}"${stroke}/>
			/svg>`;
}
function GetFileTypeSvg(sExtension) {
    let sFileType = jsFileExtensionMap[sExtension];
    if (!sFileType)
        sFileType = "*";
    else if (sFileType == "video") {
        let ret = "<rect style=\"fill:#660066;stroke:none;\" width=\"16\" height=\"14\" x=\"0\"	y=\"1\"/>" +
            "<path style=\"fill:#ffffff;stroke:none;\" d=\"M 5.5,5 V 11 L 11,8 Z\"/>";
        for (let i = 0; i < 4; i++) {
            ret += "<rect style=\"fill:#ffffff;stroke:none;\" width=\"2.5\" height=\"1.75\" x=\"" + (1.2 + i * 3.7) + "\" y=\"2\"/>";
        }
        for (let i = 0; i < 4; i++) {
            ret += "<rect style=\"fill:#ffffff;stroke:none;\" width=\"2.5\" height=\"1.75\" x=\"" + (1.2 + i * 3.7) + "\" y=\"12.25\"/>";
        }
        return ret;
    }
    else if (sFileType == "audio") {
        return "<circle style=\"fill:#990000;\" cx=\"8\" cy=\"8\" r=\"8\"/>" +
            "<rect style=\"fill:#ffffff;\" width=\"1.4\" height=\"9\" x=\"7.3\" y=\"3\"/>" +
            "<ellipse style=\"fill:#ffffff;\" cx=\"6.1\" cy=\"12\" rx=\"2.6\" ry=\"2\"/>" +
            "<path style=\"fill:#ffffff;\" d=\"M 7.3,2.5 12,4 V 6.5 L 7.3,5 Z\"/>";
    }
    else if (sFileType == "img") {
        return "<circle	style=\"fill:#ff0000;\" cx=\"11.7\" cy=\"8\" r=\"4\" />" +
            "<rect style=\"fill:#0800ff;\" width=\"8\" height=\"8\" x=\"0.5\" y=\"1.3\" ry=\"0\"/>" +
            "<path style=\"fill:#00a90b;\" d=\"M 1.8,15.9 H 10.8 L 6.3,8 Z\"/>";
    }
    let sFileColor = jsFileTypeColors[sFileType];
    let len = sExtension.length;
    let text = "<text style=\"font-size:8px; font-family: 'Arial Black', Helvetica, sans-serif; fill:" + sFileColor + ";\" y=\"13\" ";
    let rect = "<rect x=\"0\" y = \"5\" width=\"16\" height=\"8.5\" fill=\"#fff\"/>";
    if (sExtension == "*") {
        sExtension = "";
        text = "";
        rect = "";
    }
    else if (len == 1) {
        text += "x=\"5\">" + sExtension + "</text>";
        rect = "";
    }
    else if (len == 2) {
        text += "x=\"2\">" + sExtension + "</text>";
    }
    else if (len == 3) {
        text += "x=\"0\" transform=\"scale(0.8,1.0)\">" + sExtension + "</text>";
    }
    else {
        text += "x=\"0\" transform=\"scale(0.8,1.0)\">" + sExtension.slice(0, 3) + "</text>";
    }
    return "<path fill=\"#fff\" style=\"stroke-width:1; stroke:" + sFileColor + ";\" d=\"m 3,0.5 0.0,15 H 13 L 13,3.7 9.8,0.5 Z\"/>" +
        "<path fill=\"none\" style=\"stroke-width:1;stroke:" + sFileColor + ";\" d=\"m 9.5,0 0,4 4,0 v 0\"/>" + rect + text;
}
function GetActStr(n) {
    switch (n) {
        case 17: return "NO COPY";
        case 18: return "STATE MERGE GENS";
        case 19: return "CONFLICT";
        case 20: return "ERROR";
        case 33: return "COPY RTOL";
        case 65: return "COPY LTOR";
        case 34: return "COPY NAME CASE RTOL";
        case 66: return "COPY NAME CASE LTOR";
        case 35: return "COPY TIME ATTR RTOL";
        case 67: return "COPY TIME ATTR LTOR";
        default: return "NO ACT";
    }
}
function GetSummary(conn) {
    if (!conn)
        return "";
    try {
        let s = "";
        const url = ParseUrl(conn.sUrlA);
        if (url.protocol == "gstps://") {
            if (conn.sUrlA.includes(".goodsync")) {
                if (conn.sUserEmailA) {
                    s = conn.sUserEmailA;
                }
                else {
                    s = conn.sUserIdA;
                }
            }
            else {
                s = conn.sUserIdA + "@" + url.hostname;
            }
        }
        else if (url.protocol == "onefile://") {
            s = GetFileNameOfPath(conn.sPrivateKeyPath);
        }
        else if (url.protocol == "icloud://" || url.protocol == "iphotos://") {
            s = conn.sUserIdA;
        }
        else {
            if (conn.sUserEmailA) {
                s = conn.sUserEmailA;
            }
            else if (conn.sDisplayNameA) {
                s = conn.sDisplayNameA;
            }
            else {
                s = conn.sUserIdA + "@" + url.hostname + url.port;
            }
        }
        if (!s) {
            s = "Main Account";
        }
        return s;
    }
    catch {
        return conn.sUrlA;
    }
}
const GS_FILE_ATTR_UNIX = 0x80000000;
const GS_FILE_ATTR_HIDDEN = 0x40000000;
const GS_FILE_ATTR_SYSTEM = 0x20000000;
const GS_FILE_ATTR_READONLY = 0x10000000;
const GS_FILE_ATTR_DEVICE = 0x08000000;
const GS_FILE_ATTR_FOLDER = 0x04000000;
const GS_FILE_ATTR_DISREGARD = 0x02000000;
const GS_FILE_ATTR_TEMP = 0x01000000;
const GS_FILE_ATTR_ARCHIVE = 0x00800000;
const GS_FILE_ATTR_COMPRESSED = 0x00400000;
const GS_FILE_ATTR_ENCRYPTED = 0x00200000;
const GS_FILE_ATTR_BUNDLE = 0x00100000;
const GS_FILE_ATTR_OSDEP = 0x0000FFFF;
const GS_FILE_ATTR_INDEP = 0xFFFF0000;
const GS_FILE_ATTR_ERROR = 0xFFFFFFFF;
function SibFileAttrToStrA(dwBitAttrs) {
    if (dwBitAttrs == GS_FILE_ATTR_ERROR) {
        return "-(invalid)-";
    }
    let s = "";
    if (dwBitAttrs & GS_FILE_ATTR_DISREGARD) {
        s += (s ? ", " : "") + "Disregard";
    }
    if (dwBitAttrs & GS_FILE_ATTR_TEMP) {
        s += (s ? ", " : "") + "Temp";
    }
    if (dwBitAttrs & GS_FILE_ATTR_READONLY) {
        s += (s ? ", " : "") + "ReadOnly";
    }
    if (dwBitAttrs & GS_FILE_ATTR_HIDDEN) {
        s += (s ? ", " : "") + "Hidden";
    }
    if (dwBitAttrs & GS_FILE_ATTR_SYSTEM) {
        s += (s ? ", " : "") + "System";
    }
    if (dwBitAttrs & GS_FILE_ATTR_ARCHIVE) {
        s += (s ? ", " : "") + "Archive";
    }
    return s;
}
