PageSourceSearch

https://www.myschoolbucks.com/scripts/MsbLanguage.js?version=26.7.35

js myschoolbucks.com collected 2026-09-24 08:56:25 UTC 8,257 bytes, 273 lines download raw bytes

1/* [MsbLanguage] a singleton class for loading/maintaining language resources.
2 *
3 * NOTES: As different resource requests are loaded (via AJAX), the list of resources is
4 *		  stored in the user's session to avoid excessive loading. When subsequent requests
5 *		  are made, only the unloaded resources (if any) are added to the list.
6 *
7 *		  If the language changes [see #getLocale() below] then the saved list will start over.
8 *
9 * Usages:
10 *
11 *		It is recommended to not use this module directly! Instead, create a new instance of
12 *		[MsbMessages] and use it to access language resources.
13 *		Examples:
14 *			let messages = new MsbMessages([
15 *				{id:'add', key:'common.words.add', value:'Add'},
16 *				{id:'cancel', key:'common.words.cancel', value:'Cancel'},
17 *				{id:'delete', key:'common.words.delete', value:'Delete'},
18 *				{id:'unexpectedError', key:'shared.errors.unexpectederror.msg', value:'An unexpected error has occurred'}
19 *			]);
20 *
21 *			Use the messages instance anywhere in script like the following:
22 *				messages.get('add'); //returns 'Add'
23 *				messages.get('cancel'); //returns 'Cancel'
24 *				messages.get('delete'); //returns 'Delete'
25 *				messages.get('unexpectedError'); //returns 'An unexpected error has occurred'
26 *
27 *		If you think you really have to use this moduel (although you should not),
28 *		the public/non-private function [getMessages] can be called with either an array of strings
29 *		that represent the resource keys or an array of objects with a key field.
30 *
31 *		Examples:
32 *			MsbLanguage.getInstance().getMessages(['common.words.id','common.words.name'...], callback);
33 *			MsbLanguage.getInstance().getMessages([{key:'common.words.name'},{key:'resource.key'}...], callback);
34 *
35 *		If an array of objects is passed to getMessages, the objects themselves will have
36 *		the values updated and the array of objects is returned to the callback function.
37 */
38class MsbLanguage {
39	sessionName = 'msbStoredLanguageValues';
40	list = [];
41	locale = 'en';
42	sessionToken = null;
43
44    constructor() {
45       if (!((new Error).stack.indexOf("getInstance") > -1)) {
46           throw new Error("Constructor is private. Use static method getInstance.");
47       }
48       this.constructor.instance = this;
49       this.size = 1;
50    }
51    static getInstance() {
52        if (this.instance) {
53            return this.instance;
54        }
55        return new this;
56    }
57
58    async #restoreValues() {
59		let res = null;
60		if (JSON && (typeof window.sessionStorage !== 'undefined')) {
61			res = JSON.parse(window.sessionStorage.getItem(this.sessionName));
62		}
63		if (res && res.list) {
64			if (!res.locale || (res.locale == this.#getLocale()))
65				this.list = res.list;
66		}
67	}
68
69	async #saveValues() {
70		if (JSON && (typeof window.sessionStorage !== 'undefined')) {
71			let res = { list:this.list, locale:this.#getLocale() };
72			window.sessionStorage.setItem(this.sessionName, JSON.stringify(res));
73		}
74	}
75	#getLocale() {
76		const localeField = document.querySelector("#msbLocale"); //try to get it from the hidden field in layout.jsp
77		if (localeField) this.locale = localeField.value;
78		return this.locale;
79	}
80	#hasValues() {
81		return !_.isEmpty(this.list);
82	}
83	#hasValue(key) {
84		if (key && this.#hasValues()) {
85			return !_.isEmpty(_.find(this.list, {'key':key}));
86		}
87		return false;
88	}
89	#getValue(key) {
90		if (key && this.#hasValues(key)) {
91			return _.find(this.list, {'key':key});
92		}
93		return false;
94	}
95	#addValue(key,value) {
96		if (key && value) {
97			let res = this.#getValue(key);
98			if (res) {
99				res.value = value;
100				//console.log('found value:',res)
101			} else {
102				res = {'key':key,'value':value};
103				this.list.push(res);
104			}
105			return res;
106		}
107		return false;
108	}
109	#isArrayOfStrings(keys) {
110		if (!keys)
111			return false;
112		return _.every(keys, function (item) { return _.isString(item); });
113	}
114	#isArrayOfObjects(keys) {
115		if (!keys || this.#isArrayOfStrings(keys))
116			return false;
117		let keyList = _.map(keys, 'key');
118		return (keyList && (keyList.length == keys.length));
119	}
120	#isTrue (val) {
121		return (_.isBoolean(val) && (val == true)) || (_.isString(val) && !_.isEmpty(val) && val.toLowerCase() == 'true')
122	}
123
124	async #loadMessages(loadKeys) {
125		let results = [];
126		if (loadKeys) {
127			let json, reqData = {'keyList':loadKeys};
128			//console.log('msbLanguage.#loadMessages - reqData:',reqData)
129
130			try {
131				json = await this.#postData('/ver2/etc/getMessages', reqData);
132			} catch (e) {
133				console.error('msbLanguage.#loadMessages - error:'+e);
134			}
135
136			if (json) {
137				if (!this.#isTrue(json.hasErrors) && json.resources) {
138					//console.log('msbLanguage._loadMessages - json.resources:',json.resources)
139					_.forEach(json.resources, (value, key) => {
140						results.push(this.#addValue(key, value));
141					});
142					//console.log('msbLanguage.#loadMessages - results:',results)
143					this.#saveValues();
144				}
145			}
146		}
147		return results;
148	}
149
150	async #postData(url = '', data = {}) {
151		const urlSearchParams = new URLSearchParams();
152		Object.entries(data).forEach(([key, value]) => {
153			if (value) {
154				if (Array.isArray(value)) {
155					//for arrays, pass the same key multiple times for each value (same as jQuery ajax)
156					_.forEach(value, function(v, k) {
157						urlSearchParams.append(key, v);
158					});
159				}
160				else urlSearchParams.append(key, value);
161			}
162		});
163		//console.log("#postData urlSearchParams:",urlSearchParams.toString());
164
165		const fetchHeaders = {
166			'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
167			sessionToken: this.#getSessionToken()
168		};
169
170		const response = await fetch(url, {
171			accept: 'application/json, text/javascript, */*; q=0.01',
172			method: 'POST',
173			cache: 'no-cache',
174			headers: fetchHeaders,
175			body: urlSearchParams
176		});
177		if (!response.ok) {
178			throw new Error(response.status);
179		}
180		return response.json();
181	}
182
183	#getSessionToken() {
184		if (this.sessionToken == null) {
185			const elm = document.querySelector("#msbSessionToken");
186			if (elm) {
187				this.sessionToken = elm.value;
188			}
189		}
190		return this.sessionToken;
191	}
192
193	async #getMessages(keys, callback) {
194		//console.log('msbLanguage._getMessages - keys:',keys,' - callback:',callback);
195
196		if (!this.#hasValues()) {
197			await this.#restoreValues();
198		}
199
200		let isArrayOfStrings = this.#isArrayOfStrings(keys),
201		isArrayOfObjects = this.#isArrayOfObjects(keys);
202
203		if (!isArrayOfStrings && !isArrayOfObjects) {
204			console.error('msbLanguage argument [keys] requires an array of Strings or objects with a {key:vale} pair');
205			return false;
206		}
207
208		let results = [], loadKeys = [],
209		keyList = (isArrayOfStrings ? keys :  _.map(keys, 'key'));
210
211		if (this.#hasValues()) {
212			/* No need to load them if we already have them,
213			 * so we create a list of any missing keys. */
214			_.forEach(keyList, (key) => {
215				if (!this.#hasValue(key))
216					loadKeys.push(key);
217			});
218		} else {
219			loadKeys = keyList;
220		}
221
222		if (!_.isEmpty(loadKeys)) {
223			// We have missing keys that need loading
224			//console.log('msbLanguage.#getMessages - loadKeys:',loadKeys);
225			let newResults = await this.#loadMessages(loadKeys);
226		}
227
228		if (this.#hasValues()) {
229			_.forEach(keyList, (key) => {
230				let res = this.#getValue(key);
231				if (res) {
232					if (isArrayOfObjects) {
233						//ensure we return the objects as passed (to maintain any extra fields such as [id])
234						let r = _.find(keys, {'key':key});
235						if (r) {
236							//returning the original object with value set
237							if (res.value == key) {
238								//resource not found and returned the key
239								res.value = r.value;
240							} else {
241								r.value = res.value;
242							}
243							results.push(r);
244						}
245					} else {
246						results.push(res);
247					}
248				}
249			});
250		} else {
251			results = keys;
252		}
253
254		//console.log('msbLanguage._getMessages - results:',results);
255
256		if (callback) {
257			callback(results);
258		}
259	}
260
261	async getMessages(keys, callback) {
262		if (!keys || (!this.#isArrayOfStrings(keys) && !this.#isArrayOfObjects(keys))) {
263			console.error('MsbLanguage argument [keys] requires an array of Strings or objects with a {key:vale} pair');
264			return false;
265		} else if (callback && !_.isFunction(callback)) {
266			console.error('MsbLanguage.getMessages argument [callback] must be a function');
267			return false;
268		}
269
270		await this.#getMessages(keys, callback);
271	}
272
273}

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.