summaryrefslogtreecommitdiffstats
path: root/lib/strnatcasecmp.js
blob: 8a580d66f313da0b52f8d21409a55752446f54a8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// Create global namespace
if(typeof(phpjs) === 'undefined')
  phpjs = {};
  
phpjs.strnatcasecmp = function(str1, str2) {
    // http://jsphp.co/jsphp/fn/view/strnatcasecmp
    // +      original by: Martin Pool
    // + reimplemented by: Pierre-Luc Paour
    // + reimplemented by: Kristof Coomans (SCK-CEN (Belgian Nucleair Research Centre))
    // + reimplemented by: Brett Zamir (http://brett-zamir.me)
    // +      bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +         input by: Devan Penner-Woelk
    // +      improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // *        example 1: strnatcasecmp(10, 1);
    // *        returns 1: 1
    // *        example 1: strnatcasecmp('1', '10');
    // *        returns 1: -1
    var a = (str1 + '').toLowerCase();
    var b = (str2 + '').toLowerCase();

    var isWhitespaceChar = function (a) {
        return a.charCodeAt(0) <= 32;
    };

    var isDigitChar = function (a) {
        var charCode = a.charCodeAt(0);
        return (charCode >= 48 && charCode <= 57);
    };

    var compareRight = function (a, b) {
        var bias = 0;
        var ia = 0;
        var ib = 0;

        var ca;
        var cb;

        // The longest run of digits wins.  That aside, the greatest
        // value wins, but we can't know that it will until we've scanned
        // both numbers to know that they have the same magnitude, so we
        // remember it in BIAS.
        for (var cnt = 0; true; ia++, ib++) {
            ca = a.charAt(ia);
            cb = b.charAt(ib);

            if (!isDigitChar(ca) && !isDigitChar(cb)) {
                return bias;
            } else if (!isDigitChar(ca)) {
                return -1;
            } else if (!isDigitChar(cb)) {
                return 1;
            } else if (ca < cb) {
                if (bias === 0) {
                    bias = -1;
                }
            } else if (ca > cb) {
                if (bias === 0) {
                    bias = 1;
                }
            } else if (ca === '0' && cb === '0') {
                return bias;
            }
        }
    };

    var ia = 0,
        ib = 0;
    var nza = 0,
        nzb = 0;
    var ca, cb;
    var result;

    while (true) {
        // only count the number of zeroes leading the last number compared
        nza = nzb = 0;

        ca = a.charAt(ia);
        cb = b.charAt(ib);

        // skip over leading spaces or zeros
        while (isWhitespaceChar(ca) || ca === '0') {
            if (ca === '0') {
                nza++;
            } else {
                // only count consecutive zeroes
                nza = 0;
            }

            ca = a.charAt(++ia);
        }

        while (isWhitespaceChar(cb) || cb === '0') {
            if (cb === '0') {
                nzb++;
            } else {
                // only count consecutive zeroes
                nzb = 0;
            }

            cb = b.charAt(++ib);
        }

        // process run of digits
        if (isDigitChar(ca) && isDigitChar(cb)) {
            if ((result = compareRight(a.substring(ia), b.substring(ib))) !== 0) {
                return result;
            }
        }

        if (ca === '0' && cb === '0') {
            // The strings compare the same.  Perhaps the caller
            // will want to call strcmp to break the tie.
            return nza - nzb;
        }

        if (ca < cb) {
            return -1;
        } else if (ca > cb) {
            return +1;
        }

        ++ia;
        ++ib;
    }
}