PageSourceSearch

https://www.iglta.org/includes/public/managed/v_a2e6ae52_e071b31d/core/libs/sv/miscLib/public/index.js

js iglta.org collected 2026-10-01 11:27:02 UTC 9,872 bytes, 328 lines download raw bytes

1// umd boilerplate for CommonJS and AMD
2if (typeof exports === "object" && typeof define !== "function") {
3	var define = function (factory) {
4		factory(require, exports, module);
5	};
6}
7
8define(function(require, exports, module) {
9	let crypto = require("crypto");
10
11	let objectLib = require("@sv/objectLib");
12
13	let async = require("async");
14
15	// Before/After sort with parallelism
16	// data = array of strings: ["a","b","c"] 
17	// rules = array of arrays: [["a", "b"],["c","b"]] indicates that b comes after a, and b comes after c
18	// baSort will organize that result set into an array or object based on running the rules on the items
19	let baSort = function(data, rules, returnType) {
20		returnType = returnType || "array";
21		
22		let nodes = {};
23		
24		let node = function(name) {
25			this.depth = null;
26			this.name = name;
27			this.befores = [];
28		};
29		
30		node.prototype.getDepth = function(temp) {			
31			if (this.depth !== null) {
32				// if we have already calculated this nodes depth, return that value
33				return this.depth;
34			}
35			
36			if (typeof temp[this.name] !== "undefined") {
37				// if we have already attempted to process this node and do not have a value we are infinitely recursive
38				throw new Error("baSort: Infinitely recursive ruleset declared.");
39			}
40			
41			let tempDepth = 0;
42			
43			// add name to object to prevent recursion
44			temp[this.name] = true;
45			
46			// loop through befores and determine which before has the highest depth, and add one to that 
47			this.befores.forEach(function(val, i) {
48				let depth = val.getDepth(temp) + 1;
49				
50				if (depth > tempDepth) {
51					tempDepth = depth;
52				}
53			});
54			
55			// set depth so that another node calling this node will return the pre-calculated value
56			this.depth = tempDepth;
57			
58			return this.depth;
59		}
60		
61		// loop through data and initialize nodes
62		data.forEach(function(val, i) {
63			nodes[val] = new node(val);
64		});
65		
66		// loop through rules adding the nodes which come before each node
67		rules.forEach(function(val, i) {
68			nodes[val[1]].befores.push(nodes[val[0]]);
69		});
70		
71		let result = returnType === "object" ? {} : [];
72		
73		// loop through data points and calculate their depth
74		data.forEach(function(val, i) {
75			let depth = nodes[val].getDepth({});
76			
77			if (returnType === "object") {
78				result[val] = depth;
79			} else {
80				result[depth] = result[depth] || [];
81				result[depth].push(val);
82			}
83		});
84		
85		return result;
86	}
87
88	// given a variable, it will recurse down that path until it finds the value
89	// in example varLookup({ foo : { bar : "barValue" } }, "foo.bar") === "barValue"
90	// if any undefined value is found it will return undefined
91	let varLookup = function(variable, path) {
92		if (variable === undefined) { return undefined; }
93		
94		let arr = (path instanceof Array) ? path : path.split(".");
95		let current = variable;
96		
97		for (let i = 0; i < arr.length; i++) {
98			current = current[arr[i]];
99			
100			if (current === undefined) {
101				return undefined;
102			}
103		}
104		
105		return current;
106	}
107
108	// given a variable, it will recurse down that path and set it to the given value
109	// if the path is valid. If the path is invalid it will throw an error
110	let setValue = function(variable, path, value) {
111		if (variable === undefined) { throw new Error("Unable to setValue. Variable undefined."); }
112		
113		let arr = (path instanceof Array) ? path : path.split(".");
114		let current = variable;
115		let last = arr.pop();
116		
117		for (let i = 0; i < arr.length; i++) {
118			current = current[arr[i]];
119			
120			if (current === undefined) { throw new Error("Unable to setValue. Interim object at key '" + arr.slice(0, i + 1).join(".") + "' is undefined.") }
121		}
122		
123		current[last] = value;
124		
125		return variable;
126	}
127
128	// hash arguments down to a md5 hash
129	let hashObject = function(args) {
130		let data = convertObjectsToArrays(args);
131		let md5sum = crypto.createHash("md5");
132		md5sum.update(JSON.stringify(data));
133		
134		return md5sum.digest("hex");
135	}
136
137	var convertObjectsToArrays = function(args) {
138		let data = args;
139		
140		if (data instanceof Array) {
141			data = Array.prototype.slice.call(args);
142			data.forEach(function(val, i) {
143				data[i] = convertObjectsToArrays(val);
144			});
145		} else if (typeof args === "object") {
146			data = [];
147			
148			objectLib.forEach(args, function(val, i) {
149				data.push([ i, convertObjectsToArrays(val) ]);
150			});
151			
152			// sorts based on the key name, case matters
153			data.sort(function(a, b) {
154				return (a[0] < b[0]) ? -1 : (a[0] > b[0]) ? 1 : 0;
155			});
156		}
157		
158		return data;
159	}
160
161	// memoize wrapper which handles serializing multi-argument functions. async.memoize in core only handles the first arg
162	// https://github.com/caolan/async/issues/575#issuecomment-61915640
163	let memoize = function(fn, args) {
164		args = args || {};
165		
166		let wrappedFn = async.memoize(fn, function() {
167			// v8 - argumentsToArray one-liner
168			let args = new Array(arguments.length); for (let i = 0; i < arguments.length; i++) { args[i] = arguments[i]; }
169			return JSON.stringify(args);
170		});
171		
172		if (args.cacheErrors === false) {
173			let newFn = function() {
174				// v8 - argumentsToArray one-liner
175				let args = new Array(arguments.length); for (let i = 0; i < arguments.length; i++) { args[i] = arguments[i]; }
176				
177				let key = JSON.stringify(args.slice(0, args.length - 1));
178				
179				let cb = args.pop();
180				args.push(function() {
181					// v8 - argumentsToArray one-liner
182					let args = new Array(arguments.length); for (let i = 0; i < arguments.length; i++) { args[i] = arguments[i]; }
183					
184					if (args[0] !== null) {
185						delete wrappedFn.memo[key];
186					}
187					
188					cb.apply(null, args);
189				});
190				
191				wrappedFn.apply(null, args);
192			}
193			
194			// pass along memo values so downstream code works
195			newFn.memo = wrappedFn.memo;
196			newFn.unmemoized = wrappedFn.unmemoized;
197			
198			return newFn;
199		} else {
200			return wrappedFn;
201		}
202	}
203	
204	let memoizeSync = function(fn, hasher) {
205		return _memoizeSyncHandler.bind(null, {}, fn, hasher || _memoizeSyncDefaultHasher);
206	}
207	
208	var _memoizeSyncHandler = function(cache, fn, hasher) {
209		let args = [];
210		for (let i = 3; i < arguments.length; i++) { args.push(arguments[i]); }
211		
212		let key = hasher.apply(null, args);
213		if (key in cache) { return cache[key]; }
214		
215		cache[key] = fn.apply(null, args);
216		
217		return cache[key];
218	}
219	
220	var _memoizeSyncDefaultHasher = function() {
221		let args = [];
222		for (let i = 0; i < arguments.length; i++) { args[i] = arguments[i]; }
223		
224		let key = JSON.stringify(args);
225		return key;
226	}
227
228	// given an array of cookie headers convert them into meaningful objects
229	let extractCookies = function(setCookie) {		
230		let result = {
231			simple: {},
232			cookies: []
233		};
234		
235		setCookie.forEach(function(val, i) {
236			let cookie = {};
237			
238			val.split(/;[\s]*/).forEach(function(val2, i2) {
239				let labelValue = val2.split("=");
240				
241				if (i2 === 0) {
242					cookie.label = labelValue[0];
243					cookie.value = labelValue[1];
244				} else {
245					let tempValue = labelValue[1];
246					
247					if (labelValue[0] === "httponly") {
248						tempValue = true;
249					} else if (labelValue[0] === "expires") {
250						cookie.expires_date = new Date(tempValue);
251					}
252					
253					cookie[labelValue[0]] = tempValue;
254				}
255			});
256			
257			result.cookies.push(cookie);
258			result.simple[cookie.label] = cookie.value;
259		});
260		
261		return result;
262	}
263
264	// syntactic sugar for binding to a method and preserve the object as the this value, useful when doing a lot of async binds
265	// miscLib.bind(foo.bar.baz.method, arg1, arg2) is equivalent to foo.bar.baz.method.bind(foo.bar.baz, "arg1", "arg2");
266	let bind = function(obj, method) {
267		// v8 - argumentsToArray one-liner
268		let args = new Array(arguments.length); for (let i = 0; i < arguments.length; i++) { args[i] = arguments[i]; }
269		args.splice(0, 2);
270		
271		return obj[method].bind.apply(obj[method], [obj].concat(args));
272	}
273
274	// trim the return of a callback so downstream functions don't get more values than they need which can throw object based async series/parallel in a tizzy
275	let trimReturn = function(count, cb) {
276		return function() {
277			// v8 - argumentsToArray one-liner
278			let args = new Array(arguments.length); for (let i = 0; i < arguments.length; i++) { args[i] = arguments[i]; }
279			cb.apply(null, args.slice(0, count));
280		}
281	}
282	
283	// try/catch causes a v8 deopt so we move it to it's own function so that we don't force larger functions to deopt
284	let tryCatch = function(fn) {
285		try {
286			return fn();
287		} catch (e) {
288			return e;
289		}
290	}
291	
292	// This function allows us to capture stack traces in callbacks from native systems which provide poor stack traces (fs, http, dns etc)
293	let wrapErrorCb = function(cb) {
294		// stash the existing state of the stack
295		let frame = {};
296		Error.captureStackTrace(frame);
297		return function(err) {
298			if (err) {
299				// use the constructor of the original error, so it may be SyntaxError, Error or who knows
300				let newErr = new err.constructor(err.message, err.code);
301				
302				if (err.code !== undefined) {
303					newErr.code = err.code;
304				}
305				
306				// replace the first line on the stack so that it properly says Error: Message based on the error type and message
307				newErr.stack = frame.stack.replace(/.*?\n/, err.name + ": " + err.message + "\n");
308				return cb(newErr);
309			}
310			
311			return cb.apply(null, arguments);
312		}
313	}
314
315	module.exports = {
316		baSort: baSort,
317		varLookup: varLookup,
318		setValue: setValue,
319		hashObject: hashObject,
320		memoize: memoize,
321		memoizeSync: memoizeSync,
322		extractCookies: extractCookies,
323		bind: bind,
324		trimReturn: trimReturn,
325		tryCatch: tryCatch,
326		wrapErrorCb: wrapErrorCb
327	};
328});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.