Changed how stript-task will be integrated into MeshCentral, added run button to device general tab.

Ylian Saint-Hilaire committed Aug 18, 2022 at 21:31 UTC 44af3a24084efcda8268d4f4937515fec019f313
10 files changed +44 -2550
agents/meshcore.js
-10
@@ -708,7 +708,6 @@ db = require('SimpleDataStore').Shared();
708 sha = require('SHA256Stream');
709 mesh = require('MeshAgent');
710 childProcess = require('child_process');
711 -try { scriptTask = require('script-task').CreateScriptTask(mesh); } catch (ex) { }
711
712 if (mesh.hasKVM == 1) { // if the agent is compiled with KVM support
713 // Check if this computer supports a desktop
@@ -1556,10 +1555,6 @@ function handleServerCommand(data) {
1555 try { require(data.plugin).consoleaction(data, data.rights, data.sessionid, this); } catch (ex) { throw ex; }
1556 break;
1557 }
1559 - case 'task': {
1560 - if (scriptTask) { scriptTask.consoleAction(data, data.rights, data.sessionid, false); }
1561 - break;
1562 - }
1558 case 'coredump':
1559 // Set the current agent coredump situation.s
1560 if (data.value === true) {
@@ -4565,11 +4560,6 @@ function processConsoleCommand(cmd, args, rights, sessionid) {
4560 }
4561 break;
4562 }
4568 - case 'task': {
4569 - if (!scriptTask) { response = "Tasks are not supported on this agent"; }
4570 - else { response = scriptTask.consoleAction(args, rights, sessionid, true); }
4571 - break;
4572 - }
4563 case 'plugin': {
4564 if (typeof args['_'][0] == 'string') {
4565 try {
agents/modules_meshcore/script-task.js deleted
-416
@@ -1,416 +0,0 @@
1 -/**
2 -* @description MeshCentral Script-Task
3 -* @author Ryan Blenis
4 -* @copyright
5 -* @license Apache-2.0
6 -*/
7 -
8 -'use strict';
9 -function CreateScriptTask(parent) {
10 - var obj = {};
11 - var db = require('SimpleDataStore').Shared();
12 - var pendingDownload = [];
13 - var debugFlag = false;
14 - var runningJobs = [];
15 - var runningJobPIDs = {};
16 -
17 - function dbg(str) {
18 - if (debugFlag !== true) return;
19 - var fs = require('fs');
20 - var logStream = fs.createWriteStream('scripttask.txt', { 'flags': 'a' });
21 - // use {'flags': 'a'} to append and {'flags': 'w'} to erase and write a new file
22 - logStream.write('\n' + new Date().toLocaleString() + ': ' + str);
23 - logStream.end('\n');
24 - }
25 -
26 - function removeFromArray(arr, from, to) {
27 - var rest = arr.slice((to || from) + 1 || arr.length);
28 - arr.length = from < 0 ? arr.length + from : from;
29 - return arr.push.apply(arr, rest);
30 - };
31 -
32 - obj.consoleAction = function(args, rights, sessionid, interactive) {
33 - //sendConsoleText('task: ' + JSON.stringify(args), sessionid); // Debug
34 -
35 - /*
36 - if (typeof args['_'] == 'undefined') {
37 - args['_'] = [];
38 - args['_'][1] = args.pluginaction; // TODO
39 - args['_'][2] = null;
40 - args['_'][3] = null;
41 - args['_'][4] = null;
42 - }
43 - */
44 -
45 - var fnname = args['_'][0];
46 - if (fnname == null) { return "Valid task commands are: trigger, cache, clear, clearCache, debug, list"; }
47 -
48 - switch (fnname.toLowerCase()) {
49 - case 'trigger': {
50 - var jObj = {
51 - jobId: args.jobId,
52 - scriptId: args.scriptId,
53 - replaceVars: args.replaceVars,
54 - scriptHash: args.scriptHash,
55 - dispatchTime: args.dispatchTime
56 - };
57 - //dbg('jObj args is ' + JSON.stringify(jObj));
58 - var sObj = getScriptFromCache(jObj.scriptId);
59 - //dbg('sobj = ' + JSON.stringify(sObj) + ', shash = ' + jObj.scriptHash);
60 - if ((sObj == null) || (sObj.contentHash != jObj.scriptHash)) {
61 - // get from the server, then run
62 - //dbg('Getting and caching script '+ jObj.scriptId);
63 - parent.SendCommand({ action: 'script-task', subaction: 'getScript', scriptId: jObj.scriptId, sessionid: sessionid, tag: 'console' });
64 - pendingDownload.push(jObj);
65 - } else {
66 - // ready to run
67 - runScript(sObj, jObj, sessionid);
68 - }
69 - break;
70 - }
71 - case 'cache': {
72 - var sObj = args.script;
73 - cacheScript(sObj);
74 - var setRun = [];
75 - if (pendingDownload.length) {
76 - pendingDownload.forEach(function (pd, k) {
77 - if ((pd.scriptId == sObj._id) && (pd.scriptHash == sObj.contentHash)) {
78 - if (setRun.indexOf(pd) === -1) { runScript(sObj, pd, sessionid); setRun.push(pd); }
79 - removeFromArray(pendingDownload, k);
80 - }
81 - });
82 - }
83 - break;
84 - }
85 - case 'clear': {
86 - clearCache();
87 - parent.SendCommand({ action: 'script-task', subaction: 'clearAllPendingTasks', sessionid: sessionid, tag: 'console' });
88 - return "Cache cleared. All pending tasks cleared.";
89 - }
90 - case 'clearcache': {
91 - clearCache();
92 - return "The script cache has been cleared";
93 - }
94 - case 'debug': {
95 - debugFlag = (debugFlag) ? false : true;
96 - var str = (debugFlag) ? 'on' : 'off';
97 - return 'Debugging is now ' + str;
98 - }
99 - case 'list': {
100 - var ret = '';
101 - if (pendingDownload.length == 0) return "No tasks pending script download";
102 - pendingDownload.forEach(function (pd, k) { ret += 'Task ' + k + ': ' + 'TaskID: ' + pd.jobId + ' ScriptID: ' + pd.scriptId + '\r\n'; });
103 - return ret;
104 - }
105 - default: {
106 - dbg('Unknown action: ' + fnname + ' with data ' + JSON.stringify(args));
107 - break;
108 - }
109 - }
110 - }
111 -
112 - function finalizeJob(job, retVal, errVal, sessionid) {
113 - if (errVal != null && errVal.stack != null) errVal = errVal.stack;
114 - removeFromArray(runningJobs, runningJobs.indexOf(job.jobId));
115 - if (typeof runningJobPIDs[job.jobId] != 'undefined') delete runningJobPIDs[job.jobId];
116 - parent.SendCommand({
117 - action: 'script-task',
118 - subaction: 'taskComplete',
119 - jobId: job.jobId,
120 - scriptId: job.scriptId,
121 - retVal: retVal,
122 - errVal: errVal,
123 - dispatchTime: job.dispatchTime, // include original run time (long running tasks could have tried a re-send)
124 - sessionid: sessionid,
125 - tag: 'console'
126 - });
127 - }
128 -
129 - //@TODO Test powershell on *nix devices with and without powershell installed
130 - function runPowerShell(sObj, jObj, sessionid) {
131 - if (process.platform != 'win32') return runPowerShellNonWin(sObj, jObj);
132 - const fs = require('fs');
133 - var rand = Math.random().toString(32).replace('0.', '');
134 -
135 - var oName = 'st' + rand + '.txt';
136 - var pName = 'st' + rand + '.ps1';
137 - var pwshout = '', pwsherr = '', cancontinue = false;
138 - try {
139 - fs.writeFileSync(pName, sObj.content);
140 - var outstr = '', errstr = '';
141 - var child = require('child_process').execFile(process.env['windir'] + '\\system32\\WindowsPowerShell\\v1.0\\powershell.exe', ['-NoLogo']);
142 - child.stderr.on('data', function (chunk) { errstr += chunk; });
143 - child.stdout.on('data', function (chunk) { });
144 - runningJobPIDs[jObj.jobId] = child.pid;
145 - child.stdin.write('.\\' + pName + ' | Out-File ' + oName + ' -Encoding UTF8\r\n');
146 - child.on('exit', function (procRetVal, procRetSignal) {
147 - dbg('Exiting with ' + procRetVal + ', Signal: ' + procRetSignal);
148 - if (errstr != '') {
149 - finalizeJob(jObj, null, errstr, sessionid);
150 - try { fs.unlinkSync(oName); fs.unlinkSync(pName); } catch (ex) { dbg('Could not unlink files, error was: ' + ex); }
151 - return;
152 - }
153 - if (procRetVal == 1) {
154 - finalizeJob(jObj, null, 'Process terminated unexpectedly.', sessionid);
155 - try { fs.unlinkSync(oName); fs.unlinkSync(pName); } catch (ex) { dbg('Could not unlink files, error was: ' + ex); }
156 - return;
157 - }
158 - try { outstr = fs.readFileSync(oName, 'utf8').toString(); } catch (ex) { outstr = (procRetVal) ? 'Failure' : 'Success'; }
159 - if (outstr) {
160 - //outstr = outstr.replace(/[^\x20-\x7E]/g, '');
161 - try { outstr = outstr.trim(); } catch (ex) { }
162 - } else {
163 - outstr = (procRetVal) ? 'Failure' : 'Success';
164 - }
165 - dbg('Output is: ' + outstr);
166 - finalizeJob(jObj, outstr, null, sessionid);
167 - try { fs.unlinkSync(oName); fs.unlinkSync(pName); } catch (ex) { }
168 - });
169 - child.stdin.write('exit\r\n');
170 - //child.waitExit(); // this was causing the event loop to stall on long-running scripts, switched to '.on exit'
171 -
172 - } catch (ex) {
173 - dbg('Error block was (PowerShell): ' + ex);
174 - finalizeJob(jObj, null, ex, sessionid);
175 - }
176 - }
177 -
178 - function runPowerShellNonWin(sObj, jObj, sessionid) {
179 - const fs = require('fs');
180 - var rand = Math.random().toString(32).replace('0.', '');
181 -
182 - var path = '';
183 - var pathTests = ['/usr/local/mesh', '/tmp', '/usr/local/mesh_services/meshagent', '/var/tmp'];
184 - pathTests.forEach(function (p) { if (path == '' && fs.existsSync(p)) { path = p; } });
185 - dbg('Path chosen is: ' + path);
186 - path = path + '/';
187 -
188 - var oName = 'st' + rand + '.txt';
189 - var pName = 'st' + rand + '.ps1';
190 - var pwshout = '', pwsherr = '', cancontinue = false;
191 - try {
192 - var childp = require('child_process').execFile('/bin/sh', ['sh']);
193 - childp.stderr.on('data', function (chunk) { pwsherr += chunk; });
194 - childp.stdout.on('data', function (chunk) { pwshout += chunk; });
195 - childp.stdin.write('which pwsh' + '\n');
196 - childp.stdin.write('exit\n');
197 - childp.waitExit();
198 - } catch (ex) { finalizeJob(jObj, null, "Couldn't determine pwsh in env: " + ex, sessionid); }
199 - if (pwsherr != '') { finalizeJob(jObj, null, "PowerShell env determination error: " + pwsherr, sessionid); return; }
200 - if (pwshout.trim() != '') { cancontinue = true; }
201 - if (cancontinue === false) { finalizeJob(jObj, null, "PowerShell is not installed", sessionid); return; }
202 - try {
203 - fs.writeFileSync(path + pName, '#!' + pwshout + '\n' + sObj.content.split('\r\n').join('\n').split('\r').join('\n'));
204 - var outstr = '', errstr = '';
205 - var child = require('child_process').execFile('/bin/sh', ['sh']);
206 - child.stderr.on('data', function (chunk) { errstr += chunk; });
207 - child.stdout.on('data', function (chunk) { });
208 - runningJobPIDs[jObj.jobId] = child.pid;
209 -
210 - child.stdin.write('cd ' + path + '\n');
211 - child.stdin.write('chmod a+x ' + pName + '\n');
212 - child.stdin.write('./' + pName + ' > ' + oName + '\n');
213 - child.on('exit', function (procRetVal, procRetSignal) {
214 - if (errstr != '') {
215 - finalizeJob(jObj, null, errstr, sessionid);
216 - try {
217 - fs.unlinkSync(path + oName);
218 - fs.unlinkSync(path + pName);
219 - } catch (ex) { dbg('Could not unlink files, error was: ' + ex + ' for path ' + path); }
220 - return;
221 - }
222 - if (procRetVal == 1) {
223 - finalizeJob(jObj, null, 'Process terminated unexpectedly.', sessionid);
224 - try {
225 - fs.unlinkSync(path + oName);
226 - fs.unlinkSync(path + pName);
227 - } catch (ex) { dbg('Could not unlink files1, error was: ' + ex + ' for path ' + path); }
228 - return;
229 - }
230 - try { outstr = fs.readFileSync(path + oName, 'utf8').toString(); } catch (es) { outstr = (procRetVal) ? 'Failure' : 'Success'; }
231 - if (outstr) {
232 - //outstr = outstr.replace(/[^\x20-\x7E]/g, '');
233 - try { outstr = outstr.trim(); } catch (ex) { }
234 - } else {
235 - outstr = (procRetVal) ? 'Failure' : 'Success';
236 - }
237 - dbg('Output is: ' + outstr);
238 - finalizeJob(jObj, outstr, null, sessionid);
239 - try { fs.unlinkSync(path + oName); fs.unlinkSync(path + pName); } catch (ex) { dbg('Could not unlink files2, error was: ' + ex + ' for path ' + path); }
240 - });
241 - child.stdin.write('exit\n');
242 - } catch (ex) {
243 - dbg('Error block was (PowerShellNonWin): ' + ex);
244 - finalizeJob(jObj, null, ex, sessionid);
245 - }
246 - }
247 -
248 - function runBat(sObj, jObj, sessionid) {
249 - if (process.platform != 'win32') { finalizeJob(jObj, null, "Platform not supported.", sessionid); return; }
250 - const fs = require('fs');
251 - var rand = Math.random().toString(32).replace('0.', '');
252 - var oName = 'st' + rand + '.txt';
253 - var pName = 'st' + rand + '.bat';
254 - try {
255 - fs.writeFileSync(pName, sObj.content);
256 - var outstr = '', errstr = '';
257 - var child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe');
258 - child.stderr.on('data', function (chunk) { errstr += chunk; });
259 - child.stdout.on('data', function (chunk) { });
260 - runningJobPIDs[jObj.jobId] = child.pid;
261 - child.stdin.write(pName + ' > ' + oName + '\r\n');
262 - child.stdin.write('exit\r\n');
263 -
264 - child.on('exit', function (procRetVal, procRetSignal) {
265 - if (errstr != '') {
266 - try { fs.unlinkSync(oName); fs.unlinkSync(pName); } catch (ex) { dbg('Could not unlink files, error was: ' + ex); }
267 - finalizeJob(jObj, null, errstr, sessionid);
268 - return;
269 - }
270 - if (procRetVal == 1) {
271 - try { fs.unlinkSync(oName); fs.unlinkSync(pName); } catch (ex) { dbg('Could not unlink files, error was: ' + ex); }
272 - finalizeJob(jObj, null, 'Process terminated unexpectedly.', sessionid);
273 - return;
274 - }
275 - try { outstr = fs.readFileSync(oName, 'utf8').toString(); } catch (ex) { outstr = (procRetVal) ? 'Failure' : 'Success'; }
276 - if (outstr) {
277 - //outstr = outstr.replace(/[^\x20-\x7E]/g, '');
278 - try { outstr = outstr.trim(); } catch (ex) { }
279 - } else {
280 - outstr = (procRetVal) ? 'Failure' : 'Success';
281 - }
282 - dbg('Output is: ' + outstr);
283 - try { fs.unlinkSync(oName); fs.unlinkSync(pName); } catch (ex) { dbg('Could not unlink files, error was: ' + ex); }
284 - finalizeJob(jObj, outstr, null, sessionid);
285 - });
286 - } catch (ex) {
287 - dbg('Error block was (BAT): ' + ex);
288 - finalizeJob(jObj, null, ex, sessionid);
289 - }
290 - }
291 -
292 - function runBash(sObj, jObj, sessionid) {
293 - if (process.platform == 'win32') { finalizeJob(jObj, null, "Platform not supported.", sessionid); return; }
294 - //dbg('proc is ' + JSON.stringify(process));
295 - const fs = require('fs');
296 - var path = '';
297 - var pathTests = ['/usr/local/mesh', '/tmp', '/usr/local/mesh_services/meshagent', '/var/tmp'];
298 - pathTests.forEach(function (p) {
299 - if (path == '' && fs.existsSync(p)) { path = p; }
300 - });
301 - dbg('Path chosen is: ' + path);
302 - path = path + '/';
303 - //var child = require('child_process');
304 - //child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 });
305 -
306 - var rand = Math.random().toString(32).replace('0.', '');
307 - var oName = 'st' + rand + '.txt';
308 - var pName = 'st' + rand + '.sh';
309 - try {
310 - fs.writeFileSync(path + pName, sObj.content);
311 - var outstr = '', errstr = '';
312 - var child = require('child_process').execFile('/bin/sh', ['sh']);
313 - child.stderr.on('data', function (chunk) { errstr += chunk; });
314 - child.stdout.on('data', function (chunk) { });
315 - runningJobPIDs[jObj.jobId] = child.pid;
316 - child.stdin.write('cd ' + path + '\n');
317 - child.stdin.write('chmod a+x ' + pName + '\n');
318 - child.stdin.write('./' + pName + ' > ' + oName + '\n');
319 - child.stdin.write('exit\n');
320 -
321 - child.on('exit', function (procRetVal, procRetSignal) {
322 - if (errstr != '') {
323 - try { fs.unlinkSync(path + oName); fs.unlinkSync(path + pName); } catch (ex) { dbg('Could not unlink files, error was: ' + ex + ' for path ' + path); }
324 - finalizeJob(jObj, null, errstr, sessionid);
325 - return;
326 - }
327 - if (procRetVal == 1) {
328 - try { fs.unlinkSync(path + oName); fs.unlinkSync(path + pName); } catch (ex) { dbg('Could not unlink files1, error was: ' + ex + ' for path ' + path); }
329 - finalizeJob(jObj, null, "Process terminated unexpectedly.", sessionid);
330 - return;
331 - }
332 - try { outstr = fs.readFileSync(path + oName, 'utf8').toString(); } catch (ex) { outstr = (procRetVal) ? 'Failure' : 'Success'; }
333 - if (outstr) {
334 - //outstr = outstr.replace(/[^\x20-\x7E]/g, '');
335 - try { outstr = outstr.trim(); } catch (ex) { }
336 - } else {
337 - outstr = (procRetVal) ? 'Failure' : 'Success';
338 - }
339 - dbg('Output is: ' + outstr);
340 - try { fs.unlinkSync(path + oName); fs.unlinkSync(path + pName); } catch (ex) { dbg('Could not unlink files2, error was: ' + ex + ' for path ' + path); }
341 - finalizeJob(jObj, outstr, null, sessionid);
342 - });
343 - } catch (ex) {
344 - dbg('Error block was (bash): ' + ex);
345 - finalizeJob(jObj, null, ex, sessionid);
346 - }
347 - }
348 -
349 - function jobIsRunning(jObj) {
350 - if (runningJobs.indexOf(jObj.jobId) === -1) return false;
351 - return true;
352 - }
353 -
354 - function runScript(sObj, jObj, sessionid) {
355 - // get current processes and clean running jobs if they are no longer running (computer fell asleep, user caused process to stop, etc.)
356 - if (process.platform != 'linux' && runningJobs.length) { // linux throws errors here in the meshagent for some reason
357 - require('process-manager').getProcesses(function (plist) {
358 - dbg('Got process list');
359 - dbg('There are currently ' + runningJobs.length + ' running jobs.');
360 - if (runningJobs.length) {
361 - runningJobs.forEach(function (jobId, idx) {
362 - dbg('Checking for running job: ' + jobId + ' with PID ' + runningJobPIDs[jobId]);
363 - if (typeof plist[runningJobPIDs[jobId]] == 'undefined' || typeof plist[runningJobPIDs[jobId]].cmd != 'string') {
364 - dbg('Found job with no process. Removing running status.');
365 - delete runningJobPIDs[jobId];
366 - removeFromArray(runningJobs, runningJobs.indexOf(idx));
367 - //dbg('RunningJobs: ' + JSON.stringify(runningJobs));
368 - //dbg('RunningJobsPIDs: ' + JSON.stringify(runningJobPIDs));
369 - }
370 - });
371 - }
372 - });
373 - }
374 - if (jobIsRunning(jObj)) { dbg('Job already running job id [' + jObj.jobId + ']. Skipping.'); return; }
375 - if (jObj.replaceVars != null) {
376 - Object.getOwnPropertyNames(jObj.replaceVars).forEach(function (key) {
377 - var val = jObj.replaceVars[key];
378 - sObj.content = sObj.content.replace(new RegExp('#' + key + '#', 'g'), val);
379 - dbg('replacing var ' + key + ' with ' + val);
380 - });
381 - sObj.content = sObj.content.replace(new RegExp('#(.*?)#', 'g'), 'VAR_NOT_FOUND');
382 - }
383 - runningJobs.push(jObj.jobId);
384 - dbg('Running Script ' + sObj._id);
385 - switch (sObj.filetype) {
386 - case 'ps1': runPowerShell(sObj, jObj, sessionid); break;
387 - case 'bat': runBat(sObj, jObj, sessionid); break;
388 - case 'bash': runBash(sObj, jObj, sessionid); break;
389 - default: dbg('Unknown filetype: ' + sObj.filetype); break;
390 - }
391 - }
392 -
393 - function getScriptFromCache(id) {
394 - var script = db.Get('scriptTask_script_' + id);
395 - if (script == '' || script == null) return null;
396 - try { script = JSON.parse(script); } catch (ex) { return null; }
397 - return script;
398 - }
399 -
400 - function cacheScript(sObj) {
401 - db.Put('scriptTask_script_' + sObj._id, sObj);
402 - }
403 -
404 - function clearCache() {
405 - db.Keys.forEach(function (k) { if (k.indexOf('scriptTask_script_') === 0) { db.Put(k, null); db.Delete(k); } });
406 - }
407 -
408 - function sendConsoleText(text, sessionid) {
409 - if (typeof text == 'object') { text = JSON.stringify(text); }
410 - parent.SendCommand({ action: 'msg', type: 'console', value: 'XXX: ' + text, sessionid: sessionid });
411 - }
412 -
413 - return obj;
414 -}
415 -
416 -module.exports = { CreateScriptTask: CreateScriptTask };
\ No newline at end of file
public/tail.datetime/tail.datetime-default-blue.min.css deleted
-3
@@ -1,3 +0,0 @@
1 -@charset "UTF-8"; /* pytesNET/tail.DateTime v.0.4.14 */
2 -/* @author SamBrishes, pytesNET <sam@pytes.net> | @license MIT */
3 -.tail-datetime-calendar,.tail-datetime-calendar *,.tail-datetime-calendar :after,.tail-datetime-calendar :before{box-sizing:border-box;-webkit-box-sizing:border-box}.tail-datetime-calendar{top:0;left:0;width:275px;height:auto;margin:15px;padding:0;z-index:3000;display:block;position:absolute;visibility:hidden;direction:ltr;border-collapse:separate;font-family:"Open Sans",Calibri,Arial,sans-serif;background-color:#fff;border-width:0;border-style:solid;border-color:transparent;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.3125);-webkit-box-shadow:0 1px 3px rgba(0,0,0,.3125)}.tail-datetime-calendar:after{clear:both;content:"";display:block;font-size:0;visibility:hidden}.tail-datetime-calendar.calendar-static{top:auto;left:auto;margin-left:auto;margin-right:auto;position:static;visibility:visible}.tail-datetime-calendar button.calendar-close{top:100%;right:15px;color:#303438;width:35px;height:25px;margin:1px 0 0 0;padding:5px 10px;opacity:.5;outline:0;display:inline-block;position:absolute;font-size:14px;line-height:1.125em;text-shadow:none;background-color:#fff;background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDEyIDE2Ij48cGF0aCBmaWxsPSIjMzAzNDM4IiBkPSJNNy40OCA4bDMuNzUgMy43NS0xLjQ4IDEuNDhMNiA5LjQ4bC0zLjc1IDMuNzUtMS40OC0xLjQ4TDQuNTIgOCAuNzcgNC4yNWwxLjQ4LTEuNDhMNiA2LjUybDMuNzUtMy43NSAxLjQ4IDEuNDhMNy40OCA4eiIvPjwvc3ZnPg==");background-repeat:no-repeat;background-position:center center;border-width:0;border-style:solid;border-color:transparent;border-radius:0 0 3px 3px;box-shadow:0 1px 3px rgba(0,0,0,.3125);-webkit-box-shadow:0 1px 3px rgba(0,0,0,.3125);transition:opacity 142ms linear;-webkit-transition:opacity 142ms linear}.tail-datetime-calendar button.calendar-close:hover{opacity:1}.tail-datetime-calendar .calendar-tooltip{color:#fff;width:auto;margin:0;padding:0;display:block;position:absolute;background-color:#202428;border-radius:3px}.tail-datetime-calendar .calendar-tooltip:before{top:-7px;left:50%;width:0;height:0;margin:0 0 0 -6px;content:"";display:block;position:absolute;border-width:0 7px 7px 7px;border-style:solid;border-color:transparent transparent #202428 transparent}.tail-datetime-calendar .calendar-tooltip .tooltip-inner{width:auto;margin:0;padding:4px 7px;display:block;font-size:12px;line-height:14px}.tail-datetime-calendar .calendar-actions{color:#fff;width:100%;height:36px;margin:0;padding:0;display:table;overflow:hidden;border-spacing:0;border-collapse:separate;background-color:#149be6;border-width:0;border-style:solid;border-color:transparent;border-radius:3px 3px 0 0}.tail-datetime-calendar .calendar-actions span{margin:0;padding:0;display:table-cell;position:relative;text-align:center;line-height:36px;text-shadow:-1px -1px 0 #0e6ca0;background-repeat:no-repeat;background-position:center center}.tail-datetime-calendar .calendar-actions span[data-action]{cursor:pointer}.tail-datetime-calendar .calendar-actions span.action{width:36px;font-size:22px}.tail-datetime-calendar .calendar-actions span.label{width:auto}.tail-datetime-calendar .calendar-actions span:first-child:before,.tail-datetime-calendar .calendar-actions span:last-child:before{top:5px;bottom:5px;width:1px;height:auto;margin:0;padding:0;content:"";display:inline-block;position:absolute;background-color:#107bb7}.tail-datetime-calendar .calendar-actions span:first-child:before{right:-1px}.tail-datetime-calendar .calendar-actions span:last-child:before{left:-1px}.tail-datetime-calendar .calendar-actions span:first-child:hover:before,.tail-datetime-calendar .calendar-actions span:last-child:hover:before{display:none}.tail-datetime-calendar .calendar-actions span[data-action]:hover{background-color:#107bb7}.tail-datetime-calendar .calendar-actions span.action-prev{background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2IiBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgNiAxNiI+PHBhdGggZmlsbD0iI2ZmZmZmZiIgZD0iTTYgMkwwIDhsNiA2VjJ6Ii8+PC9zdmc+")}.tail-datetime-calendar .calendar-actions span.action-next{background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2IiBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgNiAxNiI+PHBhdGggZmlsbD0iI2ZmZmZmZiIgZD0iTTAgMTRsNi02LTYtNnYxMnoiLz48L3N2Zz4=")}.tail-datetime-calendar .calendar-actions span.action-submit{background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDEyIDE2Ij48cGF0aCBmaWxsPSIjZmZmZmZmIiBkPSJNMTIgNWwtOCA4LTQtNCAxLjUtMS41TDQgMTBsNi41LTYuNUwxMiA1eiIvPjwvc3ZnPg==")}.tail-datetime-calendar .calendar-actions span.action-cancel{background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDEyIDE2Ij48cGF0aCBmaWxsPSIjZmZmZmZmIiBkPSJNNy40OCA4bDMuNzUgMy43NS0xLjQ4IDEuNDhMNiA5LjQ4bC0zLjc1IDMuNzUtMS40OC0xLjQ4TDQuNTIgOCAuNzcgNC4yNWwxLjQ4LTEuNDhMNiA2LjUybDMuNzUtMy43NSAxLjQ4IDEuNDhMNy40OCA4eiIvPjwvc3ZnPg==")}.tail-datetime-calendar .calendar-datepicker{width:100%;margin:0;padding:0;display:block;position:relative}.tail-datetime-calendar .calendar-datepicker table{width:100%;margin:0;padding:0;border-spacing:0;border-collapse:separate}.tail-datetime-calendar .calendar-datepicker table tr td,.tail-datetime-calendar .calendar-datepicker table tr th{color:#303438;height:30px;padding:0;position:relative;font-size:13px;text-align:center;font-weight:400;text-shadow:none;line-height:30px;background-color:transparent;border-width:0;border-style:solid;border-color:transparent;border-radius:0}.tail-datetime-calendar .calendar-datepicker table tr th{color:#fff;background-color:#303438}.tail-datetime-calendar .calendar-datepicker table tr td{cursor:pointer}.tail-datetime-calendar .calendar-datepicker table tr td span.inner{margin:0;padding:0;display:inline-block}.tail-datetime-calendar .calendar-datepicker table tr td.date-disabled{cursor:not-allowed;color:#909498;background-color:#f0f0f0}.tail-datetime-calendar .calendar-datepicker table tr td.date-disabled:after{left:3px;bottom:3px;width:35px;height:1px;margin:0;padding:0;content:"";display:inline-block;position:absolute;background-color:#bfbfbf;transform-origin:2px -5px;transform:rotate(-45deg);-moz-transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}.tail-datetime-calendar .calendar-datepicker table tr td.date-next,.tail-datetime-calendar .calendar-datepicker table tr td.date-previous{color:#909498;background-color:#f0f0f0}.tail-datetime-calendar .calendar-datepicker table tr td .tooltip-tick,.tail-datetime-calendar .calendar-datepicker table tr td.date-today:before{top:5px;width:5px;height:5px;margin:0;padding:0;z-index:20;content:"";display:inline-block;position:absolute;border-width:0;border-style:solid;border-color:transparent;border-radius:50%}.tail-datetime-calendar .calendar-datepicker table tr td.date-today:before{left:5px;background-color:#32b93c}.tail-datetime-calendar .calendar-datepicker table tr td .tooltip-tick{right:5px;background-color:#202428}.tail-datetime-calendar .calendar-datepicker table tr td .tooltip-tick:after,.tail-datetime-calendar .calendar-datepicker table tr td .tooltip-tick:before{display:none}.tail-datetime-calendar .calendar-datepicker table tr td.calendar-day,.tail-datetime-calendar .calendar-datepicker table tr th.calendar-week{width:14.28571429%;height:35px}.tail-datetime-calendar .calendar-datepicker table tr td.calendar-day span.inner,.tail-datetime-calendar .calendar-datepicker table tr th.calendar-week span.inner{width:31px;height:31px;line-height:29px;border-width:1px;border-style:solid;border-color:transparent;border-radius:50%}.tail-datetime-calendar .calendar-datepicker table tr td.calendar-day:hover span.inner,.tail-datetime-calendar .calendar-datepicker table tr th.calendar-week:hover span.inner{border-color:#ccc}.tail-datetime-calendar .calendar-datepicker table tr td.calendar-day.date-disabled span.inner,.tail-datetime-calendar .calendar-datepicker table tr td.calendar-day.date-disabled:hover span.inner,.tail-datetime-calendar .calendar-datepicker table tr th.calendar-week.date-disabled span.inner,.tail-datetime-calendar .calendar-datepicker table tr th.calendar-week.date-disabled:hover span.inner{border-color:transparent}.tail-datetime-calendar .calendar-datepicker table tr td.calendar-day.date-select span.inner,.tail-datetime-calendar .calendar-datepicker table tr td.calendar-day.date-select:hover span.inner,.tail-datetime-calendar .calendar-datepicker table tr th.calendar-week.date-select span.inner,.tail-datetime-calendar .calendar-datepicker table tr th.calendar-week.date-select:hover span.inner{color:#32b93c;border-color:#32b93c}.tail-datetime-calendar .calendar-datepicker table tr td.calendar-decade,.tail-datetime-calendar .calendar-datepicker table tr td.calendar-month,.tail-datetime-calendar .calendar-datepicker table tr td.calendar-year{width:33.33333333%;height:40px;transition:color 142ms linear;-webkit-transition:color 142ms linear}.tail-datetime-calendar .calendar-datepicker table tr td.calendar-decade.date-today:before,.tail-datetime-calendar .calendar-datepicker table tr td.calendar-month.date-today:before,.tail-datetime-calendar .calendar-datepicker table tr td.calendar-year.date-today:before{left:50%;margin-left:-2.5px}.tail-datetime-calendar .calendar-datepicker table tr td.calendar-decade span.inner,.tail-datetime-calendar .calendar-datepicker table tr td.calendar-month span.inner,.tail-datetime-calendar .calendar-datepicker table tr td.calendar-year span.inner{width:auto;height:31px;line-height:29px}.tail-datetime-calendar .calendar-datepicker table tr td.calendar-decade span.inner:after,.tail-datetime-calendar .calendar-datepicker table tr td.calendar-decade span.inner:before,.tail-datetime-calendar .calendar-datepicker table tr td.calendar-month span.inner:after,.tail-datetime-calendar .calendar-datepicker table tr td.calendar-month span.inner:before,.tail-datetime-calendar .calendar-datepicker table tr td.calendar-year span.inner:after,.tail-datetime-calendar .calendar-datepicker table tr td.calendar-year span.inner:before{width:20px;height:20px;content:"";z-index:15;display:inline-block;position:absolute;border-width:1px;border-style:solid;border-color:transparent;transition:all 142ms linear;-webkit-transition:all 142ms linear}.tail-datetime-calendar .calendar-datepicker table tr td.calendar-decade span.inner:before,.tail-datetime-calendar .calendar-datepicker table tr td.calendar-month span.inner:before,.tail-datetime-calendar .calendar-datepicker table tr td.calendar-year span.inner:before{top:0;left:0}.tail-datetime-calendar .calendar-datepicker table tr td.calendar-decade:hover span.inner:before,.tail-datetime-calendar .calendar-datepicker table tr td.calendar-month:hover span.inner:before,.tail-datetime-calendar .calendar-datepicker table tr td.calendar-year:hover span.inner:before{top:6px;left:6px;border-top-color:#ccc;border-left-color:#ccc}.tail-datetime-calendar .calendar-datepicker table tr td.calendar-decade span.inner:after,.tail-datetime-calendar .calendar-datepicker table tr td.calendar-month span.inner:after,.tail-datetime-calendar .calendar-datepicker table tr td.calendar-year span.inner:after{right:0;bottom:0}.tail-datetime-calendar .calendar-datepicker table tr td.calendar-decade:hover span.inner:after,.tail-datetime-calendar .calendar-datepicker table tr td.calendar-month:hover span.inner:after,.tail-datetime-calendar .calendar-datepicker table tr td.calendar-year:hover span.inner:after{right:6px;bottom:6px;border-right-color:#ccc;border-bottom-color:#ccc}.tail-datetime-calendar .calendar-datepicker table tr td.calendar-decade,.tail-datetime-calendar .calendar-datepicker table tr td.calendar-year{width:25%}.tail-datetime-calendar .calendar-datepicker table tr td.calendar-decade span.inner{height:54px;padding:7px 15px;text-align:left;line-height:20px}.tail-datetime-calendar .calendar-timepicker{width:100%;margin:0;padding:0;display:block;text-align:center;border-width:1px 0 0 0;border-style:solid;border-color:#d9d9d9}.tail-datetime-calendar .calendar-timepicker .timepicker-field{width:28%;margin:0;padding:15px 0 7px 0;display:inline-block;position:relative;text-align:center}.tail-datetime-calendar .calendar-timepicker .timepicker-field:first-of-type{text-align:right}.tail-datetime-calendar .calendar-timepicker .timepicker-field:last-of-type{text-align:left}.tail-datetime-calendar .calendar-timepicker .timepicker-field input[type=text]{color:#303438;width:100%;height:29px;margin:0;z-index:4;padding:3px 20px 3px 5px;outline:0;display:inline-block;position:relative;font-size:12px;text-align:center;line-height:23px;appearance:textfield;-moz-appearance:textfield;-webkit-appearance:textfield;background-color:#f0f0f0;border-width:0;border-style:solid;border-color:transparent;border-radius:3px;box-shadow:none;-webkit-box-shadow:none;transition:color 142ms linear,border 142ms linear,background 142ms linear;-webkit-transition:color 142ms linear,border 142ms linear,background 142ms linear}.tail-datetime-calendar .calendar-timepicker .timepicker-field input[type=text]:hover{color:#303438;background-color:#e0e0e0}.tail-datetime-calendar .calendar-timepicker .timepicker-field input[type=text]:focus{color:#fff;background-color:#32b93c}.tail-datetime-calendar .calendar-timepicker .timepicker-field input[type=text]:disabled{cursor:not-allowed;color:#a0a4a8;background-color:#f6f6f6}.tail-datetime-calendar .calendar-timepicker .timepicker-field button.picker-step{width:20px;height:15px;right:0;margin:0;padding:0;z-index:15;display:inline-block;position:absolute;background-color:#f0f0f0;box-shadow:none;-webkit-box-shadow:none;transition:border 142ms linear,background 142ms linear;-webkit-transition:border 142ms linear,background 142ms linear}.tail-datetime-calendar .calendar-timepicker .timepicker-field button.picker-step:before{top:4px;left:50%;width:0;height:0;margin:0 0 0 -4px;padding:0;content:"";display:inline-block;position:absolute;transition:border 142ms linear;-webkit-transition:border 142ms linear}.tail-datetime-calendar .calendar-timepicker .timepicker-field button.picker-step.step-up{top:15px;border-width:0 0 1px 1px;border-style:solid;border-color:#fff;border-radius:0 2px 0 0}.tail-datetime-calendar .calendar-timepicker .timepicker-field button.picker-step.step-up:hover{background-color:#e0e0e0}.tail-datetime-calendar .calendar-timepicker .timepicker-field button.picker-step.step-up:before{border-width:0 4px 5px 4px;border-style:solid;border-color:transparent transparent #303438 transparent}.tail-datetime-calendar .calendar-timepicker .timepicker-field button.picker-step.step-down{top:29px;border-width:1px 0 0 1px;border-style:solid;border-color:#fff;border-radius:0 0 2px 0}.tail-datetime-calendar .calendar-timepicker .timepicker-field button.picker-step.step-down:hover{background-color:#e0e0e0}.tail-datetime-calendar .calendar-timepicker .timepicker-field button.picker-step.step-down:before{border-width:5px 4px 0 4px;border-style:solid;border-color:#303438 transparent transparent transparent}.tail-datetime-calendar .calendar-timepicker .timepicker-field input:focus+button.step-up{border-color:rgba(255,255,255,.8);background-color:#32b93c}.tail-datetime-calendar .calendar-timepicker .timepicker-field input:focus+button.step-up:hover{background-color:#27912f}.tail-datetime-calendar .calendar-timepicker .timepicker-field input:focus+button.step-up:before{border-bottom-color:#fff}.tail-datetime-calendar .calendar-timepicker .timepicker-field input:focus+button+button.step-down{border-color:rgba(255,255,255,.8);background-color:#32b93c}.tail-datetime-calendar .calendar-timepicker .timepicker-field input:focus+button+button.step-down:hover{background-color:#27912f}.tail-datetime-calendar .calendar-timepicker .timepicker-field input:focus+button+button.step-down:before{border-top-color:#fff}.tail-datetime-calendar .calendar-timepicker .timepicker-field input:disabled+button.step-up{cursor:not-allowed;border-color:rgba(255,255,255,.8);background-color:#f6f6f6}.tail-datetime-calendar .calendar-timepicker .timepicker-field input:disabled+button.step-up:hover{background-color:#f6f6f6}.tail-datetime-calendar .calendar-timepicker .timepicker-field input:disabled+button.step-up:before{border-bottom-color:#a0a4a8}.tail-datetime-calendar .calendar-timepicker .timepicker-field input:disabled+button+button.step-down{cursor:not-allowed;border-color:rgba(255,255,255,.8);background-color:#f6f6f6}.tail-datetime-calendar .calendar-timepicker .timepicker-field input:disabled+button+button.step-down:hover{background-color:#f6f6f6}.tail-datetime-calendar .calendar-timepicker .timepicker-field input:disabled+button+button.step-down:before{border-top-color:#a0a4a8}.tail-datetime-calendar .calendar-timepicker .timepicker-field label{color:#303438;margin:0;padding:0;display:block;font-size:12px;text-align:center}.tail-datetime-calendar .calendar-timepicker label.timepicker-switch{cursor:pointer;margin:15px 0 -5px 0;display:block;text-align:center;vertical-align:top}.tail-datetime-calendar .calendar-timepicker label.timepicker-switch:after,.tail-datetime-calendar .calendar-timepicker label.timepicker-switch:before{width:auto;margin:0;padding:0 5px;font-size:12px;line-height:16px;vertical-align:top}.tail-datetime-calendar .calendar-timepicker label.timepicker-switch:before{content:attr(data-am)}.tail-datetime-calendar .calendar-timepicker label.timepicker-switch:after{content:attr(data-pm)}.tail-datetime-calendar .calendar-timepicker label.timepicker-switch input[type=checkbox]{display:none}.tail-datetime-calendar .calendar-timepicker label.timepicker-switch input[type=checkbox]+span{display:inline-block;position:relative;vertical-align:top}.tail-datetime-calendar .calendar-timepicker label.timepicker-switch input[type=checkbox]+span:before{width:50px;height:16px;content:"";display:inline-block;vertical-align:top;border-width:1px;border-style:solid;border-color:#149be6;border-radius:14px;transition:border 284ms linear;-webkit-transition:border 284ms linear}.tail-datetime-calendar .calendar-timepicker label.timepicker-switch input[type=checkbox]+span:after{top:3px;left:4px;right:30px;width:auto;height:10px;margin:0;padding:0;content:"";display:inline-block;position:absolute;background-color:#149be6;border-radius:15px;vertical-align:top;transition:left 284ms linear,right 284ms linear 284ms,background 284ms linear;-webkit-transition:left 284ms linear,right 284ms linear 284ms,background 284ms linear}.tail-datetime-calendar .calendar-timepicker label.timepicker-switch input[type=checkbox]:checked+span:before{border-color:#32b93c}.tail-datetime-calendar .calendar-timepicker label.timepicker-switch input[type=checkbox]:checked+span:after{left:30px;right:4px;background-color:#32b93c;transition:right 284ms linear,left 284ms linear 284ms,background 284ms linear;-webkit-transition:right 284ms linear,left 284ms linear 284ms,background 284ms linear}.tail-datetime-calendar .calendar-actions+.calendar-timepicker{border-width:0}.tail-datetime-calendar.rtl{direction:rtl}.tail-datetime-calendar.rtl .calendar-actions span.action-next,.tail-datetime-calendar.rtl .calendar-actions span.action-prev{transform:rotate(180deg);-moz-transform:rotate(180deg);-webkit-transform:rotate(180deg)}.tail-datetime-calendar.rtl .calendar-datepicker table tr td.date-disabled:after{right:3px;transform:rotate(45deg);-moz-transform:rotate(45deg);-webkit-transform:rotate(45deg)}.tail-datetime-calendar.rtl .calendar-datepicker table tr td.date-today:before{right:5px}.tail-datetime-calendar.rtl .calendar-datepicker table tr td .tooltip-tick{left:5px}.tail-datetime-calendar.rtl .calendar-datepicker table tr td.calendar-decade.date-today:before,.tail-datetime-calendar.rtl .calendar-datepicker table tr td.calendar-month.date-today:before,.tail-datetime-calendar.rtl .calendar-datepicker table tr td.calendar-year.date-today:before{right:50%;margin-right:-2.5px}.tail-datetime-calendar.rtl .calendar-datepicker table tr td.calendar-decade:hover span.inner:before,.tail-datetime-calendar.rtl .calendar-datepicker table tr td.calendar-month:hover span.inner:before,.tail-datetime-calendar.rtl .calendar-datepicker table tr td.calendar-year:hover span.inner:before{right:6px;border-right-color:#ccc}.tail-datetime-calendar.rtl .calendar-datepicker table tr td.calendar-decade span.inner:after,.tail-datetime-calendar.rtl .calendar-datepicker table tr td.calendar-month span.inner:after,.tail-datetime-calendar.rtl .calendar-datepicker table tr td.calendar-year span.inner:after{left:0}.tail-datetime-calendar.rtl .calendar-datepicker table tr td.calendar-decade:hover span.inner:after,.tail-datetime-calendar.rtl .calendar-datepicker table tr td.calendar-month:hover span.inner:after,.tail-datetime-calendar.rtl .calendar-datepicker table tr td.calendar-year:hover span.inner:after{left:6px;border-left-color:#ccc}.tail-datetime-calendar.rtl .calendar-datepicker table tr td.calendar-decade span.inner{text-align:right}.tail-datetime-calendar.rtl .calendar-timepicker .timepicker-field:first-child{text-align:left;padding-left:0;padding-right:25px}.tail-datetime-calendar.rtl .calendar-timepicker .timepicker-field:last-child{text-align:right;padding-left:25px;padding-right:0}.tail-datetime-calendar.rtl .calendar-timepicker .timepicker-field:first-child input[type=text]{margin-left:-1px;margin-right:0;border-radius:0 3px 3px 0}.tail-datetime-calendar.rtl .calendar-timepicker .timepicker-field:last-child input[type=text]{margin-left:0;margin-right:-1px;border-radius:3px 0 0 3px}
\ No newline at end of file
public/tail.datetime/tail.datetime-default-blue.min.map deleted
-1
@@ -1 +0,0 @@
1 -{"version":3,"sources":["$stdin"],"names":[],"mappings":"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"}
\ No newline at end of file
public/tail.datetime/tail.datetime.min.js deleted
-2
@@ -1,2 +0,0 @@
1 -/* pytesNET/tail.DateTime v.0.4.14 | Basic Version | @author SamBrishes, pytesNET <sam@pytes.net> | @license MIT */
2 -!function(t,e){"function"==typeof define&&define.amd?define(function(){return e(t,t.document)}):"object"==typeof module&&module.exports?module.exports=e(t,t.document):(void 0===t.tail&&(t.tail={}),t.tail.DateTime=t.tail.datetime=e(t,t.document),"undefined"!=typeof jQuery&&(jQuery.fn.DateTime=jQuery.fn.datetime=function(t){var e,i=[];return this.each(function(){!1!==(e=tail.DateTime(this,t))&&i.push(e)}),1===i.length?i[0]:0!==i.length&&i}),"undefined"!=typeof MooTools&&(Element.implement({DateTime:function(t){return new tail.DateTime(this,t)}}),Element.implement({datetime:function(t){return new tail.DateTime(this,t)}})))}(window,function(h,d){"use strict";function u(t,e){return!!(t&&"classList"in t)&&t.classList.contains(e)}function a(t,e){return t&&"classList"in t?t.classList.add(e):void 0}function n(t,e){return t&&"classList"in t?t.classList.remove(e):void 0}function s(t,e,i){if(CustomEvent&&CustomEvent.name)var a=new CustomEvent(e,i);else(a=d.createEvent("CustomEvent")).initCustomEvent(e,!!i.bubbles,!!i.cancelable,i.detail);return t.dispatchEvent(a)}function o(t,e){if("function"==typeof Object.assign)return Object.assign({},t,e||{});var i=Object.constructor();for(var a in t)i[a]=a in e?e[a]:t[a];return i}function p(t,e){var i=d.createElement(t);return i.className=e&&e.join?e.join(" "):e||"",i}function r(t){return t.charAt(0).toUpperCase()+t.slice(1)}function c(t,e,i){var a=t instanceof Date?t:!!t&&new Date(t);return a instanceof Date&&!isNaN(a.getDate())&&(i&&a.setHours(0,0,0,0),!0===e?a.getTime():a)}d.forms.inputmode=!0;var l=function(t,e){if((t="string"==typeof t?d.querySelectorAll(t):t)instanceof NodeList||t instanceof HTMLCollection||t instanceof Array){for(var i=[],a=t.length,n=0;n<a;n++)i.push(new l(t[n],e));return 1===i.length?i[0]:0!==i.length&&i}if(!(t instanceof Element))return!1;if(!(this instanceof l))return new l(t,e);if(l.inst[t.getAttribute("data-tail-datetime")])return l.inst[t.getAttribute("data-tail-datetime")];if(t.getAttribute("data-datetime")){var s=JSON.parse(t.getAttribute("data-datetime").replace(/\'/g,'"'));s instanceof Object&&(e=o(e,s))}return this.e=t,this.id=++l.count,this.con=o(l.defaults,e),(l.inst["tail-"+this.id]=this).e.setAttribute("data-tail-datetime","tail-"+this.id),this.init()};return l.version="0.4.14",l.status="beta",l.count=0,l.inst={},l.defaults={animate:!0,classNames:!1,closeButton:!0,dateFormat:"YYYY-mm-dd",dateStart:!1,dateRanges:[],dateBlacklist:!0,dateEnd:!1,locale:"en",position:"bottom",rtl:"auto",startOpen:!1,stayOpen:!1,time12h:!1,timeFormat:"HH:ii:ss",timeHours:!0,timeMinutes:!0,timeSeconds:0,timeIncrement:!0,timeStepHours:1,timeStepMinutes:5,timeStepSeconds:5,today:!0,tooltips:[],viewDefault:"days",viewDecades:!0,viewYears:!0,viewMonths:!0,viewDays:!0,weekStart:0},l.strings={en:{months:["January","February","March","April","May","June","July","August","September","October","November","December"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shorts:["SUN","MON","TUE","WED","THU","FRI","SAT"],time:["Hours","Minutes","Seconds"],header:["Select a Month","Select a Year","Select a Decade","Select a Time"]},modify:function(t,e,i){if(!(t in this))return!1;if(e instanceof Object)for(var a in e)this.modify(t,a,e[a]);else this[t][e]="string"==typeof i?i:this[t][e];return!0},register:function(t,e){return"string"==typeof t&&e instanceof Object&&(this[t]=e,!0)}},l.prototype={init:function(){this.prepare();var t=this.__.shorts.slice(this.con.weekStart).concat(this.__.shorts.slice(0,this.con.weekStart));this.weekdays="<thead>\n<tr>\n";for(var e=0;e<7;e++)this.weekdays+='<th class="calendar-week">'+t[e]+"</th>";this.weekdays+="\n</tr>\n</thead>",this.select=c(this.e.getAttribute("data-value")||this.e.value),(!this.select||this.select<this.con.dateStart||this.select>this.con.dateEnd)&&(this.select=null),null==this.view&&(this.view={type:this.con.viewDefault,date:this.select||new Date});var i=["Hours","Minutes","Seconds"];for(e=0;e<3;e++)if("number"==typeof this.con["time"+i[e]])this.view.date["set"+i[e]](this.con["time"+i[e]]);else for(;this.view.date["get"+i[e]]()%this.con["timeStep"+i[e]]!=0;)this.view.date["set"+i[e]](this.view.date["get"+i[e]]()+1);return this.ampm=!!this.con.time12h&&12<this.view.date.getHours(),this.events={},this.dt=this.renderCalendar(),this.con.startOpen&&this.open(),this.select&&this.selectDate(this.select),this.bind()},prepare:function(){if(this.__=o(l.strings.en,l.strings[this.con.locale]||{}),this.con.dateStart=c(this.con.dateStart,!0,!0)||-9999999999999,this.con.dateEnd=c(this.con.dateEnd,!0,!0)||9999999999999,this.con.viewDefault=this.con.dateFormat?this.con.viewDefault:"time","string"==typeof this.con.weekStart&&(this.con.weekStart=l.strings.en.shorts.indexOf(this.con.weekStart)),this.con.weekStart<0&&6<this.con.weekStart&&(this.con.weekStart=0),0<this.con.dateRanges.length){for(var t=[],e=(n=this.con.dateRanges).length,i=0;i<e;i++)n[i]instanceof Object&&(n[i].start||n[i].days)&&(!1===(n[i].start=c(n[i].start||!1,!0,!0))?n[i].start=n[i].end=1/0:(!1===(n[i].end=c(n[i].end||!1,!0,!0))&&(n[i].end=n[i].start),n[i].start=n[i].start>n[i].end?[n[i].end,n[i].end=n[i].start][0]:n[i].start),n[i].days=!("days"in n[i])||n[i].days,n[i].days="boolean"!=typeof n[i].days?function(t){for(var e=[],i=t.length,a=0;a<i;a++)"string"==typeof t[a]&&(t[a]=l.strings.en.shorts.indexOf(t[a])),0<=t[a]&&t[a]<=6&&e.push(t[a]);return e}(n[i].days instanceof Array?n[i].days:[n[i].days]):[0,1,2,3,4,5,6],t.push({start:n[i].start,end:n[i].end,days:n[i].days}));this.con.dateRanges=t}if(0<this.con.tooltips.length){t=[];var a,n,s=this.con.tooltips;for(e=s.length,i=0;i<e;i++)s[i]instanceof Object&&s[i].date&&(s[i].date instanceof Array?(a=c(s[i].date[0]||!1,!0,!0),n=c(s[i].date[1]||!1,!0,!0)||a):a=n=c(s[i].date||!1,!0,!0),a&&t.push({date:a!==n?[a,n]:a,text:s[i].text||"Tooltip",color:s[i].color||"inherit",element:s[i].element||(r=d.createElement("DIV"),r.className="calendar-tooltip",r.innerHTML='<div class="tooltip-inner">'+s[i].text||"Tooltip</div>",r)}));this.con.tooltips=t}var r;return this},bind:function(){var e=this;return void 0===this._bind&&(this.e.addEventListener("focusin",function(t){e.open.call(e)}),this.e.addEventListener("keyup",function(t){e.callback.call(e,t)}),d.addEventListener("keyup",function(t){e.dt.contains(t.target)&&e.callback.call(e,t)}),d.addEventListener("click",function(t){e.dt.contains(t.target)?e.callback.call(e,t):!e.e.contains(t.target)&&u(e.dt,"calendar-open")&&(t.target==e.dt||t.target==e.e||e.con.stayOpen||e.close.call(e))}),d.addEventListener("mouseover",function(t){e.dt.contains(t.target)&&e.callback.call(e,t)}),this._bind=!0),this},callback:function(t){var e,i=t.target,a="getAttribute",n="data-action",s=i[a](n)?i:i.parentElement[a](n)?i.parentElement:i,r="data-tooltip";if("mouseover"==t.type&&(!1!==(e=i[a](r)?i:!!s[a](r)&&s)?this.dt.querySelector("#tooltip-"+e[a](r)+"-"+e[a](r+"-time"))||this.showTooltip(e[a](r),e,e[a](r+"-time")):this.dt.querySelector(".calendar-tooltip:not(.remove)")&&this.hideTooltip(this.dt.querySelector(".calendar-tooltip").id.slice(8))),"click"==t.type){if(!s||1!=t.buttons&&1!=(t.which||t.button))return;if(s.hasAttribute("data-disabled"))return;switch(s[a](n)){case"prev":case"next":return this.browseView(s[a](n));case"cancel":this.con.stayOpen||this.close();break;case"submit":return this.con.stayOpen||this.close(),this.selectDate(this.fetchDate(parseInt(s[a]("data-date"))));case"view":return this.switchDate(s[a]("data-year")||null,s[a]("data-month")||null,s[a]("data-day")||null),this.switchView(s[a]("data-view"))}}if("keyup"==t.type){if("INPUT"!=t.target.tagName&&t.target!==this.e&&/calendar-(static|close)/i.test(this.dt.className))return!1;13==(t.keyCode||t.which)&&(this.selectDate(this.fetchDate(this.select)),t.stopPropagation(),this.con.stayOpen||this.close()),27==(t.keyCode||t.which)&&(this.con.stayOpen||this.close())}},trigger:function(t){var e={bubbles:!1,cancelable:!0,detail:{args:arguments,self:this}};"change"==t&&(s(this.e,"input",e),s(this.e,"change",e)),s(this.dt,"tail::"+t,e);for(var i=(this.events[t]||[]).length,a=0;a<i;a++)this.events[t][a].cb.apply(this,function(t,e,i){for(var a=e.length,n=0;n<a;++n)t[n-1]=e[n];return t[n]=i,t}(new Array(arguments.length),arguments,this.events[t][a].args));return!0},calcPosition:function(){var t=this.dt.style,e=h.getComputedStyle(this.dt),i=parseInt(e.marginLeft)+parseInt(e.marginRight),a=parseInt(e.marginTop)+parseInt(e.marginBottom),n=this.e.getBoundingClientRect().top+h.scrollY,s=this.e.getBoundingClientRect().left-h.scrollX,r=this.e.offsetWidth||0,o=this.e.offsetHeight||0;switch(t.visibility="hidden",this.con.position){case"top":var c=n-(this.dt.offsetHeight+a),l=s+r/2-(this.dt.offsetWidth/2+i/2);break;case"left":c=n+o/2-(this.dt.offsetHeight/2+a),l=s-(this.dt.offsetWidth+i);break;case"right":c=n+o/2-(this.dt.offsetHeight/2+a),l=s+r;break;default:c=n+o,l=s+r/2-(this.dt.offsetWidth/2+i/2)}return t.top=(0<=c?c:this.e.offsetTop)+"px",t.left=(0<=l?l:0)+"px",t.visibility="visible",this},convertDate:function(t,e){var i,a={H:String("00"+t.getHours()).toString().slice(-2),G:(i=t.getHours(),i%12?i%12:12),A:12<=t.getHours()?"PM":"AM",a:12<=t.getHours()?"pm":"am",i:String("00"+t.getMinutes()).toString().slice(-2),s:String("00"+t.getSeconds()).toString().slice(-2),Y:t.getFullYear(),y:parseInt(t.getFullYear().toString().slice(2)),m:String("00"+(t.getMonth()+1)).toString().slice(-2),M:this.__.months[t.getMonth()].slice(0,3),F:this.__.months[t.getMonth()],d:String("00"+t.getDate()).toString().slice(-2),D:this.__.days[t.getDay()],l:this.__.shorts[t.getDay()].toLowerCase()};return e.replace(/([HGismd]{1,2}|[Y]{2,4}|y{2})/g,function(t){return 4==t.length||2==t.length?a[t.slice(-1)].toString().slice(-Math.abs(t.length)):1==t.length&&"0"==t[0]?a[t.slice(-1)].toString().slice(-1):a[t.slice(-1)].toString()}).replace(/(A|a|M|F|D|l)/g,function(t){return a[t]})},renderCalendar:function(){var t=["tail-datetime-calendar","calendar-close"],e=!0===this.con.classNames?this.e.className.split(" "):this.con.classNames;if(["top","left","right","bottom"].indexOf(this.con.position)<0){var i=d.querySelector(this.con.position);t.push("calendar-static")}(!0===this.con.rtl||0<=["ar","he","mdr","sam","syr"].indexOf(this.con.rtl))&&t.push("rtl"),this.con.stayOpen&&t.push("calendar-stay"),(e="function"==typeof e.split?e.split(" "):e)instanceof Array&&(t=t.concat(e));var a=p("DIV",t),n=!1;if(a.id="tail-datetime-"+this.id,this.con.dateFormat?n='<span class="action action-prev" data-action="prev"></span><span class="label" data-action="view" data-view="up"></span><span class="action action-next" data-action="next"></span>':this.con.timeFormat&&(n='<span class="action action-submit" data-action="submit"></span><span class="label"></span><span class="action action-cancel" data-action="cancel"></span>'),a.innerHTML=n?'<div class="calendar-actions">'+n+"</div>":"",this.con.dateFormat&&this.renderDatePicker(a,this.con.viewDefault),this.con.timeFormat&&this.renderTimePicker(a),this.con.closeButton&&!i){var s=p("BUTTON","calendar-close"),r=this;s.addEventListener("click",function(t){t.preventDefault(),r.close()}),a.appendChild(s)}return(i||d.body).appendChild(a),a},renderDatePicker:function(t,e){if((!e||["decades","years","months","days"].indexOf(e)<0)&&(e=this.con.viewDays?"days":this.con.viewMonths?"months":this.con.viewYears?"years":!!this.con.viewDecades&&"decades"),!e||!this.con["view"+r(e)]||!this.con.dateFormat)return!1;var i=d.createElement("DIV");return i.className="calendar-datepicker calendar-view-"+e,i.innerHTML=this["view"+r(e)](),t.querySelector(".calendar-datepicker")?t.replaceChild(i,t.querySelector(".calendar-datepicker")):t.appendChild(i),this.view.type=e,this.handleLabel(t)},renderTimePicker:function(t){if(!this.con.timeFormat)return!1;var e,i,a=[],n=0;if(this.con.time12h){var s=12<this.view.date.getHours()?'checked="checked" ':"";a.push('<label class="timepicker-switch" data-am="AM" data-pm="PM"><input type="checkbox" value="1" data-input="PM" '+s+"/><span></span></label>")}for(var r in{Hours:0,Minutes:0,Seconds:0})!1!==this.con["time"+r]?((e=d.createElement("INPUT")).type="text",e.disabled=null===this.con["time"+r],e.setAttribute("min","Hours"===r&&this.con.time12h?"01":"00"),e.setAttribute("max","Hours"!==r?"60":this.con.time12h?"13":"24"),e.setAttribute("step",this.con["timeStep"+r]),e.setAttribute("value",(i=this.view.date["get"+r]())<10?"0"+i:i),e.setAttribute("pattern","d*"),e.setAttribute("inputmode","numeric"),e.setAttribute("data-input",r.toLowerCase()),a.push('<div class="timepicker-field timepicker-'+r.toLowerCase()+'">'+e.outerHTML+'<button class="picker-step step-up"></button><button class="picker-step step-down"></button><label>'+this.__.time[n++]+"</label></div>")):a.push((n++,null));var o=p("DIV","calendar-timepicker"),c=this;o.innerHTML=a.join("\n");var l=o.querySelectorAll("input");for(n=0;n<l.length;n++)"checkbox"!==l[n].type?(l[n].addEventListener("input",function(t){c.handleTime.call(c,this)}),l[n].addEventListener("keydown",function(t){var e=event.keyCode||event.which||0;if(38===e||40===e)return t.preventDefault(),c.handleStep.call(c,this,38===e?"up":"down"),!1})):l[n].addEventListener("change",function(t){c.handleTime.call(c,this)});for(l=o.querySelectorAll("button"),n=0;n<l.length;n++)l[n].addEventListener("mousedown",function(t){t.preventDefault();var e=this.parentElement.querySelector("input");return c.handleStep.call(c,e,u(this,"step-up")?"up":"down"),!1});var h=t.querySelector(".calendar-timepicker");return t[h?"replaceChild":"appendChild"](o,h),this.handleLabel(t)},handleTime:function(t){this.con.time12h&&"checkbox"===t.type&&(this.ampm=t.checked);var e=t.parentElement.parentElement;e=[e.querySelector("input[data-input=hours]")||{value:0},e.querySelector("input[data-input=minutes]")||{value:0},e.querySelector("input[data-input=seconds]")||{value:0}],this.selectTime(parseInt(e[0].value)+(this.ampm?12:0),parseInt(e[1].value),parseInt(e[2].value)),e[2].value=this.view.date.getSeconds(),e[1].value=this.view.date.getMinutes(),this.con.time12h?e[0].value=12<this.view.date.getHours()?this.view.date.getHours()-12:this.view.date.getHours():e[0].value=this.view.date.getHours()},handleStep:function(t,e,i){var a=null,n=parseInt(t.value),s=parseInt(t.getAttribute("min")),r=parseInt(t.getAttribute("max")),o=t.getAttribute("data-input"),c=parseInt(t.getAttribute("step"));if("up"===e?(a=r<=n+c||null,t.value=r<=n+c?13===r?1:0:n+c,this.ampm=!!this.con.time12h&&12<=this.view.date.getHours()+1):"down"===e&&(a=!(n-c<s)&&null,t.value=n-c<s?r-c:n-c,this.ampm=!!this.con.time12h&&this.view.date.getHours()-1<=0),t.value<10&&(t.value="0"+t.value),this.con.timeIncrement&&null!==a){var l=t.parentElement.previousElementSibling;l&&!1===l.disabled?this.handleStep(l.querySelector("input"),a?"up":"down",!0):"hours"==o&&this.view.date.setDate(this.view.date.getDate()+(a?1:-1))}if(void 0!==i&&!0===i)return!1;var h=t.parentElement.parentElement;if(this.selectTime(parseInt((h.querySelector("input[data-input=hours]")||{value:0}).value)+(this.ampm?12:0),parseInt((h.querySelector("input[data-input=minutes]")||{value:0}).value),parseInt((h.querySelector("input[data-input=seconds]")||{value:0}).value)),this.con.time12h){var d=t.parentElement.parentElement.querySelector("input[type=checkbox]");d&&d.checked!==12<this.view.date.getHours()&&(d.checked=12<this.view.date.getHours())}return!0},handleLabel:function(t){var e,i,a=t.querySelector(".label");switch(this.view.type){case"days":e=this.__.months[this.view.date.getMonth()]+", "+this.view.date.getFullYear();break;case"months":e=this.view.date.getFullYear();break;case"years":e=(i=parseInt(this.view.date.getFullYear().toString().slice(0,3)+"0"))+" - "+(i+10);break;case"decades":e=(i=parseInt(this.view.date.getFullYear().toString().slice(0,2)+"00"))+" - "+(i+100);break;case"time":e=this.__.header[3]}return a.innerText=e,t},viewDecades:function(){var t=this.view.date.getFullYear(),e=new Date(this.view.date.getTime()),i=this.con.today?(new Date).getYear():0;e.setFullYear(t-parseInt(t.toString()[3])-30);for(var a,n,s=[],r=[],o=1;o<=16;o++)a="calendar-decade"+(i>=e.getYear()&&i<=e.getYear()+10?" date-today":""),n='data-action="view" data-view="down" data-year="'+e.getFullYear()+'"',s.push('<td class="'+a+'" '+n+'><span class="inner">'+e.getFullYear()+" - "+(e.getFullYear()+10)+"</span></td>"),4<=o&&o%4==0&&(r.push("<tr>\n"+s.join("\n")+"\n</tr>"),s=[]),e.setFullYear(e.getFullYear()+10);return'<table class="calendar-decades"><thead><tr><th colspan="4">'+this.__.header[2]+"</th></tr></thead><tbody>"+r.join("\n")+"</tbody></table>"},viewYears:function(){var t=this.view.date.getFullYear(),e=new Date(this.view.date.getTime()),i=this.con.today?(new Date).getYear():0;e.setFullYear(t-parseInt(t.toString()[3])-2);for(var a,n,s=[],r=[],o=1;o<=16;o++)a="calendar-year"+(e.getYear()==i?" date-today":""),n='data-action="view" data-view="down" data-year="'+e.getFullYear()+'"',s.push('<td class="'+a+'" '+n+'><span class="inner">'+e.getFullYear()+"</span></td>"),4<=o&&o%4==0&&(r.push("<tr>\n"+s.join("\n")+"\n</tr>"),s=[]),e.setFullYear(e.getFullYear()+1);return'<table class="calendar-years"><thead><tr><th colspan="4">'+this.__.header[1]+"</th></tr></thead><tbody>"+r.join("\n")+"</tbody></table>"},viewMonths:function(){var t=this.__.months,e=this.con.today?(new Date).getMonth():-1;e=this.view.date.getYear()==(new Date).getYear()?e:-1;for(var i,a,n=[],s=[],r=0;r<12;r++)i="calendar-month"+(e==r?" date-today":""),a='data-action="view" data-view="down" data-month="'+r+'"',n.push('<td class="'+i+'" '+a+'><span class="inner">'+t[r]+"</span></td>"),3==n.length&&(s.push("<tr>\n"+n.join("\n")+"\n</tr>"),n=[]);return'<table class="calendar-months"><thead><tr><th colspan="3">'+this.__.header[0]+"</th></tr></thead><tbody>"+s.join("\n")+"</tbody></table>"},viewDays:function(){var i,t,e,a,n,s,r=new Date(this.view.date.getTime()),o=(new Date).toDateString(),c=r.getMonth(),l=[],h=[],d=[0,[]],u=([].concat(this.con.tooltips),[0,0]);for(r.setHours(0,0,0,0),r.setDate(1),r.setDate(1-(r.getDay()-this.con.weekStart));h.length<6;)i=r.getTime(),s=[].concat(this.con.dateRanges),e='data-action="submit" data-date="'+r.getTime()+'"',t="calendar-day date-"+(r.getMonth()>c?"next":r.getMonth()<c?"previous":"current"),this.con.today&&o==r.toDateString()&&(t+=" date-today"),this.con.dateBlacklist&&(i<this.con.dateStart||i>this.con.dateEnd)?d=[i<this.con.dateStart?this.con.dateStart:1/0,[0,1,2,3,4,5,6],!0]:0<this.con.dateRanges.length?s.filter(function(t){return t.start==1/0||i>=t.start&&i<=t.end?!(d=[t.end,t.days]):t.start>i},this):3==d.length&&(d=[0,[0,1,2,3,4,5,6]]),0<this.con.tooltips.length&&this.con.tooltips.filter(function(t,e){t.date instanceof Array?t.date[0]<=i&&t.date[1]>=i&&(u=[t.date[1],e,t.color]):t.date==i&&(u=[t.date,e,t.color])},this),u[0]<i&&(u=[0,0]),(n=d[0]>=i&&0<=d[1].indexOf(r.getDay()))&&this.con.dateBlacklist||!n&&!this.con.dateBlacklist?(t+=" date-disabled",e+=' data-disabled="true"'):0!==d[0]&&d[0]<=i&&(d=[0,[]]),this.select&&this.select.toDateString()==r.toDateString()&&(t+=" date-select"),a='<span class="inner">'+r.getDate()+"</span>",0<u[0]&&(t+=" date-tooltip",e+=' data-tooltip="'+u[1]+'" data-tooltip-time="'+i+'"',"inherit"!==u[2]?a+='<span class="tooltip-tick" style="background:'+u[2]+';"></span>':a+='<span class="tooltip-tick"></span>'),l.push('<td class="'+t+'" '+e+">"+a+"</td>"),7==l.length&&(h.push("<tr>\n"+l.join("\n")+"\n</tr>"),l=[]),r.setDate(r.getDate()+1);return h="<tbody>"+h.join("\n")+"</tbody>",'<table class="calendar-days">'+this.weekdays+h+"</table>"},showTooltip:function(t,e,i){var a,n=this.con.tooltips[t].element,s=n.style,r=this.dt.querySelector(".calendar-datepicker");s.cssText="opacity:0;visibility:hidden;",n.id="tooltip-"+t+"-"+i,r.appendChild(n),a=n.offsetWidth,n.offsetHeight,s.top=e.offsetTop+e.offsetHeight+"px",s.left=e.offsetLeft+e.offsetWidth/2-a/2+"px",s.visibility="visible",this.con.animate?(n.setAttribute("data-top",parseInt(s.top)),s.top=parseInt(s.top)+5+"px",function t(){parseFloat(s.top)>n.getAttribute("data-top")&&(s.top=parseFloat(s.top)-.5+"px"),(s.opacity=parseFloat(s.opacity)+.125)<1&&setTimeout(t,20)}()):s.opacity=1},hideTooltip:function(t){var e=this.dt.querySelector("#tooltip-"+t),i=e.style;this.con.animate?(e.className+=" remove",function t(){if(parseFloat(i.top)<parseInt(e.getAttribute("data-top"))+5&&(i.top=parseFloat(i.top)+.5+"px"),(i.opacity-=.125)<0)return(e.className="calendar-tooltip")?e.parentElement.removeChild(e):"";setTimeout(t,20)}()):e.parentElement.removeChild(e)},switchView:function(t){var e=[null,"days","months","years","decades",null];return-1==e.indexOf(t)&&("up"==t?t=e[(e.indexOf(this.view.type)||5)+1]||null:"down"==t&&(t=e[(e.indexOf(this.view.type)||1)-1]||null),t&&this.con["view"+r(t)]||(t=!1)),!!t&&(this.renderDatePicker(this.dt,t),this.trigger("view",t))},switchDate:function(t,e,i,a){return"auto"===i&&(i=this.view.date.getDate(),(1===this.view.date.getMonth()&&28<=i||30<=i)&&(i=(i=new Date(t,e+1,0)).getDate())),this.view.date.setFullYear(null==t?this.view.date.getFullYear():t,null==e?this.view.date.getMonth():e,i||this.view.date.getDate()),!0===a||this.switchView(this.view.type)},switchMonth:function(t,e){return"string"==typeof t&&(t=0<=["previous","prev"].indexOf(t)?-1:1,t=this.view.date.getMonth()+type),this.switchDate(e||this.getFullYear(),t)},switchYear:function(t){return"string"==typeof t&&(t=0<=["previous","prev"].indexOf(t)?-1:1,t=this.view.date.getFullYear()+type),this.switchDate(t)},browseView:function(t){switch(t=0<=["previous","prev"].indexOf(t)?-1:1,this.view.type){case"days":return this.switchDate(null,this.view.date.getMonth()+t,"auto");case"months":return this.switchDate(this.view.date.getFullYear()+t,null,"auto");case"years":return this.switchDate(this.view.date.getFullYear()+10*t,null,"auto");case"decades":return this.switchDate(this.view.date.getFullYear()+100*t,null,"auto")}return!1},fetchDate:function(t){t=c(t||!1)||this.view.date;var e=this.dt.querySelectorAll("input[type=number]");return e&&3==e.length&&t.setHours(e[0].value||0,e[1].value||0,e[2].value||0,0),t},selectDate:function(t,e,i,a,n,s){var r=new Date,o=[];return this.con.dateFormat&&o.push(this.con.dateFormat),this.con.timeFormat&&o.push(this.con.timeFormat),this.select=t instanceof Date?t:new Date(t||(null==t?this.view.date.getFullYear():r.getFullYear()),e||(null==e?this.view.date.getMonth():r.getMonth()),i||(null==i?this.view.date.getDate():r.getDate()),a||(null==a?this.view.date.getHours():0),n||(null==n?this.view.date.getMinutes():0),s||(null==s?this.view.date.getSeconds():0)),this.view.date=new Date(this.select.getTime()),this.e.value=this.convertDate(this.select,o.join(" ")),this.e.setAttribute("data-value",this.select.getTime()),this.switchView("days"),this.trigger("change")},selectTime:function(t,e,i){return this.selectDate(void 0,void 0,void 0,t,e,i)},open:function(){if(!u(this.dt,"calendar-close"))return this;var e=this,i=this.dt.style;return i.display="block",i.opacity=this.con.animate?0:1,n(this.dt,"calendar-close"),a(this.dt,"calendar-idle"),u(this.dt,"calendar-static")||e.calcPosition(),function t(){if(1<=(i.opacity=parseFloat(i.opacity)+.125))return n(e.dt,"calendar-idle"),a(e.dt,"calendar-open"),e.trigger("open");setTimeout(t,20)}(),this},close:function(){if(!u(this.dt,"calendar-open"))return this;var e=this,i=this.dt.style;return i.display="block",i.opacity=this.con.animate?1:0,n(this.dt,"calendar-open"),a(this.dt,"calendar-idle"),function t(){if((i.opacity-=.125)<=0)return n(e.dt,"calendar-idle"),a(e.dt,"calendar-close"),i.display="none",e.trigger("close");setTimeout(t,20)}(),this},toggle:function(){return u(this.dt,"calendar-open")?this.close():u(this.dt,"calendar-close")?this.open():this},on:function(t,e,i){return!(["open","close","change","view"].indexOf(t)<0||"function"!=typeof e)&&(t in this.events||(this.events[t]=[]),this.events[t].push({cb:e,args:i instanceof Array?i:[]}),this)},remove:function(){return this.e.removeAttribute("data-tail-datetime"),this.e.removeAttribute("data-value"),this.dt.parentElement.removeChild(this.dt),this},reload:function(){return this.remove(),this.init()},config:function(t,e,i){if(t instanceof Object){for(var a in t)this.config(a,t[a],!1);return this.reload(),this.con}return void 0===t?this.con:t in this.con&&(void 0===e?this.con[t]:(this.con[t]=e,!1!==this.rebuild&&this.reload(),this))}},l});
taskmanager.js
+10 -704
@@ -1,714 +1,20 @@
1 -/**
2 -* @description MeshCentral ScriptTask
3 -* @author Ryan Blenis
4 -* @copyright
1 +/**
2 +* @description MeshCentral task manager
3 +* @author Ylian Saint-Hilaire
4 +* @copyright Intel Corporation 2018-2022
5 * @license Apache-2.0
6 +* @version v0.0.1
7 */
8
9 +/*jslint node: true */
10 +/*jshint node: true */
11 +/*jshint strict:false */
12 +/*jshint -W097 */
13 +/*jshint esversion: 6 */
14 'use strict';
15
16 module.exports.createTaskManager = function (parent) {
17 var obj = {};
12 - obj.parent = parent.webserver;
13 - obj.meshServer = parent;
14 - obj.db = null;
15 - obj.intervalTimer = null;
16 - obj.debug = obj.meshServer.debug;
17 - obj.VIEWS = __dirname + '/views/';
18 - obj.exports = [
19 - 'onDeviceRefreshEnd',
20 - 'resizeContent',
21 - 'historyData',
22 - 'variableData',
23 - 'malix_triggerOption'
24 - ];
25 -
26 - obj.malix_triggerOption = function(selectElem) {
27 - selectElem.options.add(new Option("ScriptTask - Run Script", "scripttask_runscript"));
28 - }
29 - obj.malix_triggerFields_scripttask_runscript = function() {
30 -
31 - }
32 - obj.resetQueueTimer = function() {
33 - clearTimeout(obj.intervalTimer);
34 - obj.intervalTimer = setInterval(obj.queueRun, 1 * 60 * 1000); // every minute
35 - };
36 -
37 - // Start the task manager
38 - obj.server_startup = function() {
39 - obj.meshServer.pluginHandler.scripttask_db = require (__dirname + '/db.js').CreateDB(obj.meshServer);
40 - obj.db = obj.meshServer.pluginHandler.scripttask_db;
41 - obj.resetQueueTimer();
42 - };
43 -
44 - obj.onDeviceRefreshEnd = function() {
45 - pluginHandler.registerPluginTab({ tabTitle: 'ScriptTask', tabId: 'pluginScriptTask' });
46 - QA('pluginScriptTask', '<iframe id="pluginIframeScriptTask" style="width: 100%; height: 800px;" scrolling="no" frameBorder=0 src="/pluginadmin.ashx?pin=scripttask&user=1" />');
47 - };
48 -
49 - /*
50 - // may not be needed, saving for later. Can be called to resize iFrame
51 - obj.resizeContent = function() {
52 - var iFrame = document.getElementById('pluginIframeScriptTask');
53 - var newHeight = 800;
54 - var sHeight = iFrame.contentWindow.document.body.scrollHeight;
55 - if (sHeight > newHeight) newHeight = sHeight;
56 - if (newHeight > 1600) newHeight = 1600;
57 - iFrame.style.height = newHeight + 'px';
58 - };
59 - */
60 -
61 - obj.queueRun = async function() {
62 - var onlineAgents = Object.keys(obj.meshServer.webserver.wsagents);
63 - //obj.debug('ScriptTask', 'Queue Running', Date().toLocaleString(), 'Online agents: ', onlineAgents);
64 -
65 - obj.db.getPendingJobs(onlineAgents)
66 - .then(function(jobs) {
67 - if (jobs.length == 0) return;
68 - //@TODO check for a large number and use taskLimiter to queue the jobs
69 - jobs.forEach(function(job) {
70 - obj.db.get(job.scriptId)
71 - .then(async function(script) {
72 - script = script[0];
73 - var foundVars = script.content.match(/#(.*?)#/g);
74 - var replaceVars = {};
75 - if (foundVars != null && foundVars.length > 0) {
76 - var foundVarNames = [];
77 - foundVars.forEach(function(fv) { foundVarNames.push(fv.replace(/^#+|#+$/g, '')); });
78 -
79 - var limiters = {
80 - scriptId: job.scriptId,
81 - nodeId: job.node,
82 - meshId: obj.meshServer.webserver.wsagents[job.node]['dbMeshKey'],
83 - names: foundVarNames
84 - };
85 - var finvals = await obj.db.getVariables(limiters);
86 - var ordering = { 'global': 0, 'script': 1, 'mesh': 2, 'node': 3 }
87 - finvals.sort(function(a, b) { return (ordering[a.scope] - ordering[b.scope]) || a.name.localeCompare(b.name); });
88 - finvals.forEach(function(fv) { replaceVars[fv.name] = fv.value; });
89 - replaceVars['GBL:meshId'] = obj.meshServer.webserver.wsagents[job.node]['dbMeshKey'];
90 - replaceVars['GBL:nodeId'] = job.node;
91 - //console.log('FV IS', finvals);
92 - //console.log('RV IS', replaceVars);
93 - }
94 - var dispatchTime = Math.floor(new Date() / 1000);
95 - var jObj = {
96 - action: 'task',
97 - subaction: 'triggerJob',
98 - jobId: job._id,
99 - scriptId: job.scriptId,
100 - replaceVars: replaceVars,
101 - scriptHash: script.contentHash,
102 - dispatchTime: dispatchTime
103 - };
104 - //obj.debug('ScriptTask', 'Sending job to agent');
105 - try {
106 - obj.meshServer.webserver.wsagents[job.node].send(JSON.stringify(jObj));
107 - obj.db.update(job._id, { dispatchTime: dispatchTime });
108 - } catch (ex) { }
109 - })
110 - .catch(function (ex) { console.log('task: Could not dispatch job.', ex) });
111 - });
112 - })
113 - .then(function() {
114 - obj.makeJobsFromSchedules();
115 - obj.cleanHistory();
116 - })
117 - .catch(function(ex) { console.log('task: Queue Run Error: ', ex); });
118 - };
119 -
120 - obj.cleanHistory = function() {
121 - if (Math.round(Math.random() * 100) == 99) {
122 - //obj.debug('Task', 'Running history cleanup');
123 - obj.db.deleteOldHistory();
124 - }
125 - };
126 -
127 - obj.downloadFile = function(req, res, user) {
128 - var id = req.query.dl;
129 - obj.db.get(id)
130 - .then(function(found) {
131 - if (found.length != 1) { res.sendStatus(401); return; }
132 - var file = found[0];
133 - res.setHeader('Content-disposition', 'attachment; filename=' + file.name);
134 - res.setHeader('Content-type', 'text/plain');
135 - //var fs = require('fs');
136 - res.send(file.content);
137 - });
138 - };
139 -
140 - obj.updateFrontEnd = async function(ids){
141 - if (ids.scriptId != null) {
142 - var scriptHistory = null;
143 - obj.db.getJobScriptHistory(ids.scriptId)
144 - .then(function(sh) {
145 - scriptHistory = sh;
146 - return obj.db.getJobSchedulesForScript(ids.scriptId);
147 - })
148 - .then(function(scriptSchedule) {
149 - var targets = ['*', 'server-users'];
150 - obj.meshServer.DispatchEvent(targets, obj, { nolog: true, action: 'task', subaction: 'historyData', scriptId: ids.scriptId, nodeId: null, scriptHistory: scriptHistory, nodeHistory: null, scriptSchedule: scriptSchedule });
151 - });
152 - }
153 - if (ids.nodeId != null) {
154 - var nodeHistory = null;
155 - obj.db.getJobNodeHistory(ids.nodeId)
156 - .then(function(nh) {
157 - nodeHistory = nh;
158 - return obj.db.getJobSchedulesForNode(ids.nodeId);
159 - })
160 - .then(function(nodeSchedule) {
161 - var targets = ['*', 'server-users'];
162 - obj.meshServer.DispatchEvent(targets, obj, { nolog: true, action: 'task', subaction: 'historyData', scriptId: null, nodeId: ids.nodeId, scriptHistory: null, nodeHistory: nodeHistory, nodeSchedule: nodeSchedule });
163 - });
164 - }
165 - if (ids.tree === true) {
166 - obj.db.getScriptTree()
167 - .then(function(tree) {
168 - var targets = ['*', 'server-users'];
169 - obj.meshServer.DispatchEvent(targets, obj, { nolog: true, action: 'task', subaction: 'newScriptTree', tree: tree });
170 - });
171 - }
172 - if (ids.variables === true) {
173 - obj.db.getVariables()
174 - .then(function(vars) {
175 - var targets = ['*', 'server-users'];
176 - obj.meshServer.DispatchEvent(targets, obj, { nolog: true, action: 'task', subaction: 'variableData', vars: vars });
177 - });
178 - }
179 - };
180 -
181 - obj.handleAdminReq = function(req, res, user) {
182 - if ((user.siteadmin & 0xFFFFFFFF) == 1 && req.query.admin == 1)
183 - {
184 - // admin wants admin, grant
185 - var vars = {};
186 - res.render(obj.VIEWS + 'admin', vars);
187 - return;
188 - } else if (req.query.admin == 1 && (user.siteadmin & 0xFFFFFFFF) == 0) {
189 - // regular user wants admin
190 - res.sendStatus(401);
191 - return;
192 - } else if (req.query.user == 1) {
193 - // regular user wants regular access, grant
194 - if (req.query.dl != null) return obj.downloadFile(req, res, user);
195 - var vars = {};
196 -
197 - if (req.query.edit == 1) { // edit script
198 - if (req.query.id == null) return res.sendStatus(401);
199 - obj.db.get(req.query.id)
200 - .then(function(scripts) {
201 - if (scripts[0].filetype == 'proc') {
202 - vars.procData = JSON.stringify(scripts[0]);
203 - res.render(obj.VIEWS + 'procedit', vars);
204 - } else {
205 - vars.scriptData = JSON.stringify(scripts[0]);
206 - res.render(obj.VIEWS + 'scriptedit', vars);
207 - }
208 - });
209 - return;
210 - } else if (req.query.schedule == 1) {
211 - var vars = {};
212 - res.render(obj.VIEWS + 'schedule', vars);
213 - return;
214 - }
215 - // default user view (tree)
216 - vars.scriptTree = 'null';
217 - obj.db.getScriptTree()
218 - .then(function(tree) {
219 - vars.scriptTree = JSON.stringify(tree);
220 - res.render(obj.VIEWS + 'user', vars);
221 - });
222 - return;
223 - } else if (req.query.include == 1) {
224 - switch (req.query.path.split('/').pop().split('.').pop()) {
225 - case 'css': res.contentType('text/css'); break;
226 - case 'js': res.contentType('text/javascript'); break;
227 - }
228 - res.sendFile(__dirname + '/includes/' + req.query.path); // don't freak out. Express covers any path issues.
229 - return;
230 - }
231 - res.sendStatus(401);
232 - return;
233 - };
234 -
235 - obj.historyData = function (message) {
236 - if (typeof pluginHandler.scripttask.loadHistory == 'function') pluginHandler.scripttask.loadHistory(message);
237 - if (typeof pluginHandler.scripttask.loadSchedule == 'function') pluginHandler.scripttask.loadSchedule(message);
238 - };
239 -
240 - obj.variableData = function (message) {
241 - if (typeof pluginHandler.scripttask.loadVariables == 'function') pluginHandler.scripttask.loadVariables(message);
242 - };
243 -
244 - obj.determineNextJobTime = function(s) {
245 - var nextTime = null;
246 - var nowTime = Math.floor(new Date() / 1000);
247 -
248 - // special case: we've reached the end of our run
249 - if (s.endAt !== null && s.endAt <= nowTime) {
250 - return nextTime;
251 - }
252 -
253 - switch (s.recur) {
254 - case 'once':
255 - if (s.nextRun == null) nextTime = s.startAt;
256 - else nextTime = null;
257 - break;
258 - case 'minutes':
259 - /*var lRun = s.nextRun || nowTime;
260 - if (lRun == null) lRun = nowTime;
261 - nextTime = lRun + (s.interval * 60);
262 - if (s.startAt > nextTime) nextTime = s.startAt;*/
263 - if (s.nextRun == null) { // hasn't run yet, set to start time
264 - nextTime = s.startAt;
265 - break;
266 - }
267 - nextTime = s.nextRun + (s.interval * 60);
268 - // this prevents "catch-up" tasks being scheduled if an endpoint is offline for a long period of time
269 - // e.g. always make sure the next scheduled time is relevant to the scheduled interval, but in the future
270 - if (nextTime < nowTime) {
271 - // initially I was worried about this causing event loop lockups
272 - // if there was a long enough time gap. Testing over 50 years of backlog for a 3 min interval
273 - // still ran under a fraction of a second. Safe to say this approach is safe! (~8.5 million times)
274 - while (nextTime < nowTime) {
275 - nextTime = nextTime + (s.interval * 60);
276 - }
277 - }
278 - if (s.startAt > nextTime) nextTime = s.startAt;
279 - break;
280 - case 'hourly':
281 - if (s.nextRun == null) { // hasn't run yet, set to start time
282 - nextTime = s.startAt;
283 - break;
284 - }
285 - nextTime = s.nextRun + (s.interval * 60 * 60);
286 - if (nextTime < nowTime) {
287 - while (nextTime < nowTime) {
288 - nextTime = nextTime + (s.interval * 60 * 60);
289 - }
290 - }
291 - if (s.startAt > nextTime) nextTime = s.startAt;
292 - break;
293 - case 'daily':
294 - if (s.nextRun == null) { // hasn't run yet, set to start time
295 - nextTime = s.startAt;
296 - break;
297 - }
298 - nextTime = s.nextRun + (s.interval * 60 * 60 * 24);
299 - if (nextTime < nowTime) {
300 - while (nextTime < nowTime) {
301 - nextTime = nextTime + (s.interval * 60 * 60 * 24);
302 - }
303 - }
304 - if (s.startAt > nextTime) nextTime = s.startAt;
305 - break;
306 - case 'weekly':
307 - var tempDate = new Date();
308 - var nowDate = new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate());
309 -
310 - if (s.daysOfWeek.length == 0) {
311 - nextTime = null;
312 - break;
313 - }
314 - s.daysOfWeek = s.daysOfWeek.map(function (el) { Number(el) });
315 - var baseTime = s.startAt;
316 - //console.log('dow is ', s.daysOfWeek);
317 - var lastDayOfWeek = Math.max(...s.daysOfWeek);
318 - var startX = 0;
319 - //console.log('ldow is ', lastDayOfWeek);
320 - if (s.nextRun != null) {
321 - baseTime = s.nextRun;
322 - //console.log('basetime 2: ', baseTime);
323 - if (nowDate.getDay() == lastDayOfWeek) {
324 - baseTime = baseTime + ( s.interval * 604800 ) - (lastDayOfWeek * 86400);
325 - //console.log('basetime 3: ', baseTime);
326 - }
327 - startX = 0;
328 - } else if (s.startAt < nowTime) {
329 - baseTime = Math.floor(nowDate.getTime() / 1000);
330 - //console.log('basetime 4: ', baseTime);
331 - }
332 - //console.log('startX is: ', startX);
333 - //var secondsFromMidnight = nowTimeDate.getSeconds() + (nowTimeDate.getMinutes() * 60) + (nowTimeDate.getHours() * 60 * 60);
334 - //console.log('seconds from midnight: ', secondsFromMidnight);
335 - //var dBaseTime = new Date(0); dBaseTime.setUTCSeconds(baseTime);
336 - //var dMidnight = new Date(dBaseTime.getFullYear(), dBaseTime.getMonth(), dBaseTime.getDate());
337 - //baseTime = Math.floor(dMidnight.getTime() / 1000);
338 - for (var x = startX; x <= 7; x++){
339 - var checkDate = baseTime + (86400 * x);
340 - var d = new Date(0); d.setUTCSeconds(checkDate);
341 - var dm = new Date(d.getFullYear(), d.getMonth(), d.getDate());
342 -
343 - console.log('testing date: ', dm.toLocaleString()); // dMidnight.toLocaleString());
344 - //console.log('if break check :', (s.daysOfWeek.indexOf(d.getDay()) !== -1 && checkDate >= nowTime));
345 - //console.log('checkDate vs nowTime: ', (checkDate - nowTime), ' if positive, nowTime is less than checkDate');
346 - if (s.nextRun == null && s.daysOfWeek.indexOf(dm.getDay()) !== -1 && dm.getTime() >= nowDate.getTime()) break;
347 - if (s.daysOfWeek.indexOf(dm.getDay()) !== -1 && dm.getTime() > nowDate.getTime()) break;
348 - //if (s.daysOfWeek.indexOf(d.getDay()) !== -1 && Math.floor(d.getTime() / 1000) >= nowTime) break;
349 - }
350 - var sa = new Date(0); sa.setUTCSeconds(s.startAt);
351 - var sad = new Date(sa.getFullYear(), sa.getMonth(), sa.getDate());
352 - var diff = (sa.getTime() - sad.getTime()) / 1000;
353 - nextTime = Math.floor(dm.getTime() / 1000) + diff;
354 - //console.log('next schedule is ' + d.toLocaleString());
355 - break;
356 - default:
357 - nextTime = null;
358 - break;
359 - }
360 -
361 - if (s.endAt != null && nextTime > s.endAt) nextTime = null; // if the next time reaches the bound of the endAt time, nullify
362 -
363 - return nextTime;
364 - };
365 -
366 - obj.makeJobsFromSchedules = function(scheduleId) {
367 - //obj.debug('ScriptTask', 'makeJobsFromSchedules starting');
368 - return obj.db.getSchedulesDueForJob(scheduleId)
369 - .then(function(schedules) {
370 - //obj.debug('ScriptTask', 'Found ' + schedules.length + ' schedules to process. Current time is: ' + Math.floor(new Date() / 1000));
371 - if (schedules.length) {
372 - schedules.forEach(function(s) {
373 - var nextJobTime = obj.determineNextJobTime(s);
374 - var nextJobScheduled = false;
375 - if (nextJobTime === null) {
376 - //obj.debug('ScriptTask', 'Removing Job Schedule for', JSON.stringify(s));
377 - obj.db.removeJobSchedule(s._id);
378 - } else {
379 - //obj.debug('ScriptTask', 'Scheduling Job for', JSON.stringify(s));
380 - obj.db.get(s.scriptId)
381 - .then(function(scripts) {
382 - // if a script is scheduled to run, but a previous run hasn't completed,
383 - // don't schedule another job for the same (device probably offline).
384 - // results in the minimum jobs running once an agent comes back online.
385 - return obj.db.getIncompleteJobsForSchedule(s._id)
386 - .then(function(jobs) {
387 - if (jobs.length > 0) { /* obj.debug('Task', 'Skipping job creation'); */ return Promise.resolve(); }
388 - else { /* obj.debug('Task', 'Creating new job'); */ nextJobScheduled = true; return obj.db.addJob( { scriptId: s.scriptId, scriptName: scripts[0].name, node: s.node, runBy: s.scheduledBy, dontQueueUntil: nextJobTime, jobSchedule: s._id } ); }
389 - });
390 - })
391 - .then(function() {
392 - if (nextJobScheduled) { /* obj.debug('Plugin', 'ScriptTask', 'Updating nextRun time'); */ return obj.db.update(s._id, { nextRun: nextJobTime }); }
393 - else { /* obj.debug('Plugin', 'ScriptTask', 'NOT updating nextRun time'); */ return Promise.resolve(); }
394 - })
395 - .then(function() {
396 - obj.updateFrontEnd( { scriptId: s.scriptId, nodeId: s.node } );
397 - })
398 - .catch(function(ex) { console.log('Task: Error managing job schedules: ', ex); });
399 - }
400 - });
401 - }
402 - });
403 - };
404 -
405 - obj.deleteElement = function (command) {
406 - var delObj = null;
407 - obj.db.get(command.id)
408 - .then(function(found) {
409 - var file = found[0];
410 - delObj = {...{}, ...found[0]};
411 - return file;
412 - })
413 - .then(function(file) {
414 - if (file.type == 'folder') return obj.db.deleteByPath(file.path); //@TODO delete schedules for scripts within folders
415 - if (file.type == 'script') return obj.db.deleteSchedulesForScript(file._id);
416 - if (file.type == 'jobSchedule') return obj.db.deletePendingJobsForSchedule(file._id);
417 - })
418 - .then(function() {
419 - return obj.db.delete(command.id)
420 - })
421 - .then(function() {
422 - var updateObj = { tree: true };
423 - if (delObj.type == 'jobSchedule') {
424 - updateObj.scriptId = delObj.scriptId;
425 - updateObj.nodeId = delObj.node;
426 - }
427 - return obj.updateFrontEnd( updateObj );
428 - })
429 - .catch(function(ex) { console.log('Task: Error deleting ', ex.stack); });
430 - };
431 -
432 - // Process 'task' commands received by an agent
433 - obj.agentAction = function (command, agent) {
434 - console.log('task-agentAction', command);
435 - switch (command.subaction) {
436 - case 'getScript':
437 - // TODO
438 - break;
439 - case 'clearAllPendingTasks':
440 - // TODO
441 - break;
442 - case 'taskComplete':
443 - // TODO
444 - break;
445 - }
446 - }
447 -
448 - obj.serveraction = function(command, myparent, grandparent) {
449 - switch (command.subaction) {
450 - case 'addScript':
451 - obj.db.addScript(command.name, command.content, command.path, command.filetype)
452 - .then(function() { obj.updateFrontEnd( { tree: true } ); });
453 - break;
454 - case 'new':
455 - var parent_path = '', new_path = '';
456 - obj.db.get(command.parent_id)
457 - .then(function(found) { if (found.length > 0) { var file = found[0]; parent_path = file.path; } else { parent_path = 'Shared'; } })
458 - .then(function () { obj.db.addScript(command.name, '', parent_path, command.filetype); })
459 - .then(function() { obj.updateFrontEnd( { tree: true } ); });
460 - break;
461 - case 'rename':
462 - obj.db.get(command.id)
463 - .then(function(docs) {
464 - var doc = docs[0];
465 - if (doc.type == 'folder') {
466 - console.log('old', doc.path, 'new', doc.path.replace(doc.path, command.name));
467 - return obj.db.update(command.id, { path: doc.path.replace(doc.name, command.name) })
468 - .then(function() { // update sub-items
469 - return obj.db.getByPath(doc.path)
470 - })
471 - .then(function(found) {
472 - if (found.length > 0) {
473 - var proms = [];
474 - found.forEach(function(f) { proms.push(obj.db.update(f._id, { path: doc.path.replace(doc.name, command.name) } )); })
475 - return Promise.all(proms);
476 - }
477 - })
478 - } else {
479 - return Promise.resolve();
480 - }
481 - })
482 - .then(function() {
483 - obj.db.update(command.id, { name: command.name })
484 - })
485 - .then(function() {
486 - return obj.db.updateScriptJobName(command.id, command.name);
487 - })
488 - .then(function() {
489 - obj.updateFrontEnd( { scriptId: command.id, nodeId: command.currentNodeId, tree: true } );
490 - });
491 - break;
492 - case 'move':
493 - var toPath = null, fromPath = null, parentType = null;
494 - obj.db.get(command.to)
495 - .then(function(found) { // get target data
496 - if (found.length > 0) {
497 - var file = found[0];
498 - toPath = file.path;
499 - } else throw Error('Target destination not found');
500 - })
501 - .then(function() { // get item to be moved
502 - return obj.db.get(command.id);
503 - })
504 - .then(function(found) { // set item to new location
505 - var file = found[0];
506 - if (file.type == 'folder') {
507 - fromPath = file.path;
508 - toPath += '/' + file.name;
509 - parentType = 'folder';
510 - if (file.name == 'Shared' && file.path == 'Shared') throw Error('Cannot move top level directory: Shared');
511 - }
512 - return obj.db.update(command.id, { path: toPath } );
513 - })
514 - .then(function() { // update sub-items
515 - return obj.db.getByPath(fromPath)
516 - })
517 - .then(function(found) {
518 - if (found.length > 0) {
519 - var proms = [];
520 - found.forEach(function(f) {
521 - proms.push(obj.db.update(f._id, { path: toPath } ));
522 - })
523 - return Promise.all(proms);
524 - }
525 - })
526 - .then(function() {
527 - return obj.updateFrontEnd( { tree: true } );
528 - })
529 - .catch(function(ex) { console.log('Task: Error moving ', ex.stack); });
530 - break;
531 - case 'newFolder':
532 - var parent_path = '';
533 - var new_path = '';
534 -
535 - obj.db.get(command.parent_id)
536 - .then(function(found) {
537 - if (found.length > 0) {
538 - var file = found[0];
539 - parent_path = file.path;
540 - } else {
541 - parent_path = 'Shared';
542 - }
543 - })
544 - .then(function() {
545 - new_path = parent_path + '/' + command.name;
546 - })
547 - .then(function() {
548 - return obj.db.addFolder(command.name, new_path);
549 - })
550 - .then(function () {
551 - return obj.updateFrontEnd( { tree: true } );
552 - })
553 - .catch(function(ex) { console.log('Task: Error creating new folder ', ex.stack); });
554 - break;
555 - case 'delete':
556 - obj.deleteElement(command);
557 - break;
558 - case 'addScheduledJob':
559 - /* {
560 - scriptId: scriptId,
561 - node: s,
562 - scheduledBy: myparent.user.name,
563 - recur: command.recur, // [once, minutes, hourly, daily, weekly, monthly]
564 - interval: x,
565 - daysOfWeek: x, // only used for weekly recur val
566 - // onTheXDay: x, // only used for monthly
567 - startAt: x,
568 - endAt: x,
569 - runCountLimit: x,
570 - lastRun: x,
571 - nextRun: x,
572 - type: "scheduledJob"
573 - } */
574 - var sj = command.schedule;
575 -
576 - var sched = {
577 - scriptId: command.scriptId,
578 - node: null,
579 - scheduledBy: myparent.user.name,
580 - recur: sj.recur,
581 - interval: sj.interval,
582 - daysOfWeek: sj.dayVals,
583 - startAt: sj.startAt,
584 - endAt: sj.endAt,
585 - lastRun: null,
586 - nextRun: null,
587 - type: "jobSchedule"
588 - };
589 - var sel = command.nodes;
590 - var proms = [];
591 - if (Array.isArray(sel)) {
592 - sel.forEach(function(s) {
593 - var sObj = {...sched, ...{ node: s }};
594 - proms.push(obj.db.addJobSchedule( sObj ));
595 - });
596 - } else { test.push(sObj);
597 - proms.push(obj.db.addJobSchedule( sObj ));
598 - }
599 - Promise.all(proms)
600 - .then(function() {
601 - obj.makeJobsFromSchedules();
602 - return Promise.resolve();
603 - })
604 - .catch(function(ex) { console.log('Task: Error adding schedules. The error was: ', ex); });
605 - break;
606 - case 'runScript':
607 - var scriptId = command.scriptId;
608 - var sel = command.nodes;
609 - var proms = [];
610 - if (Array.isArray(sel)) {
611 - sel.forEach(function(s) {
612 - proms.push(obj.db.addJob( { scriptId: scriptId, node: s, runBy: myparent.user.name } ));
613 - });
614 - } else {
615 - proms.push(obj.db.addJob( { scriptId: scriptId, node: sel, runBy: myparent.user.name } ));
616 - }
617 - Promise.all(proms)
618 - .then(function() {
619 - return obj.db.get(scriptId);
620 - })
621 - .then(function(scripts) {
622 - return obj.db.updateScriptJobName(scriptId, scripts[0].name);
623 - })
624 - .then(function() {
625 - obj.resetQueueTimer();
626 - obj.queueRun();
627 - obj.updateFrontEnd( { scriptId: scriptId, nodeId: command.currentNodeId } );
628 - });
629 - break;
630 - case 'getScript':
631 - //obj.debug('ScriptTask', 'getScript Triggered', JSON.stringify(command));
632 - obj.db.get(command.scriptId)
633 - .then(function(script) {
634 - myparent.send(JSON.stringify({
635 - action: 'task',
636 - subaction: 'cacheScript',
637 - nodeid: myparent.dbNodeKey,
638 - rights: true,
639 - sessionid: true,
640 - script: script[0]
641 - }));
642 - });
643 - break;
644 - case 'jobComplete':
645 - //obj.debug('ScriptTask', 'jobComplete Triggered', JSON.stringify(command));
646 - var jobNodeHistory = null, scriptHistory = null;
647 - var jobId = command.jobId, retVal = command.retVal, errVal = command.errVal, dispatchTime = command.dispatchTime;
648 - var completeTime = Math.floor(new Date() / 1000);
649 - obj.db.update(jobId, {
650 - completeTime: completeTime,
651 - returnVal: retVal,
652 - errorVal: errVal,
653 - dispatchTime: dispatchTime
654 - })
655 - .then(function() {
656 - return obj.db.get(jobId)
657 - .then(function(jobs) {
658 - return Promise.resolve(jobs[0].jobSchedule);
659 - })
660 - .then(function(sId) {
661 - if (sId == null) return Promise.resolve();
662 - return obj.db.update(sId, { lastRun: completeTime } )
663 - .then(function() {
664 - obj.makeJobsFromSchedules(sId);
665 - });
666 - });
667 - })
668 - .then(function() {
669 - obj.updateFrontEnd( { scriptId: command.scriptId, nodeId: myparent.dbNodeKey } );
670 - })
671 - .catch(function(ex) { console.log('Task: Failed to complete job. ', ex); });
672 - // update front end by eventing
673 - break;
674 - case 'loadNodeHistory':
675 - obj.updateFrontEnd( { nodeId: command.nodeId } );
676 - break;
677 - case 'loadScriptHistory':
678 - obj.updateFrontEnd( { scriptId: command.scriptId } );
679 - break;
680 - case 'editScript':
681 - obj.db.update(command.scriptId, { type: command.scriptType, name: command.scriptName, content: command.scriptContent })
682 - .then(function() { obj.updateFrontEnd( { scriptId: command.scriptId, tree: true } ); });
683 - break;
684 - case 'clearAllPendingJobs':
685 - obj.db.deletePendingJobsForNode(myparent.dbNodeKey);
686 - break;
687 - case 'loadVariables':
688 - obj.updateFrontEnd( { variables: true } );
689 - break;
690 - case 'newVar':
691 - obj.db.addVariable(command.name, command.scope, command.scopeTarget, command.value)
692 - .then(function() { obj.updateFrontEnd( { variables: true } ); })
693 - break;
694 - case 'editVar':
695 - obj.db.update(command.id, {
696 - name: command.name,
697 - scope: command.scope,
698 - scopeTarget: command.scopeTarget,
699 - value: command.value
700 - })
701 - .then(function() { obj.updateFrontEnd( { variables: true } ); })
702 - break;
703 - case 'deleteVar':
704 - obj.db.delete(command.id)
705 - .then(function() { obj.updateFrontEnd( { variables: true } ); })
706 - break;
707 - default:
708 - console.log('Task: unknown action');
709 - break;
710 - }
711 - };
18
19 return obj;
20 }
\ No newline at end of file
views/default.handlebars
+34 -22
@@ -5537,25 +5537,7 @@
5537 p2downloadDeviceInfo();
5538 } else if (op == 106) {
5539 // Run commands
5540 - var wintype = false, linuxtype = false, agenttype = false, chkNodeIds = getCheckedDevices();
5541 - for (var i in chkNodeIds) {
5542 - var n = getNodeFromId(chkNodeIds[i]);
5543 - if (n.agent) { if ((GetNodeRights(n) & 24) == 24) { agenttype = true; } if ((n.agent.id > 0) && (n.agent.id < 5)) { wintype = true; } else { linuxtype = true; } }
5544 - }
5545 - if ((wintype == true) || (linuxtype == true) || (agenttype == true)) {
5546 - var x = "Run commands on selected devices." + '<br />';
5547 - x += '<select id=d2cmdtype onclick=d2runCommandValidate() style=width:100%;margin-bottom:4px;margin-top:4px>';
5548 - if (wintype == true) { x += '<option value=1>' + "Windows Command Prompt" + '</option><option value=2>' + "Windows PowerShell" + '</option>'; }
5549 - if (linuxtype == true) { x += '<option value=3>' + "Linux/BSD/macOS Command Shell" + '</option>'; }
5550 - if (agenttype == true) { x += '<option value=4>' + "Agent Console" + '</option>'; } // MESHRIGHT_REMOTECONTROL & MESHRIGHT_AGENTCONSOLE are needed
5551 - x += '</select>';
5552 - x += '<select id=d2cmduser style=width:100%;margin-bottom:4px><option value=0>' + "Run as agent" + '</option><option value=1>' + "Run as user, agent if no user" + '</option><option value=2>' + "Must run as user" + '</option></select>';
5553 - x += '<textarea id=d2runcmd style=background-color:#fcf3cf;width:100%;height:200px;resize:none;overflow-y:scroll></textarea>';
5554 - setDialogMode(2, "Run Commands", 3, d2groupActionFunctionRunCommands, x);
5555 - Q('d2runcmd').focus();
5556 - //QE('idx_dlgOkButton', true);
5557 - d2runCommandValidate();
5558 - }
5540 + d2runCommandDialog({ nodeids: getCheckedDevices(), title: "Run commands on selected devices.", func: uncheckAllDevices });
5541 } else if (op == 107) {
5542 // Edit tags
5543 var x = "Perform batch device tag operation" + '<br /><br />';
@@ -5611,7 +5593,6 @@
5593 }
5594 }
5595
5614 - function d2runCommandValidate() { QV('d2cmduser', Q('d2cmdtype').value < 4); }
5596 function d2batchUploadValidate() { QE('idx_dlgOkButton', (Q('d2uploadinput').files.length != 0) && ((Q('d2winuploadpath') == null) || (Q('d2winuploadpath').value != '')) && ((Q('d2linuxuploadpath') == null) || (Q('d2linuxuploadpath').value != ''))); }
5597 function d2batchUploadValidateOk() { Q('d2batchUploadSubmit').click(); }
5598 function d2groupActionFunctionAgentUpdateExec() { meshserver.send({ action: 'updateAgents', nodeids: getCheckedDevices() }); }
@@ -5728,10 +5709,34 @@
5709
5710 function d2groupActionFunctionDelCheck() { QE('idx_dlgOkButton', Q('d2check').checked); }
5711 function d2groupActionFunctionDelExec() { meshserver.send({ action: 'removedevices', nodeids: getCheckedDevices() }); uncheckAllDevices(); }
5731 - function d2groupActionFunctionRunCommands() {
5712 +
5713 + function d2runCommandDialog(options) {
5714 + var wintype = false, linuxtype = false, agenttype = false;
5715 + for (var i in options.nodeids) {
5716 + var n = getNodeFromId(options.nodeids[i]);
5717 + if (n.agent) { if ((GetNodeRights(n) & 24) == 24) { agenttype = true; } if ((n.agent.id > 0) && (n.agent.id < 5)) { wintype = true; } else { linuxtype = true; } }
5718 + }
5719 + if ((wintype == true) || (linuxtype == true) || (agenttype == true)) {
5720 + var x = options.title + '<br />';
5721 + x += '<select id=d2cmdtype onclick=d2runCommandValidate() style=width:100%;margin-bottom:4px;margin-top:4px>';
5722 + if (wintype == true) { x += '<option value=1>' + "Windows Command Prompt" + '</option><option value=2>' + "Windows PowerShell" + '</option>'; }
5723 + if (linuxtype == true) { x += '<option value=3>' + "Linux/BSD/macOS Command Shell" + '</option>'; }
5724 + if (agenttype == true) { x += '<option value=4>' + "Agent Console" + '</option>'; } // MESHRIGHT_REMOTECONTROL & MESHRIGHT_AGENTCONSOLE are needed
5725 + x += '</select>';
5726 + x += '<select id=d2cmduser style=width:100%;margin-bottom:4px><option value=0>' + "Run as agent" + '</option><option value=1>' + "Run as user, agent if no user" + '</option><option value=2>' + "Must run as user" + '</option></select>';
5727 + x += '<textarea id=d2runcmd style=background-color:#fcf3cf;width:100%;height:200px;resize:none;overflow-y:scroll></textarea>';
5728 + setDialogMode(2, "Run Commands", 3, d2groupActionFunctionRunCommands, x, options);
5729 + Q('d2runcmd').focus();
5730 + //QE('idx_dlgOkButton', true);
5731 + d2runCommandValidate();
5732 + }
5733 + }
5734 + function d2runCommandValidate() { QV('d2cmduser', Q('d2cmdtype').value < 4); }
5735 + function d2groupActionFunctionRunCommands(b, options) {
5736 var type = 3;
5737 try { type = parseInt(Q('d2cmdtype').value); } catch (ex) { }
5734 - meshserver.send({ action: 'runcommands', nodeids: getCheckedDevices(), type: type, cmds: Q('d2runcmd').value, runAsUser: parseInt(Q('d2cmduser').value) }); uncheckAllDevices();
5738 + meshserver.send({ action: 'runcommands', nodeids: options.nodeids, type: type, cmds: Q('d2runcmd').value, runAsUser: parseInt(Q('d2cmduser').value) });
5739 + if (options.func) { options.func(); }
5740 }
5741
5742 function onSortSelectChange(skipsave) {
@@ -7174,6 +7179,7 @@
7179 if (((meshrights & (4 + 8 + 64 + 262144)) != 0) && (node.mtype < 3) && ((node.agent == null) || (node.agent.id != 34))) { x += '<input type=button value="' + "Actions" + '" title="' + "Perform power actions on the device" + '" onclick=deviceActionFunction() />'; }
7180 x += '<input type=button value="' + "Notes" + '" title="' + "View notes about this device" + '" onclick=showNotes(' + ((meshrights & 128) == 0) + ',"' + encodeURIComponentEx(node._id) + '") />';
7181 x += '<input type=button value="' + "Log Event" + '" title="' + "Write an event for this device" + '" onclick=writeDeviceEvent("' + encodeURIComponentEx(node._id) + '") />';
7182 + if ((node.mtype == 2) && (connectivity & 1) && (meshrights == 0xFFFFFFFF)) { x += '<input type=button value="' + "Run" + '" title="' + "Run commands on this device" + '" onclick=runDeviceCmd("' + encodeURIComponentEx(node._id) + '") />'; }
7183 if (node.mtype != 4) {
7184 if ((meshrights & 8) && ((connectivity & 1) || ((node.pmt == 1) && ((features2 & 2) != 0)))) { x += '<input type=button value="' + "Message" + '" title="' + "Display a text message on the remote device" + '" onclick=deviceMessageFunction() />'; }
7185 //if ((connectivity & 1) && (meshrights & 8) && (node.agent.id < 5)) { x += '<input type=button value=Toast title="' + "Display a text message of the remote device" + '" onclick=deviceToastFunction() />'; }
@@ -7632,6 +7638,12 @@
7638 return str.join(', ');
7639 }
7640
7641 + // Run commands on current device
7642 + function runDeviceCmd(nodeid) {
7643 + if (xxdialogMode) return;
7644 + d2runCommandDialog({ nodeids: [ decodeURIComponent(nodeid) ], title: "Run commands on this device." });
7645 + }
7646 +
7647 function writeDeviceEvent(nodeid) {
7648 if (xxdialogMode) return;
7649 setDialogMode(2, "Add Device Event", 3, writeDeviceEventEx, '<textarea id=d2devEvent style=background-color:#fcf3cf;width:100%;height:200px;resize:none;overflow-y:scroll></textarea><span style=font-size:10px>' + "This will add an entry to this device's event log." + '<span>', nodeid);
views/task-schedule.handlebars deleted
-249
@@ -1,249 +0,0 @@
1 -<html>
2 -<head>
3 - <script type="text/javascript" src="scripts/common-0.0.1.js"></script>
4 - <link rel="stylesheet" type="text/css" href="/public/tail.DateTime/tail.datetime-default-blue.min.css" />
5 - <style>
6 - body {
7 - font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
8 - color: white;
9 - }
10 -
11 - #scriptContent {
12 - width: 80%;
13 - height: 80%;
14 - }
15 -
16 - #schedContentC {
17 - padding: 20px;
18 - }
19 -
20 - #controlBar button {
21 - cursor: pointer;
22 - }
23 -
24 - #scriptNameC {
25 - padding: 20px;
26 - }
27 -
28 - #scriptName {
29 - width: 300px;
30 - }
31 -
32 - #controlBar {
33 - padding: 5px;
34 - padding-left: 20px;
35 - }
36 -
37 - #left {
38 - height: 100%;
39 - width: 25%;
40 - float: left;
41 - }
42 -
43 - #right {
44 - height: 100%;
45 - width: 75%;
46 - float: right;
47 - }
48 -
49 - body {
50 - background-color: #036;
51 - }
52 -
53 - #intervalListC {
54 - list-style-type: none;
55 - }
56 -
57 - #daysListC {
58 - list-style-type: none;
59 - }
60 -
61 - .rOpt {
62 - height: 40px;
63 - }
64 -
65 - #daysListC {
66 - display: inline-grid;
67 - }
68 -
69 - li {
70 - padding: 2px;
71 - }
72 - </style>
73 -</head>
74 -<body onload="doOnLoad();">
75 - <script type="text/javascript" src="/public/tail.DateTime/tail.datetime.min.js"></script>
76 - <div id="scriptTaskSchedule">
77 - <div id="controlBar">
78 - <button onclick="goSave();">Schedule</button>
79 - <button onclick="goCancel();">Cancel</button>
80 - </div>
81 - <div id="schedContentC">
82 - <div id="left">
83 - <span class="oTitle">Recurrence</span>
84 - <ul id="intervalListC">
85 - <li><label><input onclick="intervalSelected(this);" type="radio" checked name="recur" value="once">Once</label></li>
86 - <li><label><input onclick="intervalSelected(this);" type="radio" name="recur" value="minutes">Minutes</label></li>
87 - <li><label><input onclick="intervalSelected(this);" type="radio" name="recur" value="hourly">Hourly</label></li>
88 - <li><label><input onclick="intervalSelected(this);" type="radio" name="recur" value="daily">Daily</label></li>
89 - <li><label><input onclick="intervalSelected(this);" type="radio" name="recur" value="weekly">Weekly</label></li>
90 - <!-- li><label><input type="radio" name="recur" value="monthly">Monthly</label></li -->
91 - </ul>
92 - </div>
93 - <div id="right">
94 - <div class="rOpt">
95 - <span class="oTitle">Start: </span>
96 - <input type="text" class="datePick" id="startDate" value="" />
97 - <input type="text" class="timePick" id="startTime" value="" />
98 - </div>
99 - <div class="rOpt" id="intervalC" style="display: none;">
100 - <span class="oTitle">Every: </span>
101 - <input type="text" id="interval" value="1" />&nbsp;<span id="hintText"></span>
102 - </div>
103 - <div class="rOpt" id="endC" style="display: none;">
104 - <span class="oTitle">End: </span>
105 - <input type="text" class="datePick" id="endDate" value="" />
106 - <input type="text" class="timePick" id="endTime" value="" />
107 - <label><input type="checkbox" id="endNever" checked onclick="checkEndNever(this);" /> Never</label>
108 - </div>
109 - <div class="rOpt" id="daysC" style="display: none;">
110 - <span class="oTitle">Days: </span>
111 - <ul id="daysListC">
112 - <li><label><input type="checkbox" name="days[]" value="0"> Sunday</label></li>
113 - <li><label><input type="checkbox" name="days[]" value="1"> Monday</label></li>
114 - <li><label><input type="checkbox" name="days[]" value="2"> Tuesday</label></li>
115 - <li><label><input type="checkbox" name="days[]" value="3"> Wednesday</label></li>
116 - <li><label><input type="checkbox" name="days[]" value="4"> Thursday</label></li>
117 - <li><label><input type="checkbox" name="days[]" value="5"> Friday</label></li>
118 - <li><label><input type="checkbox" name="days[]" value="6"> Saturday</label></li>
119 - </ul>
120 - </div>
121 - </div>
122 - </div>
123 - </div>
124 - <script type="text/javascript">
125 -
126 - function checkEndNever(el) {
127 - if (el.checked) {
128 - Q('endDate').value = '';
129 - Q('endTime').value = '';
130 - }
131 - }
132 - function setTimePick() {
133 - var d = new Date();
134 - document.getElementById("startDate").value = d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + d.getDate();
135 - document.getElementById("startTime").value = d.getHours() + ':' + d.getMinutes();
136 - tail.DateTime(".datePick", { position: "bottom", dateStart: Date(), timeFormat: false });
137 - tail.DateTime(".timePick", { position: "bottom", dateFormat: false, timeFormat: "HH:ii", timeStepMinutes: 15 });
138 - tail.datetime.inst[document.getElementById('endDate').getAttribute('data-tail-datetime')].on('change', function () {
139 - document.getElementById('endNever').checked = false;
140 - });
141 - tail.datetime.inst[document.getElementById('endTime').getAttribute('data-tail-datetime')].on('change', function () {
142 - document.getElementById('endNever').checked = false;
143 - });
144 - }
145 -
146 - function doOnLoad() {
147 - try {
148 - if (scriptId == null) {
149 - alert('Page reloaded and data lost. Please re-run scheduler from the main window.');
150 - goCancel();
151 - return;
152 - }
153 - } catch (e) {
154 - alert('Page reloaded and data lost. Please re-run scheduler from the main window.');
155 - goCancel();
156 - return;
157 - }
158 - setTimePick();
159 - }
160 -
161 - function intervalSelected(el) {
162 - var v = el.value;
163 - switch (v) {
164 - case 'once':
165 - QV('intervalC', false);
166 - QV('endC', false);
167 - QV('daysC', false);
168 - break;
169 - case 'minutes':
170 - QV('intervalC', true);
171 - QV('endC', true);
172 - QV('daysC', false);
173 - QH('hintText', 'minute(s)');
174 - break;
175 - case 'hourly':
176 - QV('intervalC', true);
177 - QV('endC', true);
178 - QV('daysC', false);
179 - QH('hintText', 'hour(s)');
180 - break;
181 - case 'daily':
182 - QV('intervalC', true);
183 - QV('endC', true);
184 - QV('daysC', false);
185 - QH('hintText', 'day(s)');
186 - break;
187 - case 'weekly':
188 - QV('intervalC', true);
189 - QV('endC', true);
190 - QV('daysC', true);
191 - QH('hintText', 'week(s)');
192 - break;
193 - }
194 - }
195 - function goSave() {
196 - var o = {};
197 - var recurEls = document.getElementsByName("recur");
198 - recurEls.forEach(function (el) {
199 - if (el.checked) o.recur = el.value;
200 - });
201 - switch (o.recur) {
202 - case 'once':
203 - o.startAt = Date.parse(Q('startDate').value + ' ' + Q('startTime').value);
204 - o.startAt = Math.floor(o.startAt / 1000);
205 - break;
206 - case 'minutes':
207 - case 'hourly':
208 - case 'daily':
209 - o.startAt = Date.parse(Q('startDate').value + ' ' + Q('startTime').value);
210 - o.startAt = Math.floor(o.startAt / 1000);
211 - o.interval = Number(Q('interval').value);
212 - if (Q('endNever').checked) o.endAt = null;
213 - else {
214 - o.endAt = Date.parse(Q('endDate').value + ' ' + Q('endTime').value);
215 - o.endAt = Math.floor(o.endAt / 1000);
216 - }
217 - break;
218 - case 'weekly':
219 - o.startAt = Date.parse(Q('startDate').value + ' ' + Q('startTime').value);
220 - o.startAt = Math.floor(o.startAt / 1000);
221 - o.interval = Number(Q('interval').value);
222 - if (Q('endNever').checked) o.endAt = null;
223 - else {
224 - o.endAt = Date.parse(Q('endDate').value + ' ' + Q('endTime').value);
225 - o.endAt = Math.floor(o.endAt / 1000);
226 - }
227 - var dayEls = document.getElementsByName("days[]");
228 - o.dayVals = [];
229 - if (dayEls.length) {
230 - dayEls.forEach(function (de) {
231 - if (de.checked) o.dayVals.push(de.value);
232 - });
233 - }
234 - break;
235 - }
236 - o.scriptId = scriptId;
237 - o.nodes = nodes;
238 -
239 - window.opener.schedCallback(o);
240 - window.close();
241 - }
242 -
243 - function goCancel() {
244 - window.close();
245 - }
246 -
247 - </script>
248 -</body>
249 -</html>
\ No newline at end of file
views/task-scriptedit.handlebars deleted
-73
@@ -1,73 +0,0 @@
1 -<html>
2 -<head>
3 - <script type="text/javascript" src="scripts/common-0.0.1.js"></script>
4 - <style>
5 - body {
6 - font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
7 - color: white;
8 - }
9 -
10 - #scriptContent {
11 - width: 80%;
12 - height: 80%;
13 - }
14 -
15 - #scriptContentC {
16 - padding: 20px;
17 - }
18 -
19 - #controlBar button {
20 - cursor: pointer;
21 - }
22 -
23 - #scriptNameC {
24 - padding: 20px;
25 - }
26 -
27 - #scriptName {
28 - width: 300px;
29 - }
30 -
31 - #controlBar {
32 - padding: 5px;
33 - padding-left: 20px;
34 - }
35 -
36 - body {
37 - background-color: #036;
38 - }
39 - </style>
40 -</head>
41 -<body onload="doOnLoad();">
42 - <div id="scriptTaskScriptEdit">
43 - <div id="scriptNameC">Script Name: <input type="text" value="" id="scriptName" /></div>
44 - <div id="controlBar">
45 - <button onclick="goSave();">Save</button>
46 - <button onclick="goClose();">Close</button>
47 - </div>
48 - <div id="scriptContentC">
49 - <textarea id="scriptContent"></textarea>
50 - </div>
51 - </div>
52 - <script type="text/javascript">
53 - var scriptData = {{{scriptData}}};
54 -
55 - function doOnLoad() {
56 - //QH('scriptContent', scriptData.content);
57 - Q('scriptContent').value = scriptData.content;
58 - Q('scriptName').value = scriptData.name;
59 - }
60 -
61 - function goSave() {
62 - scriptData.content = Q('scriptContent').value;
63 - scriptData.name = Q('scriptName').value;
64 - window.opener.callback(scriptData);
65 - //goClose();
66 - }
67 -
68 - function goClose() {
69 - window.close();
70 - }
71 - </script>
72 -</body>
73 -</html>
\ No newline at end of file
views/task-user.handlebars deleted
-1070
@@ -1,1070 +0,0 @@
1 -<html>
2 -<head>
3 - <script type="text/javascript" src="scripts/common-0.0.1.js"></script>
4 - <style>
5 - body {
6 - font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
7 - }
8 -
9 - #dropBlock {
10 - /*background-color: gray;
11 - text-align: center;
12 - height: 100%;
13 - width: 100%;
14 - position: absolute;
15 - float:left;*/
16 - width: 100%;
17 - overflow: hidden;
18 - position: absolute;
19 - right: 437px;
20 - padding-top: 100px;
21 - text-align: center;
22 - font-size: 1600%;
23 - color: #AAA;
24 - }
25 -
26 - #scripts_endpoints {
27 - width: 100%;
28 - height: 100%;
29 - }
30 -
31 - #scriptContainer {
32 - width: 35%;
33 - height: 100%;
34 - float: left;
35 - }
36 -
37 - #infoContainer {
38 - width: 65%;
39 - height: 100%;
40 - float: right;
41 - border-left: 1px;
42 - }
43 -
44 - .lifolder {
45 - background: url(data:image/gif;base64,R0lGODlhEAAQAJEDAM2xV/Xur+XPgP///yH5BAEAAAMALAAAAAAQABAAAAJD3ISZIGHWUGihznesYDYATFVM+D2hJ4lgN1olxALAtAlmPCJvuMmJd6PJckDYwicrHhTD5o7plJmg0Uc0asNMkphHAQA7) no-repeat 16px;
46 - padding-left: 35px;
47 - cursor: pointer;
48 - border: none;
49 - margin-top: 1px;
50 - }
51 -
52 - .liscript {
53 - background: url(data:image/gif;base64,R0lGODlhEAAQAJEDAPb19IGBgbq6uv///yH5BAEAAAMALAAAAAAQABAAAAIy3ISpxgcPH2ouQgFEw1YmxnUXKEaaEZZnVWZk66JwzKpvuwZzwOgwb/C1gIOA8Yg8DgoAOw==) no-repeat 16px;
54 - padding-left: 35px;
55 - cursor: pointer;
56 - border: none;
57 - margin-top: 1px;
58 - }
59 -
60 - .liselected {
61 - background-color: lightgray;
62 - }
63 -
64 - #controlBar {
65 - width: 100%;
66 - height: 20px;
67 - border-bottom: 1px solid black;
68 - font-size: smaller;
69 - margin-bottom: 7px;
70 - }
71 -
72 - #controlBar span {
73 - cursor: pointer;
74 - padding-left: 4px;
75 - padding-right: 4px;
76 - }
77 -
78 - #controlBar > span:nth-child(n+2) {
79 - border-left: 1px solid black;
80 - }
81 -
82 - .infoBar {
83 - background-color: #777;
84 - color: white;
85 - cursor: pointer;
86 - padding: 10px;
87 - width: 100%;
88 - border: none;
89 - text-align: left;
90 - outline: none;
91 - font-size: 15px;
92 - }
93 -
94 - .infoBar.active, .infoBar:hover {
95 - background-color: #555;
96 - }
97 -
98 - .infoContent {
99 - display: none;
100 - }
101 -
102 - #nHistTbl, #sHistTbl, #nSchTbl, #sSchTbl, #varTbl {
103 - font-size: smaller;
104 - width: 100%;
105 - }
106 -
107 - #nHistTbl td, #sHistTbl td, #nSchTbl td, #sSchTbl td, #varTbl td {
108 - padding-left: 5px;
109 - padding-right: 5px;
110 - }
111 -
112 - .stNHRow, .stSHRow, .stNSRow, .stSSRow {
113 - height: 36px;
114 - max-height: 40px;
115 - width: 100%;
116 - }
117 -
118 - .stNHRow:nth-child(odd) {
119 - background-color: #CCC;
120 - }
121 -
122 - .stSHRow:nth-child(odd) {
123 - background-color: #CCC;
124 - }
125 -
126 - .stNSRow:nth-child(odd) {
127 - background-color: #CCC;
128 - }
129 -
130 - .stSSRow:nth-child(odd) {
131 - background-color: #CCC;
132 - }
133 -
134 - .stVRow:nth-child(odd) {
135 - background-color: #CCC;
136 - }
137 -
138 - .delSched {
139 - cursor: pointer;
140 - }
141 -
142 - .nIcon {
143 - width: 16px;
144 - margin-top: 1px;
145 - margin-left: 2px;
146 - height: 16px;
147 - display: inline-block;
148 - margin-right: 10px;
149 - }
150 -
151 - .j1 {
152 - background: url(../images/icons16.png) 0px 0px;
153 - height: 16px;
154 - width: 16px;
155 - border: none;
156 - }
157 -
158 - .j2 {
159 - background: url(../images/icons16.png) -16px 0px;
160 - height: 16px;
161 - width: 16px;
162 - border: none;
163 - }
164 -
165 - .j3 {
166 - background: url(../images/icons16.png) -32px 0px;
167 - height: 16px;
168 - width: 16px;
169 - border: none;
170 - }
171 -
172 - .j4 {
173 - background: url(../images/icons16.png) -48px 0px;
174 - height: 16px;
175 - width: 16px;
176 - border: none;
177 - }
178 -
179 - .j5 {
180 - background: url(../images/icons16.png) -64px 0px;
181 - height: 16px;
182 - width: 16px;
183 - border: none;
184 - }
185 -
186 - .j6 {
187 - background: url(../images/icons16.png) -80px 0px;
188 - height: 16px;
189 - width: 16px;
190 - border: none;
191 - }
192 -
193 - .ftype {
194 - font-size: small;
195 - }
196 -
197 - .tblFloat {
198 - float: left;
199 - width: 33%;
200 - }
201 -
202 - .flink {
203 - cursor: pointer;
204 - }
205 - </style>
206 -</head>
207 -<body onload="doOnLoad();">
208 - <div id="scriptTaskUser">
209 - <div id="dropBlock" style="display:none;"><b>&checkmark;</b></div>
210 - Upload: <input type="file" id="files" name="files[]" multiple onchange="fileUpload();" />
211 - <hr />
212 - <div id="controlBar">
213 - <span onclick="goNew();">New</span>
214 - <span onclick="goRename();">Rename</span>
215 - <span onclick="goEdit();">Edit</span>
216 - <span onclick="goDelete();">Delete</span>
217 - <span onclick="goNewFolder();">New Folder</span>
218 - <span onclick="goDownload();">Download</span>
219 - <span onclick="goRun();">Run</span>
220 - </div>
221 - <div id="scripts_endpoints">
222 - <div id="scriptContainer">
223 - </div>
224 - <div id="infoContainer">
225 - <div id="history">
226 - <div class="infoBar">Advanced Run</div>
227 - <div id="multiRun" class="infoContent">
228 - <div style="padding-top: 15px;"><button onclick="goAdvancedRun();">Schedule on Selected</button></div>
229 - <div class="tblFloat">
230 - <table id="mRunTbl" cellspacing="0" cellpadding="0">
231 - <tr><td><label><input type="checkbox" onclick="selAllNodes(this);"> Select All</label></td></tr>
232 - </table>
233 - </div>
234 - <div class="tblFloat">
235 - <table id="mRunTblMesh" cellspacing="0" cellpadding="0">
236 - <tr><td>Meshes</td></tr>
237 - </table>
238 - </div>
239 - <div class="tblFloat">
240 - <table id="mRunTblTag" cellspacing="0" cellpadding="0">
241 - <tr><td>Tags</td></tr>
242 - </table>
243 - </div>
244 - </div>
245 - <div class="infoBar">Node Schedules</div>
246 - <div id="nSch" class="infoContent">
247 - <table id="nSchTbl" cellspacing="0" cellpadding="0">
248 - <th>Script</th>
249 - <th>Author</th>
250 - <th>Every</th>
251 - <th>Starting</th>
252 - <th>Ending</th>
253 - <th>Last Run</th>
254 - <th>Next Run</th>
255 - <th>Action</th>
256 - </table>
257 - </div>
258 - <div class="infoBar">Script Schedules</div>
259 - <div id="sSch" class="infoContent">
260 - <table id="sSchTbl" cellspacing="0" cellpadding="0">
261 - <th>Node</th>
262 - <th>Author</th>
263 - <th>Every</th>
264 - <th>Starting</th>
265 - <th>Ending</th>
266 - <th>Last Run</th>
267 - <th>Next Run</th>
268 - <th>Action</th>
269 - </table>
270 - </div>
271 - <div class="infoBar">Node History</div>
272 - <div id="nodeHistory" class="infoContent">
273 - <table id="nHistTbl" cellspacing="0" cellpadding="0">
274 - <th>Time</th>
275 - <th>Run By</th>
276 - <th>Script</th>
277 - <th>Status</th>
278 - <th>Return Value</th>
279 - </table>
280 - </div>
281 - <div class="infoBar">Script History</div>
282 - <div id="scriptHistory" class="infoContent">
283 - <table id="sHistTbl" cellspacing="0" cellpadding="0">
284 - <th>Time</th>
285 - <th>Run By</th>
286 - <th>Node</th>
287 - <th>Status</th>
288 - <th>Return Value</th>
289 - </table>
290 - </div>
291 - <div class="infoBar">Variables</div>
292 - <div id="variables" class="infoContent">
293 - <table id="varTbl" cellspacing="0" cellpadding="0">
294 - <th>Variable Name</th>
295 - <th>Value</th>
296 - <th>Scope</th>
297 - <th>Scope Target</th>
298 - <th>Action</th>
299 - </table>
300 - <br />
301 - <span class="flink" onclick="newVar();return false;">[+]</span>
302 - </div>
303 - </div>
304 -
305 - </div>
306 - </div>
307 - </div>
308 - <script type="text/javascript">
309 - var scriptTree = {{{scriptTree}}};
310 - var elementDragged = false;
311 - var dragCounter = 0;
312 - var draggedId = null;
313 - var nodesObj = {};
314 - var variables = [];
315 - var varScopes = { global: 'Global', script: 'Script', mesh: 'Mesh', node: 'Node' };
316 -
317 - function onlyUnique(value, index, self) {
318 - return self.indexOf(value) === index;
319 - }
320 - function resizeIframe() {
321 - document.body.style.height = 0;
322 - parent.pluginHandler.scripttask.resizeContent();
323 - }
324 - function updateNodesTable() {
325 - let dRows = document.querySelectorAll('.stNodeRow');
326 - dRows.forEach((r) => {
327 - r.parentNode.removeChild(r);
328 - });
329 - var tagList = [];
330 - var nodeRowIns = document.querySelector('#mRunTbl');
331 - parent.nodes.forEach(function(i) {
332 - var item = {...i, ...{}};
333 - if (item.mtype == 2) {
334 - item.meshName = parent.meshes[item['meshid']].name;
335 - if (item._id == parent.currentNode._id) item.checked = 'checked '; else item.checked = '';
336 - let tpl = `<tr class="stNodeRow"><td><label><input type="checkbox" ${item.checked} name="runOn[]" value="${item._id}"> <div class="nIcon j${item.icon}"></div>${item.name}</label></td></tr>`;
337 - nodeRowIns.insertAdjacentHTML('beforeend', tpl);
338 - if (i.tags && i.tags.length) item.tags.forEach(function(t) { tagList.push(t) });
339 - nodesObj[i._id] = i;
340 - }
341 - });
342 - tagList = tagList.filter(onlyUnique); tagList = tagList.sort();
343 - var nodeRowIns = document.querySelector('#mRunTblMesh');
344 - for (const i in parent.meshes) { // parent.meshes.forEach(function(i) {
345 - var item = {...parent.meshes[i], ...{}};
346 - if (item.mtype == 2) {
347 - let tpl = `<tr class="stNodeRow"><td><label><input type="checkbox" onclick="selNodesByMesh(this);" value="${item._id}"> ${item.name}</label></td></tr>`;
348 - nodeRowIns.insertAdjacentHTML('beforeend', tpl);
349 - }
350 - }
351 - var nodeRowIns = document.querySelector('#mRunTblTag');
352 - tagList.forEach(function(i) {
353 - let tpl = `<tr class="stNodeRow"><td><label><input type="checkbox" onclick="selNodesByTag(this)" value="${i}"> ${i}</label></td></tr>`;
354 - nodeRowIns.insertAdjacentHTML('beforeend', tpl);
355 - });
356 - }
357 -
358 - function selNodesByTag(el) {
359 - var t = el.value;
360 - var allNodes = Q('mRunTbl').querySelectorAll('input[type="checkbox"][name="runOn[]"]');
361 - var checked = false;
362 - if (el.checked) checked = true;
363 - allNodes.forEach(function(n) {
364 - if (nodesObj[n.value].tags && nodesObj[n.value].tags.indexOf(t) > -1) n.checked = checked;
365 - });
366 - return true;
367 - }
368 - function selNodesByMesh(el) {
369 - var mid = el.value;
370 - var allNodes = Q('mRunTbl').querySelectorAll('input[type="checkbox"][name="runOn[]"]');
371 - var checked = false;
372 - if (el.checked) checked = true;
373 - allNodes.forEach(function(n) {
374 - if (nodesObj[n.value].meshid == mid) n.checked = checked;
375 - });
376 - return true;
377 - }
378 - function selAllNodes(el) {
379 - var allNodes = Q('mRunTbl').querySelectorAll('input[type="checkbox"][name="runOn[]"]');
380 - var checked = false;
381 - if (el.checked) checked = true;
382 - allNodes.forEach(function(n) {
383 - n.checked = checked;
384 - });
385 - return true;
386 - }
387 -
388 - function doOnLoad() {
389 - redrawScriptTree();
390 - selectPreviouslySelectedScript();
391 - updateNodesTable();
392 - parent.meshserver.send({ 'action': 'plugin', 'plugin': 'scripttask', 'pluginaction': 'loadNodeHistory', 'nodeId': parent.currentNode._id });
393 - parent.meshserver.send({ 'action': 'plugin', 'plugin': 'scripttask', 'pluginaction': 'loadVariables', 'nodeId': parent.currentNode._id });
394 - }
395 -
396 - function selectPreviouslySelectedScript() {
397 - var sel_item = parent.getstore('_scripttask_sel_item', null)
398 - if (sel_item != null) {
399 - var s = document.getElementById(sel_item);
400 - if (s != null) {
401 - s.classList.toggle('liselected');
402 - goScript(s);
403 - }
404 - }
405 - if (sel_item != null) parent.meshserver.send({ 'action': 'plugin', 'plugin': 'scripttask', 'pluginaction': 'loadScriptHistory', 'scriptId': sel_item });
406 - }
407 -
408 - function goRun() {
409 - var selScript = document.querySelectorAll('.liselected');
410 - if (selScript.length) {
411 - var scriptId = selScript[0].getAttribute('x-data-id');
412 - if (scriptId == selScript[0].getAttribute('x-folder-id'))
413 - {
414 - parent.setDialogMode(2, "Oops!", 1, null, 'Please select a script. A folder is currently selected.');
415 - }
416 - else {
417 - parent.meshserver.send({ 'action': 'plugin', 'plugin': 'scripttask', 'pluginaction': 'runScript', 'scriptId': scriptId, 'nodes': [ parent.currentNode._id ], 'currentNodeId': parent.currentNode._id });
418 - }
419 - } else {
420 - parent.setDialogMode(2, "Oops!", 1, null, 'No script has been selected to run on the machines.');
421 - }
422 - }
423 -
424 - function goEdit() {
425 - var selScript = document.querySelectorAll('.liselected');
426 - if (selScript.length && (selScript[0].getAttribute('x-data-id') != selScript[0].getAttribute('x-data-folder'))) {
427 - var scriptId = selScript[0].getAttribute('x-data-id');
428 - window.open('/pluginadmin.ashx?pin=scripttask&user=1&edit=1&id=' + scriptId, '_blank');
429 - window.callback = function(sd) {
430 - parent.meshserver.send({ 'action': 'plugin', 'plugin': 'scripttask', 'pluginaction': 'editScript', 'scriptId': sd._id, 'scriptType': sd.type, 'scriptName': sd.name, 'scriptContent': sd.content, 'currentNodeId': parent.currentNode._id });
431 - };
432 - } else {
433 - parent.setDialogMode(2, "Oops!", 1, null, 'No script has been selected to edit.');
434 - }
435 - }
436 -
437 - function goAdvancedRun() {
438 - var cboxes = document.getElementsByName("runOn[]");
439 - var sel = [];
440 -
441 - cboxes.forEach((n) => {
442 - if (n.checked) sel.push(n.value);
443 - });
444 - if (sel.length == 0) {
445 - parent.setDialogMode(2, "Oops!", 1, null, 'No machines have been selected.');
446 - return;
447 - }
448 - var selScript = document.querySelectorAll('.liselected');
449 - if (selScript.length) {
450 - var scriptId = selScript[0].getAttribute('x-data-id');
451 - var sWin = window.open('/pluginadmin.ashx?pin=scripttask&user=1&schedule=1', 'schedule', "width=800,height=600");
452 - sWin.scriptId = scriptId;
453 - sWin.nodes = sel;
454 - window.schedCallback = function(opts) {
455 - parent.meshserver.send({
456 - 'action': 'plugin',
457 - 'plugin': 'scripttask',
458 - 'pluginaction': 'addScheduledJob',
459 - 'scriptId': opts.scriptId,
460 - 'nodes': opts.nodes,
461 - 'currentNodeId': parent.currentNode._id,
462 - 'schedule': opts
463 - });
464 - };
465 - } else {
466 - parent.setDialogMode(2, "Oops!", 1, null, 'No script has been selected to run on the machines.');
467 - }
468 - }
469 -
470 - var coll = document.getElementsByClassName("infoBar");
471 - for (var i = 0; i < coll.length; i++) {
472 - coll[i].addEventListener("click", function() {
473 - this.classList.toggle("active");
474 - var content = this.nextElementSibling;
475 - if (content.style.display === "block") {
476 - content.style.display = "none";
477 - } else {
478 - content.style.display = "block";
479 - }
480 - content.style.maxHeight = '300px';
481 - content.style.overflowY = 'scroll';
482 - resizeIframe();
483 - });
484 - }
485 -
486 - function goDownload() {
487 - var isSelected = document.querySelectorAll('.liselected');
488 - if (isSelected.length == 0) return;
489 - var sel = isSelected[0];
490 - var id = sel.getAttribute('x-data-id');
491 - if (id == sel.getAttribute('x-data-folder')) return;
492 - window.location = '/pluginadmin.ashx?pin=scripttask&user=1&dl='+id;
493 - }
494 - function addScript(name, content, path) {
495 - // file type testing
496 - var n = name.split('.').pop().toLowerCase();
497 - if (content.split('\n')[0][0] == '#' && content.split('\n')[0][1] == '!') n = 'bash';
498 - if (['ps1', 'bat', 'bash'].indexOf(n) !== -1) {
499 - parent.meshserver.send({ action: 'plugin', plugin: 'scripttask', pluginaction: 'addScript', name: name, content: content, path: path, filetype: n });
500 - }
501 - else {
502 - parent.setDialogMode(2, "Oops!", 1, null, 'Currently accepted filetypes are .ps1, .bat, and bash scripts.');
503 - }
504 - }
505 - function redrawScriptTree() {
506 - var lastpath = null;
507 - var str = '';
508 - var indent = 0;
509 - var folder_id = null;
510 - scriptTree.forEach(function(f) {
511 - if (f.path != lastpath && f.type == 'folder') {
512 - indent = (f.path.match(/\//g) || []).length + 1;
513 - var name = f.path.match(/[^\/]+$/);
514 - folder_id = f._id;
515 - str += '<div draggable="true" x-data-path="' + f.path + '" x-data-id="' + f._id + '" x-data-folder="' + folder_id + '" style="margin-left: ' + indent + 'em;" class="lifolder" onclick="toggleCollapse(this);"><span class="fname">' + name + '</span></div>';
516 - lastpath = f.path;
517 - indent += 1;
518 - }
519 - if (f.type != 'folder') {
520 - str += '<div id="' + f._id + '" draggable="true" x-data-path="' + f.path + '" x-data-id="' + f._id + '" x-data-folder="' + folder_id + '" style="margin-left: ' + indent + 'em;" class="liscript" onclick="goScript(this);"><span class="fname">' + f.name + '</span> [<span class="ftype">' + f.filetype + '</span>]</div>';
521 - }
522 - document.getElementById('scriptContainer').innerHTML = str;
523 - });
524 -
525 - var liScripts = document.querySelectorAll('.liscript');
526 - var liFolders = document.querySelectorAll('.lifolder');
527 - liScripts.forEach(function(el) {
528 - el.addEventListener('mousedown', function() { elementDragged = true; });
529 - el.addEventListener('mouseup', function() { elementDragged = false; });
530 - el.addEventListener('dragstart', function(evt) { evt.dataTransfer.setData('text/plain', evt.target.getAttribute('x-data-id')); });
531 - });
532 - liFolders.forEach(function(el) {
533 - el.addEventListener('drop', dropMove);
534 - el.addEventListener('dragover', function(e) { e.preventDefault(); });
535 - el.addEventListener('mousedown', function() { elementDragged = true; });
536 - el.addEventListener('mouseup', function() { elementDragged = false; });
537 - el.addEventListener('dragstart', function(evt) { evt.dataTransfer.setData('text/plain', evt.target.getAttribute('x-data-id')); });
538 - });
539 - resizeIframe();
540 - selectPreviouslySelectedScript();
541 - }
542 - parent.pluginHandler.scripttask.newScriptTree = function(message) {
543 - scriptTree = message.event.tree;
544 - redrawScriptTree();
545 - }
546 - parent.pluginHandler.scripttask.loadHistory = function(message) {
547 - // cache script names
548 - var nNames = {};
549 - parent.nodes.forEach(function(n){
550 - nNames[n._id] = n.name;
551 - });
552 - if (message.event.nodeHistory != null && message.event.nodeId == parent.currentNode._id) {
553 - var nHistTbl = document.getElementById('nHistTbl');
554 - var rows = nHistTbl.querySelectorAll('.stNHRow');
555 - if (rows.length) {
556 - rows.forEach(function(r) {
557 - r.parentNode.removeChild(r);
558 - });
559 - }
560 - if (message.event.nodeHistory.length) {
561 - message.event.nodeHistory.forEach(function(nh) {
562 - nh.latestTime = Math.max(nh.completeTime, nh.queueTime, nh.dispatchTime, nh.dontQueueUntil);
563 - });
564 - message.event.nodeHistory.sort((a, b) => (a.latestTime < b.latestTime) ? 1 : -1);
565 - message.event.nodeHistory.forEach(function(nh) {
566 - nh = prepHistory(nh);
567 - let tpl = '<td>' + nh.timeStr + '</td> \
568 - <td>' + nh.runBy + '</td> \
569 - <td>' + nh.scriptName + '</td> \
570 - <td>' + nh.statusTxt + '</td> \
571 - <td>' + nh.returnTxt + '</td>';
572 - let tr = nHistTbl.insertRow(-1);
573 - tr.innerHTML = tpl;
574 - tr.classList.add('stNHRow');
575 - });
576 - }
577 - }
578 - var currentScript = document.getElementById('scriptHistory');
579 - var currentScriptId = currentScript.getAttribute('x-data-id');
580 - if (message.event.scriptHistory != null && message.event.scriptId == currentScriptId) {
581 - var sHistTbl = document.getElementById('sHistTbl');
582 - var rows = sHistTbl.querySelectorAll('.stSHRow');
583 - if (rows.length) {
584 - rows.forEach(function(r) {
585 - r.parentNode.removeChild(r);
586 - });
587 - }
588 - if (message.event.scriptHistory.length) {
589 - message.event.scriptHistory.forEach(function(nh) {
590 - nh.latestTime = Math.max(nh.completeTime, nh.queueTime, nh.dispatchTime, nh.dontQueueUntil);
591 - });
592 - message.event.scriptHistory.sort((a, b) => (a.latestTime < b.latestTime) ? 1 : -1);
593 - message.event.scriptHistory.forEach(function(nh) {
594 - nh = prepHistory(nh);
595 - let tpl = '<td>' + nh.timeStr + '</td> \
596 - <td>' + nh.runBy + '</td> \
597 - <td>' + nNames[nh.node] + '</td> \
598 - <td>' + nh.statusTxt + '</td> \
599 - <td>' + nh.returnTxt + '</td>';
600 - let tr = sHistTbl.insertRow(-1);
601 - tr.innerHTML = tpl;
602 - tr.classList.add('stSHRow');
603 - });
604 - }
605 - }
606 - resizeIframe();
607 - }
608 - function prepHistory(nh) {
609 - var nowTime = Math.floor(new Date() / 1000);
610 - var d = new Date(0);
611 - d.setUTCSeconds(nh.latestTime);
612 - nh.timeStr = d.toLocaleString();
613 - if (nh.errorVal != null) { nh.returnTxt = nh.errorVal; } else { nh.returnTxt = nh.returnVal; }
614 - nh.statusTxt = 'Queued';
615 - if (nh.dispatchTime != null) nh.statusTxt = 'Running';
616 - if (nh.errorVal != null) nh.statusTxt = 'Error';
617 - if (nh.returnVal != null) nh.statusTxt = 'Completed';
618 - if (nh.dontQueueUntil > nowTime) nh.statusTxt = 'Scheduled';
619 - if (nh.returnTxt == null) nh.returnTxt = '&nbsp;';
620 - if (nh.statusTxt == 'Completed') {
621 - nh.statusTxt = '<span title="Completed ' + secondsToHms((nh.completeTime - nh.dispatchTime)) + '">' + nh.statusTxt + '</span>';
622 - }
623 - if (isJsonString(nh.returnTxt)) {
624 - try {
625 - nh.returnObj = JSON.parse(nh.returnTxt);
626 - nh.returnTxt = 'Object: ';
627 - nh.returnTxt += '' + JSON.stringify(nh.returnObj, null, 2);
628 - nh.returnTxt = nh.returnTxt.replace(/\n\s*\n/g, '\n');
629 - nh.returnTxt = nh.returnTxt.replace(/(?:\r\n|\r|\n)/g, '<br />');
630 - } catch(e) { }
631 - } else {
632 - if (typeof nh.returnTxt == 'string') {
633 - nh.returnTxt = nh.returnTxt.replace(/\n\s*\n/g, '\n');
634 - nh.returnTxt = nh.returnTxt.replace(/(?:\r\n|\r|\n)/g, '<br />');
635 - }
636 - }
637 - return nh;
638 - }
639 - parent.pluginHandler.scripttask.loadVariables = function(message) {
640 - if (message.event.vars.length) {
641 - var vars = message.event.vars;
642 - vars.forEach(function(vd) {
643 - switch (vd.scope) {
644 - case 'global':
645 - vd.scopeTargetTxt = vd.scopeTargetHtml = 'N/A';
646 - break;
647 - case 'script':
648 - var s = scriptTree.filter(obj => { return obj._id === vd.scopeTarget })[0]
649 - vd.scopeTargetHtml = '<span title="' + s.path + '">' + s.name + '</span>';
650 - vd.scopeTargetTxt = s.name;
651 - break;
652 - case 'mesh':
653 - vd.scopeTargetTxt = vd.scopeTargetHtml = parent.meshes[vd.scopeTarget].name;
654 - break;
655 - case 'node':
656 - var n = parent.nodes.filter(obj => { return obj._id === vd.scopeTarget })[0]
657 - vd.scopeTargetHtml = '<span title="' + n.meshnamel + '">' + n.name + '</span>';
658 - vd.scopeTargetTxt = n.name;
659 - break;
660 - default:
661 - vd.scopeTargetTxt = vd.scopeTargetHtml = 'N/A';
662 - break;
663 - }
664 - vd.scopeTxt = varScopes[vd.scope];
665 - })
666 - var ordering = { 'global': 0, 'script': 1, 'mesh': 2, 'node': 3 }
667 - vars.sort((a, b) => {
668 - return (ordering[a.scope] - ordering[b.scope])
669 - || a.name.localeCompare(b.name)
670 - || a.scopeTargetTxt.localeCompare(b.scopeTargetTxt);
671 - });
672 - variables = vars;
673 - parseVariables();
674 - }
675 - }
676 - function parseVariables() {
677 - var vTbl = document.getElementById('varTbl');
678 - var rows = vTbl.querySelectorAll('.stVRow');
679 -
680 - if (rows.length) {
681 - rows.forEach(function(r) {
682 - r.parentNode.removeChild(r);
683 - });
684 - }
685 - var scriptEl = document.querySelectorAll('.liselected');
686 - if (scriptEl.length != 1) return;
687 - var el = scriptEl[0];
688 - scopeTargetScriptId = el.getAttribute('x-data-id');
689 - variables.forEach(function(vd) {
690 - if (vd.scope == 'script' && vd.scopeTarget != scopeTargetScriptId) return;
691 - if (vd.scope == 'mesh' && vd.scopeTarget != parent.currentNode.meshid) return;
692 - if (vd.scope == 'node' && vd.scopeTarget != parent.currentNode._id) return;
693 - let actionHtml = '<span class="flink" onclick="editVar(this);">Edit</span> <span class="flink" onclick="delVar(this);">Delete</span>';
694 - let tpl = '<td>' + vd.name + '</td> \
695 - <td>' + vd.value + '</td> \
696 - <td>' + vd.scopeTxt + '</td> \
697 - <td>' + vd.scopeTargetHtml + '</td> \
698 - <td>' + actionHtml + '</td>';
699 - let tr = vTbl.insertRow(-1);
700 - tr.innerHTML = tpl;
701 - tr.classList.add('stVRow');
702 - tr.setAttribute('x-data-id', vd._id);
703 - })
704 - }
705 - parent.pluginHandler.scripttask.loadSchedule = function(message) {
706 - // cache script names
707 - var nNames = {}, sNames = {};
708 - parent.nodes.forEach(function(n){
709 - nNames[n._id] = n.name;
710 - });
711 - scriptTree.forEach(function(s) {
712 - if (s.type == 'script') sNames[s._id] = s.name;
713 - });
714 - if (message.event.nodeSchedule != null && message.event.nodeId == parent.currentNode._id) {
715 - var nTbl = document.getElementById('nSchTbl');
716 - var rows = nTbl.querySelectorAll('.stNSRow');
717 - if (rows.length) {
718 - rows.forEach(function(r) {
719 - r.parentNode.removeChild(r);
720 - });
721 - }
722 - if (message.event.nodeSchedule.length) {
723 - message.event.nodeSchedule.forEach(function(nh) {
724 - nh = prepSchedule(nh);
725 - let tpl = '<td>' + sNames[nh.scriptId] + '</td> \
726 - <td>' + nh.scheduledBy + '</td> \
727 - <td>' + nh.everyTxt + '</td> \
728 - <td>' + nh.startedTxt + '</td> \
729 - <td>' + nh.endingTxt + '</td> \
730 - <td>' + nh.lastRunTxt + '</td> \
731 - <td>' + nh.nextRunTxt + '</td> \
732 - <td>' + nh.actionTxt + '</td>';
733 - let tr = nTbl.insertRow(-1);
734 - tr.innerHTML = tpl;
735 - tr.classList.add('stNSRow');
736 - tr.setAttribute('x-data-id', nh._id);
737 - });
738 - }
739 - }
740 - var currentScript = document.getElementById('scriptHistory');
741 - var currentScriptId = currentScript.getAttribute('x-data-id');
742 - if (message.event.scriptSchedule != null && message.event.scriptId == currentScriptId) {
743 - var sTbl = document.getElementById('sSchTbl');
744 - var rows = sTbl.querySelectorAll('.stSSRow');
745 - if (rows.length) {
746 - rows.forEach(function(r) {
747 - r.parentNode.removeChild(r);
748 - });
749 - }
750 - if (message.event.scriptSchedule.length) {
751 - message.event.scriptSchedule.forEach(function(nh) {
752 - nh = prepSchedule(nh);
753 - let tpl = '<td>' + nNames[nh.node] + '</td> \
754 - <td>' + nh.scheduledBy + '</td> \
755 - <td>' + nh.everyTxt + '</td> \
756 - <td>' + nh.startedTxt + '</td> \
757 - <td>' + nh.endingTxt + '</td> \
758 - <td>' + nh.lastRunTxt + '</td> \
759 - <td>' + nh.nextRunTxt + '</td> \
760 - <td>' + nh.actionTxt + '</td>';
761 - let tr = sTbl.insertRow(-1);
762 - tr.innerHTML = tpl;
763 - tr.classList.add('stSSRow');
764 - tr.setAttribute('x-data-id', nh._id);
765 - });
766 - }
767 - }
768 - resizeIframe();
769 - }
770 - function prepSchedule(nh) {
771 - nh.everyTxt = nh.interval + ' ';
772 - switch (nh.recur) {
773 - case 'once':
774 - nh.everyTxt = 'Once';
775 - break;
776 - case 'minutes':
777 - nh.everyTxt += 'minute';
778 - break;
779 - case 'hourly':
780 - nh.everyTxt += 'hour';
781 - break;
782 - case 'daily':
783 - nh.everyTxt += 'day';
784 - break;
785 - case 'weekly':
786 - nh.everyTxt += 'week';
787 - break;
788 - case 'monthly':
789 - nh.everyTxt += 'month';
790 - break;
791 - }
792 - if (nh.interval > 1) nh.everyTxt += 's';
793 -
794 - if (nh.recur == 'weekly') {
795 - nh.daysOfWeek = nh.daysOfWeek.map(el => Number(el));
796 - nh.everyTxt += ' (';
797 - nh.daysOfWeek.forEach(function(num) {
798 - switch(num) {
799 - case 0: nh.everyTxt += 'S'; break;
800 - case 1: nh.everyTxt += 'M'; break;
801 - case 2: nh.everyTxt += 'T'; break;
802 - case 3: nh.everyTxt += 'W'; break;
803 - case 4: nh.everyTxt += 'R'; break;
804 - case 5: nh.everyTxt += 'F'; break;
805 - case 6: nh.everyTxt += 'S'; break;
806 - }
807 - });
808 - nh.everyTxt += ')';
809 - }
810 -
811 - var d = new Date(0); d.setUTCSeconds(nh.startAt);
812 - nh.startedTxt = d.toLocaleString();
813 - d = new Date(0); d.setUTCSeconds(nh.endAt);
814 - nh.endingTxt = d.toLocaleString();
815 - if (nh.endAt == null) nh.endingTxt = 'Never';
816 - if (nh.recur == 'once') nh.endingTxt = 'After first run';
817 - d = new Date(0); d.setUTCSeconds(nh.lastRun);
818 - nh.lastRunTxt = d.toLocaleString();
819 - if (nh.lastRun == null) nh.lastRunTxt = 'Never';
820 - d = new Date(0); d.setUTCSeconds(nh.nextRun);
821 - nh.nextRunTxt = d.toLocaleString();
822 - if (nh.nextRun == null) nh.nextRunTxt = 'Never';
823 - if (nh.nextRun < nh.lastRun) nh.nextRunTxt = 'Running now';
824 -
825 - nh.actionTxt = '<span class="delSched" onclick="deleteSchedule(this);">Delete</span>';
826 - return nh;
827 - }
828 - function secondsToHms(d) {
829 - d = Number(d);
830 - if (d == 0) return "immediately";
831 - var h = Math.floor(d / 3600);
832 - var m = Math.floor(d % 3600 / 60);
833 - var s = Math.floor(d % 3600 % 60);
834 -
835 - var hDisplay = h > 0 ? h + (h == 1 ? " hour, " : " hours, ") : "";
836 - var mDisplay = m > 0 ? m + (m == 1 ? " minute, " : " minutes, ") : "";
837 - var sDisplay = s > 0 ? s + (s == 1 ? " second" : " seconds") : "";
838 - return "in " + hDisplay + mDisplay + sDisplay;
839 - }
840 - function isJsonString(str) {
841 - try {
842 - JSON.parse(str);
843 - } catch (e) {
844 - return false;
845 - }
846 - return true;
847 - }
848 - function newVarEx() {
849 - var name = parent.document.getElementById('stvarname').value;
850 - var scope = parent.document.getElementById('stvarscope').value;
851 - var value = parent.document.getElementById('stvarvalue').value;
852 - var scopeTarget = null;
853 - if (scope == 'script') {
854 - var scriptEl = document.querySelectorAll('.liselected');
855 - if (scriptEl.length != 1) return;
856 - var el = scriptEl[0];
857 - scopeTarget = el.getAttribute('x-data-id');
858 - } else if (scope == 'mesh') {
859 - scopeTarget = parent.currentNode.meshid;
860 - } else if (scope == 'node') {
861 - scopeTarget = parent.currentNode._id;
862 - }
863 - parent.meshserver.send({ action: 'plugin', plugin: 'scripttask', pluginaction: 'newVar', name: name, scope: scope, scopeTarget: scopeTarget, value: value, currentNodeId: parent.currentNode._id });
864 -
865 - }
866 - function newVar() {
867 - parent.setDialogMode(2, "New Variable", 3, newVarEx, 'Variable Name: <input type="text" id=stvarname /><br />Scope: <select id=stvarscope><option value="global">Global</option><option value="script">Script</option><option value="mesh">Mesh</option><option value="node">Node</option></select><br />Value: <input id="stvarvalue" type="text" />');
868 - parent.focusTextBox('stvarname');
869 - }
870 - function editVarEx() {
871 - var varid = parent.document.getElementById('stvarid').value;
872 - var name = parent.document.getElementById('stvarname').value;
873 - var scope = parent.document.getElementById('stvarscope').value;
874 - var value = parent.document.getElementById('stvarvalue').value;
875 - var scopeTarget = null;
876 - if (scope == 'script') {
877 - var scriptEl = document.querySelectorAll('.liselected');
878 - if (scriptEl.length != 1) return;
879 - var el = scriptEl[0];
880 - scopeTarget = el.getAttribute('x-data-id');
881 - } else if (scope == 'mesh') {
882 - scopeTarget = parent.currentNode.meshid;
883 - } else if (scope == 'node') {
884 - scopeTarget = parent.currentNode._id;
885 - }
886 - parent.meshserver.send({ action: 'plugin', plugin: 'scripttask', pluginaction: 'editVar', id: varid, name: name, scope: scope, scopeTarget: scopeTarget, value: value, currentNodeId: parent.currentNode._id });
887 - }
888 - function editVar(el) {
889 - var vid = el.parentNode.parentNode.getAttribute('x-data-id');
890 - var v = variables.filter(obj => { return obj._id === vid })[0];
891 - var soptHtml = '';
892 - for (const [k, t] of Object.entries(varScopes)) {
893 - soptHtml += '<option value="' + k + '"';
894 - if (v.scope == k) soptHtml += ' selected';
895 - soptHtml += '>' + t + '</option>';
896 - }
897 - parent.setDialogMode(2, "Edit Variable", 3, editVarEx, 'Variable Name: <input type="text" id=stvarname value="' + v.name + '" /><br />Scope: <select id=stvarscope>' + soptHtml + '</select><br />Value: <input id="stvarvalue" type="text" value="' + v.value + '" /><input type="hidden" id="stvarid" value="' + vid + '" />');
898 - parent.focusTextBox('stvarname');
899 - }
900 - function delVarEx() {
901 - var varid = parent.document.getElementById('stvarid').value;
902 - parent.meshserver.send({ action: 'plugin', plugin: 'scripttask', pluginaction: 'deleteVar', id: varid, currentNodeId: parent.currentNode._id });
903 -
904 - }
905 - function delVar(el) {
906 - var vid = el.parentNode.parentNode.getAttribute('x-data-id');
907 - var v = variables.filter(obj => { return obj._id === vid })[0];
908 - parent.setDialogMode(2, "Delete Variable", 3, delVarEx, 'Are you sure you want to delete this?<input type="hidden" id="stvarid" value="' + vid + '" /><br />Name: '+ v.name +'<br />Scope: '+ varScopes[v.scope] +'<br />Value: '+ v.value);
909 - }
910 - function renameEx() {
911 - var name = parent.document.getElementById('stfilename').value;
912 - var id = parent.document.getElementById('stid').value;
913 - parent.meshserver.send({ action: 'plugin', plugin: 'scripttask', pluginaction: 'rename', name: name, id: id, currentNodeId: parent.currentNode._id });
914 - }
915 - function goRename() {
916 - var scriptEl = document.querySelectorAll('.liselected');
917 - if (scriptEl.length != 1) return;
918 - var el = scriptEl[0];
919 - var name = el.querySelector('.fname').innerHTML;
920 - var id = el.getAttribute('x-data-id');
921 - parent.setDialogMode(2, "Rename " + name, 3, renameEx, '<input type="text" value="' + name + '" id=stfilename style=width:100% /><input type="hidden" id="stid" value="' + id + '" />');
922 - parent.focusTextBox('stfilename');
923 - }
924 - function newEx() {
925 - var name = parent.document.getElementById('stfilename').value;
926 - var parent_id = parent.document.getElementById('stfolderid').value;
927 - var fileType = parent.document.getElementById('stfiletype').value;
928 - parent.meshserver.send({ action: 'plugin', plugin: 'scripttask', pluginaction: 'new', name: name, parent_id: parent_id, filetype: fileType, currentNodeId: parent.currentNode._id });
929 - }
930 - function goNew() {
931 - var scriptEl = document.querySelectorAll('.liselected');
932 - var folder_id = null;
933 - if (scriptEl.length > 0) {
934 - var el = scriptEl[0];
935 - folder_id = el.getAttribute('x-data-folder');
936 - }
937 - parent.setDialogMode(2, "New Script", 3, newEx, 'Name: <input type="text" value="' + name + '" id=stfilename style=width:100% /><br />Type:<select id="stfiletype"><option value="bash">Bash</option><option value="bat">BAT</option><option value="ps1">PS1</option></select><input type="hidden" id="stfolderid" value="' + folder_id + '" />');
938 - parent.focusTextBox('stfilename');
939 - }
940 - function newFolderEx() {
941 - var name = parent.document.getElementById('stfoldername').value;
942 - var parent_id = parent.document.getElementById('stfolderid').value;
943 - parent.meshserver.send({ action: 'plugin', plugin: 'scripttask', pluginaction: 'newFolder', name: name, parent_id: parent_id });
944 - }
945 - function goNewFolder() {
946 - var scriptEl = document.querySelectorAll('.liselected');
947 - var folder_id = null;
948 - if (scriptEl.length > 0) {
949 - var el = scriptEl[0];
950 - folder_id = el.getAttribute('x-data-folder');
951 - }
952 - parent.setDialogMode(2, "New Folder", 3, newFolderEx, '<input type="text" value="" id=stfoldername style=width:100% /><input type="hidden" id="stfolderid" value="' + folder_id + '" />');
953 - parent.focusTextBox('stfoldername');
954 - }
955 - function goScript(el) {
956 - var xdi = el.getAttribute('x-data-id');
957 - var scriptEls = document.querySelectorAll('.liselected');
958 - parent.putstore('_scripttask_sel_item', xdi);
959 - scriptEls.forEach(function(e) {
960 - e.classList.remove('liselected');
961 - })
962 - el.classList.add('liselected');
963 - Q('scriptHistory').setAttribute('x-data-id', el.getAttribute('x-data-id'));
964 - if (xdi != el.getAttribute('x-data-folder')) {
965 - parent.meshserver.send({ action: 'plugin', plugin: 'scripttask', pluginaction: 'loadScriptHistory', scriptId: xdi });
966 - }
967 - parseVariables();
968 - }
969 - function deleteEx() {
970 - var id = parent.document.getElementById('stdelid').value;
971 - parent.meshserver.send({ action: 'plugin', plugin: 'scripttask', pluginaction: 'delete', id: id });
972 - }
973 - function goDelete() {
974 - var els = document.querySelectorAll('.liselected');
975 - if (els.length == 0) return;
976 - var el = els[0];
977 - var name = el.innerHTML;
978 - var id = el.getAttribute('x-data-id');
979 - parent.setDialogMode(2, "Delete " + name, 3, deleteEx, 'Are you sure? <input type="hidden" id="stdelid" value="' + id + '" />');
980 - }
981 - function deleteScheduleEx() {
982 - var id = parent.document.getElementById('stdelid').value;
983 - parent.meshserver.send({ action: 'plugin', plugin: 'scripttask', pluginaction: 'delete', id: id });
984 - }
985 - function deleteSchedule(el) {
986 - var id = el.parentNode.parentNode.getAttribute('x-data-id');
987 - parent.setDialogMode(2, "Delete Schedule", 3, deleteScheduleEx, 'Are you sure you want to delete this schedule? <input type="hidden" id="stdelid" value="' + id + '" />');
988 - }
989 - function toggleCollapse(el) {
990 - var xdf = el.getAttribute('x-data-path');
991 - var folderEls = document.querySelectorAll('.lifolder, .liscript');
992 - var showHide = null;
993 - folderEls.forEach(function(e){
994 - if (e === el) return;
995 - if (e.getAttribute('x-data-path').indexOf(xdf) !== -1) {
996 - if (e.style.display == 'none') {
997 - if (showHide === null) showHide = '';
998 - } else {
999 - if (showHide === null) showHide = 'none';
1000 - }
1001 - e.style.display = showHide;
1002 - }
1003 - });
1004 - goScript(el);
1005 - }
1006 - function handleFileSelect(evt) {
1007 - evt.preventDefault();
1008 - var files = evt.dataTransfer.files; // FileList object
1009 - // files is a FileList of File objects. List some properties.
1010 - QV('dropBlock', false);
1011 - var output = [];
1012 - fileUpload(files);
1013 - elementDragged = false;
1014 - if (dragTimer != null) dragTimer = null;
1015 - //document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>';
1016 - }
1017 -
1018 - function fileUpload(files) {
1019 - if (files == null) files = document.getElementById('files').files;
1020 - var path = null;
1021 - var isSelected = document.querySelectorAll('.liselected');
1022 - if (isSelected.length) {
1023 - var sel = isSelected[0];
1024 - path = sel.getAttribute('x-data-path');
1025 - }
1026 - for (var i = 0, f; f = files[i]; i++) {
1027 - var reader = new FileReader();
1028 - reader.fileName = f.name;
1029 - reader.readAsBinaryString(f);
1030 - reader.addEventListener('loadend', function(e, file){
1031 - addScript(e.currentTarget.fileName, e.currentTarget.result, path);
1032 - });
1033 - }
1034 - }
1035 -
1036 - var dropZone = document.getElementById('scriptTaskUser');
1037 - var dropBlock = document.getElementById('dropBlock');
1038 - var dragTimer = null;
1039 - function allowDrag(e) {
1040 - if (!elementDragged) { // Test that the item being dragged is a valid one
1041 - e.dataTransfer.dropEffect = 'copy';
1042 - QV('dropBlock', true);
1043 - e.preventDefault();
1044 - clearTimeout(dragTimer);
1045 - dragTimer = setTimeout(function(){ dragCounter = 0; QV('dropBlock', false); }, 100);
1046 - }
1047 - }
1048 -
1049 - function dropMove(evt) {
1050 - const move_id = evt.dataTransfer.getData('text');
1051 - const container_id = evt.target.parentNode.getAttribute('x-data-id');
1052 - parent.meshserver.send({ action: 'plugin', plugin: 'scripttask', pluginaction: 'move', id: move_id, to: container_id });
1053 - }
1054 - // file upload events
1055 - window.addEventListener('dragenter', function(e) {
1056 - dragCounter++;
1057 - });
1058 - dropZone.addEventListener('dragenter', allowDrag);
1059 - dropZone.addEventListener('dragover', allowDrag);
1060 - dropZone.addEventListener('dragleave', function(e) {
1061 - dragCounter--;
1062 - if (dragCounter == 0) {
1063 - QV('dropBlock', false);
1064 - elementDragged = false;
1065 - }
1066 - });
1067 - dropZone.addEventListener('drop', handleFileSelect);
1068 - </script>
1069 -</body>
1070 -</html>
\ No newline at end of file