From 732b6bba35fe1fdb67d5ec33243934398800a6f9 Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Wed, 31 Jan 2024 15:02:23 -0800 Subject: [PATCH] prepare workspace by eu5, add some scripts and node modules on Documents folder, extends compile script and also node module to automate usefull most commonly used compilation tasks and also create required npm scripts and vscode debug lunch tasks based on them ... --- .gitignore | 4 + .vscode/launch.json | 28 +- .../x-saherelm.class.lib.mq5 | 0 .../Classes => Classes}/x-saherelm.nn.lib.mq5 | 0 .../x-saherelm.provider.lib.mq5 | 0 Compile-MQL.ps1 | 47 - Documents/JsModules/x-color.tools.js | 506 ++++++ Documents/JsModules/x-file.tools.js | 830 +++++++++ Documents/JsModules/x-shell.tools.js | 219 +++ Documents/JsModules/x-type-detector.tools.js | 268 +++ Documents/JsModules/x-value.tools.js | 1528 +++++++++++++++++ .../x-saherelm.xeu5.ea.mq5 | 0 .../x-saherelm.xama.indicator.helper.lib.mq5 | 0 ...x-saherelm.xchlh.oscillator.helper.lib.mq5 | 0 ...x-saherelm.xchma.oscillator.helper.lib.mq5 | 0 .../x-saherelm.xfi.oscillator.helper.lib.mq5 | 0 .../x-saherelm.xlh.indicator.helper.lib.mq5 | 0 .../x-saherelm.xobd.indicator.helper.lib.mq5 | 0 .../x-saherelm.xrsi.oscillator.helper.lib.mq5 | 0 .../x-saherelm.xtd.oscillator.helper.lib.mq5 | 0 .../x-saherelm.xtm.indicator.helper.lib.mq5 | 0 .../x-saherelm.xts.oscillator.helper.lib.mq5 | 0 .../x-saherelm.xama.indicator.mq5 | 0 .../x-saherelm.xchlh.oscillator.mq5 | 0 .../x-saherelm.xchma.oscillator.mq5 | 0 .../x-saherelm.xfi.oscillator.mq5 | 0 .../x-saherelm.xlh.indicator.mq5 | 0 .../x-saherelm.xobd.indicator.mq5 | 0 .../x-saherelm.xrsi.oscillator.mq5 | 0 .../x-saherelm.xtd.oscillator.mq5 | 0 .../x-saherelm.xtm.indicator.mq5 | 0 .../x-saherelm.xts.oscillator.mq5 | 0 .../x-saherelm.alert.lib.mq5 | 0 .../x-saherelm.common.lib.mq5 | 0 .../x-saherelm.draw.lib.mq5 | 0 .../x-saherelm.http.lib.mq5 | 0 .../x-saherelm.log.lib.mq5 | 0 .../x-saherelm.models.lib.mq5 | 0 Packs/.gitkeep | 0 ...-saherelm.xeu5.provider.indicators.lib.mq5 | 0 .../x-saherelm.xeu5.provider.inputs.lib.mq5 | 0 .../x-saherelm.xeu5.provider.lib.mq5 | 0 compile.cmd | 26 + compile.js | 398 +++++ package.json | 8 + 45 files changed, 3813 insertions(+), 49 deletions(-) rename {BKP/Main/Classes => Classes}/x-saherelm.class.lib.mq5 (100%) rename {BKP/Main/Classes => Classes}/x-saherelm.nn.lib.mq5 (100%) rename {BKP/Main/Classes => Classes}/x-saherelm.provider.lib.mq5 (100%) delete mode 100644 Compile-MQL.ps1 create mode 100644 Documents/JsModules/x-color.tools.js create mode 100644 Documents/JsModules/x-file.tools.js create mode 100644 Documents/JsModules/x-shell.tools.js create mode 100644 Documents/JsModules/x-type-detector.tools.js create mode 100644 Documents/JsModules/x-value.tools.js rename {BKP/Main/Experts => Experts}/x-saherelm.xeu5.ea.mq5 (100%) rename {BKP/Main/Helpers => Helpers}/x-saherelm.xama.indicator.helper.lib.mq5 (100%) rename {BKP/Main/Helpers => Helpers}/x-saherelm.xchlh.oscillator.helper.lib.mq5 (100%) rename {BKP/Main/Helpers => Helpers}/x-saherelm.xchma.oscillator.helper.lib.mq5 (100%) rename {BKP/Main/Helpers => Helpers}/x-saherelm.xfi.oscillator.helper.lib.mq5 (100%) rename {BKP/Main/Helpers => Helpers}/x-saherelm.xlh.indicator.helper.lib.mq5 (100%) rename {BKP/Main/Helpers => Helpers}/x-saherelm.xobd.indicator.helper.lib.mq5 (100%) rename {BKP/Main/Helpers => Helpers}/x-saherelm.xrsi.oscillator.helper.lib.mq5 (100%) rename {BKP/Main/Helpers => Helpers}/x-saherelm.xtd.oscillator.helper.lib.mq5 (100%) rename {BKP/Main/Helpers => Helpers}/x-saherelm.xtm.indicator.helper.lib.mq5 (100%) rename {BKP/Main/Helpers => Helpers}/x-saherelm.xts.oscillator.helper.lib.mq5 (100%) rename {BKP/Main/Indicators => Indicators}/x-saherelm.xama.indicator.mq5 (100%) rename {BKP/Main/Indicators => Indicators}/x-saherelm.xchlh.oscillator.mq5 (100%) rename {BKP/Main/Indicators => Indicators}/x-saherelm.xchma.oscillator.mq5 (100%) rename {BKP/Main/Indicators => Indicators}/x-saherelm.xfi.oscillator.mq5 (100%) rename {BKP/Main/Indicators => Indicators}/x-saherelm.xlh.indicator.mq5 (100%) rename {BKP/Main/Indicators => Indicators}/x-saherelm.xobd.indicator.mq5 (100%) rename {BKP/Main/Indicators => Indicators}/x-saherelm.xrsi.oscillator.mq5 (100%) rename {BKP/Main/Indicators => Indicators}/x-saherelm.xtd.oscillator.mq5 (100%) rename {BKP/Main/Indicators => Indicators}/x-saherelm.xtm.indicator.mq5 (100%) rename {BKP/Main/Indicators => Indicators}/x-saherelm.xts.oscillator.mq5 (100%) rename {BKP/Main/Libraries => Libraries}/x-saherelm.alert.lib.mq5 (100%) rename {BKP/Main/Libraries => Libraries}/x-saherelm.common.lib.mq5 (100%) rename {BKP/Main/Libraries => Libraries}/x-saherelm.draw.lib.mq5 (100%) rename {BKP/Main/Libraries => Libraries}/x-saherelm.http.lib.mq5 (100%) rename {BKP/Main/Libraries => Libraries}/x-saherelm.log.lib.mq5 (100%) rename {BKP/Main/Libraries => Libraries}/x-saherelm.models.lib.mq5 (100%) create mode 100644 Packs/.gitkeep rename {BKP/Main/Providers => Providers}/x-saherelm.xeu5.provider.indicators.lib.mq5 (100%) rename {BKP/Main/Providers => Providers}/x-saherelm.xeu5.provider.inputs.lib.mq5 (100%) rename {BKP/Main/Providers => Providers}/x-saherelm.xeu5.provider.lib.mq5 (100%) create mode 100644 compile.cmd create mode 100644 compile.js diff --git a/.gitignore b/.gitignore index 85592b0b..edadfb88 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,7 @@ package-lock.json # .VSCodeCounter + +# +Packs/* +!Packs/.gitkeep diff --git a/.vscode/launch.json b/.vscode/launch.json index 0aa69099..7eb44952 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,13 +7,37 @@ { "type": "node", "request": "launch", - "name": "Compile XEA", + "name": "Compile Experts", "skipFiles": [ "/**" ], "runtimeExecutable": "npm", "runtimeArgs": [ - "run-script", "compileEA" + "run-script", "compileExperts" + ] + }, + { + "type": "node", + "request": "launch", + "name": "Compile Workspace", + "skipFiles": [ + "/**" + ], + "runtimeExecutable": "npm", + "runtimeArgs": [ + "run-script", "compileWorkspace" + ] + }, + { + "type": "node", + "request": "launch", + "name": "Compile Indicators", + "skipFiles": [ + "/**" + ], + "runtimeExecutable": "npm", + "runtimeArgs": [ + "run-script", "compileIndicators" ] } ] diff --git a/BKP/Main/Classes/x-saherelm.class.lib.mq5 b/Classes/x-saherelm.class.lib.mq5 similarity index 100% rename from BKP/Main/Classes/x-saherelm.class.lib.mq5 rename to Classes/x-saherelm.class.lib.mq5 diff --git a/BKP/Main/Classes/x-saherelm.nn.lib.mq5 b/Classes/x-saherelm.nn.lib.mq5 similarity index 100% rename from BKP/Main/Classes/x-saherelm.nn.lib.mq5 rename to Classes/x-saherelm.nn.lib.mq5 diff --git a/BKP/Main/Classes/x-saherelm.provider.lib.mq5 b/Classes/x-saherelm.provider.lib.mq5 similarity index 100% rename from BKP/Main/Classes/x-saherelm.provider.lib.mq5 rename to Classes/x-saherelm.provider.lib.mq5 diff --git a/Compile-MQL.ps1 b/Compile-MQL.ps1 deleted file mode 100644 index 63eb9eb0..00000000 --- a/Compile-MQL.ps1 +++ /dev/null @@ -1,47 +0,0 @@ -#gets the File To Compile as an external parameter... Defaults to a Test file... -Param( $FileToCompile = "C:\Users\saherelm\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Experts\x-saherelm.ea.mq5") - -#cleans the terminal screen and sets the log file name... -Clear-Host -$LogFile = $FileToCompile + ".log" - -#before continue check if the Compile File has any spaces in it... -if ($FileToCompile.Contains(" ")) { - ""; ""; - Write-Host "ERROR! Impossible to Compile! Your Filename or Path contains SPACES!" -ForegroundColor Red; - ""; - Write-Host $FileToCompile -ForegroundColor Red; - ""; ""; - return; -} - -#first of all, kill MT Terminal (if running)... otherwise it will not see the new compiled version of the code... -Get-Process -Name terminal64 -ErrorAction SilentlyContinue | Where-Object { $_.Id -gt 0 } | Stop-Process - -#fires up the Metaeditor compiler... -& "C:\Program Files\MetaTrader 5\MetaEditor64.exe" /compile:"$FileToCompile" /log:"$LogFile" /inc:"C:\Users\saherelm\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5" | Out-Null - -#get some clean real state and tells the user what is being compiled (just the file name, no path)... -""; ""; ""; ""; "" -$JustTheFileName = Split-Path $FileToCompile -Leaf -Write-Host "Compiling........: $JustTheFileName" -"" - -#reads the log file. Eliminates the blank lines. Skip the first line because it is useless. -$Log = Get-Content -Path $LogFile | Where-Object { $_ -ne "" } | Select-Object -Skip 1 - -#Green color for successful Compilation. Otherwise (error/warning), Red! -$WhichColor = "Red" -$Log | ForEach-Object { if ($_.Contains("0 error(s), 0 warning(s)")) { $WhichColor = "Green" } } - -#runs through all the log lines... -$Log | ForEach-Object { - #ignores the ": information: error generating code" line when ME was successful - if (-Not $_.Contains("information:")) { - #common log line... just print it... - Write-Host $_ -ForegroundColor $WhichColor - } -} - -#get the MT Terminal back if all went well... -if ( $WhichColor -eq "Green") { & "C:\\Program Files\\MetaTrader 5\\MetaEditor64.exe" } \ No newline at end of file diff --git a/Documents/JsModules/x-color.tools.js b/Documents/JsModules/x-color.tools.js new file mode 100644 index 00000000..c3ac5f9e --- /dev/null +++ b/Documents/JsModules/x-color.tools.js @@ -0,0 +1,506 @@ +/** + * XColor Tools Module ... + * a module for handle colorify text contents in node js ... + * + * Maintainer + * + * Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com) + */ + +// +//#region Module Imports ... +const XValueTools = require("./x-value.tools"); +//#endregion + +// +//#region Constants ... +/** + * these are available style which can applied to an string ... + */ +const AVAILABLE_STYLES = { + // + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Underlined: "\x1b[4m", + Blink: "\x1b[5m", + ReverseFandB: "\x1b[7m", + Hidden: "\x1b[8m", + // + // Commonly used for reset all Styles ... + Reset: "\x1b[0m" +}; + +/** + * these are available foreground colors which can applied to an string ... + */ +const AVAILABLE_FOREGROUND_COLORS = { + Default: "\x1b[39m", + Black: "\x1b[30m", + Red: "\x1b[31m", + Green: "\x1b[32m", + Yellow: "\x1b[33m", + Blue: "\x1b[34m", + Magenta: "\x1b[35m", + Cyan: "\x1b[36m", + LightGray: "\x1b[37m", + DarkGray: "\x1b[90m", + LightRed: "\x1b[91m", + LightGreen: "\x1b[92m", + LightYellow: "\x1b[93m", + LightBlue: "\x1b[94m", + LightMagenta: "\x1b[95m", + LightCyan: "\x1b[96m", + White: "\x1b[97m", +}; + +/** + * these are available background colors which can applied to an string ... + */ +const AVAILABLE_BACKGROUND_COLORS = { + Default: "\x1b[49m", + Black: "\x1b[40m", + Red: "\x1b[41m", + Green: "\x1b[42m", + Yellow: "\x1b[43m", + Blue: "\x1b[44m", + Magenta: "\x1b[45m", + Cyan: "\x1b[46m", + LightGray: "\x1b[47m", + DarkGray: "\x1b[100m", + LightRed: "\x1b[101m", + LightGreen: "\x1b[102m", + LightYellow: "\x1b[103m", + LightBlue: "\x1b[104m", + LightMagenta: "\x1b[105m", + LightCyan: "\x1b[106m", + White: "\x1b[107m", +}; + +/** + * these are available style names, which exports from module and + * users can use them ... + */ +const STYLE_NAMES = { + Bold: "Bold", + Dim: "Dim", + Underlined: "Underlined", + Blink: "Blink", + ReverseFandB: "ReverseFandB", + Hidden: "Hidden", + Reset: "Reset", +}; + +/** + * these are available color names, which exports from module and + * users can use them ... + */ +const COLOR_NAMES = { + Default: "Default", + Black: "Black", + Red: "Red", + Green: "Green", + Yellow: "Yellow", + Blue: "Blue", + Magenta: "Magenta", + Cyan: "Cyan", + LightGray: "LightGray", + DarkGray: "DarkGray", + LightRed: "LightRed", + LightGreen: "LightGreen", + LightYellow: "LightYellow", + LightBlue: "LightBlue", + LightMagenta: "LightMagenta", + LightCyan: "LightCyan", + White: "White", +}; +//#endregion + +// +//#region Actions ... +/** + * apply specified style and color on a content ... + * + * @param {string} content specified content for styling ... + * @param {string} color specific color name for using to styling ... + * @param {string} style soecufic style name to use ... + * @param {boolean} toForeground apply specified color as foreground ... + * @param {boolean} toBackground apply specified color as background ... + * @returns {string} styled content ... + */ +function apply( + content, + color, + style, + toForeground = true, + toBackground = false +) { + // + let result = content; + + // + // Validate Arg ... + if (!XValueTools.isValidArg(content)) { + return result; + } + + // + // Detect and Validate Style and Apply it ... + let eStyle = AVAILABLE_STYLES[style]; + if (XValueTools.isValidArg(eStyle)) { + result = `${eStyle}${result}${AVAILABLE_STYLES.Reset}`; + } + + // + // Detect and Validate Foreground Color and Apply it ... + let eFColor = AVAILABLE_FOREGROUND_COLORS[color]; + if ( + !!toForeground + && XValueTools.isValidArg(eFColor) + ) { + result = `${eFColor}${result}${AVAILABLE_STYLES.Reset}`; + } + + // + // Detect and Validate Background Color and Apply it ... + let eBColor = AVAILABLE_BACKGROUND_COLORS[color]; + if ( + !!toBackground + && XValueTools.isValidArg(eBColor) + ) { + result = `${eBColor}${result}${AVAILABLE_STYLES.Reset}`; + } + +} + +/** + * apply specific style on a content ... + * + * @param {string} content specific content which going to styled ... + * @param {string} style a member of STYLE_NAMES which specified that which style going to applied to content ... + * @returns {string} styled content ... + */ +function applyStyle(content, style) { + // + // Validate Arg ... + if (!XValueTools.isValidArg(content)) { + return content; + } + + // + let eStyle = AVAILABLE_STYLES[style]; + if (eStyle === undefined) { + return content; + } + + // + return `${eStyle}${content}${AVAILABLE_STYLES.Reset}`; +} + +/** + * apply specific foreground color on a content ... + * + * @param {string} content specific content which going to colorified ... + * @param {string} color a member of COLOR_NAMES which specified that which color going to applied to content ... + * @returns {string} colorified content ... + */ +function applyForegroundColor(content, color) { + // + // Validate Arg ... + if (!XValueTools.isValidArg(content)) { + return content; + } + + // + let eColor = AVAILABLE_FOREGROUND_COLORS[color]; + if (eColor === undefined) { + return content; + } + + // + return `${eColor}${content}${AVAILABLE_STYLES.Reset}`; +} + +/** + * apply specific background color on a content ... + * + * @param {string} content specific content which going to colorified ... + * @param {string} color a member of COLOR_NAMES which specified that which color going to applied to content ... + * @returns {string} colorified content ... + */ +function applyBackgroundColor(content, color) { + // + // Validate Arg ... + if (!XValueTools.isValidArg(content)) { + return content; + } + + // + let eColor = AVAILABLE_BACKGROUND_COLORS[color]; + if (eColor === undefined) { + return content; + } + + // + return `${eColor}${content}${AVAILABLE_STYLES.Reset}`; +} + +/** + * generate style and color applier expression ... + * + * @param {string} color specific color name for using to styling ... + * @param {string} style soecufic style name to use ... + * @param {boolean} reset close applier string by reset styles ... + * @param {boolean} toForeground apply specified color as foreground ... + * @param {boolean} toBackground apply specified color as background ... + * @returns {string} style and color applier string ... + */ +function getApplier( + style = "", + color = "", + reset = false, + toForeground = true, + toBackground = false +) { + // + let result = ""; + + // + // Detect and Validate Style and Apply it ... + let eStyle = AVAILABLE_STYLES[style]; + if (XValueTools.isValidArg(eStyle)) { + result = `${eStyle}`; + } + + // + // Detect and Validate Foreground Color and Apply it ... + let eFColor = AVAILABLE_FOREGROUND_COLORS[color]; + if ( + !!toForeground + && XValueTools.isValidArg(eFColor) + ) { + result = `${eFColor}`; + } + + // + // Detect and Validate Background Color and Apply it ... + let eBColor = AVAILABLE_BACKGROUND_COLORS[color]; + if ( + !!toBackground + && XValueTools.isValidArg(eBColor) + ) { + result = `${eBColor}`; + } + + // + if ( + !!reset && + result.length > 0 + ) { + result = `${result}${AVAILABLE_STYLES.Reset}`; + } + + // + return result; +} + +/** + * generate style applier expression ... + * + * @param {string} style soecufic style name to use ... + * @param {boolean} reset close applier string by reset styles ... + * @returns {string} style applier string ... + */ +function getStyleApplier( + style = "", + reset = false +) { + // + let result = ""; + + // + // Detect and Validate Style and Apply it ... + let eStyle = AVAILABLE_STYLES[style]; + if (XValueTools.isValidArg(eStyle)) { + result = `${eStyle}`; + } + + // + if ( + !!reset && + result.length > 0 + ) { + result = `${result}${AVAILABLE_STYLES.Reset}`; + } + + // + return result; +} + +/** + * generate color applier expression ... + * + * @param {string} color specific color name for using to styling ... + * @param {boolean} reset close applier string by reset styles ... + * @param {boolean} toForeground apply specified color as foreground ... + * @param {boolean} toBackground apply specified color as background ... + * @returns {string} color applier string ... + */ +function getColorApplier( + color = "", + reset = false, + toForeground = true, + toBackground = false +) { + // + let result = ""; + + // + // Detect and Validate Foreground Color and Apply it ... + let eFColor = AVAILABLE_FOREGROUND_COLORS[color]; + if ( + !!toForeground + && XValueTools.isValidArg(eFColor) + ) { + result = `${eFColor}`; + } + + // + // Detect and Validate Background Color and Apply it ... + let eBColor = AVAILABLE_BACKGROUND_COLORS[color]; + if ( + !!toBackground + && XValueTools.isValidArg(eBColor) + ) { + result = `${eBColor}`; + } + + // + if ( + !!reset && + result.length > 0 + ) { + result = `${result}${AVAILABLE_STYLES.Reset}`; + } + + // + return result; +} + +/** + * colorified specific content ... + * + * @param {string} content specified content for styling ... + * @param {string} color specific color name for using to styling ... + * @param {boolean} toForeground apply specified color as foreground ... + * @param {boolean} toBackground apply specified color as background ... + * @returns {string} + */ +function colorifyContent( + content = "", + color = "", + toForeground = true, + toBackground = false +) { + // + let result = content; + + // + if (!XValueTools.isValidArg(content)) { + return result; + } + + // + // Finde Colors ... + + // + // Detect and Validate Foreground Color and Apply it ... + let eFColor = AVAILABLE_FOREGROUND_COLORS[color]; + if ( + !!toForeground + && XValueTools.isValidArg(eFColor) + ) { + result = `${eFColor}${result}`; + } + + // + // Detect and Validate Background Color and Apply it ... + let eBColor = AVAILABLE_BACKGROUND_COLORS[color]; + if ( + !!toBackground + && XValueTools.isValidArg(eBColor) + ) { + result = `${eBColor}${result}`; + } + + // + if ( + result.length > 0 + && ( + XValueTools.isValidArg(eFColor) || + XValueTools.isValidArg(eBColor) + ) + ) { + result = `${result}${AVAILABLE_STYLES.Reset}`; + } + + // + return result; +} + +/** + * apply style on specific content ... + * + * @param {string} content specified content for styling ... + * @param {string} style soecufic style name to use ... + * @returns {string} + */ +function stylifiyContent( + content = "", + style = "", +) { + // + let result = content; + + // + if (!XValueTools.isValidArg(content)) { + return result; + } + + // + // Detect and Validate Style and Apply it ... + let eStyle = AVAILABLE_STYLES[style]; + if (XValueTools.isValidArg(eStyle)) { + result = `${eStyle}${result}`; + } + + // + if ( + result.length > 0 + && XValueTools.isValidArg(eStyle) + ) { + result = `${result}${AVAILABLE_STYLES.Reset}`; + } + + // + return result; +} +//#endregion + +// +//#region Module Exports ... +module.exports = { + // + STYLE_NAMES, + COLOR_NAMES, + // + apply, + applyStyle, + getApplier, + getColorApplier, + getStyleApplier, + colorifyContent, + stylifiyContent, + applyForegroundColor, + applyBackgroundColor, +} +//#endregion \ No newline at end of file diff --git a/Documents/JsModules/x-file.tools.js b/Documents/JsModules/x-file.tools.js new file mode 100644 index 00000000..a1642a48 --- /dev/null +++ b/Documents/JsModules/x-file.tools.js @@ -0,0 +1,830 @@ +/** + * XFile Tools Module ... + * a module for handle all file/folder manipulating task in node js ... + * + * Maintainer + * + * Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com) + */ + +// +//#region Module Imports ... +const fs = require('fs'); +const os = require('os'); +const Path = require('path'); +const http = require('http'); +const https = require('https'); +const XValueTools = require('./x-value.tools'); +//#endregion + +// +//#region Constants ... +/** + * current os path separators ... + */ +const PathSeparator = Path.sep; + +/** + * current directory ... + */ +const CurrentDir = __dirname; +//#endregion + +// +//#region Actions ... +// +//#region Global ... +/** + * retrieve a path status ... + * + * @param {string} path a path value to check ... + * @returns an stat object ... + */ +function getStatus(path = '') { + // + if (!XValueTools.isValidArg(path)) { + return undefined; + } + + // + return fs.statSync(path); +} + +/** + * retrieve user's Home path ... + * + * @returns {string} a path ... + */ +function getHomePath() { + return os.homedir(); +} +//#endregion + +// +//#region Path ... +/** + * retrieve the base name of specific address path ... + * + * @param {string} path address of file or folder ... + * @returns string ... + */ +function basename(path = '') { + // + if (!XValueTools.isValidArg(path)) { + return ''; + } + + // + const result = Path.basename(path); + return result; +} + +/** + * join several path segments together ... + * + * @param {...string} path path params ... + * @returns a joined paths ... + */ +function joinPath(...path) { + return Path.join(...path); +} + +/** + * resolve a relative path to absolute ... + * + * @param {string[]} path a path value to check ... + * @returns {string} + */ +function resolvePath(...path) { + return Path.resolve(...path); +} +//#endregion + +// +//#region File ... +/** + * determines a path destination is a file or not ... + * + * @param {string} path a path value to check ... + * @returns a boolean value ... + */ +function isFileExists(path = '') { + // + if (!XValueTools.isValidArg(path)) { + return false; + } + + // + try { + const stat = getStatus(path); + if (!stat) { + return false; + } + + // + return stat.isFile(); + } catch { + return false; + } +} + +/** + * remove a file ... + * + * @param {string} path a file path ... + * @returns {Promise} action done or not ... + */ +function removeFile(path = '') { + return new Promise((resolve) => { + // + if (!isFileExists(path)) { + resolve(false); + } + + // + fs.unlink(path, (err) => { + // + if (err) { + resolve(false); + return; + } + + // + resolve(true); + }); + }); +} + +/** + * copy a file to destination path ... + * + * @param {string} source source file path ... + * @param {string} dest dest folder path ... + * @returns {Promise} action done or not ... + */ +function copyFile( + source = '', + dest = '' +) { + return new Promise((resolve) => { + // + if ( + !isFileExists(source) || + !isDirectoryExists(dest) + ) { + resolve(false); + return; + } + + // + const destFilePath = Path.join(dest, Path.basename(source)); + fs.copyFile(source, destFilePath, (err) => { + // + if (err) { + resolve(false); + return; + } + + // + resolve(true); + }); + }); +} + +/** + * reading specified file content ... + * + * @param {string} path a file path ... + * @returns {Promise} file content ... + */ +function readFile(path = '') { + return new Promise((resolve) => { + // + if (!isFileExists(path)) { + resolve(''); + return; + } + + // + fs.readFile(path, 'utf8', (err, content) => { + // + if (err) { + resolve(undefined); + return; + } + + // + resolve(content); + }); + }); +} + +/** + * write content to a file ... + * + * @param {string} path a file path ... + * @param {string} content the content which going to write to the file ... + * @param {bool} overwrite determines file overwrite if exists ... + * @returns {Promise} action done or not ... + */ +function writeFile( + path = '', + content = '', + overwrite = true +) { + return new Promise((resolve) => { + // + if (isFileExists(path) && !overwrite) { + // + resolve(false); + return; + } + + // + // Normalize Content ... + content = XValueTools.isValidArg(content) ? + content : + ''; + + // + fs.writeFile(path, content, (err) => { + // + if (err) { + // + resolve(false); + return; + } + + // + resolve(true); + }); + }); +} + +/** + * create a file ... + * + * @param {string} path file path ... + * @param {string} fileName file name ... + * @returns {Promise} action done or not ... + */ +function createFile( + path = '', + fileName = '' +) { + return new Promise((resolve) => { + // + const filePath = Path.join(path, fileName); + if ( + isFileExists(filePath) || + !XValueTools.isValidArg(path) || + !XValueTools.isValidArg(fileName) + ) { + // + resolve(false); + return; + } + + // + fs.writeFile(filePath, '', (err) => { + // + if (err) { + // + resolve(false); + return; + } + + // + resolve(true); + }); + }); +} + +/** + * remove a file name extension ... + * + * @param {string} name + * @returns {string} name without extension ... + */ +function removeFileExtension(name = '') { + return name.substring(0, name.lastIndexOf('.')) || name; +} + +/** + * retrieve a file extension ... + * + * @param {string} path a path which locate a file ... + * @returns {string} + */ +function getFileExtension(path = '') { + // + let result = ""; + + // + // Validate Arg ... + if ( + !XValueTools.isValidArg(path) + || !isFileExists(path) + ) { + return result; + } + + // + // Retrieve just file name ... + const fileName = basename(path); + result = fileName.replace( + removeFileExtension(fileName), + "" + ); + + // + return result; +} + +/** + * retrieve all files list recursively from specific path ... + * + * @param {string} path a source folder path ... + * @param {string[]} extensions which file extensions need to be listed, live empty for all files ... + * @returns {Promise} + */ +async function getRecursiveFilesList( + path = "", + extensions = [] +) { + // + let result = []; + + // + // Validate Args ... + if ( + !XValueTools.isValidArg(path) + || !isDirectoryExists(path) + ) { + return result; + } + + // + const contents = await getDirectoryContents(path); + for(const item of contents) { + // + const itemPath = joinPath( + path, + item + ); + + // + const isItemFile = isFileExists(itemPath); + if (isItemFile) { + // + const itemFileExtension = getFileExtension(itemPath); + const isFileInSupportedExtensions = + extensions === undefined || extensions.length === 0 + ? true + : extensions.includes(itemFileExtension) + ; + if (isFileInSupportedExtensions) { + result.push(itemPath); + } + } else if (isDirectoryExists(itemPath)) { + // + const itemPathFiles = await getRecursiveFilesList(itemPath, extensions); + result.push(...itemPathFiles); + } + } + + // + return result; +} +//#endregion + +// +//#region Directory ... +/** + * create a directory ... + * + * @param {string} path destination path including dir name ... + * @param {boolean} recursive create directories recursively ... + * @returns action done or not ... + */ +function createDirectory( + path = '', + recursive = true +) { + // + let result = false; + + // + if ( + isDirectoryExists(path) || + !XValueTools.isValidArg(path) + ) { + return false; + } + + // + try { + // + fs.mkdirSync(path, { recursive: recursive }); + result = true; + return result; + } catch { + return false; + } +} + +/** + * remove a directory ... + * + * @param {string} path destination path including dir name ... + * @param {boolean} recursive removes directories recursively ... + * @returns {Promise} action done or not ... + */ +function removeDirectory( + path = '', + recursive = false +) { + return new Promise((resolve) => { + // + if (!isDirectoryExists(path)) { + resolve(false); + return; + } + + // + fs.rm(path, { + recursive + }, (err) => { + // + if (err) { + resolve(false); + return; + } + + // + resolve(true); + }); + }); +} + +/** + * determines a path destination is a directory or not ... + * + * @param {string} path a folder path ... + * @returns represent destnation path is Directory or not ... + */ +function isDirectoryExists(path = '') { + // + if (!XValueTools.isValidArg(path)) { + return false; + } + + // + try { + // + const isExists = fs.existsSync(path); + if (!isExists) { + return false; + } + + // + const stat = getStatus(path); + if (!stat) { + return false; + } + + // + const result = stat.isDirectory(); + return result; + } catch { + return false; + } +} + +/** + * retrieve a directory content ... + * + * @param {string} path a folder path ... + * @returns {Promise} a collection of folder files ... + */ +function getDirectoryContents(path = '') { + return new Promise((resolve) => { + // + if (!isDirectoryExists(path)) { + resolve([]); + return; + } + + // + fs.readdir(path, (err, content) => { + // + if (err) { + resolve([]); + return; + } + + // + resolve(content); + }); + }); +} + +/** + * retrieve a directory files ... + * + * @param {string} path a folder path ... + * @returns {Promise} a collection of folder files ... + */ +function getDirectoryFiles( + path = '', + containsHiddenFiles = false +) { + return new Promise((resolve) => { + // + if (!isDirectoryExists(path)) { + resolve([]); + return; + } + + // + fs.readdir(path, (err, content) => { + // + if (err) { + resolve([]); + return; + } + + // + if (!containsHiddenFiles) { + content = content.filter(c => !c.startsWith('.')); + } + + // + const result = []; + content + .forEach(c => { + // + const cPath = Path.join(path, c); + if (isFileExists(cPath)) { + result.push(c); + } + }); + + // + resolve(result); + }); + }); +} + +/** + * retrieve a directory folders ... + * + * @param {string} path a folder path ... + * @returns {Promise} a collection of folder names ... + */ +function getDirectoryFolders(path = '') { + return new Promise((resolve) => { + // + if (!isDirectoryExists(path)) { + resolve([]); + return; + } + + // + fs.readdir(path, (err, content) => { + // + if (err) { + resolve([]); + return; + } + + // + const result = []; + content.forEach(c => { + // + const cPath = Path.join(path, c); + if (isDirectoryExists(cPath)) { + result.push(c); + } + }); + + // + resolve(result); + }); + }); +} + +/** + * copy a folder with all of it's content to dest ... + * + * @param {string} source source folder path ... + * @param {string} dest dest folder path ... + * @returns {Promise} action done or not ... + */ +async function copyFolder( + source = '', + dest = '' +) { + // + if ( + !isDirectoryExists(source) || + !XValueTools.isValidArg(dest) || + !XValueTools.isValidArg(source) + ) { + return false; + } + + // + const folderName = Path.basename(source); + const destPath = Path.join(dest, folderName); + + // + // Create Dest Path folder if not exists ... + if (!isDirectoryExists(destPath)) { + // + let result = createDirectory(destPath, true); + if (!result) { + return false; + } + } + + // + // Files ... + const files = await getDirectoryFiles(source); + if (files && files.constructor === Array && files.length > 0) { + // + const filesPromises = files.map(file => copyFile(Path.join(source, file), destPath)); + const filesResult = (await Promise.all(filesPromises)).every(r => !!r); + if (!filesResult) { + return false; + } + } + + // + // Folders ... + const folders = await getDirectoryFolders(source); + if (folders && folders.constructor === Array && folders.length > 0) { + // + const folderPromises = folders.map(folder => copyFolder(Path.join(source, folder), destPath)); + const filesResult = (await Promise.all(folderPromises)).every(r => !!r); + if (!filesResult) { + return false; + } + } + + // + return true; +} +//#endregion + +// +//#region Downloader ... +/** + * download a file from specific url and store it ... + * + * @param {string} filepath the file name and path which required to put download file on it ... + * @param {string} url the web url for downloading ... + */ +function download(filepath, url) { + // + // Validate Args ... + if ( + !XValueTools.isValidArg(url) + || !XValueTools.isValidURL(url) + || !XValueTools.isValidArg(filepath) + ) { + return; + } + + // + var file = fs.createWriteStream(filepath); + + // + if (url.startsWith('https:')) { + https.get(url, function (response) { + response.pipe(file); + }); + } else if (url.startsWith('http:')) { + http.get(url, function (response) { + response.pipe(file); + }); + } +} +//#endregion + +// +//#region JSON ... +/** + * read and parse a JSON content from a file ... + * + * @param {string} path source file path ... + * @returns {any} + */ +function readJSON(path = "") { + // + // Validate Args ... + if (!isFileExists(path)) { + return undefined; + } + + // + let result = undefined; + try { + result = require(path); + } catch { + result = undefined; + } + + // + return result; +} + +/** + * write a JSON Object content into a file ... + * + * @param {string} path dest file path ... + * @param {any} content an object which required to write to file ... + * @returns {Promise} + */ +async function writeJSON( + path = "", + content = undefined +) { + // + let result = false; + + // + // Validate Args ... + if (!XValueTools.isValidArg(path)) { + return result; + } + + // + // Normallize Content ... + if (content === undefined) { + content = {}; + } + + // + const contentString = XValueTools.beautifyJSON(content); + + // + result = await writeFile( + path, + contentString, + true + ); + + // + return result; +} +//#endregion +//#endregion + +// +//#region Module Exports ... +module.exports = { + // + CurrentDir, + PathSeparator, + + // + getStatus, + getHomePath, + + // + basename, + joinPath, + resolvePath, + + // + download, + + // + readJSON, + writeJSON, + + // + copyFile, + readFile, + writeFile, + createFile, + removeFile, + isFileExists, + getFileExtension, + removeFileExtension, + getRecursiveFilesList, + + // + copyFolder, + createDirectory, + removeDirectory, + createDirectory, + removeDirectory, + isDirectoryExists, + isDirectoryExists, + getDirectoryFiles, + getDirectoryFolders, + getDirectoryContents, +} +//#endregion \ No newline at end of file diff --git a/Documents/JsModules/x-shell.tools.js b/Documents/JsModules/x-shell.tools.js new file mode 100644 index 00000000..0ee13de9 --- /dev/null +++ b/Documents/JsModules/x-shell.tools.js @@ -0,0 +1,219 @@ +/** + * XShell Tools Module ... + * a module for handling shell actions and retrieve OS Info in node js ... + * + * Maintainer + * + * Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com) + */ + +// +//#region Imports ... +const os = require('os'); +const process = require('process'); +const { exec } = require("child_process"); +const XFileTools = require('./x-file.tools'); +//#endregion + +// +//#region Constants ... +// +const OS = { + Aix: 'aix', + Darwin: 'darwin', + FreeBSD: 'freebsd', + Linux: 'linux', + OpenBSD: 'openbsd', + SnOS: 'sunos', + Windows: 'win32' +}; + +// +const isWindows = process.platform === OS.Windows; +//#endregion + +// +//#region Actions ... +// +//#region Pure shell commands ... +/** + * execute a command using NodeJS on shell ... + * + * @param {string} cmd command to execute ... + * @param {string} cwd working directory ... + * + * @returns Promise instance ... + */ +function execute(cmd, cwd) { + return new Promise((resolve, reject) => { + // + if (!cmd || cmd.toString().length === 0 || (cwd && !XFileTools.isDirectoryExists(cwd))) { + reject('invalid args ...'); + return; + } + + // + exec(cmd, { cwd }, (err, result, stdError) => { + // + if (err) { + reject(err); + return; + } + + // + if (stdError) { + // + // reject(stdError); + // return; + } + + // + resolve(result); + }); + }); +}; + +/** + * determines a command exists on host or not ... + * + * @param {string} name specific command name ... + * + * @returns boolean Promise ... + */ +function checkCommandExists(name) { + return new Promise(resolve => { + // + if (!name) { + resolve(false); + return; + } + + // + const cmd = isWindows ? `${name} >nul 2>&1` : `type ${name} >/dev/null 2>&1`; + execute(cmd).then(result => { + resolve(true); + }) + .catch(err => { + resolve(false); + }); + }); +} +//#endregion + +// +//#region required commands state ... +/** + * check al required commands exists or not ... + * + * @returns + */ +async function isRequiredCommandsExists() { + // + let result = false; + + // + // const isTarExists = await isTarCommandExists(); + // const isCatExists = await isCatCommandExists(); + // const isGrepExists = await isGrepCommandExists(); + // const isSedExists = await isSedCommandExists(); + const isNpmExists = await isNpmCommandExists(); + const isNgExists = await isNgCommandExists(); + const isIonicExists = await isIonicCommandExists(); + const isCordovaExists = await isCordovaCommandExists(); + + // + result = isNpmExists + && isNgExists + && isIonicExists + && isCordovaExists + ; + + // + return result; +} + +/** + * retrieve required commands state object ... + * + * @returns + */ +async function getRequiredCommandsStates() { + // + const result = {}; + + // + // const isTarExists = await isTarCommandExists(); + // const isCatExists = await isCatCommandExists(); + // const isGrepExists = await isGrepCommandExists(); + // const isSedExists = await isSedCommandExists(); + const isNpmExists = await isNpmCommandExists(); + const isNgExists = await isNgCommandExists(); + const isIonicExists = await isIonicCommandExists(); + const isCordovaExists = await isCordovaCommandExists(); + + // + result['npm'] = isNpmExists; + result['ng'] = isNgExists; + result['ionic'] = isIonicExists; + result['cordova'] = isCordovaExists; + + // + return result; +} +//#endregion + +// +//#region Commonly used Command Checkers ... +/** + * determines npm command exists or not ... + * + * @returns + */ +function isNpmCommandExists() { + return checkCommandExists('npm'); +} + +/** + * determines ng command exists or not ... + * + * @returns + */ +function isNgCommandExists() { + return checkCommandExists('ng'); +} + +/** + * determines ionic command exists or not ... + * + * @returns + */ +function isIonicCommandExists() { + return checkCommandExists('ionic'); +} + +/** + * determines cordova command exists or not ... + * + * @returns + */ +function isCordovaCommandExists() { + return checkCommandExists('cordova'); +} +//#endregion +//#endregion + +// +//#region Module Exports ... +module.exports = { + // + execute, + checkCommandExists, + isRequiredCommandsExists, + getRequiredCommandsStates, + + // + isNgCommandExists, + isIonicCommandExists, + isCordovaCommandExists, +} +//#endregion \ No newline at end of file diff --git a/Documents/JsModules/x-type-detector.tools.js b/Documents/JsModules/x-type-detector.tools.js new file mode 100644 index 00000000..86ed3d80 --- /dev/null +++ b/Documents/JsModules/x-type-detector.tools.js @@ -0,0 +1,268 @@ +/** + * DataType Tools Module ... + * a module for manipulate and detect supported data types in node js ... + * + * Maintainer + * + * Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com) + */ + +// +//#region Constants ... +/** + * all supported data types ... + */ +const DataTypes = { + Null: 'null', + Date: 'date', + Array: 'array', + Object: 'object', + String: 'string', + Number: 'number', + Unknown: 'unknown', + Boolean: 'boolean', + Function: 'function', + Undefined: 'undefined', +}; +//#endregion + +// +//#region Actions ... +/** + * detect type of a content ... + * + * @param {any} value the content which going to check ... + * @returns + */ +function getType(value) { + // + const type = typeof value; + + // + let result = DataTypes.Unknown; + let dateIdentifier = '[object Date]'; + let constructor = value && value.constructor ? + value.constructor.toString() : + ''; + + // + switch (type) { + // + case 'undefined': + result = DataTypes.Undefined; + break; + + // + case 'boolean': + result = DataTypes.Boolean; + break; + + // + case 'string': + result = DataTypes.String; + break; + + // + case 'number': + result = DataTypes.Number; + break; + + // + case 'function': + result = DataTypes.Function; + break; + + // + case 'object': + // + // Null ... + if (value === null) { + result = DataTypes.Null; + } else + // + // Array ... + if (Array.isArray(value)) { + result = DataTypes.Array; + } else + // + // Data ... + if ( + value instanceof Date || + isFunction(value.getMonth) || + constructor.includes(dateIdentifier) || + Object.prototype.toString.call(value) === dateIdentifier + ) { + result = DataTypes.Date; + } else + // + // Object ... + { + result = DataTypes.Object; + } + break; + + // + default: + result = DataTypes.Unknown; + break; + } + + // + return result; +} + +/** + * retrieve an object constructor ... + * + * @param {any} value the content which going to check ... + * @returns + */ +function getConstructor(value) { + // + const result = value && value.constructor ? + value.constructor.toString() : + ''; + + // + return result; +} + +/** + * retrieve an object prototype ... + * + * @param {any} value the content which going to check ... + * @returns + */ +function getPrototype(value) { + // + const result = value ? + Object.prototype.toString.call(value) : + ''; + + // + return result; +} + +/** + * check an object is null or not ... + * + * @param {any} value the content which going to check ... + * @returns + */ +function isNull(value) { + return getType(value) === DataTypes.Null; +} + +/** + * check an object is undefined or not ... + * + * @param {any} value the content which going to check ... + * @returns + */ +function isUndefined(value) { + return getType(value) === DataTypes.Undefined; +} + +/** + * check an object is null or undefined or not ... + * + * @param {any} value the content which going to check ... + * @returns + */ +function isNullOrUndefined(value) { + return isNull(value) || isUndefined(value); +} + +/** + * check an object is a date or not ... + * + * @param {any} value the content which going to check ... + * @returns + */ +function isDate(value) { + return getType(value) === DataTypes.Date; +} + +/** + * check an object is number or not ... + * + * @param {any} value the content which going to check ... + * @returns + */ +function isNumber(value) { + return getType(value) === DataTypes.Number; +} + +/** + * check an object is string or not ... + * + * @param {any} value the content which going to check ... + * @returns + */ +function isString(value) { + return getType(value) === DataTypes.String; +} + +/** + * check an object is boolean or not ... + * + * @param {any} value the content which going to check ... + * @returns + */ +function isBoolean(value) { + return getType(value) === DataTypes.Boolean; +} + +/** + * check an object is an Array or not ... + * + * @param {any} value the content which going to check ... + * @returns + */ +function isArray(value) { + return getType(value) === DataTypes.Array; +} + +/** + * check an object is and Object or not ... + * + * @param {any} value the content which going to check ... + * @returns + */ +function isObject(value) { + return getType(value) === DataTypes.Object; +} + +/** + * check an object is a Function or not ... + * + * @param {any} value the content which going to check ... + * @returns + */ +function isFunction(value) { + return getType(value) === DataTypes.Function; +} +//#endregion + +// +//#region Module Exports ... +module.exports = { + // + DataTypes, + + // + getType, + getConstructor, + getPrototype, + isNull, + isUndefined, + isNullOrUndefined, + isDate, + isNumber, + isString, + isBoolean, + isArray, + isObject, + isFunction, +} +//#endregion \ No newline at end of file diff --git a/Documents/JsModules/x-value.tools.js b/Documents/JsModules/x-value.tools.js new file mode 100644 index 00000000..81cfcc4b --- /dev/null +++ b/Documents/JsModules/x-value.tools.js @@ -0,0 +1,1528 @@ +/** + * Value Tools Module ... + * a module for Validate args and values ... + * + * Maintainer + * + * Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com) + */ + +// +//#region Imports ... +const XTypeTools = require('./x-type-detector.tools'); +//#endregion + +// +//#region Actions ... +/** +* validate a value as an argument ... +* +* @param {string} value the value which going to validate ... +* @returns +*/ +function isValidArg(value) { + // + let result = false; + + // + result = value && + value.length > 0; + + // + return result; +} + +/** + * validate a collection of valuea as an arguments ... + * + * @param {string|string[]} values the value collection which going to checked ... + * @returns + */ +function isValidArgs(values) { + // + // Normalize Values ... + const normalValues = toNormalArray(values); + if (!hasChildArray(normalValues)) { + return false; + } + + // + let result = normalValues.every(nv => isValidArg(nv)); + return result; +} + +/** + * converts content to an array object ... + * + * @param {string|string[]} content the content which required to normalized ... + * @returns {string[]} array object ... + */ +function toNormalArray(content = '') { + // + // Validate Args ... + if (!isValidArg(content)) { + return []; + } + + // + const result = Array.isArray(content) ? + [...content] : + content.includes(',') && + content !== ',' ? + [...content.split(',')] : + [content]; + + // + return result; +} + +/** + * determines value is array and has atleast one child ... + * + * @param {any} value the value which going to checked ... + * @returns {boolean} result of checking ... + */ +function hasChildArray(value) { + // + let result = false; + result = value && + Array.isArray(value) && + value.length > 0; + + // + return result; +} + +/** + * check an string is ends with specified symbol ... + * + * @param {string} symbol specified search symbol ... + * @param {string} value specified search content ... + * @param {boolean} ignoreCase specified search case sensitive or not ... + * @returns + */ +function endsWidth( + symbol = '', + value = '', + ignoreCase = true +) { + // + if (!isValidArg(value)) { + return false; + } + + // + if (!isValidArg(symbol)) { + return true; + } + + // + // TODO: uncomment it if necessary ... + // symbol = symbol.trim(); + + // + let result = false; + const subtractedContent = value.substring(value.length - symbol.length); + result = !!ignoreCase ? + subtractedContent + .toLowerCase() === symbol + .toLowerCase() : + subtractedContent === symbol; + + // + return result; +} + +/** + * surround specified content by provided symbol ... + * + * @param {string} symbol surround string ... + * @param {string} content used content to surrounded by symbol ... + * @returns surrounded content ... + */ +function surroundBy( + symbol = '', + content = '' +) { + // + // Validate Args ... + if (!isValidArg(content)) { + content = ''; + } + + // + if (!isValidArg(symbol)) { + symbol = ''; + } + + // + if (isSurrounded(symbol, content)) { + return content; + } + + // + const result = `${symbol}${content}${symbol}`; + return result; +} + +/** + * convert a parsed sign to clear string ... + * + * @param {string} value a parsed content ... + * @returns clear parsed signs ... + */ +function toStringExpression(value) { + // + let result = ''; + result = value; + + // + if ( + !value || + !isValidArg(value) || + !isSurroundedString(value) + ) { + return result; + } + + // + const startSymbol = value.charAt(0); + const endSymbol = value.charAt(value.length - 1); + + // + if (startSymbol !== endSymbol) { + return result; + } + + // + if ( + startSymbol === "\"" || + startSymbol === "\'" + ) { + result = clearSurround(startSymbol, value); + } + + // + return result; +} + + + +/** + * find and cleare proposed candidates from specified content ... + * + * @param {string|string[]} candidates the proposed string(s) whic going to cleared from content ... + * @param {string} content the destination content which used to find and replace candidates on it ... + * @returns cleared content ... + */ +function clearContent( + candidates, + content +) { + // + // Validate Args ... + if ( + !candidates || + !isValidArg(content) + ) { + return ''; + } + + // + // Normalize candidates ... + const normalCandidates = toNormalArray(candidates) + .filter(c => isValidArg(c)); + if (!hasChildArray(normalCandidates)) { + return content; + } + + // + // define regexp for content ... + const regExpExpression = normalCandidates.join('|'); + const regExp = new RegExp( + regExpExpression, + 'gi' + ); + + // + let result = ''; + result = content.replace(regExp, ''); + + // + return result; +} + +/** + * clear surrounded symbol from content ... + * + * @param {string} symbol specified search symbol ... + * @param {string} content specified content to clear ... + * @returns + */ +function clearSurround( + symbol = '', + content = '' +) { + // + // Validate Args ... + if ( + !isValidArg(content) || + (isValidArg(symbol) && + symbol.length >= content.length - 1) + ) { + return ''; + } + + // + // Validate Surround ... + if (!isSurrounded(symbol, content)) { + return content; + } + + // + if (!isValidArg(symbol)) { + return content; + } + + // + let result = content.substring(symbol.length, content.length - symbol.length); + return result; +} + +/** + * clear object sign ... + * + * @param {string} content + * @returns cleared content ... + */ +function clearObjectSurround(content = '') { + // + // Validate Args ... + if ( + !isValidArg(content) || + !isSurroundedObject(content) + ) { + return content; + } + + // + const endIndex = content.length - ( + endsWidth('}', content) ? + 1 : + endsWidth('},', content) || + endsWidth('};', content) || + endsWidth('}\n', content) ? + 2 : + 0 + ); + + // + const result = content.substring(1, endIndex); + return result; +} + +/** + * clear array sign ... + * + * @param {string} content + * @returns cleared content ... + */ +function clearArraySurround(content = '') { + // + // Validate Args ... + if ( + !isValidArg(content) || + !isSurroundedArray(content) + ) { + return content; + } + + // + const endIndex = content.length - ( + endsWidth(']', content) ? + 1 : + endsWidth('],', content) || + endsWidth('];', content) || + endsWidth(']\n', content) ? + 2 : + 0 + ); + + // + const result = content.substring(1, endIndex); + return result; +} + +/** + * slice a content to individual parts ... + * + * @param {number|number[]} indexes which indexes used to slice ... + * @param {strng} content the content which going to sliced ... + * @returns sliced parts of content ... + */ +function sliceContent(indexes, content) { + // + // Validate Args ... + if ( + !isValidArg(indexes) || + !isValidArg(content) + ) { + return []; + } + + // + // Normalize indexes ... + const normalIndexes = toNormalArray(indexes) + .filter(index => index > -1 && index < content.length); + if (!hasChildArray(normalIndexes)) { + return []; + } + + // + let result = ['']; + result.pop(); + + // + let lastIndex = 0; + for (const index of normalIndexes) { + // + const part = content.substring(lastIndex, index); + if (isValidArg(part)) { + result.push(part.trim()); + } + + // + lastIndex = index + 1; + } + + // + // check remained parts ... + if (lastIndex < content.length) { + // + const part = content.substring(lastIndex); + if (isValidArg(part)) { + result.push(part.trim()); + } + } + + // + return result; +} + + + +/** + * check an string is starts and ends with specified symbol ... + * + * @param {string} symbol specified search symbol ... + * @param {string} content specified search content ... + * @param {boolean} ignoreCase specified search case sensitive or not ... + * @returns + */ +function isSurrounded( + symbol = '', + content = '', + ignoreCase = true +) { + // + // Validate Args ... + if (!isValidArg(content)) { + return false; + } + + // + if (!isValidArg(symbol)) { + return true; + } + + // + symbol = symbol.trim(); + + // + let result = false; + result = (!!ignoreCase ? + content + .toLowerCase() + .startsWith(symbol.toLowerCase()) : + content.startsWith(symbol) + ) && + endsWidth(symbol, content, ignoreCase); + + // + return result; +} + +/** + * determines a content is an string surrounded value or not ... + * + * @param {string} content a content which going to check ... + * @returns + */ +function isSurroundedString(content = '') { + // + // Validate Args ... + if (!isValidArg(content)) { + return false; + } + + // + let result = false; + result = ( + content.startsWith('\'') && + ( + endsWidth('\'', content) || + endsWidth('\',', content) || + endsWidth('\';', content) || + endsWidth('\'\n', content) + ) || + content.startsWith('\"') && + ( + endsWidth('\"', content) || + endsWidth('\",', content) || + endsWidth('\";', content) || + endsWidth('\"\n', content) + ) || + content.startsWith('`') && + ( + endsWidth('\`', content) || + endsWidth('\`,', content) || + endsWidth('\`;', content) || + endsWidth('\`\n', content) + ) + ); + return result; +} + +/** + * check a content is an array content or not ... + * + * @param {string} content + * @returns + */ +function isSurroundedArray(content = '') { + // + // Validate Args ... + if (!isValidArg(content)) { + return false; + } + + // + let result = false; + result = content.startsWith('[') && + ( + endsWidth(']', content) || + endsWidth('],', content) || + endsWidth('];', content) || + endsWidth(']\n', content) + ); + return result; +} + +/** + * check a content is an object content or not ... + * + * @param {string} content + * @returns + */ +function isSurroundedObject(content = '') { + // + // Validate Args ... + if (!isValidArg(content)) { + return false; + } + + // + let result = false; + result = content.startsWith('{') && + ( + endsWidth('}', content) || + endsWidth('},', content) || + endsWidth('};', content) || + endsWidth('}\n', content) + ); + return result; +} + +/** + * check an index model is contains inside anothers or not ... + * + * @param {{ start: number, end: number}} source the source index model to check ... + * @param {...{ start: number, end: number}} dest the collection of index models which going to check ... + * @returns + */ +function isIndexInside(source, ...dest) { + // + let result = false; + + // + // Validate Args ... + if (!source || !dest) { + return result; + } + + // + // Check idx is standard ... + if ( + !source || + !source.end || + !source.start || + source.start > source.end + ) { + return false; + } + + // + // Check types of destinations ... + for (let index of dest) { + // + // Check idx is standard ... + if ( + !index || + !index.end || + !index.start || + index.start > index.end + ) { + return false; + } + } + + // + if (dest.includes(source)) { + dest = dest.filter(d => d !== source); + } + + // + const insideContentIndex = dest.find(dIndex => { + // + const result = source.start >= dIndex.start && + source.start <= dIndex.end && + source.end >= dIndex.start && + source.end <= dIndex.end; + return result; + }); + + // + result = insideContentIndex; + return result; +} + +/** + * check a number exists in + * @param {number} idx the number which going to check ... + * @param {{ start: number, end: number}} index the index model + * @returns + */ +function isInsideIndex(idx = -1, index) { + // + let result = false; + + // + // Validate Args ... + if ( + !index || + !index.hasOwnProperty('end') || + !index.hasOwnProperty('start') + ) { + return result; + } + + // + result = idx > index.start && idx < index.end; + return result; +} + +/** + * determines an object is key/value and type ... + * + * @param {any} content the object which we are going to check ... + * @returns + */ +function isKeyValueType(content) { + // + const result = content && + content.hasOwnProperty('key') && + content.hasOwnProperty('type') && + content.hasOwnProperty('value'); + + // + return result; +} + + + +/** + * find all indexes of token(s) in content ... + * + * @param {string|string[]} tokens the tokens which require to search ... + * @param {string} content the content which using to search ... + * @returns {number[]} all occured indexes ... + */ +function findAllIndexes( + tokens, + content +) { + // + // Validate Args ... + if ( + !isValidArg(tokens) || + !isValidArg(content) + ) { + return []; + } + + // + // normalize tokens ... + const normalTokens = toNormalArray(tokens); + if (!hasChildArray(normalTokens)) { + return []; + } + + // + // Parse contents ... + let match; + const result = []; + const regExp = new RegExp( + normalTokens.join('|'), + 'g' + ); + while ((match = regExp.exec(content)) !== null) { + result.push(match.index); + } + if (!hasChildArray(result)) { + return []; + } + + // + return result; +} + +/** + * find nearest symbol in a content from specified index ... + * + * @param {string|string[]} candidates specifies which symbols to find ... + * @param {string} content te content for seasrch ... + * @param {number} startFromIndex the index of start position ... + * @returns + */ +function findNearest( + candidates, + content = '', + startFromIndex = 0 +) { + // + // Validate Args ... + if ( + !isValidArg(content) || + !isValidArg(candidates) || + !isValidArg(content.trim()) + ) { + return ''; + } + + // + // Normalize Content ... + content = content.trim(); + + // + // Normalize Starts From ... + startFromIndex = startFromIndex < 0 || startFromIndex > content.length - 1 ? 0 : startFromIndex; + + // + // Normalize Symbols ... + const normalCandidates = toNormalArray(candidates); + if (!hasChildArray(normalCandidates)) { + return ''; + } + + // + let nearestIndex = findNearestIndex( + normalCandidates, + content, + startFromIndex + ); + if (nearestIndex < 0) { + return ''; + } + + // + let result = content.substring( + nearestIndex, + nearestIndex + Math.max(...normalCandidates.map(nc => nc.length)) + ).trim(); + return result; +} + +/** + * search candidates in content and find nearest one and return it ... + * + * @param {string|string[]} candidates which candidates required to check ... + * @param {string} content the content which required to search candidates ... + * @param {number} startFromIndex the proposed index to start searching content from on ... + * @returns + */ +function findNearestIndex( + candidates = [''], + content = '', + startFromIndex = 0, + ignoreClosedItems = false +) { + // + // Validate Args ... + if ( + !isValidArg(content) || + !isValidArg(candidates) + ) { + return -1; + } + + // + // Mormalize startFromIndex value ... + startFromIndex = startFromIndex < 0 ? + 0 : + startFromIndex > content.length - 1 ? + content.length - 1 : + startFromIndex; + + // + // Normalize candidates ... + let normalCandidates = toNormalArray(candidates); + + // + if (!!ignoreClosedItems) { + normalCandidates = normalCandidates + .map(c => content.indexOf( + c, + startFromIndex + ) + ).map(c => +c) + .filter(c => +c > -1); + } else { + normalCandidates = findAllIndexesOutOfCloseds(normalCandidates, content) + .filter(c => c >= startFromIndex); + } + if (!hasChildArray(normalCandidates)) { + return -1; + } + + // + let result = -1; + result = Math.min(...normalCandidates); + + // + return result; +} + +/** + * find close index of specific sign in destination content ... + * + * @param {string} openSymbol open of area sign ... + * @param {string} closeSymbol close of area sign ... + * @param {string} content the destination content for parsing ... + * @param {number} startsFrom an start index of content for start parsing ... + * @returns + */ +function findCloseIndex( + openSymbol = '', + closeSymbol = '', + content = '', + startsFrom = -1 +) { + // + // Validate Args ... + if ( + startsFrom < 0 || + !isValidArg(content) || + !isValidArg(openSymbol) || + !isValidArg(closeSymbol) || + openSymbol === closeSymbol || + !content.includes(openSymbol) || + !content.includes(closeSymbol) || + startsFrom > content.length - 1 + ) { + return -1; + } + + // + let result = -1; + + // + let index = startsFrom; + const openSignStack = []; + while (index < content.length) { + // + const openCandidate = content.substring(index, index + openSymbol.length); + const closeCandidate = content.substring(index, index + closeSymbol.length); + + // + if (openCandidate === openSymbol) { + openSignStack.push(index); + } else if (closeCandidate === closeSymbol) { + // + if (openSignStack.length > 0) { + openSignStack.pop(); + } + + // + if (openSignStack.length === 0) { + result = index; + break; + } + } + + // + index++; + } + + // + return result; +} + +/** + * find closed contents of specific sign in destination content ... + * + * @param {string} openSymbol open of area sign ... + * @param {string} closeSymbol close of area sign ... + * @param {string} content the destination content for parsing ... + * @param {number} startsFrom an start index of content for start parsing ... + * @returns + */ +function findClosedContent( + openSymbol = '', + closeSymbol = '', + content = '', + startsFrom = 0, +) { + // + let result = [{ + start: -1, + end: -1, + content: '' + }]; + result.pop(); + + // + // Validate Args ... + if ( + !isValidArg(content) || + !isValidArg(openSymbol) || + !isValidArg(closeSymbol) || + !content.includes(openSymbol) || + !content.includes(closeSymbol) || + openSymbol.length >= content.length || + closeSymbol.length >= content.length + ) { + return result; + } + + // + // Normalize Start From ... + startsFrom = startsFrom < 0 || startsFrom >= content.length ? 0 : startsFrom; + + // + let canContinue = true; + while (!!canContinue) { + // + let openSymbolIndex = content.indexOf(openSymbol, startsFrom); + if (openSymbolIndex < 0) { + // + canContinue = false; + continue; + } + + // + let closeSymbolIndex = findCloseIndex( + openSymbol, + closeSymbol, + content, + openSymbolIndex + ); + if (closeSymbolIndex < 0) { + // + canContinue = false; + continue; + } + if (content.charAt(closeSymbolIndex) !== closeSymbol) { + closeSymbolIndex++; + } + + // + // Prevent ${} string interpolations ... + if ( + openSymbol === '{' && + openSymbolIndex - 1 >= 0 + ) { + // + const prevChar = content.charAt(openSymbolIndex - 1); + if (prevChar === '$') { + continue; + } + } + + // + const item = content + .substring(openSymbolIndex, closeSymbolIndex + 1) + .trim(); + startsFrom = closeSymbolIndex + 1; + result.push({ + start: openSymbolIndex, + end: closeSymbolIndex, + content: item + }); + } + + // + if (!hasChildArray(result)) { + return result; + } + + // + return result; +} + +/** + * extract a collection of closed contents exists in a content ... + * + * @param symbols a collection of open and close contents ... + * @param {string} content a content for searching inside it ... + * @returns a collection of closed content ... + */ +function findClosedContents( + symbols = [ + { + openSymbol: '', + closeSymbol: '' + } + ], + content = '' +) { + // + let result = [{ + start: -1, + end: -1, + content: '' + }]; + result.pop(); + + // + if ( + !isValidArg(content) || + !hasChildArray(symbols) + ) { + return []; + } + + // + symbols.forEach(symbol => { + // + const closedContent = findClosedContent( + symbol.openSymbol, + symbol.closeSymbol, + content + ); + + // + if (hasChildArray(closedContent)) { + result.push( + ...closedContent + ); + } + }); + + // + return result; +} + +/** + * find all string content closed items ... + * + * @param {string} content a content which going to search ... + * @returns a collection of index descriptors ... + */ +function findClosedStrings(content = '') { + // + // Validate Args ... + if (!isValidArg(content)) { + return []; + } + + // + // there are 3 types of strings ... + // ' " and ` + + // + const token1 = '\''; + const token1Stack = [0]; + token1Stack.pop(); + + // + const token2 = '"'; + const token2Stack = [0]; + token2Stack.pop(); + + // + const token3 = '`'; + const token3Stack = [0]; + token3Stack.pop(); + + // + const result = [ + { + start: -1, + end: -1, + content: '' + } + ]; + result.pop(); + + // + for (let i = 0; i < content.length; i++) { + // + const currentChart = content.charAt(i); + + // + //#region Token 1 ... + if (currentChart === token1) { + // + if (!hasChildArray(token1Stack)) { + token1Stack.push(i); + } else { + // + const start = token1Stack.pop(); + const end = i + 1; + const pContent = content.substring(start, end); + result.push({ + start, + end, + content: pContent + }); + } + } + //#endregion + + // + //#region Token 2 ... + if (currentChart === token2) { + // + if (!hasChildArray(token2Stack)) { + token2Stack.push(i); + } else { + // + const start = token2Stack.pop(); + const end = i + 1; + const pContent = content.substring(start, end); + result.push({ + start, + end, + content: pContent + }); + } + } + //#endregion + + // + //#region Token 3 ... + if (currentChart === token3) { + // + if (!hasChildArray(token3Stack)) { + token3Stack.push(i); + } else { + // + const start = token3Stack.pop(); + const end = i + 1; + const pContent = content.substring(start, end); + result.push({ + start, + end, + content: pContent + }); + } + } + //#endregion + } + + // + return result; +} + +/** + * find container indexes from within indexes ... + * + * @param {...{start: number, end: number}} index the collection of indexes to find beiggers inside ... + * @returns + */ +function findContainerIndexes(...index) { + // + let result = [ + { + start: -1, + end: -1, + content: '' + } + ]; + result.pop(); + + // + let concatedIndexes = []; + for (let idx of index) { + // + // Index Childs ... + for (let iix of idx) { + // + // Check idx is standard ... + if ( + !iix || + !iix.end || + !iix.start || + iix.start > iix.end + ) { + continue; + } + + // + // Check index inside another index ... + concatedIndexes.push(iix); + } + } + + // + if (!hasChildArray(concatedIndexes)) { + return result; + } + + // + for (let ccIdx of concatedIndexes) { + // + const isInside = isIndexInside(ccIdx, ...concatedIndexes); + if (isInside) { + continue; + } + + // + if (hasChildArray(result)) { + // + const isInsideResult = isIndexInside(ccIdx, ...result); + if (isInsideResult) { + continue; + } + } + + // + result.push(ccIdx); + } + + // + return result; +} + +/** + * find all tokens indexes inside a content which they are not inside closed items ... + * + * @param {string|string[]} tokens the tokens which require to search ... + * @param {string} content the content which using to search ... + * @returns + */ +function findAllIndexesOutOfCloseds1( + tokens, + content, +) { + // + // Validate Args ... + if (!isValidArg(content)) { + return []; + } + + // + // Extract token Indexes ... + const tokensIndexes = findAllIndexes(tokens, content); + if (!hasChildArray(tokensIndexes)) { + return []; + } + + // + // Extract Closed Objects Indexes ... + const closedArrays = findClosedContent('[', ']', content); + const closedObjects = findClosedContent('{', '}', content); + const closedStrings = findClosedStrings(content); + if ( + !hasChildArray(closedObjects) && + !hasChildArray(closedArrays) && + !hasChildArray(closedStrings) + ) { + return tokensIndexes; + } + + // + // find bigger sloded ... + let containerClosedItems = findContainerIndexes( + closedArrays, + closedObjects, + closedStrings + ).filter(i => !isSurroundedString(i.content)); + + // + let result = [-1]; + result.pop(); + + // + for (let tIdx of tokensIndexes) { + // + let canAdd = true; + for (let ccIndex of containerClosedItems) { + // + canAdd = isInsideIndex(tIdx, ccIndex); + if (canAdd) { + break; + } + } + + // + if (!canAdd) { + result.push(tIdx); + } + } + + // + return result; +} + +/** + * find all tokens indexes inside a content which they are not inside closed items ... + * + * @param {string|string[]} tokens the tokens which require to search ... + * @param {string} content the content which using to search ... + * @returns + */ +function findAllIndexesOutOfCloseds( + tokens, + content, +) { + // + // Define Result ... + let result = [-1]; + result.pop(); + + // + // Validate Args ... + if (!isValidArg(content)) { + return []; + } + + // + // Normalize Tokens ... + const normalizeTokens = toNormalArray(tokens); + if (!hasChildArray(normalizeTokens)) { + return result; + } + + // + // Extract Closed Objects Indexes ... + let openSymbols = [ + '{', + '[', + '\'', + '"', + '`' + ]; + let closeSymbols = [ + '}', + ']', + '\'', + '"', + '`' + ]; + let openStack = []; + let lastOpenSymbol = ''; + let lastClosedIndex = -1; + for (let i = 0; i < content.length; i++) { + // + const prevChar = content.charAt(i - 1) || ''; + const char = content.charAt(i); + const nextChar = content.charAt(i + 1) || ''; + + // + const isToken = normalizeTokens.includes(char); + const isOpenSymbol = openSymbols.includes(char); + const isCloseSymbol = closeSymbols.includes(char); + + // + if (isOpenSymbol && lastOpenSymbol !== char) { + openStack.push(i); + lastOpenSymbol = char === '\'' || char === '"' || char === '`' ? char : lastOpenSymbol; + } else if (isCloseSymbol) { + // + lastOpenSymbol = char === '\'' || char === '"' || char === '`' ? '' : lastOpenSymbol; + let lastPop = openStack.pop(); + if (openStack.length === 0) { + lastClosedIndex = lastPop; + } + } else if (isToken) { + if ( + i > lastClosedIndex && + openStack.length === 0 + ) { + result.push(i); + } + } + } + + // + return result; +} +//#endregion + +// +//#region Validator Actions ... +/** + * check specified value is Valid URL Address or not ... + * + * @param {string} value specified string which required to check + * @returns {boolean} + */ +function isValidURL(value = "") { + // + if (!isValidArg(value)) { + return false; + } + + const pattern = new RegExp( + '^([a-zA-Z]+:\\/\\/)?' + // protocol + '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name + '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR IP (v4) address + '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path + '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string + '(\\#[-a-z\\d_]*)?$', // fragment locator + 'i' + ); + + // + const result = pattern.test(value); + return result; +} + +/** + * check specified value is Valid URL Address or not ... + * + * @param {string} value specified string which required to check + */ +function isValidEmail(value = "") { + // + if (!isValidArg(value)) { + return false; + } + + // + const pattern = /[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}/gim; + + // + const result = pattern.test(value); + return result; +} +//#endregion + +// +//#region Normallizers ... +/** + * normallize an string ... + * + * @param {string} value specified value ... + * @returns {string} + */ +function toNormalString(value = "") { + // + if (!XTypeTools.isString(value)) { + value = ""; + } + + // + const result = value + .trim() + .toLowerCase(); + + // + return result; +} +//#endregion + +// +//#region Beautifiers ... +/** + * converts an object to string based on json and beautify result ... + * + * @param {any} object + * @returns {string} + */ +function beautifyJSON(object = undefined) { + // + // TODO: Complete this ... + let result = ""; + + // + // validate object ... + if (object === undefined) { + return result; + } + + // + // Convert result ... + result = JSON.stringify( + object, + null, + 4 + ); + + // + return result; +} +//#endregion + +// +//#region Module Exports ... +module.exports = { + // + endsWidth, + surroundBy, + isValidArg, + isValidArgs, + toNormalArray, + hasChildArray, + toStringExpression, + + // + sliceContent, + clearContent, + clearSurround, + clearArraySurround, + clearObjectSurround, + + // + isSurrounded, + isIndexInside, + isInsideIndex, + isKeyValueType, + isSurroundedArray, + isSurroundedString, + isSurroundedObject, + + // + findNearest, + findAllIndexes, + findCloseIndex, + findNearestIndex, + findClosedContent, + findClosedStrings, + findClosedContents, + findAllIndexesOutOfCloseds, + + // + // Validator Actions ... + isValidURL, + isValidEmail, + + // + // Normallizers ... + toNormalString, + + // + beautifyJSON, +} +//#endregion \ No newline at end of file diff --git a/BKP/Main/Experts/x-saherelm.xeu5.ea.mq5 b/Experts/x-saherelm.xeu5.ea.mq5 similarity index 100% rename from BKP/Main/Experts/x-saherelm.xeu5.ea.mq5 rename to Experts/x-saherelm.xeu5.ea.mq5 diff --git a/BKP/Main/Helpers/x-saherelm.xama.indicator.helper.lib.mq5 b/Helpers/x-saherelm.xama.indicator.helper.lib.mq5 similarity index 100% rename from BKP/Main/Helpers/x-saherelm.xama.indicator.helper.lib.mq5 rename to Helpers/x-saherelm.xama.indicator.helper.lib.mq5 diff --git a/BKP/Main/Helpers/x-saherelm.xchlh.oscillator.helper.lib.mq5 b/Helpers/x-saherelm.xchlh.oscillator.helper.lib.mq5 similarity index 100% rename from BKP/Main/Helpers/x-saherelm.xchlh.oscillator.helper.lib.mq5 rename to Helpers/x-saherelm.xchlh.oscillator.helper.lib.mq5 diff --git a/BKP/Main/Helpers/x-saherelm.xchma.oscillator.helper.lib.mq5 b/Helpers/x-saherelm.xchma.oscillator.helper.lib.mq5 similarity index 100% rename from BKP/Main/Helpers/x-saherelm.xchma.oscillator.helper.lib.mq5 rename to Helpers/x-saherelm.xchma.oscillator.helper.lib.mq5 diff --git a/BKP/Main/Helpers/x-saherelm.xfi.oscillator.helper.lib.mq5 b/Helpers/x-saherelm.xfi.oscillator.helper.lib.mq5 similarity index 100% rename from BKP/Main/Helpers/x-saherelm.xfi.oscillator.helper.lib.mq5 rename to Helpers/x-saherelm.xfi.oscillator.helper.lib.mq5 diff --git a/BKP/Main/Helpers/x-saherelm.xlh.indicator.helper.lib.mq5 b/Helpers/x-saherelm.xlh.indicator.helper.lib.mq5 similarity index 100% rename from BKP/Main/Helpers/x-saherelm.xlh.indicator.helper.lib.mq5 rename to Helpers/x-saherelm.xlh.indicator.helper.lib.mq5 diff --git a/BKP/Main/Helpers/x-saherelm.xobd.indicator.helper.lib.mq5 b/Helpers/x-saherelm.xobd.indicator.helper.lib.mq5 similarity index 100% rename from BKP/Main/Helpers/x-saherelm.xobd.indicator.helper.lib.mq5 rename to Helpers/x-saherelm.xobd.indicator.helper.lib.mq5 diff --git a/BKP/Main/Helpers/x-saherelm.xrsi.oscillator.helper.lib.mq5 b/Helpers/x-saherelm.xrsi.oscillator.helper.lib.mq5 similarity index 100% rename from BKP/Main/Helpers/x-saherelm.xrsi.oscillator.helper.lib.mq5 rename to Helpers/x-saherelm.xrsi.oscillator.helper.lib.mq5 diff --git a/BKP/Main/Helpers/x-saherelm.xtd.oscillator.helper.lib.mq5 b/Helpers/x-saherelm.xtd.oscillator.helper.lib.mq5 similarity index 100% rename from BKP/Main/Helpers/x-saherelm.xtd.oscillator.helper.lib.mq5 rename to Helpers/x-saherelm.xtd.oscillator.helper.lib.mq5 diff --git a/BKP/Main/Helpers/x-saherelm.xtm.indicator.helper.lib.mq5 b/Helpers/x-saherelm.xtm.indicator.helper.lib.mq5 similarity index 100% rename from BKP/Main/Helpers/x-saherelm.xtm.indicator.helper.lib.mq5 rename to Helpers/x-saherelm.xtm.indicator.helper.lib.mq5 diff --git a/BKP/Main/Helpers/x-saherelm.xts.oscillator.helper.lib.mq5 b/Helpers/x-saherelm.xts.oscillator.helper.lib.mq5 similarity index 100% rename from BKP/Main/Helpers/x-saherelm.xts.oscillator.helper.lib.mq5 rename to Helpers/x-saherelm.xts.oscillator.helper.lib.mq5 diff --git a/BKP/Main/Indicators/x-saherelm.xama.indicator.mq5 b/Indicators/x-saherelm.xama.indicator.mq5 similarity index 100% rename from BKP/Main/Indicators/x-saherelm.xama.indicator.mq5 rename to Indicators/x-saherelm.xama.indicator.mq5 diff --git a/BKP/Main/Indicators/x-saherelm.xchlh.oscillator.mq5 b/Indicators/x-saherelm.xchlh.oscillator.mq5 similarity index 100% rename from BKP/Main/Indicators/x-saherelm.xchlh.oscillator.mq5 rename to Indicators/x-saherelm.xchlh.oscillator.mq5 diff --git a/BKP/Main/Indicators/x-saherelm.xchma.oscillator.mq5 b/Indicators/x-saherelm.xchma.oscillator.mq5 similarity index 100% rename from BKP/Main/Indicators/x-saherelm.xchma.oscillator.mq5 rename to Indicators/x-saherelm.xchma.oscillator.mq5 diff --git a/BKP/Main/Indicators/x-saherelm.xfi.oscillator.mq5 b/Indicators/x-saherelm.xfi.oscillator.mq5 similarity index 100% rename from BKP/Main/Indicators/x-saherelm.xfi.oscillator.mq5 rename to Indicators/x-saherelm.xfi.oscillator.mq5 diff --git a/BKP/Main/Indicators/x-saherelm.xlh.indicator.mq5 b/Indicators/x-saherelm.xlh.indicator.mq5 similarity index 100% rename from BKP/Main/Indicators/x-saherelm.xlh.indicator.mq5 rename to Indicators/x-saherelm.xlh.indicator.mq5 diff --git a/BKP/Main/Indicators/x-saherelm.xobd.indicator.mq5 b/Indicators/x-saherelm.xobd.indicator.mq5 similarity index 100% rename from BKP/Main/Indicators/x-saherelm.xobd.indicator.mq5 rename to Indicators/x-saherelm.xobd.indicator.mq5 diff --git a/BKP/Main/Indicators/x-saherelm.xrsi.oscillator.mq5 b/Indicators/x-saherelm.xrsi.oscillator.mq5 similarity index 100% rename from BKP/Main/Indicators/x-saherelm.xrsi.oscillator.mq5 rename to Indicators/x-saherelm.xrsi.oscillator.mq5 diff --git a/BKP/Main/Indicators/x-saherelm.xtd.oscillator.mq5 b/Indicators/x-saherelm.xtd.oscillator.mq5 similarity index 100% rename from BKP/Main/Indicators/x-saherelm.xtd.oscillator.mq5 rename to Indicators/x-saherelm.xtd.oscillator.mq5 diff --git a/BKP/Main/Indicators/x-saherelm.xtm.indicator.mq5 b/Indicators/x-saherelm.xtm.indicator.mq5 similarity index 100% rename from BKP/Main/Indicators/x-saherelm.xtm.indicator.mq5 rename to Indicators/x-saherelm.xtm.indicator.mq5 diff --git a/BKP/Main/Indicators/x-saherelm.xts.oscillator.mq5 b/Indicators/x-saherelm.xts.oscillator.mq5 similarity index 100% rename from BKP/Main/Indicators/x-saherelm.xts.oscillator.mq5 rename to Indicators/x-saherelm.xts.oscillator.mq5 diff --git a/BKP/Main/Libraries/x-saherelm.alert.lib.mq5 b/Libraries/x-saherelm.alert.lib.mq5 similarity index 100% rename from BKP/Main/Libraries/x-saherelm.alert.lib.mq5 rename to Libraries/x-saherelm.alert.lib.mq5 diff --git a/BKP/Main/Libraries/x-saherelm.common.lib.mq5 b/Libraries/x-saherelm.common.lib.mq5 similarity index 100% rename from BKP/Main/Libraries/x-saherelm.common.lib.mq5 rename to Libraries/x-saherelm.common.lib.mq5 diff --git a/BKP/Main/Libraries/x-saherelm.draw.lib.mq5 b/Libraries/x-saherelm.draw.lib.mq5 similarity index 100% rename from BKP/Main/Libraries/x-saherelm.draw.lib.mq5 rename to Libraries/x-saherelm.draw.lib.mq5 diff --git a/BKP/Main/Libraries/x-saherelm.http.lib.mq5 b/Libraries/x-saherelm.http.lib.mq5 similarity index 100% rename from BKP/Main/Libraries/x-saherelm.http.lib.mq5 rename to Libraries/x-saherelm.http.lib.mq5 diff --git a/BKP/Main/Libraries/x-saherelm.log.lib.mq5 b/Libraries/x-saherelm.log.lib.mq5 similarity index 100% rename from BKP/Main/Libraries/x-saherelm.log.lib.mq5 rename to Libraries/x-saherelm.log.lib.mq5 diff --git a/BKP/Main/Libraries/x-saherelm.models.lib.mq5 b/Libraries/x-saherelm.models.lib.mq5 similarity index 100% rename from BKP/Main/Libraries/x-saherelm.models.lib.mq5 rename to Libraries/x-saherelm.models.lib.mq5 diff --git a/Packs/.gitkeep b/Packs/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/BKP/Main/Providers/x-saherelm.xeu5.provider.indicators.lib.mq5 b/Providers/x-saherelm.xeu5.provider.indicators.lib.mq5 similarity index 100% rename from BKP/Main/Providers/x-saherelm.xeu5.provider.indicators.lib.mq5 rename to Providers/x-saherelm.xeu5.provider.indicators.lib.mq5 diff --git a/BKP/Main/Providers/x-saherelm.xeu5.provider.inputs.lib.mq5 b/Providers/x-saherelm.xeu5.provider.inputs.lib.mq5 similarity index 100% rename from BKP/Main/Providers/x-saherelm.xeu5.provider.inputs.lib.mq5 rename to Providers/x-saherelm.xeu5.provider.inputs.lib.mq5 diff --git a/BKP/Main/Providers/x-saherelm.xeu5.provider.lib.mq5 b/Providers/x-saherelm.xeu5.provider.lib.mq5 similarity index 100% rename from BKP/Main/Providers/x-saherelm.xeu5.provider.lib.mq5 rename to Providers/x-saherelm.xeu5.provider.lib.mq5 diff --git a/compile.cmd b/compile.cmd new file mode 100644 index 00000000..4fc256cf --- /dev/null +++ b/compile.cmd @@ -0,0 +1,26 @@ +@echo off + + set METAEDITOR="C:\Program Files\MetaTrader 5\MetaEditor64.exe" + set "LOGFILE=%~dpn1.log" + + if "%~1"=="" ( + echo "You must specify the first parameter to this batch file." + goto end + ) + + :: Compile the MQL5 way + %METAEDITOR% /compile:%1 /log + :: 0 = failed + :: 1 = everything good + echo "Error level %ERRORLEVEL%" + + :: This dumps the log file to stdout so it can be seen. + :: It's assumed to be in the same directory as the expert file. + type "%LOGFILE%" + + :: Once shown, get rid of the .log file. + erase "%LOGFILE%" + +:end + :: If I want the CMD.exe window to go away, use exit + ::exit %ERRORLEVEL% \ No newline at end of file diff --git a/compile.js b/compile.js new file mode 100644 index 00000000..83ed6527 --- /dev/null +++ b/compile.js @@ -0,0 +1,398 @@ +// +// Import Requirements ... +const execSync = require("child_process").execSync; +const XFileTools = require("./Documents/JsModules/x-file.tools"); +const XShellTools = require("./Documents/JsModules/x-shell.tools"); +const XColorTools = require("./Documents/JsModules/x-color.tools"); + +// +// define constants ... +const projectsPath = XFileTools.joinPath(".", "Projects"); +const packsPath = XFileTools.joinPath(".", "Packs"); +const excludeWorkspaceFolders = [ + "BKP", + "Documents", + "Images", + "Includes", + "Logs", + "Packs", + "Profiles", + "Projects", + "Scripts", + "Services", + "Shared Projects" +]; + +// +// Read Command Arguments ... +let projectName = process.argv[2]; +let doPackProject = process.argv[3] || false; + +// +// Make Decission to what to Do based on given args ... +if (!projectName || + projectName == "" || + projectName.length == 0) { + // + // Compile Whole MQL Workspace ... + doPackProject = false; + const workSpacePath = XFileTools.joinPath(".", __dirname); + + // + doWorkspaceCompile(workSpacePath); +} else if (projectName == "--experts" || projectName == "**experts") { + // + // Compile Experts on Workspace ... + doPackProject = false; + const workSpacePath = XFileTools.joinPath(".", __dirname, "Experts"); + + // + doWorkspaceCompile(workSpacePath); +} else if (projectName == "--indicators" || projectName == "**indicators") { + // + // Compile Indicators on Workspace ... + doPackProject = false; + const workSpacePath = XFileTools.joinPath(".", __dirname, "Indicators"); + + // + doWorkspaceCompile(workSpacePath); +} else if (projectName == "--libraries" || projectName == "**libraries") { + // + // Compile Libraries on Workspace ... + doPackProject = false; + const workSpacePath = XFileTools.joinPath(".", __dirname, "Libraries"); + + // + doWorkspaceCompile(workSpacePath); +} else { + // + // Compile Specific Projects ... + // + // Prepare Project Folder Path ... + const prjPath = XFileTools.joinPath(projectsPath, projectName); + + // + // Check Project Folder Exists ... + const isProjectFolderExists = XFileTools.isDirectoryExists(prjPath); + if (!isProjectFolderExists) { + // + const msg = "Project doesn't Exists ..."; + console.log(XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Red)); + throw msg; + } + + // + // Call Handler Function ... + doProjectCompile(prjPath); +} + +// +async function doProjectCompile(path = "") { + // + let result = await handleProjectCompile(path); + if (result && doPackProject) { + result = await handleProjectPack(path); + } +} + +// +async function doWorkspaceCompile(path = "") { + // + let mustCompileFiles = await readMQLFiles(path); + + // + if (mustCompileFiles && mustCompileFiles.length > 0) { + // + // Loop through items ... + for (const f of mustCompileFiles) { + // + // Check Exclude ... + const fBaseName = XFileTools.basename(f); + let fPath = f.replace(fBaseName, ""); + let isExclude = false; + for (const e of excludeWorkspaceFolders) { + // + isExclude = fPath.includes(e); + if (isExclude) { + break; + } + } + + // + if (isExclude) { + continue; + } + + // + // Log ... + console.log( + XColorTools.applyForegroundColor("try to compile: ", XColorTools.COLOR_NAMES.Yellow), + XColorTools.applyForegroundColor(f, XColorTools.COLOR_NAMES.Blue) + ); + + // + const result = await compileMQLFile(f); + + // + if (result.includes("0 errors")) { + // + const msg = "compiled successfully ..."; + console.log(XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Green)); + } else { + // + const msg = "failed ..."; + console.log(XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Red)); + throw msg; + } + } + + // + return true; + } + + // + return false; +} + +/** + * read all content in project path and compile it one by one ... + * + * @param {string} path Project Path ... + */ +async function handleProjectCompile(path = "") { + // + let mustCompileFiles = await readMQLFiles(path); + + // + if (mustCompileFiles && mustCompileFiles.length > 0) { + // + // Loop through items ... + for (const f of mustCompileFiles) { + // + // Log ... + console.log( + XColorTools.applyForegroundColor("try to compile: ", XColorTools.COLOR_NAMES.Yellow), + XColorTools.applyForegroundColor(f, XColorTools.COLOR_NAMES.Blue) + ); + + // + const result = await compileMQLFile(f); + + // + if (result.includes("0 errors")) { + // + const msg = "compiled successfully ..."; + console.log(XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Green)); + } else { + // + const msg = "failed ..."; + console.log(XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Red)); + throw msg; + } + } + + // + return true; + } + + // + return false; +} + +/** + * Pack Compiled Project ... + * + * @param {string} path Project Path ... + */ +async function handleProjectPack(path = "") { + // + const projectName = XFileTools.basename(path); + + // + // check packs exists or not ... + const isPacksExists = XFileTools.isDirectoryExists(packsPath); + if (!isPacksExists) { + XFileTools.createDirectory(packsPath); + } + + // + // Reading Compiled Files ... + const mustCopyFiles = await readEXTFiles(path); + if (!mustCopyFiles || mustCopyFiles.length == 0) { + return false; + } + + // + const prjPackPath = XFileTools.joinPath(packsPath, projectName); + + // + // Check Project Packs Folder ... + const isProjectPackExists = XFileTools.isDirectoryExists(prjPackPath); + if (isProjectPackExists) { + await XFileTools.removeDirectory(prjPackPath, true); + } + + // + // Create Empty Project Folder on Pack ... + XFileTools.createDirectory(prjPackPath); + + // + // Loop through Files ... + for (const f of mustCopyFiles) { + // + // Prepare Destination Path ... + const fBasePath = XFileTools.basename(f); + let fDestPath = f.replace(fBasePath, "") + .replace(projectsPath, packsPath); + fDestPath = fDestPath.substring(0, fDestPath.length - 1); + + // + const isFDestPathExists = XFileTools.isDirectoryExists(fDestPath); + if (!isFDestPathExists) { + XFileTools.createDirectory(fDestPath); + } + + // + // Log ... + console.log( + XColorTools.applyForegroundColor("try to copy: ", XColorTools.COLOR_NAMES.Yellow), + XColorTools.applyForegroundColor(f, XColorTools.COLOR_NAMES.Blue), + XColorTools.applyForegroundColor(" to : ", XColorTools.COLOR_NAMES.Yellow), + XColorTools.applyForegroundColor(fDestPath, XColorTools.COLOR_NAMES.Magenta) + ); + + // + let result = await XFileTools.copyFile(f, fDestPath); + if (result) { + // + const msg = "copy successfully ..."; + console.log( + XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Green) + ); + } else { + // + const msg = "copy failed ..."; + console.log(XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Red)); + throw msg; + } + } + + // + return true; +} + +/** + * Extract Project MQL files ... + * + * @param {string} path Folder Path ... + * @returns + */ +async function readMQLFiles(path = "") { + // + const result = await extractFiles(path, [".mq5", ".mq4"]); + + // + return result; +} + +/** + * Extract Project Compiled files ... + * + * @param {string} path Folder Path ... + * @returns + */ +async function readEXTFiles(path = "") { + // + const result = await extractFiles(path, [".ex5", ".ex4"]); + + // + return result; +} + +/** + * Compile Specific MQL File ... + * + * @param {string} path MQL file path ... + * @returns + */ +async function compileMQLFile(path = "") { + // + // Check File Exists ... + const isExists = XFileTools.isFileExists(path); + if (!isExists) { + // + const msg = "file not found ..."; + console.log(XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Red)); + throw msg; + } + + // + const ext = XFileTools.getFileExtension(path); + if (ext !== ".mq5" && ext !== "mq4") { + // + const msg = "invalid file extension ..."; + console.log(XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Red)); + throw msg; + } + + // + const cwd = __dirname; + const cmd = "compile.cmd " + path; + + // + const result = await XShellTools.execute(cmd, cwd); + return result; +} + + +/** + * Extract Specific files from a folder and it's content ... + * + * @param {string} path Folder Path ... + * @returns + */ +async function extractFiles(path = "", ext = []) { + // + const result = []; + + // + if (!ext || ext.length == 0) { + return result; + } + + // + const pathContent = await XFileTools.getDirectoryContents(path); + if (!pathContent || pathContent.length == 0) { + return result; + } + + // + // Loop through Content ... + for (const c of pathContent) { + // + // Prepare full path ... + const cPath = XFileTools.joinPath(path, c); + + // + // check content path is file or not ... + const isCFile = XFileTools.isFileExists(cPath); + const isCDirectory = XFileTools.isDirectoryExists(cPath); + if (isCFile) { + // + // Check it is MQL file or not ... + const cExt = XFileTools.getFileExtension(cPath); + if (ext.includes(cExt)) { + result.push(cPath); + } + } else if (isCDirectory) { + // + const cFiles = await extractFiles(cPath, ext); + if (cFiles && cFiles.length > 0) { + result.push(...cFiles); + } + } + } + + // + return result; +} diff --git a/package.json b/package.json index 43c038b8..efcf5f50 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,10 @@ "aaa": "|----------|", "aab": "| Projects |", "aac": "|----------|", + "compile": "node compile.js", + "compileWorkspace": "npm run compile", + "compileExperts": "npm run compile **experts", + "compileIndicators": "npm run compile **indicators", "rmExperts": "shx --silent rm ./Experts/* || npm run true", "rmHelpers": "shx --silent rm ./Helpers/* || npm run true", "rmClasses": "shx --silent rm ./Classes/* || npm run true", @@ -41,6 +45,7 @@ "xEU5UpSourceFromWorkspace": "npm run xEU5UpSourceExperts && npm run xEU5UpSourceProviders && npm run xEU5UpSourceIndicators && npm run xEU5UpSourceLibraries && npm run xEU5UpSourceHelpers && npm run xEU5UpSourceClasses", "xEU5PrepareWorkspace": "npm run backupWorkspace && npm run cleanupWorkspace && npm run xEU5CpWorkspace", "xEU5ApplyFromWorkspace": "npm run backupWorkspace && npm run xEU5UpSourceFromWorkspace", + "xEU5Compile": "npm run compile XEU5", "abd": "|----------|", "abe": "| /XEU5 |", "abf": "|----------|", @@ -63,6 +68,7 @@ "xAU5UpSourceFromWorkspace": "npm run xAU5UpSourceExperts && npm run xAU5UpSourceProviders && npm run xAU5UpSourceIndicators && npm run xAU5UpSourceLibraries && npm run xAU5UpSourceHelpers && npm run xAU5UpSourceClasses", "xAU5PrepareWorkspace": "npm run backupWorkspace && npm run cleanupWorkspace && npm run xAU5CpWorkspace", "xAU5ApplyFromWorkspace": "npm run backupWorkspace && npm run xAU5UpSourceFromWorkspace", + "xAU5Compile": "npm run compile XAU5", "acd": "|----------|", "ace": "| /XAU5 |", "acf": "|----------|", @@ -85,6 +91,7 @@ "xUSPY5UpSourceFromWorkspace": "npm run xUSPY5UpSourceExperts && npm run xUSPY5UpSourceProviders && npm run xUSPY5UpSourceIndicators && npm run xUSPY5UpSourceLibraries && npm run xUSPY5UpSourceHelpers && npm run xUSPY5UpSourceClasses", "xUSPY5PrepareWorkspace": "npm run backupWorkspace && npm run cleanupWorkspace && npm run xUSPY5CpWorkspace", "xUSPY5ApplyFromWorkspace": "npm run backupWorkspace && npm run xUSPY5UpSourceFromWorkspace", + "xUSPY5Compile": "npm run compile XUSPY5", "add": "|-----------|", "ade": "| /XUSPY5 |", "adf": "|-----------|", @@ -107,6 +114,7 @@ "xGPUS5UpSourceFromWorkspace": "npm run xGPUS5UpSourceExperts && npm run xGPUS5UpSourceProviders && npm run xGPUS5UpSourceIndicators && npm run xGPUS5UpSourceLibraries && npm run xGPUS5UpSourceHelpers && npm run xGPUS5UpSourceClasses", "xGPUS5PrepareWorkspace": "npm run backupWorkspace && npm run cleanupWorkspace && npm run xGPUS5CpWorkspace", "xGPUS5ApplyFromWorkspace": "npm run backupWorkspace && npm run xGPUS5UpSourceFromWorkspace", + "xGPU5Compile": "npm run compile XGPU5", "aed": "|-----------|", "aee": "| /XGPUS5 |", "aef": "|-----------|",