You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1263 lines
48 KiB

////// ----------------------- Connection "class" ---------------------- ////////////
/* jshint browser:true */
/* global document*/
/* global console*/
/* global session*/
/* global window*/
/* global location*/
/* global setTimeout*/
/* global clearTimeout*/
/* global io*/
/* global $*/
/* global socketNamespace */
/* global socketUrl */
/* global socketSession */
/* global storage */
/* jshint -W097 */// jshint strict:false
'use strict';
// The idea of servConn is to use this class later in every addon.
// The addon just must say, what must be loaded (values, objects, indexes) and
// the class loads it for addon. Authentication will be done automatically, so addon does not care about it.
// It will be .js file with localData and servConn
var servConn = {
_socket: null,
_onConnChange: null,
_onUpdate: null,
_isConnected: false,
_disconnectedSince: null,
_connCallbacks: {
onConnChange: null,
onUpdate: null,
onRefresh: null,
onAuth: null,
onCommand: null,
onError: null
},
_authInfo: null,
_isAuthDone: false,
_isAuthRequired: false,
_authRunning: false,
_cmdQueue: [],
_connTimer: null,
_type: 'socket.io', // [SignalR | socket.io | local]
_timeout: 0, // 0 - use transport default timeout to detect disconnect
_reconnectInterval: 10000, // reconnect interval
_reloadInterval: 30, // if connection was absent longer than 30 seconds
_cmdData: null,
_cmdInstance: null,
_isSecure: false,
_defaultMode: 0x644,
_useStorage: false,
_objects: null, // used if _useStorage === true
_enums: null, // used if _useStorage === true
namespace: 'vis.0',
getType: function () {
return this._type;
},
getIsConnected: function () {
return this._isConnected;
},
getIsLoginRequired: function () {
return this._isSecure;
},
getUser: function () {
return this._user;
},
setReloadTimeout: function (timeout){
this._reloadInterval = parseInt(timeout, 10);
},
setReconnectInterval: function (interval){
this._reconnectInterval = parseInt(interval, 10);
},
_checkConnection: function (func, _arguments) {
if (!this._isConnected) {
console.log('No connection!');
return false;
}
if (this._queueCmdIfRequired(func, _arguments)) return false;
//socket.io
if (this._socket === null) {
console.log('socket.io not initialized');
return false;
}
return true;
},
_monitor: function () {
if (this._timer) return;
var ts = (new Date()).getTime();
if (this._reloadInterval && ts - this._lastTimer > this._reloadInterval * 1000) {
// It seems, that PC was in a sleep => Reload page to request authentication anew
window.location.reload();
} else {
this._lastTimer = ts;
}
var that = this;
this._timer = setTimeout(function () {
that._timer = null;
that._monitor();
}, 10000);
},
_onAuth: function (objectsRequired, isSecure) {
var that = this;
this._isSecure = isSecure;
if (this._isSecure) {
that._lastTimer = (new Date()).getTime();
this._monitor();
}
this._socket.emit('subscribe', '*');
if (objectsRequired) this._socket.emit('subscribeObjects', '*');
if (this._isConnected === true) {
// This seems to be a reconnect because we're already connected!
// -> prevent firing onConnChange twice
return;
}
this._isConnected = true;
if (this._connCallbacks.onConnChange) {
setTimeout(function () {
that._socket.emit('authEnabled', function (auth, user) {
that._user = user;
that._connCallbacks.onConnChange(that._isConnected);
if (typeof app !== 'undefined') app.onConnChange(that._isConnected);
});
}, 0);
}
},
reconnect: function (connOptions) {
var that = this;
// reconnect
if ((!connOptions.mayReconnect || connOptions.mayReconnect()) && !this._connectInterval) {
this._connectInterval = setInterval(function () {
console.log('Trying connect...');
that._socket.connect();
that._countDown = Math.floor(that._reconnectInterval / 1000);
if (typeof $ !== 'undefined') {
$('.splash-screen-text').html(that._countDown + '...').css('color', 'red');
}
}, this._reconnectInterval);
this._countDown = Math.floor(this._reconnectInterval / 1000);
if (typeof $ !== 'undefined') {
$('.splash-screen-text').html(this._countDown + '...');
}
this._countInterval = setInterval(function () {
that._countDown--;
if (typeof $ !== 'undefined') {
$('.splash-screen-text').html(that._countDown + '...');
}
}, 1000);
}
},
init: function (connOptions, connCallbacks, objectsRequired) {
var that = this; // support of old safary
// init namespace
if (typeof socketNamespace !== 'undefined') this.namespace = socketNamespace;
connOptions = connOptions || {};
if (!connOptions.name) connOptions.name = this.namespace;
// To start vis as local use one of:
// - start vis from directory with name local, e.g. c:/blbla/local/yunkong2.vis/www/index.html
// - do not create "_socket/info.js" file in "www" directory
// - create "_socket/info.js" file with
// var socketUrl = "local"; var socketSession = ""; sysLang="en";
// in this case you can overwrite browser language settings
if (document.URL.split('/local/')[1] || (typeof socketUrl === 'undefined' && !connOptions.connLink) || (typeof socketUrl !== 'undefined' && socketUrl === 'local')) {
this._type = 'local';
}
if (typeof session !== 'undefined') {
var user = session.get('user');
if (user) {
that._authInfo = {
user: user,
hash: session.get('hash'),
salt: session.get('salt')
};
}
}
this._connCallbacks = connCallbacks;
var connLink = connOptions.connLink || window.localStorage.getItem('connLink');
// Connection data from "/_socket/info.js"
if (!connLink && typeof socketUrl !== 'undefined') connLink = socketUrl;
if (!connOptions.socketSession && typeof socketSession !== 'undefined') connOptions.socketSession = socketSession;
if (connOptions.socketForceWebSockets === undefined &&
typeof socketForceWebSockets !== 'undefined') {
connOptions.socketForceWebSockets = socketForceWebSockets;
}
// if no remote data
if (this._type === 'local') {
// report connected state
this._isConnected = true;
if (this._connCallbacks.onConnChange) this._connCallbacks.onConnChange(this._isConnected);
if (typeof app !== 'undefined') app.onConnChange(this._isConnected);
} else
if (typeof io !== 'undefined') {
connOptions.socketSession = connOptions.socketSession || 'nokey';
var url;
if (connLink) {
url = connLink;
if (typeof connLink !== 'undefined') {
if (connLink[0] === ':') connLink = location.protocol + '://' + location.hostname + connLink;
}
} else {
url = location.protocol + '//' + location.host;
}
this._socket = io.connect(url, {
query: 'key=' + connOptions.socketSession,
'reconnection limit': 10000,
'max reconnection attempts': Infinity,
reconnection: false,
upgrade: !connOptions.socketForceWebSockets,
rememberUpgrade: connOptions.socketForceWebSockets,
transports: connOptions.socketForceWebSockets ? ['websocket'] : undefined
});
this._socket.on('connect', function () {
if (that._disconnectedSince) {
var offlineTime = (new Date()).getTime() - that._disconnectedSince;
console.log('was offline for ' + (offlineTime / 1000) + 's');
// reload whole page if no connection longer than some period
if (that._reloadInterval && offlineTime > that._reloadInterval * 1000) window.location.reload();
that._disconnectedSince = null;
}
if (that._connectInterval) {
clearInterval(that._connectInterval);
that._connectInterval = null;
}
if (that._countInterval) {
clearInterval(that._countInterval);
that._countInterval = null;
}
var elem = document.getElementById('server-disconnect');
if (elem) elem.style.display = 'none';
that._socket.emit('name', connOptions.name);
console.log((new Date()).toISOString() + ' Connected => authenticate');
setTimeout(function () {
var wait = setTimeout(function() {
console.error('No answer from server')
window.location.reload();
}, 3000);
that._socket.emit('authenticate', function (isOk, isSecure) {
clearTimeout(wait);
console.log((new Date()).toISOString() + ' Authenticated: ' + isOk);
if (isOk) {
that._onAuth(objectsRequired, isSecure);
} else {
console.log('permissionError');
}
});
}, 50);
});
this._socket.on('reauthenticate', function () {
if (that._connCallbacks.onConnChange) {
that._connCallbacks.onConnChange(false);
if (typeof app !== 'undefined') app.onConnChange(false);
}
console.warn('reauthenticate');
window.location.reload();
});
this._socket.on('connect_error', function () {
$('.splash-screen-text').css('color', '#002951');
that.reconnect(connOptions);
});
this._socket.on('disconnect', function () {
that._disconnectedSince = (new Date()).getTime();
// called only once when connection lost (and it was here before)
that._isConnected = false;
if (that._connCallbacks.onConnChange) {
setTimeout(function () {
var elem = document.getElementById('server-disconnect');
if (elem) elem.style.display = '';
that._connCallbacks.onConnChange(that._isConnected);
if (typeof app !== 'undefined') app.onConnChange(that._isConnected);
}, 5000);
} else {
var elem = document.getElementById('server-disconnect');
if (elem) elem.style.display = '';
}
// reconnect
that.reconnect(connOptions);
});
// after reconnect the "connect" event will be called
this._socket.on('reconnect', function () {
var offlineTime = (new Date()).getTime() - that._disconnectedSince;
console.log('was offline for ' + (offlineTime / 1000) + 's');
// reload whole page if no connection longer than one minute
if (that._reloadInterval && offlineTime > that._reloadInterval * 1000) {
window.location.reload();
}
// anyway "on connect" is called
});
this._socket.on('objectChange', function (id, obj) {
// If cache used
if (that._useStorage && typeof storage !== 'undefined') {
var objects = that._objects || storage.get('objects');
if (objects) {
if (obj) {
objects[id] = obj;
} else {
if (objects[id]) delete objects[id];
}
storage.set('objects', objects);
}
}
if (that._connCallbacks.onObjectChange) that._connCallbacks.onObjectChange(id, obj);
});
this._socket.on('stateChange', function (id, state) {
if (!id || state === null || typeof state !== 'object') return;
if (that._connCallbacks.onCommand && id === that.namespace + '.control.command') {
if (state.ack) return;
if (state.val &&
typeof state.val === 'string' &&
state.val[0] === '{' &&
state.val[state.val.length - 1] === '}') {
try {
state.val = JSON.parse(state.val);
} catch (e) {
console.log('Command seems to be an object, but cannot parse it: ' + state.val);
}
}
// if command is an object {instance: 'iii', command: 'cmd', data: 'ddd'}
if (state.val && state.val.instance) {
if (that._connCallbacks.onCommand(state.val.instance, state.val.command, state.val.data)) {
// clear state
that.setState(id, {val: '', ack: true});
}
} else {
if (that._connCallbacks.onCommand(that._cmdInstance, state.val, that._cmdData)) {
// clear state
that.setState(id, {val: '', ack: true});
}
}
} else if (id === that.namespace + '.control.data') {
that._cmdData = state.val;
} else if (id === that.namespace + '.control.instance') {
that._cmdInstance = state.val;
} else if (that._connCallbacks.onUpdate) {
that._connCallbacks.onUpdate(id, state);
}
});
this._socket.on('permissionError', function (err) {
if (that._connCallbacks.onError) {
/* {
command:
type:
operation:
arg:
}*/
that._connCallbacks.onError(err);
} else {
console.log('permissionError');
}
});
}
},
logout: function (callback) {
if (!this._isConnected) {
console.log('No connection!');
return;
}
this._socket.emit('logout', callback);
},
getVersion: function (callback) {
if (!this._checkConnection('getVersion', arguments)) return;
this._socket.emit('getVersion', function (version) {
if (callback) callback(version);
});
},
_checkAuth: function (callback) {
if (!this._isConnected) {
console.log('No connection!');
return;
}
//socket.io
if (this._socket === null) {
console.log('socket.io not initialized');
return;
}
this._socket.emit('getVersion', function (version) {
if (callback)
callback(version);
});
},
readFile: function (filename, callback, isRemote) {
if (!callback) throw 'No callback set';
if (this._type === 'local') {
try {
var data = storage.get(filename);
callback(null, data ? JSON.parse(storage.get(filename)) : null);
} catch (err) {
callback(err, null);
}
} else {
if (!this._checkConnection('readFile', arguments)) return;
if (!isRemote && typeof app !== 'undefined') {
app.readLocalFile(filename.replace(/^\/vis\.0\//, ''), callback);
} else {
var adapter = this.namespace;
if (filename[0] === '/') {
var p = filename.split('/');
adapter = p[1];
p.splice(0, 2);
filename = p.join('/');
}
this._socket.emit('readFile', adapter, filename, function (err, data, mimeType) {
setTimeout(function () {
callback(err, data, filename, mimeType);
}, 0);
});
}
}
},
getMimeType: function (ext) {
if (ext.indexOf('.') !== -1) ext = ext.toLowerCase().match(/\.[^.]+$/);
var _mimeType;
if (ext === '.css') {
_mimeType = 'text/css';
} else if (ext === '.bmp') {
_mimeType = 'image/bmp';
} else if (ext === '.png') {
_mimeType = 'image/png';
} else if (ext === '.jpg') {
_mimeType = 'image/jpeg';
} else if (ext === '.jpeg') {
_mimeType = 'image/jpeg';
} else if (ext === '.gif') {
_mimeType = 'image/gif';
} else if (ext === '.tif') {
_mimeType = 'image/tiff';
} else if (ext === '.js') {
_mimeType = 'application/javascript';
} else if (ext === '.html') {
_mimeType = 'text/html';
} else if (ext === '.htm') {
_mimeType = 'text/html';
} else if (ext === '.json') {
_mimeType = 'application/json';
} else if (ext === '.xml') {
_mimeType = 'text/xml';
} else if (ext === '.svg') {
_mimeType = 'image/svg+xml';
} else if (ext === '.eot') {
_mimeType = 'application/vnd.ms-fontobject';
} else if (ext === '.ttf') {
_mimeType = 'application/font-sfnt';
} else if (ext === '.woff') {
_mimeType = 'application/font-woff';
} else if (ext === '.wav') {
_mimeType = 'audio/wav';
} else if (ext === '.mp3') {
_mimeType = 'audio/mpeg3';
} else {
_mimeType = 'text/javascript';
}
return _mimeType;
},
readFile64: function (filename, callback, isRemote) {
var that = this;
if (!callback) {
throw 'No callback set';
}
if (!this._checkConnection('readFile', arguments)) return;
if (!isRemote && typeof app !== 'undefined') {
app.readLocalFile(filename.replace(/^\/vis\.0\//, ''), function (err, data, mimeType) {
setTimeout(function () {
if (data) {
callback(err, {mime: mimeType || that.getMimeType(filename), data: btoa(data)}, filename);
} else {
callback(err, filename);
}
}, 0);
});
} else {
var adapter = this.namespace;
if (filename[0] === '/') {
var p = filename.split('/');
adapter = p[1];
p.splice(0, 2);
filename = p.join('/');
}
this._socket.emit('readFile64', adapter, filename, function (err, data, mimeType) {
setTimeout(function () {
if (data) {
callback(err, {mime: mimeType || that.getMimeType(filename), data: data}, filename);
} else {
callback(err, {mime: mimeType || that.getMimeType(filename)}, filename);
}
}, 0);
});
}
},
writeFile: function (filename, data, mode, callback) {
if (typeof mode === 'function') {
callback = mode;
mode = null;
}
if (this._type === 'local') {
storage.set(filename, JSON.stringify(data));
if (callback) callback();
} else {
if (!this._checkConnection('writeFile', arguments)) return;
if (typeof data === 'object') data = JSON.stringify(data, null, 2);
var parts = filename.split('/');
var adapter = parts[1];
parts.splice(0, 2);
if (adapter === 'vis') {
this._socket.emit('writeFile', adapter, parts.join('/'), data, mode ? {mode: this._defaultMode} : {}, callback);
} else {
this._socket.emit('writeFile', this.namespace, filename, data, mode ? {mode: this._defaultMode} : {}, callback);
}
}
},
// Write file base 64
writeFile64: function (filename, data, callback) {
if (!this._checkConnection('writeFile', arguments)) return;
var parts = filename.split('/');
var adapter = parts[1];
parts.splice(0, 2);
this._socket.emit('writeFile', adapter, parts.join('/'), atob(data), {mode: this._defaultMode}, callback);
},
readDir: function (dirname, callback) {
//socket.io
if (this._socket === null) {
console.log('socket.io not initialized');
return;
}
if (!dirname) dirname = '/';
var parts = dirname.split('/');
var adapter = parts[1];
parts.splice(0, 2);
this._socket.emit('readDir', adapter, parts.join('/'), {filter: true}, function (err, data) {
if (callback) callback(err, data);
});
},
mkdir: function (dirname, callback) {
var parts = dirname.split('/');
var adapter = parts[1];
parts.splice(0, 2);
this._socket.emit('mkdir', adapter, parts.join('/'), function (err) {
if (callback) callback(err);
});
},
unlink: function (name, callback) {
var parts = name.split('/');
var adapter = parts[1];
parts.splice(0, 2);
this._socket.emit('unlink', adapter, parts.join('/'), function (err) {
if (callback) callback(err);
});
},
renameFile: function (oldname, newname, callback) {
var parts1 = oldname.split('/');
var adapter = parts1[1];
parts1.splice(0, 2);
var parts2 = newname.split('/');
parts2.splice(0, 2);
this._socket.emit('rename', adapter, parts1.join('/'), parts2.join('/'), function (err) {
if (callback) callback(err);
});
},
setState: function (pointId, value, callback) {
//socket.io
if (this._socket === null) {
//console.log('socket.io not initialized');
return;
}
this._socket.emit('setState', pointId, value, callback);
},
// callback(err, data)
getStates: function (IDs, callback) {
if (typeof IDs === 'function') {
callback = IDs;
IDs = null;
}
if (this._type === 'local') {
return callback(null, []);
} else {
if (!this._checkConnection('getStates', arguments)) return;
this.gettingStates = this.gettingStates || 0;
this.gettingStates++;
if (this.gettingStates > 1) {
// fix for slow devices
console.log('Trying to get empty list, because the whole list could not be loaded');
IDs = [];
}
var that = this;
this._socket.emit('getStates', IDs, function (err, data) {
that.gettingStates--;
if (err || !data) {
if (callback) {
callback(err || 'Authentication required');
}
} else if (callback) {
callback(null, data);
}
});
}
},
_fillChildren: function (objects) {
var items = [];
for (var id in objects) {
items.push(id);
}
items.sort();
for (var i = 0; i < items.length; i++) {
if (objects[items[i]].common) {
var j = i + 1;
var children = [];
var len = items[i].length + 1;
var name = items[i] + '.';
while (j < items.length && items[j].substring(0, len) === name) {
children.push(items[j++]);
}
objects[items[i]].children = children;
}
}
},
// callback(err, data)
getObjects: function (useCache, callback) {
if (typeof useCache === 'function') {
callback = useCache;
useCache = false;
}
// If cache used
if (this._useStorage && useCache) {
if (typeof storage !== 'undefined') {
var objects = this._objects || storage.get('objects');
if (objects) return callback(null, objects);
} else if (this._objects) {
return callback(null, this._objects);
}
}
if (!this._checkConnection('getObjects', arguments)) return;
var that = this;
this._socket.emit('getObjects', function (err, data) {
// Read all enums
that._socket.emit('getObjectView', 'system', 'enum', {startkey: 'enum.', endkey: 'enum.\u9999'}, function (err, res) {
if (err) {
callback(err);
return;
}
var result = {};
var enums = {};
for (var i = 0; i < res.rows.length; i++) {
data[res.rows[i].id] = res.rows[i].value;
enums[res.rows[i].id] = res.rows[i].value;
}
// Read all adapters for images
that._socket.emit('getObjectView', 'system', 'instance', {startkey: 'system.adapter.', endkey: 'system.adapter.\u9999'}, function (err, res) {
if (err) {
callback(err);
return;
}
var result = {};
for (var i = 0; i < res.rows.length; i++) {
data[res.rows[i].id] = res.rows[i].value;
}
// find out default file mode
if (data['system.adapter.' + that.namespace] &&
data['system.adapter.' + that.namespace].native &&
data['system.adapter.' + that.namespace].native.defaultFileMode) {
that._defaultMode = data['system.adapter.' + that.namespace].native.defaultFileMode;
}
// Read all channels for images
that._socket.emit('getObjectView', 'system', 'channel', {startkey: '', endkey: '\u9999'}, function (err, res) {
if (err) {
callback(err);
return;
}
var result = {};
for (var i = 0; i < res.rows.length; i++) {
data[res.rows[i].id] = res.rows[i].value;
}
// Read all devices for images
that._socket.emit('getObjectView', 'system', 'device', {startkey: '', endkey: '\u9999'}, function (err, res) {
if (err) {
callback(err);
return;
}
var result = {};
for (var i = 0; i < res.rows.length; i++) {
data[res.rows[i].id] = res.rows[i].value;
}
if (that._useStorage) {
that._fillChildren(data);
that._objects = data;
that._enums = enums;
if (typeof storage !== 'undefined') {
storage.set('objects', data);
storage.set('enums', enums);
storage.set('timeSync', (new Date()).getTime());
}
}
if (callback) callback(err, data);
});
});
});
});
});
},
getChildren: function (id, useCache, callback) {
if (!this._checkConnection('getChildren', arguments)) return;
if (typeof id === 'function') {
callback = id;
id = null;
useCache = false;
}
if (typeof id === 'boolean') {
callback = useCache;
useCache = id;
id = null;
}
if (typeof useCache === 'function') {
callback = useCache;
useCache = false;
}
if (!id) return callback('getChildren: no id given');
var that = this;
var data = [];
if (this._useStorage && useCache) {
if (typeof storage !== 'undefined') {
var objects = storage.get('objects');
if (objects && objects[id] && objects[id].children) {
return callback(null, objects[id].children);
}
} else if (this._objects && this._objects[id] && this._objects[id].children) {
return callback(null, this._objects[id].children);
}
}
// Read all devices
that._socket.emit('getObjectView', 'system', 'device', {startkey: id + '.', endkey: id + '.\u9999'}, function (err, res) {
if (err) {
callback(err);
return;
}
var result = {};
for (var i = 0; i < res.rows.length; i++) {
data[res.rows[i].id] = res.rows[i].value;
}
that._socket.emit('getObjectView', 'system', 'channel', {startkey: id + '.', endkey: id + '.\u9999'}, function (err, res) {
if (err) {
callback(err);
return;
}
var result = {};
for (var i = 0; i < res.rows.length; i++) {
data[res.rows[i].id] = res.rows[i].value;
}
// Read all adapters for images
that._socket.emit('getObjectView', 'system', 'state', {startkey: id + '.', endkey: id + '.\u9999'}, function (err, res) {
if (err) {
callback(err);
return;
}
var result = {};
for (var i = 0; i < res.rows.length; i++) {
data[res.rows[i].id] = res.rows[i].value;
}
var list = [];
var count = id.split('.').length;
// find direct children
for (var _id in data) {
var parts = _id.split('.');
if (count + 1 === parts.length) {
list.push(_id);
}
}
list.sort();
if (this._useStorage && typeof storage !== 'undefined') {
var objects = storage.get('objects') || {};
for (var id_ in data) {
objects[id_] = data[id_];
}
if (objects[id] && objects[id].common) {
objects[id].children = list;
}
// Store for every element theirs children
var items = [];
for (var __id in data) {
items.push(__id);
}
items.sort();
for (var k = 0; k < items.length; k++) {
if (objects[items[k]].common) {
var j = k + 1;
var children = [];
var len = items[k].length + 1;
var name = items[k] + '.';
while (j < items.length && items[j].substring(0, len) === name) {
children.push(items[j++]);
}
objects[items[k]].children = children;
}
}
storage.set('objects', objects);
}
if (callback) callback(err, list);
}.bind(this));
}.bind(this));
}.bind(this));
},
getObject: function (id, useCache, callback) {
if (typeof id === 'function') {
callback = id;
id = null;
useCache = false;
}
if (typeof id === 'boolean') {
callback = useCache;
useCache = id;
id = null;
}
if (typeof useCache === 'function') {
callback = useCache;
useCache = false;
}
if (!id) return callback('no id given');
// If cache used
if (this._useStorage && useCache && typeof storage !== 'undefined') {
if (typeof storage !== 'undefined') {
var objects = this._objects || storage.get('objects');
if (objects && objects[id]) return callback(null, objects[id]);
} else if (this._enums) {
return callback(null, this._enums);
}
}
this._socket.emit('getObject', id, function (err, obj) {
if (err) {
callback(err);
return;
}
if (this._useStorage && typeof storage !== 'undefined') {
var objects = storage.get('objects') || {};
objects[id] = obj;
storage.set('objects', objects);
}
return callback(null, obj);
}.bind(this));
},
getEnums: function (enumName, useCache, callback) {
if (typeof enumName === 'function') {
callback = enumName;
enumName = null;
useCache = false;
}
if (typeof enumName === 'boolean') {
callback = useCache;
useCache = enumName;
enumName = null;
}
if (typeof useCache === 'function') {
callback = useCache;
useCache = false;
}
// If cache used
if (this._useStorage && useCache) {
if (typeof storage !== 'undefined') {
var enums = this._enums || storage.get('enums');
if (enums) return callback(null, enums);
} else if (this._enums) {
return callback(null, this._enums);
}
}
if (this._type === 'local') {
return callback(null, []);
} else {
enumName = enumName ? enumName + '.' : '';
// Read all enums
this._socket.emit('getObjectView', 'system', 'enum', {startkey: 'enum.' + enumName, endkey: 'enum.' + enumName + '\u9999'}, function (err, res) {
if (err) {
callback(err);
return;
}
var enums = {};
for (var i = 0; i < res.rows.length; i++) {
var obj = res.rows[i].value;
enums[obj._id] = obj;
}
if (this._useStorage && typeof storage !== 'undefined') {
storage.set('enums', enums);
}
callback(null, enums);
}.bind(this));
}
},
// return time when the objects were synchronized
getSyncTime: function () {
if (this._useStorage && typeof storage !== 'undefined') {
var timeSync = storage.get('timeSync');
if (timeSync) return new Date(timeSync);
}
return null;
},
addObject: function (objId, obj, callback) {
if (!this._isConnected) {
console.log('No connection!');
return;
}
//socket.io
if (this._socket === null) {
console.log('socket.io not initialized');
return;
}
},
delObject: function (objId) {
if (!this._checkConnection('delObject', arguments)) return;
this._socket.emit('delObject', objId);
},
httpGet: function (url, callback) {
if (!this._isConnected) {
console.log('No connection!');
return;
}
//socket.io
if (this._socket === null) {
console.log('socket.io not initialized');
return;
}
this._socket.emit('httpGet', url, function (data) {
if (callback) callback(data);
});
},
logError: function (errorText) {
console.log("Error: " + errorText);
if (!this._isConnected) {
//console.log('No connection!');
return;
}
//socket.io
if (this._socket === null) {
console.log('socket.io not initialized');
return;
}
this._socket.emit('log', 'error', 'Addon DashUI ' + errorText);
},
_queueCmdIfRequired: function (func, args) {
var that = this;
if (!this._isAuthDone) {
// Queue command
this._cmdQueue.push({func: func, args: args});
if (!this._authRunning) {
this._authRunning = true;
// Try to read version
this._checkAuth(function (version) {
// If we have got version string, so there is no authentication, or we are authenticated
that._authRunning = false;
if (version) {
that._isAuthDone = true;
// Repeat all stored requests
var __cmdQueue = that._cmdQueue;
// Trigger GC
that._cmdQueue = null;
that._cmdQueue = [];
for (var t = 0, len = __cmdQueue.length; t < len; t++) {
that[__cmdQueue[t].func].apply(that, __cmdQueue[t].args);
}
} else {
// Auth required
that._isAuthRequired = true;
// What for AuthRequest from server
}
});
}
return true;
} else {
return false;
}
},
authenticate: function (user, password, salt) {
this._authRunning = true;
if (user !== undefined) {
this._authInfo = {
user: user,
hash: password + salt,
salt: salt
};
}
if (!this._isConnected) {
console.log('No connection!');
return;
}
if (!this._authInfo) {
console.log("No credentials!");
}
},
getConfig: function (useCache, callback) {
if (!this._checkConnection('getConfig', arguments)) return;
if (typeof useCache === 'function') {
callback = useCache;
useCache = false;
}
if (this._useStorage && useCache) {
if (typeof storage !== 'undefined') {
var objects = storage.get('objects');
if (objects && objects['system.config']) {
return callback(null, objects['system.config'].common);
}
} else if (this._objects && this._objects['system.config']) {
return callback(null, this._objects['system.config'].common);
}
}
var that = this;
this._socket.emit('getObject', 'system.config', function (err, obj) {
if (callback && obj && obj.common) {
if (that._useStorage && typeof storage !== 'undefined') {
var objects = storage.get('objects') || {};
objects['system.config'] = obj;
storage.set('objects', objects);
}
callback(null, obj.common);
} else {
callback('Cannot read language');
}
});
},
sendCommand: function (instance, command, data, ack) {
this.setState(this.namespace + '.control.instance', {val: instance || 'notdefined', ack: true});
this.setState(this.namespace + '.control.data', {val: data, ack: true});
this.setState(this.namespace + '.control.command', {val: command, ack: ack === undefined ? true : ack});
},
_detectViews: function (projectDir, callback) {
this.readDir('/' + this.namespace + '/' + projectDir, function (err, dirs) {
// find vis-views.json
for (var f = 0; f < dirs.length; f++) {
if (dirs[f].file === 'vis-views.json' && (!dirs[f].acl || dirs[f].acl.read)) {
return callback(err, {name: projectDir, readOnly: (dirs[f].acl && !dirs[f].acl.write), mode: dirs[f].acl ? dirs[f].acl.permissions : 0});
}
}
callback(err);
});
},
readProjects: function (callback) {
var that = this;
this.readDir('/' + this.namespace, function (err, dirs) {
var result = [];
var count = 0;
for (var d = 0; d < dirs.length; d++) {
if (dirs[d].isDir) {
count++;
that._detectViews(dirs[d].file, function (subErr, project) {
if (project) result.push(project);
err = err || subErr;
if (!(--count)) callback(err, result);
});
}
}
});
},
chmodProject: function (projectDir, mode, callback) {
//socket.io
if (this._socket === null) {
console.log('socket.io not initialized');
return;
}
this._socket.emit('chmodFile', this.namespace, projectDir + '*', {mode: mode}, function (err, data) {
if (callback) callback(err, data);
});
},
clearCache: function () {
if (typeof storage !== 'undefined') {
storage.empty();
}
},
getHistory: function (id, options, callback) {
if (!this._checkConnection('getHistory', arguments)) return;
if (!options) options = {};
if (!options.timeout) options.timeout = 2000;
var timeout = setTimeout(function () {
timeout = null;
callback('timeout');
}, options.timeout);
this._socket.emit('getHistory', id, options, function (err, result) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
callback(err, result);
});
},
getLiveHost: function (cb) {
var that = this;
this._socket.emit('getObjectView', 'system', 'host', {startkey: 'system.host.', endkey: 'system.host.\u9999'}, function (err, res) {
var _hosts = [];
for (var h = 0; h < res.rows.length; h++) {
_hosts.push(res.rows[h].id + '.alive');
}
if (!_hosts.length) {
cb('');
return;
}
that.getStates(_hosts, function (err, states) {
for (var h in states) {
if (states[h].val) {
cb(h.substring(0, h.length - '.alive'.length));
return;
}
}
cb('');
});
});
},
readDirAsZip: function (project, useConvert, callback) {
if (!callback) {
callback = useConvert;
useConvert = undefined;
}
if (!this._isConnected) {
console.log('No connection!');
return;
}
//socket.io
if (this._socket === null) {
console.log('socket.io not initialized');
return;
}
if (project.match(/\/$/)) project = project.substring(0, project.length - 1);
var that = this;
this.getLiveHost(function (host) {
if (!host) {
window.alert('No active host found');
return;
}
// to do find active host
that._socket.emit('sendToHost', host, 'readDirAsZip', {
id: that.namespace,
name: project || 'main',
options: {
settings: useConvert
}
}, function (data) {
if (data.error) console.error(data.error);
if (callback) callback(data.error, data.data);
});
});
},
writeDirAsZip: function (project, base64, callback) {
if (!this._isConnected) {
console.log('No connection!');
return;
}
//socket.io
if (this._socket === null) {
console.log('socket.io not initialized');
return;
}
if (project.match(/\/$/)) project = project.substring(0, project.length - 1);
var that = this;
this.getLiveHost(function (host) {
if (!host) {
window.alert('No active host found');
return;
}
that._socket.emit('sendToHost', host, 'writeDirAsZip', {
id: that.namespace,
name: project || 'main',
data: base64
}, function (data) {
if (data.error) console.error(data.error);
if (callback) callback(data.error);
});
});
}
};