This commit is contained in:
Namhyeon Go 2020-07-27 11:31:52 +09:00
parent 0c36e8cf61
commit 2c88ce06ed
9 changed files with 454 additions and 412 deletions

View File

@ -25,6 +25,8 @@ WelsonJS - Build a Windows desktop apps with JavaScript, HTML, and CSS based on
- lib/timer (`setTimeout` implementation for not supported environment) - lib/timer (`setTimeout` implementation for not supported environment)
- lib/powershell (Windows Powershell interface) - lib/powershell (Windows Powershell interface)
- lib/service (Windows Service interface) - lib/service (Windows Service interface)
- lib/oldbrowser (Compatibility interface)
- lib/uri (URI scheme interface)
- lib/autohotkey ([AutoHotKey](https://catswords.re.kr/go/autohotkey) interface) - lib/autohotkey ([AutoHotKey](https://catswords.re.kr/go/autohotkey) interface)
- lib/autoit3 ([AutoIt3](https://catswords.re.kr/go/autoit3) interface) - lib/autoit3 ([AutoIt3](https://catswords.re.kr/go/autoit3) interface)
- lib/cloudflare ([Cloudflare Argo Tunnel](https://catswords.re.kr/go/argotunnel) interface) - lib/cloudflare ([Cloudflare Argo Tunnel](https://catswords.re.kr/go/argotunnel) interface)

18
app.js
View File

@ -156,22 +156,23 @@ var __config = require("config").config;
function init_console() { function init_console() {
if (typeof(WScript) === "undefined") { if (typeof(WScript) === "undefined") {
console.error("Error, WScript is not defined", 1); console.error("Error, WScript is not defined");
exit(1);
} }
var n = WScript.arguments.length; var argl = WScript.arguments.length;
if (n > 0) { if (argl > 0) {
var args = []; var args = [];
for (var i = 0; i < n; i++) { for (var i = 0; i < argl; i++) {
args.push(WScript.arguments(i)); args.push(WScript.arguments(i));
} }
var name = args.shift(); var name = args.shift();
var app = require(name); var app = require(name);
if (app) { if (app) {
if (app.main) { if (app.main) {
var exitstatus = app.main.call(app, args); var exitstatus = app.main(args);
if (typeof exitstatus != undefined) { if (typeof(exitstatus) !== "undefined") {
WScript.quit(exitstatus); exit(exitstatus);
} }
} else { } else {
console.error("Error, missing main entry point in " + name + ".js", 1); console.error("Error, missing main entry point in " + name + ".js", 1);
@ -184,7 +185,8 @@ function init_console() {
function init_window(name, args, w, h) { function init_window(name, args, w, h) {
if (typeof(window) === "undefined") { if (typeof(window) === "undefined") {
console.error("Error, window is not defined", 1); console.error("Error, window is not defined");
exit(1);
} }
var app = require(name); var app = require(name);

4
bootstrap.js vendored
View File

@ -9,8 +9,7 @@ var REG = require("lib/registry");
var SYS = require("lib/system"); var SYS = require("lib/system");
var SHELL = require("lib/shell"); var SHELL = require("lib/shell");
return { exports.main = function(args) {
main: function(args) {
// unlock file // unlock file
console.log("Starting unlock files..."); console.log("Starting unlock files...");
PS.execCommand("dir | Unblock-File"); PS.execCommand("dir | Unblock-File");
@ -37,5 +36,4 @@ return {
// echo welcome // echo welcome
console.log("welcome"); console.log("welcome");
}
}; };

View File

@ -1,5 +1,3 @@
return { exports.main: function() {
main: function() {
console.log("Hello world"); console.log("Hello world");
}
}; };

226
lib/oldbrowser.js Normal file
View File

@ -0,0 +1,226 @@
////////////////////////////////////////////////////////////////////////
// OldBrowser API
////////////////////////////////////////////////////////////////////////
exports.VERSIONINFO = "OldBrowser Lib (oldbrowser.js) version 0.1";
exports.global = global;
exports.require = global.require;
////////////////////////////////////////////////////////////////////////
// only less than IE 9
////////////////////////////////////////////////////////////////////////
if (!window.addEventListener) {
Element = function() {};
(function(WindowPrototype, DocumentPrototype, ElementPrototype, registry) {
if (!DocumentPrototype.head) {
DocumentPrototype.head = (function() {
return DocumentPrototype.getElementsByTagName("head")[0];
})();
}
if (!DocumentPrototype.getElementsByClassName) {
DocumentPrototype.getElementsByClassName = function(search) {
var d = document,
elements, pattern, i, results = [];
if (d.querySelectorAll) { // IE8
return d.querySelectorAll("." + search);
}
if (d.evaluate) { // IE6, IE7
pattern = ".//*[contains(concat(' ', @class, ' '), ' " + search + " ')]";
elements = d.evaluate(pattern, d, null, 0, null);
while ((i = elements.iterateNext())) {
results.push(i);
}
} else {
elements = d.getElementsByTagName("*");
pattern = new RegExp("(^|\\s)" + search + "(\\s|$)");
for (i = 0; i < elements.length; i++) {
if (pattern.test(elements[i].className)) {
results.push(elements[i]);
}
}
}
return results;
}
}
var enableEventListener = function(obj, registry) {
obj.addEventListener = function(type, listener) {
var target = this;
if (typeof(registry) === "undefined")
registry = [];
registry.unshift([target, type, listener, function(event) {
event.currentTarget = target;
event.preventDefault = function() {
event.returnValue = false
};
event.stopPropagation = function() {
event.cancelBubble = true
};
event.target = event.srcElement || target;
listener.call(target, event);
}]);
this.attachEvent("on" + type, registry[0][3]);
};
obj.removeEventListener = function(type, listener) {
for (var index = 0, register; register = registry[index]; ++index) {
if (register[0] == this && register[1] == type && register[2] == listener) {
return this.detachEvent("on" + type, registry.splice(index, 1)[0][3]);
}
}
};
obj.dispatchEvent = function(eventObject) {
return this.fireEvent("on" + eventObject.type, eventObject);
};
};
WindowPrototype.enableEventListener = function(obj) {
if (!obj.addEventListener) {
enableEventListener(obj);
}
};
enableEventListener(WindowPrototype, registry);
enableEventListener(DocumentPrototype, registry);
enableEventListener(ElementPrototype, registry);
var __createElement = DocumentPrototype.createElement;
DocumentPrototype.createElement = function(tagName) {
var element = __createElement(tagName);
if (element == null) {
return null;
}
for (var key in ElementPrototype)
element[key] = ElementPrototype[key];
return element;
}
var __getElementById = DocumentPrototype.getElementById;
DocumentPrototype.getElementById = function(id) {
var element = __getElementById(id);
if (element == null) {
return null;
}
for (var key in ElementPrototype)
element[key] = ElementPrototype[key];
return element;
}
})(window, document, Element.prototype, []);
}
////////////////////////////////////////////////////////////////////////
// exports.getIEVersion()
////////////////////////////////////////////////////////////////////////
exports.getIEVersion = function() {
var undef,
v = 3,
div = document.createElement('div'),
all = div.getElementsByTagName('i');
while (
div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
all[0]
);
return v > 4 ? v : undef;
};
////////////////////////////////////////////////////////////////////////
// exports.addScript()
////////////////////////////////////////////////////////////////////////
exports.addScript = function(url, callback, test, ttl) {
var _callback = function(el, ttl) {
var result = test(el);
if (typeof(result) !== "undefined") {
callback(el);
} else {
setTimeout(function() {
if (ttl > 0) {
_callback(el, ttl - 1);
} else {
console.log("failed load " + url);
}
}, 1);
}
};
var el = document.createElement("script");
el.src = url;
el.type = "text/javascript";
el.charset = "utf-8";
document.head.appendChild(el);
if (typeof(test) === "function" && typeof(callback) === "function") {
// "Time-To-Live: default value is 30 seconds";
ttl = (typeof(ttl) == "number" ? ttl : 30000);
el.onload = _callback(el, ttl);
} else if (typeof(callback) === "function") {
el.onload = callback(el);
}
return el;
};
////////////////////////////////////////////////////////////////////////
// exports.addStylesheet()
////////////////////////////////////////////////////////////////////////
exports.addStylesheet = function(url, callback) {
var el = document.createElement("link");
el.href = url;
el.rel = "stylesheet";
el.type = "text/css";
el.media = "screen, projection";
document.head.appendChild(el);
if (typeof(callback) === "function") {
el.onload = callback(el);
}
return el;
};
////////////////////////////////////////////////////////////////////////
// exports.start()
////////////////////////////////////////////////////////////////////////
exports.start = function(callback) {
var IEVersion = exports.getIEVersion();
if (IEVersion == 8) {
exports.addScript("app/assets/css/jquery/webreflection-ie8-0.8.1.min.js");
}
// "load javascripts dynamically";
exports.addScript("app/assets/js/es5-shim-4.5.14.min.js");
exports.addScript("app/assets/js/es5-sham-4.5.14.min.js");
exports.addScript("app/assets/js/json3-3.3.2.min.js");
exports.addScript("app/assets/js/es6-shim-0.35.5.min.js");
exports.addScript("app/assets/js/es6-sham-0.35.5.min.js");
exports.addScript("app/assets/js/html5shiv-printshiv-3.7.3.min.js");
if (IEVersion < 9) {
exports.addScript("app/assets/js/welsonjs-respond-1.4.2-modified.js");
exports.addScript("app/assets/js/welsonjs-selectivizr-1.0.2-modified.js");
exports.addScript("app/assets/js/excanvas-565afad.js");
exports.addScript("app/assets/js/jquery-1.11.3.min.js", callback, function(el) {
return window.jQuery;
});
exports.addScript("http://api.html5media.info/1.1.6/html5media.min.js");
} else {
exports.addScript("app/assets/js/jquery-3.5.1.min.js", callback, function(el) {
return window.jQuery;
});
}
exports.addScript("app/assets/js/modernizr-2.8.3.min.js");
// "load jQuery UI (1.12.1)";
exports.addScript("app/assets/js/jquery-ui-1.21.1.min.js");
// "load jQuery plugins";
if (IEVersion < 10) {
exports.addScript("app/assets/js/PIE-1.0.0.js");
exports.addScript("app/assets/js/jquery.html5-placeholder-shim-5a87f05.js");
}
};

View File

@ -4,7 +4,7 @@
var FILE = require("lib/file"); var FILE = require("lib/file");
exports.VERSIONINFO = "Shell Module (shell.js) version 0.1"; exports.VERSIONINFO = "Shell Lib (shell.js) version 0.1";
exports.global = global; exports.global = global;
exports.require = global.require; exports.require = global.require;

49
lib/uri.js Normal file
View File

@ -0,0 +1,49 @@
//////////////////////////////////////////////////////////////////////////////////
//
// uri.js
//
/////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////
// Private APIs / Utility functions
/////////////////////////////////////////////////////////////////////////////////
exports.VERSIONINFO = "URI Lib (uri.js) version 0.1";
exports.global = global;
exports.require = global.require;
/////////////////////////////////////////////////////////////////////////////////
// exports.parseQueryString
/////////////////////////////////////////////////////////////////////////////////
exports.parseQueryString = function(queryString) {
var query = {};
var pairs = [];
if(queryString.substring(0, 1) == '?') {
pairs = queryString.substring(1).split('&');
} else {
pairs = queryString.split('&');
}
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split('=');
var _k = decodeURIComponent(pair[0]);
var _v = decodeURIComponent(pair[1] || '');
var path = _k.split('[').map(function(s) {
return (s.indexOf(']') < 0 ? s : s.substring(0, s.length -1));
}).join('/');
if(path in query) {
if (Array.isArray(query[path])) {
query[path].push(_v);
} else {
query[path] = [query[path], _v];
}
} else {
query[path] = _v;
}
};
return query;
};

View File

@ -5,9 +5,9 @@
///////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////
var SHELL = require("lib/shell"); var SHELL = require("lib/shell");
var URI = require("lib/uri");
return { exports.main = function(args) {
main: function(args) {
var uri = args[0]; var uri = args[0];
var pos = uri.indexOf(":///"); var pos = uri.indexOf(":///");
if(pos < 0) { if(pos < 0) {
@ -15,7 +15,7 @@ return {
} else { } else {
var cmd = [], var cmd = [],
queryString = uri.substring(pos + 4), queryString = uri.substring(pos + 4),
query = this.parseQueryString(queryString); query = URI.parseQueryString(queryString);
if(!query.application) { if(!query.application) {
query.application = ""; query.application = "";
@ -23,7 +23,7 @@ return {
switch(query.application) { switch(query.application) {
case "app": case "app":
cmd.push(["app.hta"].concat(args)); cmd.push("app.hta");
break; break;
case "mscalc": case "mscalc":
cmd.push("calc.exe"); cmd.push("calc.exe");
@ -46,7 +46,6 @@ return {
break; break;
dafault: dafault:
console.log("Unknown application"); console.log("Unknown application");
break;
} }
if(typeof(query.args) !== "undefined") { if(typeof(query.args) !== "undefined") {
@ -54,31 +53,7 @@ return {
} }
SHELL.run(cmd); SHELL.run(cmd);
}
return 0; return 0;
}
},
parseQueryString: function(queryString) {
var query = {};
var pairs = (queryString.substring(0, 1) === '?' ? queryString.substring(1) : queryString).split('&');
for (var i in pairs) {
var pair = pairs[i].split('=');
var _k = decodeURIComponent(pair[0]);
var _v = decodeURIComponent(pair[1] || '');
var path = _k.split('[').map(function(s) {
return (s.indexOf(']') < 0 ? s : s.substring(0, s.length -1));
}).join('/');
if(path in query) {
if (Array.isArray(query[path])) {
query[path].push(_v);
} else {
query[path] = [query[path], _v];
}
} else {
query[path] = _v;
}
}
return query;
}; };
}

View File

@ -1,14 +1,13 @@
/* ////////////////////////////////////////////////////////////////////////
* webloader.js // Webloader
* Go Namhyeon (gnh1201@gmail.com) ////////////////////////////////////////////////////////////////////////
* 2020-07-07
* https://github.com/gnh1201/welsonjs
*
*/
var FILE = require("lib/file"); var FILE = require("lib/file");
var OldBrowser = require("lib/oldbrowser");
var URILoader = require("uriloader"); var URILoader = require("uriloader");
// "override console.__echo()"; ////////////////////////////////////////////////////////////////////////
// Override global.console.__echo()
////////////////////////////////////////////////////////////////////////
global.console.__echo = function(msg) { global.console.__echo = function(msg) {
if (typeof(window.jQuery) !== "undefined") { if (typeof(window.jQuery) !== "undefined") {
window.jQuery.toast({ window.jQuery.toast({
@ -23,137 +22,24 @@ global.console.__echo = function(msg) {
global.console.__messages.push(msg); global.console.__messages.push(msg);
}; };
// "override exit"; ////////////////////////////////////////////////////////////////////////
// Override global.exit()
////////////////////////////////////////////////////////////////////////
global.exit = function() { global.exit = function() {
if (typeof(window) !== "undefined") { if (typeof(window) !== "undefined") {
window.close(); window.close();
} }
}; };
// "less than IE 9"; ////////////////////////////////////////////////////////////////////////
window.enableEventListener = function(obj) {}; // exports.IEVersion
if (!window.addEventListener) { ////////////////////////////////////////////////////////////////////////
Element = function() {}; exports.IEVersion = OldBrowser.getIEVersion();
(function(WindowPrototype, DocumentPrototype, ElementPrototype, registry) { ////////////////////////////////////////////////////////////////////////
if (!DocumentPrototype.head) { // exports.enableMovableWindow()
DocumentPrototype.head = (function() { ////////////////////////////////////////////////////////////////////////
return DocumentPrototype.getElementsByTagName("head")[0]; exports.enableMovableWindow = function() {
})();
}
if (!DocumentPrototype.getElementsByClassName) {
DocumentPrototype.getElementsByClassName = function(search) {
var d = document,
elements, pattern, i, results = [];
if (d.querySelectorAll) { // IE8
return d.querySelectorAll("." + search);
}
if (d.evaluate) { // IE6, IE7
pattern = ".//*[contains(concat(' ', @class, ' '), ' " + search + " ')]";
elements = d.evaluate(pattern, d, null, 0, null);
while ((i = elements.iterateNext())) {
results.push(i);
}
} else {
elements = d.getElementsByTagName("*");
pattern = new RegExp("(^|\\s)" + search + "(\\s|$)");
for (i = 0; i < elements.length; i++) {
if (pattern.test(elements[i].className)) {
results.push(elements[i]);
}
}
}
return results;
}
}
var enableEventListener = function(obj, registry) {
obj.addEventListener = function(type, listener) {
var target = this;
if (typeof(registry) === "undefined")
registry = [];
registry.unshift([target, type, listener, function(event) {
event.currentTarget = target;
event.preventDefault = function() {
event.returnValue = false
};
event.stopPropagation = function() {
event.cancelBubble = true
};
event.target = event.srcElement || target;
listener.call(target, event);
}]);
this.attachEvent("on" + type, registry[0][3]);
};
obj.removeEventListener = function(type, listener) {
for (var index = 0, register; register = registry[index]; ++index) {
if (register[0] == this && register[1] == type && register[2] == listener) {
return this.detachEvent("on" + type, registry.splice(index, 1)[0][3]);
}
}
};
obj.dispatchEvent = function(eventObject) {
return this.fireEvent("on" + eventObject.type, eventObject);
};
};
WindowPrototype.enableEventListener = function(obj) {
if (!obj.addEventListener) {
enableEventListener(obj);
}
};
enableEventListener(WindowPrototype, registry);
enableEventListener(DocumentPrototype, registry);
enableEventListener(ElementPrototype, registry);
var __createElement = DocumentPrototype.createElement;
DocumentPrototype.createElement = function(tagName) {
var element = __createElement(tagName);
if (element == null) {
return null;
}
for (var key in ElementPrototype)
element[key] = ElementPrototype[key];
return element;
}
var __getElementById = DocumentPrototype.getElementById;
DocumentPrototype.getElementById = function(id) {
var element = __getElementById(id);
if (element == null) {
return null;
}
for (var key in ElementPrototype)
element[key] = ElementPrototype[key];
return element;
}
})(window, document, Element.prototype, []);
}
// "get IE version";
var IEVersion = (function() {
var undef,
v = 3,
div = document.createElement('div'),
all = div.getElementsByTagName('i');
while (
div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
all[0]
);
return v > 4 ? v : undef;
})();
return {
enableMovableWindow: function() {
var grip = document.getElementById("app"), var grip = document.getElementById("app"),
oX, oY, oX, oY,
mouseDown = function(e) { mouseDown = function(e) {
@ -176,143 +62,54 @@ return {
window.removeEventListener("mouseup", mouseUp); window.removeEventListener("mouseup", mouseUp);
}; };
enableEventListener(grip);
grip.addEventListener("mousedown", mouseDown); grip.addEventListener("mousedown", mouseDown);
grip.addEventListener("mousemove", gripMouseMove); grip.addEventListener("mousemove", gripMouseMove);
},
getIEVersion: function() {
return IEVersion;
},
addScript: function(url, callback, test, ttl) {
var _callback = function(el, ttl) {
var result = test(el);
if (typeof(result) !== "undefined") {
callback(el);
} else {
setTimeout(function() {
if (ttl > 0) {
_callback(el, ttl - 1);
} else {
console.log("failed load " + url);
}
}, 1);
}
}; };
var el = document.createElement("script"); ////////////////////////////////////////////////////////////////////////
el.src = url; // exports.main()
el.type = "text/javascript"; ////////////////////////////////////////////////////////////////////////
el.charset = "utf-8"; exports.main = function(args) {
document.head.appendChild(el); // load contents
if (typeof(test) === "function" && typeof(callback) === "function") {
// "Time-To-Live: default value is 30 seconds";
ttl = (typeof(ttl) == "number" ? ttl : 30000);
el.onload = _callback(el, ttl);
} else if (typeof(callback) === "function") {
el.onload = callback(el);
}
return el;
},
addStylesheet: function(url, callback) {
var el = document.createElement("link");
el.href = url;
el.rel = "stylesheet";
el.type = "text/css";
el.media = "screen, projection";
document.head.appendChild(el);
if (typeof(callback) === "function") {
el.onload = callback(el);
}
return el;
},
main: function(args) {
// "set variable 'self'";
var self = this;
// "load contents";
var contents = FILE.readFile("app\\index.html", "utf-8"); var contents = FILE.readFile("app\\index.html", "utf-8");
document.getElementById("app").innerHTML = contents; document.getElementById("app").innerHTML = contents;
// "load stylesheets dynamically"; // load components dynamically
self.addStylesheet("app/assets/css/jquery-ui-1.21.1.min.css"); OldBrowser.addStylesheet("app/assets/css/jquery-ui-1.21.1.min.css");
self.addStylesheet("app/assets/css/jquery.toast-1.3.2.min.css"); OldBrowser.addStylesheet("app/assets/css/jquery.toast-1.3.2.min.css");
self.addStylesheet("app/assets/css/style.css"); OldBrowser.addStylesheet("app/assets/css/style.css");
OldBrowser.start(function(el) {
// "go to entrypoint";
var start = function() {
self.addScript("app/index.js");
};
// "when loaded jquery (strictly)";
var jqLoaded = function(el) {
jQuery.support.cors = true; jQuery.support.cors = true;
self.addScript("app/assets/js/jquery.toast-1.3.2.min.js", function(el) { OldBrowser.addScript("app/assets/js/jquery.toast-1.3.2.min.js", function(el) {
var messages = global.console.__messages; var messages = global.console.__messages;
if (messages.length > 0) { if (messages.length > 0) {
for (var i in messages) { for (var i in messages) {
console.log(messages[i]); console.log(messages[i]);
} }
// "start this app"; // start this app
start(); OldBrowser.addScript("app/index.js");
} }
}, function(el) { }, function(el) {
return window.jQuery.toast; return window.jQuery.toast;
}); });
};
if (self.getIEVersion() == 8) { OldBrowser.addScript("app/assets/js/jquery.form-4.3.0.min.js");
self.addScript("app/assets/css/jquery/webreflection-ie8-0.8.1.min.js");
}
// "load javascripts dynamically";
self.addScript("app/assets/js/es5-shim-4.5.14.min.js");
self.addScript("app/assets/js/es5-sham-4.5.14.min.js");
self.addScript("app/assets/js/json3-3.3.2.min.js");
self.addScript("app/assets/js/es6-shim-0.35.5.min.js");
self.addScript("app/assets/js/es6-sham-0.35.5.min.js");
self.addScript("app/assets/js/html5shiv-printshiv-3.7.3.min.js");
if (self.getIEVersion() < 9) {
self.addScript("app/assets/js/welsonjs-respond-1.4.2-modified.js");
self.addScript("app/assets/js/welsonjs-selectivizr-1.0.2-modified.js");
self.addScript("app/assets/js/excanvas-565afad.js");
self.addScript("app/assets/js/jquery-1.11.3.min.js", jqLoaded, function(el) {
return window.jQuery;
}); });
self.addScript("http://api.html5media.info/1.1.6/html5media.min.js");
} else {
self.addScript("app/assets/js/jquery-3.5.1.min.js", jqLoaded, function(el) {
return window.jQuery;
});
}
self.addScript("app/assets/js/modernizr-2.8.3.min.js");
// "load jQuery UI (1.12.1)"; // hook drag and drop
self.addScript("app/assets/js/jquery-ui-1.21.1.min.js");
// "load jQuery plugins";
if (self.getIEVersion() < 10) {
self.addScript("app/assets/js/PIE-1.0.0.js");
self.addScript("app/assets/js/jquery.html5-placeholder-shim-5a87f05.js");
}
self.addScript("app/assets/js/jquery.form-4.3.0.min.js");
// "prevent text drag and drop"; {
document.body.ondragstart = function() { document.body.ondragstart = function() {
return false; return false;
}; };
document.body.ondrop = function() { document.body.ondrop = function() {
return false; return false;
}; };
// };
// "set movable window"; // set movable window
self.enableMovableWindow(); exports.enableMovableWindow();
// "assign click event if it is matched URI scheme"; // assign click event
var elems = document.getElementsByTagName("a"); var elems = document.getElementsByTagName("a");
for(var i in elems) { for(var i in elems) {
var uri = elems[i].href || ""; var uri = elems[i].href || "";
@ -326,10 +123,5 @@ return {
} }
} }
// "get HTA application arguments";
//var appArguments = args[0].split(' ');
// TODO
return 0; return 0;
} };
}