/**=====LICENSE STATEMENT START=====
Translator++
CAT (Computer-Assisted Translation) tools and framework to create quality
translations and localizations efficiently.
Copyright (C) 2018 Dreamsavior<dreamsavior@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
=====LICENSE STATEMENT END=====*/
var fs = fs || require('fs');
var _7z = _7z||require('7zip-min');
var nwPath = nwPath||require('path');
var ThirdParty = function(config) {
this.config = config;
this.defaultConfigPath = '3rdParty\\config.json'
this.configPaths = [this.defaultConfigPath]
this.configUrl = "https://dreamsavior.net/mirror/config/config.json"
this.acceptedArchive = ["7z", "7zip", "zip", "lzma", "cab", "gzip"]
this.config = {};
this.isInitialized = false;
this.init.apply(this, arguments);
}
/**
* Add config file to be loaded at initialization
* @param {String} configPath
*/
ThirdParty.prototype.addConfigFile = async function(configPath) {
if (!await common.isFileAsync(configPath)) return console.warn("Invalid config file:", configPath);
this.configPaths.push(configPath);
}
/**
* Add configuration from object
* @param {Object} config
*/
ThirdParty.prototype.addConfig = function(config) {
this.config = {...this.config, ...config.products };
console.log(this.config);
}
/**
* Append configuration from file
* @param {String} configFile
*/
ThirdParty.prototype.loadConfigFile = async function(configFile) {
try {
var content = await common.fileGetContents(configFile);
var contentVar = JSON.parse(content);
this.addConfig(contentVar);
} catch (e) {
console.warn("Error when trying to add file to config:", configFile);
}
console.log(this.config);
}
ThirdParty.prototype.initConfig = async function(configPaths) {
console.log("Third party load config");
configPaths = configPaths || this.configPaths;
if (Array.isArray(configPaths) == false) configPaths = [configPaths]
try{
// handling main config
// main config is configPaths[0]
var content = await common.fileGetContents(configPaths[0]);
var contentVar = JSON.parse(content);
this.addConfig(contentVar);
this.configDate = contentVar.info.date;
// load additional config
for (var i=1; i<configPaths.length; i++) {
this.loadConfigFile(configPaths[i]);
}
} catch (e) {
console.warn("Error on loading 3rdParty configuration : ", e)
}
}
ThirdParty.prototype.init = function(config) {
if (!this.isInitialized) {
this.initConfig();
this.isInitialized = true;
}
}
ThirdParty.prototype.update = function(url, options) {
url = url||this.configUrl;
options = options||{};
options.onDone = options.onDone || function() {};
var request = require('request');
var progress = require('request-progress');
request(url, function (error, response, body) {
options.onDone.call(this, body);
})
.pipe(fs.createWriteStream(this.defaultConfigPath))
}
ThirdParty.prototype.getWorkingLink = async function(urls) {
if (typeof urls == "string") urls = [urls];
for (var i in urls) {
if (await common.isUrlWorking(urls[i])) return urls[i];
}
return "";
}
ThirdParty.prototype.checkWorkingLinks = function(urls, callback, options) {
urls = urls||[];
urls = JSON.parse(JSON.stringify(urls))
callback = callback||function(){};
options = options||{};
console.log("URLS are : ", urls);
var urlStack = urls;
var that = this;
var request = request||require('request');
this.__urlTestCache = this.__urlTestCache||{};
var urlCheckRecursive = function(url) {
if (typeof that.__urlTestCache[url] !== 'undefined') {
if (that.__urlTestCache[url]) {
callback.call(that, url);
} else {
var newUrl = urlStack.pop();
urlCheckRecursive(newUrl);
}
return;
}
request.get(url, function(err, httpResponse, body) {
console.log(arguments);
}).on('data', function(data) {
this.abort();
console.log("Status Code : ", this.response.statusCode);
if (this.response.statusCode == 200) {
that.__urlTestCache[url] = true;
callback.call(that, url, this.response);
} else {
that.__urlTestCache[url] = false;
var newUrl = urlStack.pop();
urlCheckRecursive(newUrl);
}
//console.log("received data length", data.length);
});
}
var newUrl = urlStack.pop();
urlCheckRecursive(newUrl);
}
ThirdParty.prototype.evalProblem = function() {
if (this.popup.find(".segments").length > 0) {
this.popup.find(".isProblem").removeClass("hidden");
this.popup.find(".noProblem").addClass("hidden");
} else {
this.popup.find(".isProblem").addClass("hidden");
this.popup.find(".noProblem").removeClass("hidden");
}
}
ThirdParty.prototype.showPopup = function($content, options={}) {
var that = this;
options.title = options.title||"Third party application installer"
options.width = options.width||Math.round($(window).width()/100*80)
options.height = options.height||Math.round($(window).height()/100*80)
options.classes = {
"ui-dialog": "topMost"
}
/*
var $popupContents = $("<h1>One or more required application(s) are not found</h1>\
<p>But don't worry, Translator++ will guide you trhoughout the installation process.</p>\
<div class='wrapper'></div>\
");
$popupContents.append($content);
*/
var $popupContents = [$("\
<div class='thirdPartyUpdater'><span class='configDateWrapper'>This list is generated by configuration dated : <span class='configDate'></span></span><span class='menuBar'><a href='#' class='updateConfig icon-download-cloud'>Update configuration file</a></span></div>\
<div class='headerBox'>\
<div class='blockBox attentionBlock withIcon hidden isProblem'><h1>You might need to install the following applications!</h1>\
<p>Translator++ can be used normally even without these applications. But Translator++ can help you avoid a lot of manual work by installing these applications.</p>\
<p>These applications are developed by 3rd parties and Translator++ is not affiliated with it. All licenses are owned by their respective owners.</p>\
</div>\
<div class='blockBox infoBlock withIcon hidden noProblem'><h1>You are good to go!</h1>\
<p>You've installed all 3rd party applications Translator++ can work with.</p>\
</div>\
</div>\
<div class='wrapper'></div>\
"),
$("<div class='wrapper'></div>").append($content)];
var nDate = new Date(this.configDate);
$popupContents[0].find(".configDate").html(nDate.toGMTString());
$popupContents[0].find(".updateConfig").on("click", function() {
var $that = $(this);
$(this).removeClass("icon-download-cloud");
$(this).addClass("icon-spin6");
$(this).addClass("spin-icon");
that.update(undefined, {
onDone : function() {
$that.addClass("icon-download-cloud");
$that.removeClass("icon-spin6");
$that.removeClass("spin-icon");
var conf = confirm("Configuration updated.\nRestart the application?");
if (conf) chrome.runtime.reload()
}
});
})
ui.showPopup("thirdParty", $popupContents, options);
this.popup = $("[data-popupid=thirdParty]");
this.popup.closest(".ui-dialog").css("z-index", "2000");
this.evalProblem();
}
ThirdParty.prototype.getConfig = function() {
return this.config;
}
ThirdParty.prototype.getLocation = function(conf) {
if (Boolean(this.config[conf]) == false) return false;
var thisConfig = this.config[conf]
return nwPath.join(__dirname, thisConfig['location']||"");
}
ThirdParty.prototype.getBasePath = function() {
return nwPath.join(__dirname, "3rdParty");
}
ThirdParty.prototype.isInstalled = function(conf) {
// check if the module is ready and already installed
if (Boolean(this.config[conf]) == false) return false;
var thisConfig = this.config[conf]
console.log("thisConfig : ", thisConfig);
if (typeof thisConfig.expectedFiles == "string") thisConfig.expectedFiles = [thisConfig.expectedFiles]
for (var i=0; i<thisConfig.expectedFiles.length; i++) {
var path = __dirname+"/"+thisConfig['location']+"/"+thisConfig.expectedFiles[i];
var expLocation = __dirname+"/"+thisConfig['location'];
expLocation = expLocation.split("/").join("\\");
try {
console.log("checking : + ", path);
if (fs.existsSync(path)) {
//console.log('file exists');
} else {
return false;
}
} catch (e) {
return false;
}
}
return true;
}
ThirdParty.prototype.install = async function(config, $segments) {
config = config || {};
var $statusInfo = $segments.find(".statusInfo");
var $info = $statusInfo.find(".info");
var $progress = $statusInfo.find(".progressValue");
$statusInfo.removeClass("hidden");
$info.text("Checking available mirror");
var url = await this.getWorkingLink(config.repo);
console.log("url", url);
if (!url) return alert("Failed to download file. Repository not exist. Please see console log for more info.");
$info.text("A working mirror found : "+url);
var filename = nwPath.basename(url);
//var tmp = nwPath.join(nw.process.env.TMP, nwPath.basename(url));
$statusInfo.removeClass("hidden");
var options = {}
options.onEnd = function(){
$progress.css("width", "100%")
$progress.html("100%")
$info.html("Download done!")
};
options.onProgress = function(state){
var percent = state.percent
var speed = state.speed;
var total = state.total;
var transfered = state.transfered;
var timeRemaining = Math.round(state.time.remaining);
$progress.css("width", percent+"%")
$progress.html(percent+"%")
$info.html("<span class='progress'>"+transfered+"kb/"+total+"kb</span> <span class='speed'>("+speed+" kb/s)</span> <span class='time'>"+timeRemaining+"s left</span>")
};
options.onSuccess = function(){};
// ==============DOWNLOADING=================
console.log("downloading ", url);
const tmp = await common.downloadFile(url, nw.process.env.TMP, options);
console.log("Request done", tmp);
await common.wait(1000);
var ext = common.getFileExtension(tmp)
var targetDir = nwPath.join(__dirname, "3rdParty", config["extractDir"]||"");
if (this.acceptedArchive.includes(ext)) {
// unpack
$info.html("Unpacking")
await common.extract(tmp, targetDir);
console.log("unpacking from", tmp);
console.log("to", targetDir);
$info.html("Done!");
$statusInfo.addClass("hidden");
if (typeof config.licenseFile !== 'undefined') {
var conf = confirm("Instalation done!\nDo you want to read the License?");
if (conf) nw.Shell.openItem(__dirname+"/"+config['location']+"/"+config.licenseFile);
}
$segments.remove();
this.evalProblem();
} else {
// destination.txt will be created or overwritten by default.
$info.html("Copying file.")
await common.copyFile(tmp, nwPath.join(__dirname, "3rdParty", filename));
$info.html("Done!");
$statusInfo.addClass("hidden");
$segments.remove();
this.evalProblem();
}
}
ThirdParty.prototype.check = function(options) {
// check if the requirement files and modules are presents
options = options||{};
options.options = options.force||false; // force popup
options.popup = options.popup||false; // force popup
options.filter = options.filter || undefined;
if (Boolean(options.filter) == true) {
if (Array.isArray(options.filter) == false) options.filter = [options.filter]
}
console.log("running thirdParty.check");
console.log("Current config:", this.config);
var $content = $("<div class='initReqs_contents'></div>");
var that = this;
for (var conf in this.config) {
if (Boolean(options.filter) == true) {
if (options.filter.includes(conf) == false) continue;
}
var thisConfig = this.config[conf]
console.log("thisConfig : ", thisConfig);
if (typeof thisConfig.expectedFiles == "string") thisConfig.expectedFiles = [thisConfig.expectedFiles]
for (var i=0; i<thisConfig.expectedFiles.length; i++) {
var path = __dirname+"/"+thisConfig['location']+"/"+thisConfig.expectedFiles[i];
var expLocation = __dirname+"/"+thisConfig['location'];
expLocation = expLocation.split("/").join("\\");
try {
console.log("checking : + ", path);
if (fs.existsSync(path)) {
console.log('file exists');
} else {
console.log("file not found : ", path)
var $segments = $("<div class='segments' data-confid='"+conf+"'>\
<div class='initReqs_title'><span>"+thisConfig['name']+"</span></div>\
<div class='initReqs_description'><span>"+thisConfig['description']+"</span></div>\
<div class='initReqs_url'>\
<span><a href='"+thisConfig['url']+"' class='icon-home' external>"+thisConfig['url']+"</a></span>\
<span class='initReqs_sourceCode icon-file-code hidden'><a href='"+thisConfig['sourceCode']+"' external>Get the source code</a></span>\
</div>\
<div class='initReqs_info'><span>"+thisConfig['info']+"</span></div>\
<div class='initReqs_expectedPath gridView'><span class='label'>Expected Location</span><a href='#' class='icon-folder-1 browseFolder' title='Browse folder'>"+expLocation+"</a></div>\
<div class='initReqs_fileSize gridView'><span class='label'>File Size</span><a href='#' class='icon-box browseFolder' title='Browse folder'>"+thisConfig['fileSize']+"</a></div>\
<div class='initReqs_action'>\
<button class='downloadFile icon-cloud'>Download the file</button>\
<button class='installFromFile icon-folder-open'>Install from my computer</button>\
<button class='automaticInstall icon-download-cloud highlight'>Download and install automatically</button>\
</div>\
<div class='statusInfo hidden'>\
<span class='progressBar'><span class='progressValue'></span></span>\
<span class='info'></span>\
</div>\
</div>");
$segments.data("id", conf);
$content.append($segments);
if (thisConfig['sourceCode']) {
$segments.find(".initReqs_sourceCode").removeClass("hidden")
}
$segments.find(".browseFolder").off("click")
$segments.find(".browseFolder").on("click", function() {
console.log("Opening : ", $(this).text());
common.openExplorer($(this).text(), $(this).text());
})
$segments.find("a.externalLink, a[external]").off("click")
$segments.find("a.externalLink, a[external]").on("click", function(e) {
e.preventDefault();
console.log("opening", $(this).attr("href"));
nw.Shell.openExternal($(this).attr("href"));
})
$segments.find(".installFromFile").off("click")
$segments.find(".installFromFile").on("click", function(e) {
var $segments = $(this).closest(".segments");
ui.openFileDialog({
accept:".7z,.7zip,.zip,.exe",
onSelect:function(path) {
var fileExt = common.getFileExtension(path)
var filename = common.getFileName(path);
console.log("Install manualy from : ", path);
if (that.acceptedArchive.includes(fileExt)) {
console.log("selected ",path);
// unpack
_7z.unpack(path, __dirname+"\\3rdParty", err => {
console.log("done unpacking to", path);
// done
$segments.remove();
that.evalProblem();
});
} else {
fs.copyFile(path, __dirname+"\\3rdParty\\"+filename, (err) => {
if (err) throw err;
console.log("copying file")
$segments.remove();
that.evalProblem();
});
}
}
});
});
$segments.find(".downloadFile").on("click", function(e) {
e.preventDefault();
var thisId = $(this).closest(".segments").data("id");
var thisConfig = thirdParty.getConfig()[thisId];
var $segments = $(this).closest(".segments");
var conf = confirm("Is it ok for Translator++ to open a browser and download a file for you?");
if (conf) {
$segments.find(".statusInfo").removeClass("hidden");
$segments.find(".statusInfo .progressBar").addClass("hidden");
$segments.find(".statusInfo .info").text("Checking available mirror");
that.checkWorkingLinks(thisConfig.repo, function(workingUrl) {
$segments.find(".statusInfo").addClass("hidden");
$segments.find(".statusInfo .progressBar").removeClass("hidden");
$segments.find(".statusInfo .info").text("");
nw.Shell.openExternal(workingUrl);
})
}
});
$segments.find(".automaticInstall").on("click", async function(e) {
var $this = $(this);
var $segments = $this.closest(".segments");
var thisId = $segments.data("id");
var thisConfig = thirdParty.getConfig()[thisId];
var conf = confirm("You are about to download and install "+thisConfig.name+"!\nYou do this action of your own volition.\nDo you wish to continue?");
if (!conf) return;
await that.install(thisConfig, $segments);
/*
var $statusInfo = $segments.find(".statusInfo");
var $info = $statusInfo.find(".info");
var $progress = $statusInfo.find(".progressValue");
var request = require('request');
var progress = require('request-progress');
$segments.find(".statusInfo").removeClass("hidden");
$segments.find(".statusInfo .info").text("Checking available mirror");
that.checkWorkingLinks(thisConfig.repo, function(url) {
$segments.find(".statusInfo .info").text("A working mirror found : "+url);
url = url||thisConfig.repo[0];
var filename = url.substring(url.lastIndexOf('/')+1);
var tmp = nw.process.env.TMP+"\\"+filename;
$statusInfo.removeClass("hidden");
console.log("downloading ", url);
progress(request(url, async function(error, response, body) {
console.log("Request done", tmp);
await common.wait(1000);
var ext = getFileExtension(tmp)
var targetDir = nwPath.join(__dirname, "3rdParty", thisConfig["extractDir"]||"");
if (that.acceptedArchive.includes(ext)) {
// unpack
$info.html("Unpacking")
await common.extract(tmp, targetDir);
console.log("unpacking from", tmp);
console.log("to", targetDir);
$info.html("Done!");
$statusInfo.addClass("hidden");
if (typeof thisConfig.licenseFile !== 'undefined') {
var conf = confirm("Instalation done!\nDo you want to read the License?");
if (conf) nw.Shell.openItem(__dirname+"/"+thisConfig['location']+"/"+thisConfig.licenseFile);
}
$segments.remove();
that.evalProblem();
} else {
// destination.txt will be created or overwritten by default.
$info.html("Copying file.")
fs.copyFile(tmp, __dirname+"\\3rdParty\\"+filename, (err) => {
if (err) throw err;
$info.html("Done!");
$statusInfo.addClass("hidden");
$segments.remove();
that.evalProblem();
});
}
}), {
throttle:200
})
.on('progress', function (state) {
//console.log(state);
var percent = Math.round(state.percent*100);
var speed = Intl.NumberFormat().format(Math.round(state.speed/1024));
var total = Intl.NumberFormat().format(Math.round(state.size.total/1024));
var transfered = Intl.NumberFormat().format(Math.round(state.size.transferred/1024));
var timeRemaining = Math.round(state.time.remaining);
$progress.css("width", percent+"%")
$progress.html(percent+"%")
$info.html("<span class='progress'>"+transfered+"kb/"+total+"kb</span> <span class='speed'>("+speed+" kb/s)</span> <span class='time'>"+timeRemaining+"s left</span>")
//$statusInfo.html(JSON.stringify(state))
})
.on('end', function () {
// Do something after request finishes
$progress.css("width", "100%")
$progress.html("100%")
$info.html("Download done!")
})
.pipe(fs.createWriteStream(tmp))
}) // checkWorkingLinks
*/
});
break;
}
} catch(err) {
console.warn(err)
}
}
}
//console.log($content.find(".segments"));
if (options.popup) {
if (options.force) {
this.showPopup($content,options);
} else {
if ($content.find(".segments").length > 0) this.showPopup($content,options);
}
}
return $content;
//this.showPopup($content);
}
var thirdParty = new ThirdParty();
$(document).ready(function() {
sys.onReady(function() {
ui.onReady(function() {
thirdParty.check({popup:true});
})
})
})