-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutil.js
383 lines (371 loc) · 12.1 KB
/
util.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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
'use strict';
const _ = require('underscore');
const defaultFilters = require('./filters').filters;
const defaultStyles = require('./styles').styles;
const FilterSort = require('./filters').FilterSort;
const GrawlixFilter = require('./filters').GrawlixFilter;
const toGrawlixFilter = require('./filters').toGrawlixFilter;
const GrawlixStyle = require('./styles').GrawlixStyle;
const toGrawlixStyle = require('./styles').toGrawlixStyle;
const GrawlixStyleError = require('./styles').GrawlixStyleError;
const GrawlixPlugin = require('./plugin').GrawlixPlugin;
const GrawlixPluginError = require('./plugin').GrawlixPluginError;
/**
* Parse grawlix options
* @param {Object} options Options object. See grawlix.js#grawlix for
* details.
* @param {Object} defaults Default options. Optional.
* @return {GrawlixSettings}
*/
exports.parseOptions = function(options, defaults) {
if (!_.isUndefined(defaults)) {
_.defaults(options, defaults);
}
// get settings
var settings = new GrawlixSettings();
settings.isRandom = options.randomize;
// load default filters
_.each(defaultFilters, function(filter) {
// check to see if word is on whitelist
var isAllowed = _.contains(options.allowed, filter.word);
// check to see if options has a replacement filter
var isReplaced = _.some(options.filters, function(optFilter) {
return (
_.has(optFilter, 'word') &&
optFilter.word === filter.word &&
_.has(optFilter, 'pattern')
);
});
if (!isAllowed && !isReplaced) {
settings.filters.push( filter.clone() );
}
});
// load default styles
_.each(defaultStyles, function(style) {
settings.styles.push( style.clone() );
});
// load plugins (if we have any)
if (options.plugins.length > 0) {
_.each(options.plugins, function(pluginInfo) {
loadPlugin(settings, pluginInfo, options);
});
}
// add option filters (if any) and/or configure filter options
loadFilters(settings, options.filters, options.allowed);
// sort filters
settings.filters.sort(FilterSort);
// add option styles / configure style options
loadStyles(settings, options.styles);
// get main style
if (!_.has(options, 'style') || options.style === null) {
throw new GrawlixStyleError({
msg: 'main style not found',
style: options.style,
trace: new Error()
});
}
// try to find style
var style;
if (_.has(options.style, 'name')) {
// if options.style is a style object
style = _.findWhere(settings.styles, { name: options.style.name });
if (!_.isUndefined(style)) {
// if style is found, configure style with object
style.configure(options.style);
} else {
// if style not found, try to create a new style with object
style = toGrawlixStyle(options.style);
}
} else {
// try to treat options.style as string
style = _.findWhere(settings.styles, { name: options.style });
}
if (style instanceof GrawlixStyle) {
settings.style = style;
} else {
throw new GrawlixStyleError({
msg: 'main style not found',
styleName: options.style,
style: options.style,
trace: new Error()
});
}
// return settings
return settings;
};
/**
* GrawlixSettings class
* Class for settings object returned by parseOptions
*/
var GrawlixSettings = function() {
this.isRandom = true;
this.filters = [];
this.style = null;
this.styles = [];
this.loadedPlugins = [];
this.loadedModules = [];
};
GrawlixSettings.prototype = {};
exports.GrawlixSettings = GrawlixSettings;
/**
* Loads the given plugin into the given GrawlixSettings object
* @param {GrawlixSettings} settings GrawlixSettings object
* @param {Object|Function} pluginInfo Either a factory function, a
* GrawlixPlugin object, or an object
* wrapping a factory function or
* GrawlixPlugin with additional
* plugin-specific config options
* @param {Object} options Main grawlix options object
* @return {GrawlixSettings} Settings object with plugin loaded
*/
var loadPlugin = function(settings, pluginInfo, options) {
// resolve plugin and plugin options
var plugin;
if (_.has(pluginInfo, 'plugin')) {
plugin = pluginInfo.plugin;
} else if (_.has(pluginInfo, 'module')) {
// make sure we don't load the same module twice
if (_.contains(settings.loadedModules, pluginInfo.module)) {
return settings;
}
// try to load module
try {
plugin = require(pluginInfo.module);
} catch (err) {
throw new GrawlixPluginError({
msg: "cannot find module '" + pluginInfo.module + "'",
plugin: pluginInfo,
trace: new Error()
});
}
settings.loadedModules.push(pluginInfo.module);
} else {
plugin = pluginInfo;
}
var pluginOpts = _.has(pluginInfo, 'options') ? pluginInfo.options : {};
// instantiate plugin if necessary
if (_.isFunction(plugin)) {
plugin = plugin(pluginOpts, options);
}
// validate plugin
if (!(plugin instanceof GrawlixPlugin)) {
throw new GrawlixPluginError({
msg: 'invalid plugin',
plugin: pluginInfo
});
} else if (plugin.name === null || _.isEmpty(plugin.name)) {
throw new GrawlixPluginError({
msg: 'invalid plugin - name property not provided',
plugin: pluginInfo
});
} else if (_.contains(settings.loadedPlugins, plugin.name)) {
// don't load the same plugin twice
return settings;
}
// initialize plugin
plugin.init(pluginOpts);
// load filters
if (!_.isUndefined(plugin.filters) && _.isArray(plugin.filters)) {
try {
loadFilters(settings, plugin.filters, options.allowed);
} catch (err) {
err.plugin = pluginInfo;
throw err;
}
}
// load styles
if (!_.isUndefined(plugin.styles) && _.isArray(plugin.styles)) {
try {
loadStyles(settings, plugin.styles);
} catch (err) {
err.plugin = pluginInfo;
throw err;
}
}
// add name to loaded plugins
settings.loadedPlugins.push(plugin.name);
// return
return settings;
};
exports.loadPlugin = loadPlugin;
/**
* Loads an array of filter objects into the GrawlixSettings object
* @param {GrawlixSettings} settings GrawlixSettings object
* @param {Array} filters Array of filter objects
* @param {Array} allowed Whitelist of words to ignore
* @return {GrawlixSettings} Settings objects with filters added
*/
var loadFilters = function(settings, filters, allowed) {
if (filters.length > 0) {
_.each(filters, function(obj) {
if (!_.has(obj, 'word')) {
return;
}
if (!_.has(obj, 'pattern')) {
// configure existing filter options
var filter = _.findWhere(settings.filters, { word: obj.word });
if (!_.isUndefined(filter)) {
filter.configure(obj);
}
} else if (!_.contains(allowed, obj.word)) {
// if filter word isn't whitelisted, add as new GrawlixFilter
settings.filters.push( toGrawlixFilter(obj) );
}
});
}
return settings;
};
exports.loadFilters = loadFilters;
/**
* Loads an array of style objects into the given GrawlixSettings instance
* @param {GrawlixSettings} settings GrawlixSettings object
* @param {Array} styles Array of style objects
* @return {GrawlixSettings}
*/
var loadStyles = function(settings, styles) {
if (_.isArray(styles) && styles.length > 0) {
_.each(styles, function(obj) {
if (!_.has(obj, 'name')) {
return;
}
var style = _.findWhere(settings.styles, { name: obj.name });
if (!_.isUndefined(style)) {
style.configure(obj);
} else {
settings.styles.push( toGrawlixStyle(obj) );
}
});
}
return settings;
};
exports.loadStyles = loadStyles;
/**
* Returns whether or not the given plugin has been added to the given options
* object.
* @param {String|GrawlixPlugin|Function} plugin Plugin name, GrawlixPlugin
* object, or factory function.
* @param {Object} options Options object.
* @return {Boolean}
*/
exports.hasPlugin = function(plugin, options) {
if (_.has(options, 'plugins') && _.isArray(options.plugins)) {
// search for matching GrawlixPlugin
if (plugin instanceof GrawlixPlugin) {
return _.some(options.plugins, function(obj) {
return (
_.has(obj, 'plugin') &&
obj.plugin instanceof GrawlixPlugin &&
(obj.plugin === plugin || obj.plugin.name === plugin.name)
);
});
}
// search for matching factory function
if (_.isFunction(plugin)) {
return _.some(options.plugins, function(obj) {
return (_.has(obj, 'plugin') && obj.plugin === plugin);
});
}
// search by module and by GrawlixPlugin name
if (_.isString(plugin)) {
return _.some(options.plugins, function(obj) {
return (
(_.has(obj, 'module') && obj.module === plugin) ||
(
_.has(obj, 'plugin') &&
obj.plugin instanceof GrawlixPlugin &&
obj.plugin.name === plugin
)
);
});
}
}
// or, if all else fails...
return false;
};
/**
* Returns whether or not any of the given filters match the given string.
* @param {String} str Content string
* @param {GrawlixSettings} settings GrawlixSettings object
* @return {Boolean} Whether or not obscenity is found in the
* given string
*/
exports.isMatch = function(str, settings) {
if (settings.filters.length === 0) {
return false;
}
return _.some(settings.filters, function(filter) {
return filter.isMatch(str);
});
};
/**
* Replaces obscenities in the given string using the given settings.
* @param {String} str String to process
* @param {GrawlixSettings} settings Grawlix settings
* @return {String} Processed string
*/
exports.replaceMatches = function(str, settings) {
_.each(settings.filters, function(filter) {
while (filter.isMatch(str)) {
str = replaceMatch(str, filter, settings);
}
});
return str;
};
/**
* Replaces a filter match in a string
* @param {String} str Content string
* @param {GrawlixFilter} filter GrawlixFilter object
* @param {GrawlixSettings} settings GrawlixSettings object
* @return {String} String with replacement applied
*/
var replaceMatch = function(str, filter, settings) {
// get filter style if provided
var style;
if (filter.hasStyle() && settings.style.isOverrideAllowed) {
style = _.findWhere(settings.styles, { name: filter.style });
}
if (_.isUndefined(style)) {
// if filter style not found, or no filter style set, use main style
style = settings.style;
}
// get replacement grawlix
var repl;
if (!settings.isRandom && style.hasFixed(filter.word)) {
// if in fixed replacement mode and style has a defined fixed replacement
// string for the filter's word
repl = style.getFixed(filter.word);
} else {
// if single-character style
repl = generateGrawlix(str, filter, style);
}
// apply filter template if necessary
if (filter.hasTemplate()) {
repl = filter.template(repl);
}
// replace the match
return str.replace(filter.regex, repl);
};
exports.replaceMatch = replaceMatch;
/**
* Replaces matched content with a grawlix, taking into account filter and style
* settings.
* @param {String} str Content string
* @param {GrawlixFilter} filter Filter object
* @param {GrawlixStyle} style Style object
* @return {String} Grawlix replacement string
*/
var generateGrawlix = function(str, filter, style) {
// determine length
var len;
if (filter.isExpandable) {
len = filter.getMatchLen(str);
} else {
len = filter.word.length;
}
// generate grawlix
if (!style.canRandomize()) {
return style.getFillGrawlix(len);
}
return style.getRandomGrawlix(len);
};
exports.generateGrawlix = generateGrawlix;