-
Notifications
You must be signed in to change notification settings - Fork 1
/
wildstring.js
177 lines (151 loc) · 6.23 KB
/
wildstring.js
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
'use strict';
/**
* @namespace wildstring
* @property {string} wildcard the wildcard to use in your strings, defaults to '*'
* @property {boolean} caseSensitive whether matches should care about case, defaults to true
*/
var wildstring = {
wildcard: '*',
caseSensitive: true,
/**
* When a match doesn't continue to the end of the string, this function rolls back to try again with the rest of the string
* @memberof wildstring
* @access private
* @param {string[]} rollbackStrings The list of substrings that appeared prior to the current match
* @param {string[]} patternSubstrings The matching list of pattens that need to be matched before the current pattern
*/
checkRollbackStrings: function (rollbackStrings, patternSubstrings) {
for (var s = 0; s < rollbackStrings.length; ++s) {
var currentString = rollbackStrings[s].string; // starting with the rolled back string
var patternIndex = rollbackStrings[s].index;
while (patternIndex < patternSubstrings.length) {
if (currentString.indexOf(patternSubstrings[patternIndex]) === -1) {
break;
}
var testString = currentString.substr(1); //remove just one char to retest
rollbackStrings.push({ string: testString, index: patternIndex });
if (testString.indexOf(patternSubstrings[patternIndex]) === -1) {
rollbackStrings.pop();
}
currentString = currentString.substr(
currentString.indexOf(patternSubstrings[patternIndex]) + patternSubstrings[patternIndex].length
);
patternIndex++;
while (patternSubstrings[patternIndex] === '') {
patternIndex++;
}
if (patternIndex >= patternSubstrings.length) {
if (patternSubstrings[patternSubstrings.length - 1] !== '' &&
currentString.length > 0) {
// not ending with a wildcard, we need to backtrack
break;
}
else {
return true;
}
}
}
}
return false;
},
/**
* Check if a string matches a pattern
* @memberof wildstring
* @param {string} pattern The pattern to match using the configured wildcard
* @param {string} string The string to test for a match
*/
match: function (pattern, string) {
if (!wildstring.caseSensitive) {
pattern = pattern.toLowerCase();
string = string.toLowerCase();
}
// if there are no wildcards, must be exact
if (pattern.indexOf(wildstring.wildcard) === -1) {
return pattern === string;
}
var patternSubstrings = pattern.split(wildstring.wildcard);
var patternIndex = 0;
var currentString = string;
// find pattern beginning
while (patternSubstrings[patternIndex] === '') {
patternIndex++;
// if the pattern is just wildcards, it matches
if (patternIndex === pattern.length) {
return true;
}
}
if (patternIndex === 0 && string.indexOf(patternSubstrings[0]) !== 0) {
// not starting with a wildcard
return false;
}
var rollbackStrings = [];
while (patternIndex < patternSubstrings.length) {
if (currentString.indexOf(patternSubstrings[patternIndex]) === -1) {
return wildstring.checkRollbackStrings(rollbackStrings, patternSubstrings);
}
// create a queue of strings to roll back and try again if we fail later
var testString = currentString.substr(1); //remove just one char to retest
rollbackStrings.push({ string: testString, index: patternIndex });
if (testString.indexOf(patternSubstrings[patternIndex]) === -1) {
rollbackStrings.pop();
}
currentString = currentString.substr(
currentString.indexOf(patternSubstrings[patternIndex]) + patternSubstrings[patternIndex].length
);
patternIndex++;
while (patternSubstrings[patternIndex] === '') {
patternIndex++;
}
}
if (patternIndex >= patternSubstrings.length &&
patternSubstrings[patternSubstrings.length - 1] !== '' &&
currentString.length > 0) {
// not ending with a wildcard, we need to backtrack
if (currentString === string) { // this string doesn't even match a little
return false;
}
return wildstring.checkRollbackStrings(rollbackStrings, patternSubstrings);
}
return true;
},
/**
* Replace wildcards in a pattern with strings (string interpolation)
* @memberof wildstring
* @param {string} pattern The start string, using wildcards as placeholders
* @param {string|string[]} strings The string or strings to replace the wildcards in the pattern.
* If you pass a single string, it will replace all the wildcards with the string.
* If you pass an array of strings, they will replace the wildcards in order from left to right.
* @throws The number of items in the strings array (if you pass an array) must match the number of wildcards in the pattern string.
* @throws You need to pass both parameters
* @throws You need to pass the right types
*/
replace: function (pattern, strings) {
if (pattern === undefined || strings === undefined) {
throw new Error('wildstring.replace takes the pattern as one parameter and either a string or an array of strings as the second. You didn\'t pass enough parameters.');
}
if (typeof(strings) === typeof('')) {
return pattern.replace(wildstring.wildcard, strings);
}
if (!Array.isArray(strings) || typeof(pattern) !== typeof('')) {
throw new Error('wildstring.replace takes the pattern as one parameter and either a string or an array of strings as the second. Your parameter types are incorrect.');
}
if (pattern.indexOf(wildstring.wildcard) === -1) {
return pattern; // if there are no wildcards, just return the pattern
}
var patternSubstrings = pattern.split(wildstring.wildcard);
if (patternSubstrings.length - 1 !== strings.length) {
var message = 'There are a different number of wildcards than strings to replace them. You have ' +
wildstring.wildcard +' wildcards in "' + wildstring.wildcard + '" and ' + wildstring.wildcard +
' replacement strings.';
throw new Error(wildstring.replace(message, [ patternSubstrings.length - 1, pattern, strings.length ]));
}
var result = '';
for (var s = 0; s < strings.length; ++s) {
result += patternSubstrings[s] + strings[s];
}
return result;
}
};
if (typeof(module) !== 'undefined') { module.exports = wildstring; }
if (typeof(angular) !== 'undefined') { angular.module('wildstring', []).factory('wildstring', function() { return wildstring; }); }
if (typeof(define) !== 'undefined') { define([], wildstring); }