Completed
Push — master ( 17669b...b8e030 )
by Rain
02:40
created

dev/Common/Cmd.js   A

Size

Lines of Code 381

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
nc 1
dl 0
loc 381
rs 10
noi 6

12 Functions

Rating   Name   Duplication   Size   Complexity  
A Cmd.js ➔ cmdError 0 3 1
A Cmd.js ➔ cmdLang 0 8 3
A Cmd.js ➔ cmdHelp 0 3 1
B Cmd.js ➔ initGlass 0 15 5
A Cmd.js ➔ cmdTheme 0 8 3
Cmd.js ➔ init 0 10 ?
A Cmd.js ➔ cmdClear 0 4 1
B Cmd.js ➔ cmdGlass 0 14 5
Cmd.js ➔ bind 0 12 ?
Cmd.js ➔ toggle 0 20 ?
A Cmd.js ➔ cmdVersion 0 4 1
A Cmd.js ➔ ??? 0 20 1
1
2
import window from 'window';
3
import $ from '$';
4
import _ from '_';
5
import ko from 'ko';
6
import {$html, $body} from 'Common/Globals';
7
import {EventKeyCode} from 'Common/Enums';
8
import {trim, inArray, changeTheme} from 'Common/Utils';
9
import {reload as translatorReload} from 'Common/Translator';
10
11
import * as Settings from 'Storage/Settings';
12
13
import ThemeStore from 'Stores/Theme';
14
import LanguageStore from 'Stores/Language';
15
16
let
17
	cmdDom = null,
18
	contoller = null;
0 ignored issues
show
Unused Code introduced by
The variable contoller seems to be never used. Consider removing it.
Loading history...
19
20
/**
21
 * @params {string} cmd
22
 * @returns {string}
23
 */
24
function cmdError(cmd) {
25
	return require('Html/Cmds/Error.html').replace('{{ cmd }}', cmd);
26
}
27
28
/**
29
 * @returns {string}
30
 */
31
function cmdClear(dom) {
32
	dom.find('.rl-cmd-history-data').empty();
33
	return '';
34
}
35
36
/**
37
 * @returns {string}
38
 */
39
function cmdHelp(cmds) {
40
	return require('Html/Cmds/Help.html').replace('{{ commands }}', cmds.join(' '));
41
}
42
43
/**
44
 * @returns {void}
45
 */
46
function initGlass() {
47
	let state = null;
48
	try {
49
		if (window.localStorage && window.localStorage.setItem)
50
		{
51
			state = '1' === '' + window.localStorage.getItem('rl-labs-glass');
52
		}
53
	}
54
	catch (e) {} // eslint-disable-line no-empty
55
56
	if (null !== state)
57
	{
58
		$html.toggleClass('glass', !!state);
59
	}
60
}
61
62
/**
63
 * @returns {string}
64
 */
65
function cmdGlass() {
66
	const state = !$html.hasClass('glass');
67
68
	try {
69
		if (window.localStorage && window.localStorage.setItem)
70
		{
71
			window.localStorage.setItem('rl-labs-glass', state ? '1' : '0');
72
		}
73
	}
74
	catch (e) {} // eslint-disable-line no-empty
75
76
	$html.toggleClass('glass', state);
77
	return '';
78
}
79
80
/**
81
 * @returns {string}
82
 */
83
function cmdTheme(param, themes) {
84
	if (param && -1 < inArray(param, themes))
85
	{
86
		changeTheme(param);
87
		return '';
88
	}
89
	return require('Html/Cmds/ThemeEmpty.html').replace('{{ themes }}', themes.join(', '));
90
}
91
92
/**
93
 * @returns {string}
94
 */
95
function cmdLang(param, isAdmin, langs) {
96
	if (param && -1 < inArray(param, langs))
97
	{
98
		translatorReload(isAdmin, param);
99
		return '';
100
	}
101
	return require('Html/Cmds/LangEmpty.html').replace('{{ langs }}', langs.join(', '));
102
}
103
104
/**
105
 * @returns {string}
106
 */
107
function cmdVersion() {
108
	return require('Html/Cmds/Version.html').replace('{{ version }}',
109
		Settings.appSettingsGet('version') + ' (' + Settings.appSettingsGet('appVersionType') + ')');
110
}
111
112
class CmdContoller
113
{
114
	constructor(dom)
115
	{
116
		this.dom = dom;
117
118
		this.opened = ko.observable(false);
119
		this.cmd = ko.observable('');
120
		this.focused = ko.observable(false);
121
122
		this.themes = ThemeStore.themes;
123
124
		this.cmdHistory = [];
125
		this.cmdHistoryShift = 0;
126
127
		this.cmdHelper = ko.observable('');
128
129
		this.cmds = ['help', 'version', 'glass', 'clear', 'theme', 'lang'];
130
		this.cmdsWithParameters = ['theme', 'lang'];
131
132
		this.isAdmin = Settings.appSettingsGet('admin');
133
	}
134
135
	runCmd(cmd, params, isTab) {
136
137
		let
138
			result = '',
139
			values = null;
0 ignored issues
show
Unused Code introduced by
The assignment to values seems to be never used. If you intend to free memory here, this is not necessary since the variable leaves the scope anyway.
Loading history...
140
141
		this.cmdHelper('');
142
143
		if (isTab)
144
		{
145
			switch (cmd) {
146
				case 'lang':
147
					values = (this.isAdmin ? LanguageStore.languagesAdmin() : LanguageStore.languages())
148
						.filter((line) => 0 === line.lastIndexOf(params, 0));
149
					break;
150
				case 'theme':
151
					values = ThemeStore.themes().filter((line) => 0 === line.lastIndexOf(params, 0));
152
					break;
153
				default:
154
					break;
155
			}
156
157
			if (cmd && values)
158
			{
159
				if (1 === values.length && values[0])
160
				{
161
					this.cmd(cmd + ' ' + values[0]);
162
				}
163
				else if (1 < values.length && values[0] && values[1])
164
				{
165
					let
166
						sub = '',
167
						index = 0;
168
169
					const
170
						list = values[0].split(''),
171
						len = list.length;
172
173
					for (; index < len; index++)
174
					{
175
						if (values[1][index] === list[index])
176
						{
177
							sub += list[index];
178
						}
179
						else
180
						{
181
							break;
182
						}
183
					}
184
185
					if (sub)
186
					{
187
						this.cmdHelper('[' + values.join(', ') + ']');
188
						this.cmd(cmd + ' ' + sub);
189
					}
190
				}
191
			}
192
193
			return '';
194
		}
195
196
		switch (cmd) {
197
			case 'hi':
198
				result = 'hello';
199
				break;
200
			case '?':
201
			case 'ls':
202
			case 'help':
203
				result = cmdHelp(this.cmds);
204
				break;
205
			case 'glass':
206
				result = cmdGlass();
207
				break;
208
			case 'v':
209
			case 'version':
210
				result = cmdVersion();
211
				break;
212
			case 'clear':
213
				result = cmdClear(this.dom);
214
				break;
215
			case 'theme':
216
				result = cmdTheme(params, ThemeStore.themes());
217
				break;
218
			case 'lang':
219
				result = cmdLang(params, this.isAdmin, this.isAdmin ? LanguageStore.languagesAdmin() : LanguageStore.languages());
220
				break;
221
			default:
222
				result = cmdError(cmd);
223
				break;
224
		}
225
226
		return result;
227
	}
228
229
	onCmd(isTab) {
230
		const
231
			cmdLine = this.cmd().replace(/[\s]+/, ' '),
232
			cmdParts = trim(cmdLine).replace().split(/[\s]+/),
233
			cmd = cmdParts.shift();
234
235
		if (isTab)
236
		{
237
			if (-1 < inArray(cmd, this.cmds))
238
			{
239
				const result = this.runCmd(cmd, cmdParts.join(' '), true);
240
				if (result)
241
				{
242
					this.cmd(result);
243
				}
244
			}
245
			else
246
			{
247
				const values = this.cmds.filter((line) => line !== cmd && 0 === line.lastIndexOf(cmd, 0));
248
				if (1 === values.length && values[0])
249
				{
250
					this.cmd(values[0] + (-1 < inArray(values[0], this.cmdsWithParameters) ? ' ' : ''));
251
				}
252
			}
253
		}
254
		else
255
		{
256
			this.cmdHistory.unshift(cmdLine);
257
			this.cmdHistory = _.uniq(this.cmdHistory);
258
			this.cmdHistoryShift = 0;
259
260
			const
261
				result = this.runCmd(cmd, cmdParts.join(' '), false),
262
				h = this.dom.find('.rl-cmd-history-data');
263
264
			if (h && h[0])
265
			{
266
				h.append($('<div></div>').html(require('Html/Cmds/Main.html').replace('{{ cmd }}', cmdLine)));
267
				if (result)
268
				{
269
					h.append($('<div></div>').html(result));
270
				}
271
272
				_.delay(() => {
273
					this.dom.find('.rl-cmd-history').scrollTop(h.height());
274
				}, 50);
275
			}
276
		}
277
	}
278
279
	onEsc() {
280
		this.opened(false);
281
		return false;
282
	}
283
284
	onTab() {
285
		this.onCmd(true);
286
		return false;
287
	}
288
289
	onEnter() {
290
		this.onCmd(false);
291
		this.cmd('');
292
		return false;
293
	}
294
295
	onKeyDown(event) {
296
		if (event && event.keyCode &&
297
			!event.metaKey && !event.ctrlKey && !event.shiftKey && 0 < this.cmdHistory.length)
298
		{
299
			const code = window.parseInt(event.keyCode, 10);
300
			if (EventKeyCode.Up === code || EventKeyCode.Down === code)
301
			{
302
				if (this.cmdHistory[this.cmdHistoryShift])
303
				{
304
					this.cmd(this.cmdHistory[this.cmdHistoryShift]);
305
					if (EventKeyCode.Up === code)
306
					{
307
						this.cmdHistoryShift += 1;
308
					}
309
					else if (EventKeyCode.Down === code)
310
					{
311
						this.cmdHistoryShift -= 1;
312
					}
313
				}
314
				else
315
				{
316
					this.cmdHistoryShift = 0;
317
				}
318
319
				return false;
320
			}
321
		}
322
323
		return true;
324
	}
325
}
326
327
/**
328
 * @returns {void}
329
 */
330
export function bind(dom)
0 ignored issues
show
Unused Code introduced by
The parameter dom is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
331
{
332
	if (!contoller)
0 ignored issues
show
Best Practice introduced by
If you intend to check if the variable contoller is declared in the current environment, consider using typeof contoller === "undefined" instead. This is safe if the variable is not actually declared.
Loading history...
333
	{
334
		contoller = new CmdContoller(dom);
0 ignored issues
show
Bug introduced by
The variable contoller seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.contoller.
Loading history...
335
336
		ko.applyBindingAccessorsToNode(dom[0], {
337
			translatorInit: true,
338
			template: () => ({name: 'Cmd'})
339
		}, contoller);
340
	}
341
}
342
343
/**
344
 * @returns {void}
345
 */
346
function init()
347
{
348
	if (null === cmdDom)
349
	{
350
		cmdDom = $('<div></div>');
351
		cmdDom.appendTo($body);
352
353
		bind(cmdDom);
354
	}
355
}
356
357
/**
358
 * @returns {void}
359
 */
360
export function toggle()
361
{
362
	if (Settings.appSettingsGet('allowCmdInterface'))
363
	{
364
		init();
365
366
		_.delay(() => {
367
			if (contoller)
0 ignored issues
show
Best Practice introduced by
If you intend to check if the variable contoller is declared in the current environment, consider using typeof contoller === "undefined" instead. This is safe if the variable is not actually declared.
Loading history...
368
			{
369
				contoller.opened(!contoller.opened());
370
				if (contoller.opened())
371
				{
372
					_.delay(() => {
373
						contoller.focused(true);
374
					}, 50);
375
				}
376
			}
377
		}, 50);
378
	}
379
}
380
381
// init
382
initGlass();
383