Source: utils/string.js

/**
 * @namespace qui.utils.string
 */

import * as ObjectUtils from '$qui/utils/object.js'


const REGEX_ESCAPE_CHARS = '.\\+*?[^]$(){}=!<>|:-'


/**
 * Replace all occurrences of `oldStr` in `s` with `newStr`.
 * @alias qui.utils.string.replaceAll
 * @param {String} s
 * @param {String} oldStr
 * @param {String} newStr
 * @returns {String}
 */
export function replaceAll(s, oldStr, newStr) {
    let p
    while ((p = s.indexOf(oldStr)) >= 0) {
        s = s.substring(0, p) + newStr + s.substring(p + oldStr.length, s.length)
    }

    return s.toString()
}

/**
 * Replace percent-formatted placeholders in a string with given values.
 *
 * Placeholders can be given as `"%s"`, `"%d"` or `"%f"`, in which case `args` will be used, in order, to replace
 * them.
 *
 * Placeholders can also be given as `"%(name)s"`, `"%(name)d"` or `"%(name)f"`, in which case the first argument in
 * `args` will be used as an object that maps names to replacement values.
 *
 * @alias qui.utils.string.formatPercent
 * @param {String} text
 * @param {...*} args values used to replace the placeholders
 * @returns {String}
 */
export function formatPercent(text, ...args) {
    let rex = new RegExp('%[sdf]')
    let match, i = 0
    while ((match = text.match(rex))) {
        text = text.substring(0, match.index) + args[i] + text.substring(match.index + 2)
        i++
    }

    if (i) { /* %s format used */
        text = text.replace(new RegExp('%%', 'g'), '%')

        return text
    }

    let keywords = args[0] || {}

    ObjectUtils.forEach(keywords, function (key, value) {
        text = text.replace(new RegExp(`%\\(${key}\\)s`, 'g'), value.toString())
        text = text.replace(new RegExp(`%\\(${key}\\)d`, 'g'), value.toString())
        text = text.replace(new RegExp(`%\\(${key}\\)f`, 'g'), value.toString())
    })

    text = text.replace('%%', '%')

    return text
}

/**
 * Transform a regular text to its camel-case representation.
 * @alias qui.utils.string.camelize
 * @param {String} s
 * @returns {String}
 */
export function camelize(s) {
    s = s.replace(/(?:^|[-_])(\w)/g, function (_, c) {
        return c ? c.toUpperCase() : ''
    })

    return s.substr(0, 1).toLowerCase() + s.substr(1)
}

/**
 * Transform a camel-case string into its dash-separated representation.
 * @alias qui.utils.string.uncamelize
 * @param {String} s
 * @param {String} [chr] separator character (defaults to dash `"-"`)
 * @returns {String}
 */
export function uncamelize(s, chr = '-') {
    return s.replace(/([A-Z])/g, function ($1) {
        return chr + $1.toLowerCase()
    }).replace(new RegExp(`^${chr}`), '')
}

/**
 * Transform the starting letter of each word of a text to uppercase.
 * @alias qui.utils.string.title
 * @param {String} s
 * @returns {String}
 */
export function title(s) {
    s = s.replace(/\s(\w)/g, function (_, c) {
        return c ? ` ${c.toUpperCase()}` : ' '
    })

    return s.substr(0, 1).toUpperCase() + s.substr(1)
}

/**
 * Encode a string to UTF8.
 * @alias qui.utils.string.toUTF8
 * @param {String} s
 * @returns {String}
 */
export function toUTF8(s) {
    s = s.replace(new RegExp('\\r\\n'), '\n')
    let utf = ''

    for (let n = 0; n < s.length; n++) {
        let c = s.charCodeAt(n)

        if (c < 128) {
            utf += String.fromCharCode(c)
        }
        else if ((c > 127) && (c < 2048)) {
            utf += String.fromCharCode((c >> 6) | 192)
            utf += String.fromCharCode((c & 63) | 128)
        }
        else {
            utf += String.fromCharCode((c >> 12) | 224)
            utf += String.fromCharCode(((c >> 6) & 63) | 128)
            utf += String.fromCharCode((c & 63) | 128)
        }
    }

    return utf
}

/**
 * Decode a string from UTF8.
 * @alias qui.utils.string.fromUTF8
 * @param {String} s
 * @returns {String}
 */
export function fromUTF8(s) {
    let result = ''
    let i = 0
    let c1 = 0
    let c2 = 0
    let c3 = 0

    while (i < s.length) {
        c1 = s.charCodeAt(i)

        if (c1 < 128) {
            result += String.fromCharCode(c1)
            i++
        }
        else if ((c1 > 191) && (c1 < 224)) {
            c2 = s.charCodeAt(i + 1)
            result += String.fromCharCode(((c1 & 31) << 6) | (c2 & 63))
            i += 2
        }
        else {
            c2 = s.charCodeAt(i + 1)
            c3 = s.charCodeAt(i + 2)
            result += String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63))
            i += 3
        }
    }

    return result
}

/**
 * Intelligently search for an input sequence in a string. All characters in the input sequence must be present in the
 * searched string, in the respective order.
 * @alias qui.utils.string.intelliSearch
 * @param {String} s string to search into
 * @param {String} search string to search for
 * @returns {?RegExpMatchArray}
 */
export function intelliSearch(s, search) {
    let rexStr = Array.prototype.map.call(search, function (c) {
        return `${REGEX_ESCAPE_CHARS.includes(c) ? '\\' : ''}${c}.*`
    }).join('')

    let rex = new RegExp(rexStr, 'i')

    return s.match(rex)
}