mirror of
https://github.com/coderaiser/cloudcmd.git
synced 2026-07-18 00:47:01 +00:00
feature(edward) add save, patch from edit
This commit is contained in:
parent
47909b13e1
commit
c75e8ad6e1
2 changed files with 274 additions and 278 deletions
|
|
@ -1,5 +1,5 @@
|
|||
var CloudCmd, Util, DOM, CloudFunc, io, Zip, Format, MenuIO, edward, restafary;
|
||||
(function(CloudCmd, Util, DOM, CloudFunc) {
|
||||
var CloudCmd, Util, DOM, CloudFunc, MenuIO, edward;
|
||||
(function(CloudCmd, Util, DOM) {
|
||||
'use strict';
|
||||
|
||||
CloudCmd.Edit = EditProto;
|
||||
|
|
@ -9,23 +9,17 @@ var CloudCmd, Util, DOM, CloudFunc, io, Zip, Format, MenuIO, edward, restafary;
|
|||
Loading = true,
|
||||
|
||||
Info = DOM.CurrentInfo,
|
||||
Files = DOM.Files,
|
||||
Storage = DOM.Storage,
|
||||
|
||||
exec = Util.exec,
|
||||
join = CloudCmd.join,
|
||||
FileName,
|
||||
Value,
|
||||
Edit = this,
|
||||
Diff,
|
||||
|
||||
Menu,
|
||||
|
||||
Dialog = DOM.Dialog,
|
||||
Images = DOM.Images,
|
||||
Element,
|
||||
ConfigView = {
|
||||
beforeClose: function() {
|
||||
isChanged();
|
||||
edward.isChanged();
|
||||
exec.ifExist(Menu, 'hide');
|
||||
},
|
||||
afterShow: function() {
|
||||
|
|
@ -46,10 +40,6 @@ var CloudCmd, Util, DOM, CloudFunc, io, Zip, Format, MenuIO, edward, restafary;
|
|||
function(callback) {
|
||||
loadFiles(element, callback);
|
||||
},
|
||||
function(callback) {
|
||||
DOM.loadSocket(initSocket);
|
||||
callback();
|
||||
},
|
||||
function(callback) {
|
||||
Edit.create(element)
|
||||
.show(callback);
|
||||
|
|
@ -57,73 +47,6 @@ var CloudCmd, Util, DOM, CloudFunc, io, Zip, Format, MenuIO, edward, restafary;
|
|||
}, callback]);
|
||||
}
|
||||
|
||||
function getHost() {
|
||||
var l = location,
|
||||
href = l.origin || l.protocol + '//' + l.host;
|
||||
|
||||
return href;
|
||||
}
|
||||
|
||||
function initSocket(error) {
|
||||
var socket,
|
||||
href = getHost(),
|
||||
FIVE_SECONDS = 5000,
|
||||
patch = function(name, data) {
|
||||
socket.emit('patch', name, data);
|
||||
};
|
||||
|
||||
if (!error) {
|
||||
socket = io.connect(href + '/edit', {
|
||||
'max reconnection attempts' : Math.pow(2, 32),
|
||||
'reconnection limit' : FIVE_SECONDS
|
||||
});
|
||||
|
||||
socket.on('connect', function() {
|
||||
Edit.save.patch = patch;
|
||||
});
|
||||
|
||||
socket.on('message', function(msg) {
|
||||
onSave(null, msg);
|
||||
});
|
||||
|
||||
socket.on('patch', function(name, data, hash) {
|
||||
if (name === FileName)
|
||||
loadDiff(function(error) {
|
||||
if (error)
|
||||
console.error(error);
|
||||
else
|
||||
DOM.Storage.get(name + '-hash', function(error, hashLocal) {
|
||||
var cursor, value;
|
||||
|
||||
if (hash === hashLocal) {
|
||||
cursor = edward.getCursor(),
|
||||
value = edward.getValue();
|
||||
value = Diff.applyPatch(value, data);
|
||||
|
||||
edward.setValue(value);
|
||||
|
||||
edward.sha(function(error, hash) {
|
||||
DOM.saveDataToStorage(name, value, hash);
|
||||
edward
|
||||
.clearSelection()
|
||||
.moveCursorTo(cursor.row, cursor.column)
|
||||
.scrollToLine(cursor.row, true);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('disconnect', function() {
|
||||
Edit.save.patch = patchHttp;
|
||||
});
|
||||
|
||||
socket.on('err', function(error) {
|
||||
Dialog.alert(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function createElement() {
|
||||
var element = DOM.load({
|
||||
name : 'div',
|
||||
|
|
@ -140,9 +63,8 @@ var CloudCmd, Util, DOM, CloudFunc, io, Zip, Format, MenuIO, edward, restafary;
|
|||
|
||||
this.show = function(callback) {
|
||||
var isDir = Info.isDir,
|
||||
name = Info.name;
|
||||
|
||||
FileName = Info.path;
|
||||
name = Info.name,
|
||||
path = Info.path;
|
||||
|
||||
Images.show.load();
|
||||
|
||||
|
|
@ -158,7 +80,7 @@ var CloudCmd, Util, DOM, CloudFunc, io, Zip, Format, MenuIO, edward, restafary;
|
|||
if (error) {
|
||||
alert(error);
|
||||
} else {
|
||||
Edit.setValueFirst(data);
|
||||
Edit.setValueFirst(path, data);
|
||||
CloudCmd.View.show(Element, ConfigView);
|
||||
}
|
||||
});
|
||||
|
|
@ -166,9 +88,8 @@ var CloudCmd, Util, DOM, CloudFunc, io, Zip, Format, MenuIO, edward, restafary;
|
|||
return this;
|
||||
};
|
||||
|
||||
this.setValueFirst = function(value) {
|
||||
Value = value;
|
||||
edward.setValueFirst(value);
|
||||
this.setValueFirst = function(path, value) {
|
||||
edward.setValueFirst(path, value);
|
||||
};
|
||||
|
||||
this.hide = function() {
|
||||
|
|
@ -179,20 +100,6 @@ var CloudCmd, Util, DOM, CloudFunc, io, Zip, Format, MenuIO, edward, restafary;
|
|||
edward.goToLine();
|
||||
};
|
||||
|
||||
function isChanged() {
|
||||
var is,
|
||||
value = edward.getValue(),
|
||||
isEqual = value === Value,
|
||||
msg = 'Do you want to save changes to ' + name + '?';
|
||||
|
||||
if (!isEqual) {
|
||||
is = Dialog.confirm(msg);
|
||||
|
||||
if (is)
|
||||
Edit.save();
|
||||
}
|
||||
}
|
||||
|
||||
this.create = function(element) {
|
||||
Element = element;
|
||||
|
||||
|
|
@ -216,7 +123,7 @@ var CloudCmd, Util, DOM, CloudFunc, io, Zip, Format, MenuIO, edward, restafary;
|
|||
name : 'save',
|
||||
bindKey : { win: 'Ctrl-S', mac: 'Command-S' },
|
||||
exec : function() {
|
||||
Edit.save();
|
||||
edward.save();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -224,7 +131,7 @@ var CloudCmd, Util, DOM, CloudFunc, io, Zip, Format, MenuIO, edward, restafary;
|
|||
name : 'saveMC',
|
||||
bindKey : { win: 'F2', mac: 'F2' },
|
||||
exec : function() {
|
||||
Edit.save();
|
||||
edward.save();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -232,7 +139,7 @@ var CloudCmd, Util, DOM, CloudFunc, io, Zip, Format, MenuIO, edward, restafary;
|
|||
name : 'beautify',
|
||||
bindKey : { win: 'Ctrl-B', mac: 'Command-B' },
|
||||
exec : function() {
|
||||
Edit.beautify();
|
||||
edward.beautify();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -240,137 +147,13 @@ var CloudCmd, Util, DOM, CloudFunc, io, Zip, Format, MenuIO, edward, restafary;
|
|||
name : 'minify',
|
||||
bindKey : { win: 'Ctrl-M', mac: 'Command-M' },
|
||||
exec : function() {
|
||||
Edit.minify();
|
||||
edward.minify();
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Edit.save = function() {
|
||||
var value = edward.getValue();
|
||||
|
||||
Files.get('config', function(error, config) {
|
||||
var isDiff = config.diff,
|
||||
isZip = config.zip;
|
||||
|
||||
exec.if(!isDiff, function(patch) {
|
||||
var query = '',
|
||||
MAX_SIZE = CloudFunc.MAX_FILE_SIZE,
|
||||
patchLength = patch && patch.length || 0,
|
||||
length = Value.length,
|
||||
isLessMaxLength = length < MAX_SIZE,
|
||||
isLessLength = isLessMaxLength && patchLength < length,
|
||||
isStr = typeof patch === 'string',
|
||||
isPatch = patch && isStr && isLessLength;
|
||||
|
||||
Value = value;
|
||||
|
||||
exec.if(!isZip || isPatch, function(equal, data) {
|
||||
var result = data || Value;
|
||||
|
||||
if (isPatch)
|
||||
Edit.save.patch(FileName, patch);
|
||||
else
|
||||
Edit.save.write(FileName + query, result);
|
||||
}, function(func) {
|
||||
zip(value, function(error, data) {
|
||||
if (error)
|
||||
console.error(error);
|
||||
|
||||
query = '?unzip';
|
||||
func(null, data);
|
||||
});
|
||||
});
|
||||
|
||||
}, exec.with(doDiff, FileName));
|
||||
});
|
||||
};
|
||||
|
||||
Edit.save.patch = patchHttp;
|
||||
Edit.save.write = writeHttp;
|
||||
|
||||
function patchHttp(path, patch) {
|
||||
restafary.patch(path, patch, onSave);
|
||||
}
|
||||
|
||||
function writeHttp(path, result) {
|
||||
restafary.write(path, result, onSave);
|
||||
}
|
||||
|
||||
function doDiff(path, callback) {
|
||||
var value = edward.getValue();
|
||||
|
||||
diff(value, function(patch) {
|
||||
var isAllowed = Storage.isAllowed();
|
||||
|
||||
exec.if(!isAllowed, callback, function(func) {
|
||||
DOM.checkStorageHash(path, function(error, equal) {
|
||||
if (!equal)
|
||||
patch = '';
|
||||
|
||||
func(patch);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function diff(newValue, callback) {
|
||||
loadDiff(function(error) {
|
||||
var patch,
|
||||
isAllowed = Storage.isAllowed();
|
||||
|
||||
if (error) {
|
||||
Dialog.alert(error);
|
||||
} else {
|
||||
exec.if(!isAllowed, function() {
|
||||
patch = Diff.createPatch(Value, newValue);
|
||||
exec(callback, patch);
|
||||
}, function(func) {
|
||||
DOM.getDataFromStorage(FileName, function(error, data) {
|
||||
if (data)
|
||||
Value = data;
|
||||
|
||||
func();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadDiff(callback) {
|
||||
var url = join([
|
||||
'/modules/google-diff-match-patch/diff_match_patch.js',
|
||||
'/modules/daffy/lib/daffy.js'
|
||||
]);
|
||||
|
||||
DOM.load.js(url, function(error) {
|
||||
if (!error && !Diff)
|
||||
Diff = window.daffy;
|
||||
|
||||
callback(error);
|
||||
});
|
||||
}
|
||||
|
||||
function zip(value, callback) {
|
||||
var dir = CloudCmd.LIBDIRCLIENT,
|
||||
url = dir + 'zip.js';
|
||||
|
||||
exec.parallel([
|
||||
function(callback) {
|
||||
DOM.load.js(url, callback);
|
||||
},
|
||||
function(callback) {
|
||||
DOM.loadRemote('pako', callback);
|
||||
}
|
||||
], function(error) {
|
||||
if (error)
|
||||
alert(error);
|
||||
else
|
||||
Zip.pack(value, callback);
|
||||
});
|
||||
}
|
||||
|
||||
function loadFiles(element, callback) {
|
||||
var dir = CloudCmd.LIBDIRCLIENT,
|
||||
name = 'edward',
|
||||
|
|
@ -388,30 +171,6 @@ var CloudCmd, Util, DOM, CloudFunc, io, Zip, Format, MenuIO, edward, restafary;
|
|||
});
|
||||
}
|
||||
|
||||
function onSave(error, text) {
|
||||
var ret,
|
||||
size = Format.size(Value.length),
|
||||
msg = '\nShould I save file anyway?';
|
||||
|
||||
if (error) {
|
||||
ret = Dialog.confirm(text + msg);
|
||||
|
||||
if (ret)
|
||||
restafary.write(FileName, Value, onSave);
|
||||
} else {
|
||||
Edit.showMessage(text);
|
||||
|
||||
edward.sha(function(error, hash) {
|
||||
if (error)
|
||||
console.error(error);
|
||||
|
||||
DOM.saveDataToStorage(FileName, Value, hash);
|
||||
});
|
||||
|
||||
DOM.setCurrentSize(size);
|
||||
}
|
||||
}
|
||||
|
||||
function setMenu() {
|
||||
if (!Menu) {
|
||||
DOM.loadRemote('menu', function(error) {
|
||||
|
|
@ -459,28 +218,6 @@ var CloudCmd, Util, DOM, CloudFunc, io, Zip, Format, MenuIO, edward, restafary;
|
|||
}
|
||||
}
|
||||
|
||||
Edit.beautify = function() {
|
||||
readWithFlag('beautify');
|
||||
};
|
||||
|
||||
Edit.minify = function() {
|
||||
readWithFlag('minify');
|
||||
};
|
||||
|
||||
function readWithFlag(flag) {
|
||||
var path = FileName;
|
||||
|
||||
restafary.read(path + '?' + flag, function(error, data) {
|
||||
if (error)
|
||||
alert(error);
|
||||
else
|
||||
edward
|
||||
.setValue(data)
|
||||
.clearSelection()
|
||||
.moveCursorTo(0, 0);
|
||||
});
|
||||
}
|
||||
|
||||
this.showMessage = function(text) {
|
||||
edward.showMessage(text);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
var join, ace, load, Util, DOM, CloudCmd;
|
||||
var join, ace, load, Util, DOM, CloudCmd, Diff, io, Zip, Format, CloudFunc, restafary;
|
||||
|
||||
(function(global, join, DOM, exec, loadRemote) {
|
||||
'use strict';
|
||||
|
|
@ -13,11 +13,15 @@ var join, ace, load, Util, DOM, CloudCmd;
|
|||
ElementMsg,
|
||||
Value,
|
||||
Ace,
|
||||
FileName,
|
||||
Modelist,
|
||||
JSHintConfig,
|
||||
Emmet,
|
||||
Config,
|
||||
|
||||
Files = DOM.Files,
|
||||
Storage = DOM.Storage,
|
||||
|
||||
edward = function(el, callback) {
|
||||
Element = el || document.body;
|
||||
|
||||
|
|
@ -28,6 +32,10 @@ var join, ace, load, Util, DOM, CloudCmd;
|
|||
|
||||
exec.series([
|
||||
loadFiles,
|
||||
function(callback) {
|
||||
DOM.loadSocket(initSocket);
|
||||
callback();
|
||||
},
|
||||
function() {
|
||||
Ace = ace.edit(Element);
|
||||
ace.require('ace/ext/language_tools');
|
||||
|
|
@ -98,15 +106,30 @@ var join, ace, load, Util, DOM, CloudCmd;
|
|||
return Ace.getValue();
|
||||
};
|
||||
|
||||
edward.isChanged = function() {
|
||||
var is,
|
||||
value = edward.getValue(),
|
||||
isEqual = value === Value,
|
||||
msg = 'Do you want to save changes to ' + name + '?';
|
||||
|
||||
if (!isEqual) {
|
||||
is = confirm(msg);
|
||||
|
||||
if (is)
|
||||
edward.save();
|
||||
}
|
||||
};
|
||||
|
||||
edward.setValue = function(value) {
|
||||
Ace.setValue(value);
|
||||
return edward;
|
||||
};
|
||||
|
||||
edward.setValueFirst = function(value) {
|
||||
edward.setValueFirst = function(name, value) {
|
||||
var session = edward.getSession(),
|
||||
UndoManager = ace.require('ace/undomanager').UndoManager;
|
||||
|
||||
FileName = name;
|
||||
Value = value;
|
||||
|
||||
Ace.setValue(value);
|
||||
|
|
@ -213,6 +236,161 @@ var join, ace, load, Util, DOM, CloudCmd;
|
|||
});
|
||||
};
|
||||
|
||||
edward.beautify = function() {
|
||||
readWithFlag('beautify');
|
||||
};
|
||||
|
||||
edward.minify = function() {
|
||||
readWithFlag('minify');
|
||||
};
|
||||
|
||||
edward.save = function() {
|
||||
var value = edward.getValue();
|
||||
|
||||
Files.get('config', function(error, config) {
|
||||
var isDiff = config.diff,
|
||||
isZip = config.zip;
|
||||
|
||||
exec.if(!isDiff, function(patch) {
|
||||
var query = '',
|
||||
MAX_SIZE = CloudFunc.MAX_FILE_SIZE,
|
||||
patchLength = patch && patch.length || 0,
|
||||
length = Value.length,
|
||||
isLessMaxLength = length < MAX_SIZE,
|
||||
isLessLength = isLessMaxLength && patchLength < length,
|
||||
isStr = typeof patch === 'string',
|
||||
isPatch = patch && isStr && isLessLength;
|
||||
|
||||
Value = value;
|
||||
|
||||
exec.if(!isZip || isPatch, function(equal, data) {
|
||||
var result = data || Value;
|
||||
|
||||
if (isPatch)
|
||||
edward.save.patch(FileName, patch);
|
||||
else
|
||||
edward.save.write(FileName + query, result);
|
||||
}, function(func) {
|
||||
zip(value, function(error, data) {
|
||||
if (error)
|
||||
console.error(error);
|
||||
|
||||
query = '?unzip';
|
||||
func(null, data);
|
||||
});
|
||||
});
|
||||
|
||||
}, exec.with(doDiff, FileName));
|
||||
});
|
||||
};
|
||||
|
||||
edward.save.patch = patchHttp;
|
||||
edward.save.write = writeHttp;
|
||||
|
||||
function patchHttp(path, patch) {
|
||||
restafary.patch(path, patch, onSave);
|
||||
}
|
||||
|
||||
function writeHttp(path, result) {
|
||||
restafary.write(path, result, onSave);
|
||||
}
|
||||
function onSave(error, text) {
|
||||
var ret,
|
||||
size = Format.size(Value.length),
|
||||
msg = '\nShould I save file anyway?';
|
||||
|
||||
if (error) {
|
||||
ret = confirm(error.message + msg);
|
||||
|
||||
if (ret)
|
||||
restafary.write(FileName, Value);
|
||||
} else {
|
||||
edward.showMessage(text);
|
||||
|
||||
edward.sha(function(error, hash) {
|
||||
if (error)
|
||||
console.error(error);
|
||||
|
||||
DOM.saveDataToStorage(FileName, Value, hash);
|
||||
});
|
||||
|
||||
DOM.setCurrentSize(size);
|
||||
}
|
||||
}
|
||||
|
||||
function doDiff(path, callback) {
|
||||
var value = edward.getValue();
|
||||
|
||||
diff(value, function(patch) {
|
||||
var isAllowed = Storage.isAllowed();
|
||||
|
||||
exec.if(!isAllowed, callback, function(func) {
|
||||
DOM.checkStorageHash(path, function(error, equal) {
|
||||
if (!equal)
|
||||
patch = '';
|
||||
|
||||
func(patch);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function diff(newValue, callback) {
|
||||
loadDiff(function(error) {
|
||||
var patch,
|
||||
isAllowed = Storage.isAllowed();
|
||||
|
||||
if (error) {
|
||||
alert(error);
|
||||
} else {
|
||||
exec.if(!isAllowed, function() {
|
||||
patch = Diff.createPatch(Value, newValue);
|
||||
exec(callback, patch);
|
||||
}, function(func) {
|
||||
DOM.getDataFromStorage(FileName, function(error, data) {
|
||||
if (data)
|
||||
Value = data;
|
||||
|
||||
func();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadDiff(callback) {
|
||||
var url = join([
|
||||
'/modules/google-diff-match-patch/diff_match_patch.js',
|
||||
'/modules/daffy/lib/daffy.js'
|
||||
]);
|
||||
|
||||
DOM.load.js(url, function(error) {
|
||||
if (!error && !Diff)
|
||||
Diff = window.daffy;
|
||||
|
||||
callback(error);
|
||||
});
|
||||
}
|
||||
|
||||
function zip(value, callback) {
|
||||
var dir = CloudCmd.LIBDIRCLIENT,
|
||||
url = dir + 'zip.js';
|
||||
|
||||
exec.parallel([
|
||||
function(callback) {
|
||||
DOM.load.js(url, callback);
|
||||
},
|
||||
function(callback) {
|
||||
DOM.loadRemote('pako', callback);
|
||||
}
|
||||
], function(error) {
|
||||
if (error)
|
||||
alert(error);
|
||||
else
|
||||
Zip.pack(value, callback);
|
||||
});
|
||||
}
|
||||
|
||||
function setEmmet() {
|
||||
var dir = '/modules/ace-builds/src-noconflict/',
|
||||
extensions = Config.extensions,
|
||||
|
|
@ -262,6 +440,87 @@ var join, ace, load, Util, DOM, CloudCmd;
|
|||
});
|
||||
}
|
||||
|
||||
function getHost() {
|
||||
var l = location,
|
||||
href = l.origin || l.protocol + '//' + l.host;
|
||||
|
||||
return href;
|
||||
}
|
||||
|
||||
function initSocket(error) {
|
||||
var socket,
|
||||
href = getHost(),
|
||||
FIVE_SECONDS = 5000,
|
||||
patch = function(name, data) {
|
||||
socket.emit('patch', name, data);
|
||||
};
|
||||
|
||||
if (!error) {
|
||||
socket = io.connect(href + '/edit', {
|
||||
'max reconnection attempts' : Math.pow(2, 32),
|
||||
'reconnection limit' : FIVE_SECONDS
|
||||
});
|
||||
|
||||
socket.on('connect', function() {
|
||||
edward.save.patch = patch;
|
||||
});
|
||||
|
||||
socket.on('message', function(msg) {
|
||||
onSave(null, msg);
|
||||
});
|
||||
|
||||
socket.on('patch', function(name, data, hash) {
|
||||
if (name === FileName)
|
||||
loadDiff(function(error) {
|
||||
if (error)
|
||||
console.error(error);
|
||||
else
|
||||
DOM.Storage.get(name + '-hash', function(error, hashLocal) {
|
||||
var cursor, value;
|
||||
|
||||
if (hash === hashLocal) {
|
||||
cursor = edward.getCursor(),
|
||||
value = edward.getValue();
|
||||
value = Diff.applyPatch(value, data);
|
||||
|
||||
edward.setValue(value);
|
||||
|
||||
edward.sha(function(error, hash) {
|
||||
DOM.saveDataToStorage(name, value, hash);
|
||||
edward
|
||||
.clearSelection()
|
||||
.moveCursorTo(cursor.row, cursor.column)
|
||||
.scrollToLine(cursor.row, true);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('disconnect', function() {
|
||||
edward.save.patch = patchHttp;
|
||||
});
|
||||
|
||||
socket.on('err', function(error) {
|
||||
alert(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function readWithFlag(flag) {
|
||||
var path = FileName;
|
||||
|
||||
restafary.read(path + '?' + flag, function(error, data) {
|
||||
if (error)
|
||||
alert(error);
|
||||
else
|
||||
edward
|
||||
.setValue(data)
|
||||
.clearSelection()
|
||||
.moveCursorTo(0, 0);
|
||||
});
|
||||
}
|
||||
|
||||
function onDrop(event) {
|
||||
var reader, files,
|
||||
onLoad = function(event) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue