diff --git a/.gitignore b/.gitignore
index a0959a1..174a518 100644
--- a/.gitignore
+++ b/.gitignore
@@ -73,4 +73,5 @@ $RECYCLE.BIN/
# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option)
calculate-anything.zip
-vendor
\ No newline at end of file
+vendor
+flags/.DS_Store
diff --git a/LICENSE b/LICENSE
index e4f13a1..3ee1a26 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2019 biati digital
+Copyright (c) 2020 biati digital
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+SOFTWARE.
\ No newline at end of file
diff --git a/README.md b/README.md
index 925c2d1..20a0325 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,7 @@ There are several workflows out there but i just needed a workflow that worked n
- **Natural language** - type 100 euros to dollars or 100 euros in usd or 100€ to $ or 100eur usd or 100 euros a dolares. It does not matter, the same result will be displayed.
- **Currency** - Up to 168 currencies
+- **Cryptocurrency** - Support for 100 cryptocurrencies
- **Units** - 100 kilometers to meters or 100 km to m or simply 100km m
- **Percentages** - 100 + 16% | 100 - 16% etc.
- **PX,Em,Rem,Pt** - 12px or 12px to em or 12px pt
@@ -47,7 +48,7 @@ You can use natural language or type a few characters and that's all, for exampl
- 100 usd in mxn
- 100 usd yen
- 100usd eur
-- 100eur (If no target the currency will be converted to the base currency that you configured)
+- 100eur (If no target the currency will be converted to the base currencies that you configured)
```
All this examples will simply work, you can add spaces between the value and the currency or don't.
@@ -61,8 +62,10 @@ By default the workflow will use exchangerates api to make the conversion, excha
The following options are available for the currency. Simply launch Alfred and type **calculate configure** and select any of the options below. [View the configuration section for more info](#configuration)
-- **Set base currency**
-This will become your base currency, if you type 100eur it will automatically be converted to mxn, examples of currency (USD, EUR, MXN, CAD, etc)
+- **Add base currency**
+This will become your base currency, if you type 100eur it will automatically be converted to the currencies you define here. You can enter multiple currencies at once separated by comma for example: USD, EUR, MXN
+- **Delete base currency**
+If you no longer want a base currency you can select this option to list all configured base currencies, you can delete a currency by simply presing enter
- **Set currency locale**
Used to give format to the converted amount using the money format of your contry
- **Set Fixer API**
@@ -112,6 +115,31 @@ TT$ | TTD | Trinidad and Tobago dollar
TT$ | TTD | Trinidad and Tobago dollar
₴ | UAH | Ukrainian hryvnia
+
+## Cryptocurrency
+
+You can use this in conjunction with currency to convert 100 cryptocurrencies to up to 168 currencies, again you can use natural language or simply pass the currency simbol and that's all.
+
+```
+- 2 bitcoin to dollars
+- 0.1 bitcoin in dollars
+- 5 bitcoins in ethereum
+- 1 ethereum to ¥
+- 10 ethereum in mxn
+- 1eth btc
+- 1btc (If no target the currency will be converted to the base currency that you configured)
+```
+
+### Cryptocurrency Options
+
+You need to get a FREE API Key from [https://coinmarketcap.com/api/pricing/](https://coinmarketcap.com/api/pricing/) it takes less than a minute.
+
+The following options are available for cryptocurrency. Simply launch Alfred and type **calculate configure** and select any of the options below. [View the configuration section for more info](#configuration)
+
+- **Set Coinmarketcap API**
+Select this option and paste your API key and press enter to save it.
+
+
## Units
You can write your query using natural language or just a few characters, either way this workflow will give you the result you need.
@@ -478,7 +506,9 @@ You can easily configure the workflow simply by opening Alfred and typing **calc
## Updates
-Starting from version 1.0.5 automatic updates were implemente, you will be notified if a new update is available or if you prefer you can launch Alfred and type **calculate update** to check for updates.
+~~Starting from version 1.0.5 automatic updates were implemente, you will be notified if a new update is available or if you prefer you can launch Alfred and type **calculate update** to check for updates.~~
+
+Starting from version 1.0.8 there's a new way to search and install automatic updates, before it was necessary to press enter when you used "Calculate Anything" and it will trigger the updater but the "Enter" key is not regularly used as this workflow does not depends on this key for the queries so you'll probably ended up with an outdated version. If you want you can still use **calculate update** to check for updates but it's not necessary anymore, the workflow will do it automatically for you.
## Performance
@@ -490,10 +520,16 @@ This workflow could not be possible without:
- [Convertor](https://github.com/olifolkerd/convertor) with some modifications
- [currency-converter-php](https://github.com/ojhaujjwal/currency-converter-php) for ExchangeRatesIo
-- [OneUpdater](https://www.alfredforum.com/topic/9224-oneupdater-—-update-workflows-with-a-single-node/) Automatic updates for Alfred Workflows
## Changelog
+### 2.0.0
+
+- New: Complete rewrite to be more maintainable and extendable
+- New: Added cryptocurrencies support
+- New: Display exchange rate conversions in multiple currencies at once
+- New: Added new workflow updater
+
### 1.0.7
- Fixed a bug where some configuration was saves in lowercase causing some problems with dates and currency formatting
diff --git a/actions.php b/actions.php
index 11d5238..0ffd33c 100644
--- a/actions.php
+++ b/actions.php
@@ -1,4 +1,5 @@
remote_plist_url = $this->getVar($config, 'plist_url', false);
+ $this->remote_workflow = $this->getVar($config, 'workflow_url', false);
+ $this->check_interval = $this->getVar($config, 'check_interval', $this->check_interval);
+ $this->force_check = $this->getVar($config, 'force_check', false);
+ $this->force_download = $this->getVar($config, 'force_download', false);
+ $this->download_type = $this->getVar($config, 'download_type', 'async');
+ }
+
+
+ /**
+ * Maybe check for updates
+ */
+ public function checkUpdates()
+ {
+ if (!$this->remote_plist_url || !$this->remote_workflow) {
+ throw new Exception("Configure the plist and workflow URL");
+ }
+
+ $updates_file = $this->updatesFile();
+
+ if (!file_exists($updates_file) || $this->force_check || $this->force_download) {
+ return $this->getRemotePlist();
+ }
+
+ $updated = filemtime($updates_file);
+ $time = time() - $updated;
+
+ if ($time > $this->check_interval) { // interval passed, check again
+ $this->deleteRemotePlist();
+ $this->getRemotePlist();
+ return;
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Download remote plist
+ */
+ private function getRemotePlist()
+ {
+ $tmp_download = $this->filePath('remote_plist.download');
+
+ if (file_exists($tmp_download)) { // pending download abort
+ return;
+ }
+
+ file_put_contents($tmp_download, time());
+
+ if (file_put_contents($this->remotePlistPath(), file_get_contents($this->remote_plist_url))) {
+ unlink($tmp_download);
+ file_put_contents($this->updatesFile(), time());
+ return $this->shouldUpdate();
+ }
+ }
+
+
+ /**
+ * shouldUpdate
+ * compare remote version with local version
+ */
+ private function shouldUpdate()
+ {
+ $remote_plist = $this->escapeEspaces($this->remotePlistPath());
+ $command = "/usr/libexec/PlistBuddy -c 'print version' {$remote_plist}";
+ $remote_version = shell_exec($command);
+ $local_version = getenv('alfred_workflow_version');
+
+ if ($this->force_download || version_compare($remote_version, $local_version) > 0) {
+ $this->notifyUpdate();
+ $this->downloadWorkflow();
+
+ return true;
+ } else {
+ $this->deleteRemotePlist();
+ touch($this->updatesFile()); // Reset timer by touching local file
+ return false;
+ }
+ }
+
+
+ /**
+ * Download updated workflow
+ */
+ private function downloadWorkflow()
+ {
+ $title = getenv('alfred_workflow_name');
+ $home = getenv('HOME');
+ $name = $title;
+ $url = $this->remote_workflow;
+ $tmp_download = $this->filePath("{$name}.download");
+ $tmp_workflow = "{$home}/Downloads/{$name}.alfredworkflow";
+
+ if (file_exists($tmp_download)) { // pending download abort
+ return;
+ }
+
+ file_put_contents($tmp_download, time());
+
+ $command = "curl --silent --location --output \"{$tmp_workflow}\" \"{$url}\"";
+ $command .= ' && rm ' . $this->escapeEspaces($tmp_download);
+ $command .= ' && rm ' . $this->escapeEspaces($this->remotePlistPath());
+ $command .= ' && open ' . $this->escapeEspaces($tmp_workflow);
+ $command .= " && osascript -e 'display notification \"Download Completed\" with title \"{$title}\"'";
+
+ if ($this->download_type == 'async') {
+ shell_exec("/usr/bin/nohup {$command} >/dev/null 2>&1 &");
+ }
+ if ($this->download_type == 'sync') {
+ shell_exec("/usr/bin/nohup {$command}");
+ }
+ }
+
+
+ /**
+ * Delete remote plist
+ */
+ private function deleteRemotePlist()
+ {
+ unlink($this->remotePlistPath());
+ }
+
+
+ /**
+ * Show a notification about the update
+ */
+ private function notifyUpdate()
+ {
+ $this->notify('New version available. Downloading and installing…');
+ }
+
+
+ /**
+ * Display notification
+ */
+ private function notify($message = '', $title = '')
+ {
+ $title = (!empty($title) ? $title : getenv('alfred_workflow_name'));
+ $command = "osascript -e 'display notification \"{$message}\" with title \"{$title}\"'";
+ shell_exec($command);
+ }
+
+ /**
+ * Escape string spaces
+ */
+ private function escapeEspaces($str = '')
+ {
+ return str_replace(' ', '\ ', $str);
+ }
+
+
+ /**
+ * Get workflow file path
+ * the base path is the variable alfred_workflow_data
+ * this is the path that Alfred uses to store
+ * data about the workflow
+ *
+ * @param string $path
+ * @return string
+ */
+ private function filePath($file)
+ {
+ $data_path = getenv('alfred_workflow_data');
+ $file_path = "{$data_path}/{$file}";
+
+ return $file_path;
+ }
+
+
+ /**
+ * Remote plist path
+ * return the local path where
+ * the remote plist is downloaded and
+ * stored while performing checks
+ *
+ * @return string
+ */
+ private function remotePlistPath()
+ {
+ return $this->filePath('remote-plist.plist');
+ }
+
+ /**
+ * Remote plist path
+ * return the local path where
+ * the remote plist is downloaded and
+ * stored while performing checks
+ *
+ * @return string
+ */
+ private function updatesFile()
+ {
+ return $this->filePath('last-update-check.txt');
+ }
+
+ /**
+ * Get var from array
+ *
+ * @param array $array
+ * @param string $key
+ * @param mixed $default
+ * @return mixed
+ */
+ private function getVar($array, $key, $default = null)
+ {
+ if (is_array($array) && isset($array[$key]) && !empty($array[$key])) {
+ return trim($array[$key]);
+ }
+ if (!is_null($default)) {
+ return $default;
+ }
+
+ return '';
+ }
+}
diff --git a/configure.php b/configure.php
index e3696b5..de37f32 100644
--- a/configure.php
+++ b/configure.php
@@ -6,144 +6,199 @@
* Options to configure the workflow
*/
-require_once(__DIR__ . '/functions.php');
+require_once __DIR__ . '/workflow/lib/functions.php';
$param = getenv('param');
$response = [];
-$strings = get_translation('config');
+$settings = getSettings();
+$strings = getTranslation('config');
+$currencies = getSetting('base_currency', 'USD', $settings);
+
+if (is_string($currencies)) { // convert old setting to array
+ $currencies = [$currencies];
+}
if (empty($param)) {
- $response[] = [
- 'title' => $strings['lang_title'],
- 'subtitle' => $strings['lang_subtitle'],
- 'valid' => true,
- 'arg' => 'language',
- 'match' => $strings['lang_title'],
- 'autocomplete' => $strings['lang_title'],
- 'autocomplete' => $curr_name,
- ];
- $response[] = [
- 'title' => $strings['currency_title'],
- 'subtitle' => $strings['currency_subtitle'],
- 'match' => $strings['currency_title'],
- 'autocomplete' => $strings['currency_title'],
- 'valid' => true,
- 'arg' => 'base_currency',
- ];
- $response[] = [
- 'title' => $strings['currency_locale_title'],
- 'subtitle' => $strings['currency_locale_subtitle'],
- 'match' => $strings['currency_locale_title'],
- 'autocomplete' => $strings['currency_locale_title'],
- 'valid' => true,
- 'arg' => 'locale_currency',
- ];
- $response[] = [
- 'title' => $strings['fixer_title'],
- 'subtitle' => $strings['fixer_subtitle'],
- 'match' => $strings['fixer_title'],
- 'autocomplete' => $strings['fixer_title'],
- 'valid' => true,
- 'arg' => 'fixer_apikey',
- ];
- $response[] = [
- 'title' => $strings['measurement_title'],
- 'subtitle' => $strings['measurement_subtitle'],
- 'match' => $strings['measurement_title'],
- 'autocomplete' => $strings['measurement_title'],
- 'valid' => true,
- 'arg' => 'measurement_system',
- ];
- $response[] = [
- 'title' => $strings['vat_title'],
- 'subtitle' => $strings['vat_subtitle'],
- 'match' => $strings['vat_title'],
- 'autocomplete' => $strings['vat_title'],
- 'valid' => true,
- 'arg' => 'vat_percentage',
- ];
- $response[] = [
- 'title' => $strings['base_timezone_title'],
- 'subtitle' => $strings['base_timezone_subtitle'],
- 'match' => $strings['base_timezone_title'],
- 'autocomplete' => $strings['base_timezone_title'],
- 'valid' => true,
- 'arg' => 'time_zone',
- ];
- $response[] = [
- 'title' => $strings['add_date_title'],
- 'subtitle' => $strings['add_date_subtitle'],
- 'match' => $strings['add_date_title'],
- 'autocomplete' => $strings['add_date_title'],
- 'valid' => true,
- 'arg' => 'add_time_zone',
- ];
- $response[] = [
- 'title' => $strings['delete_date_title'],
- 'subtitle' => $strings['delete_date_subtitle'],
- 'match' => $strings['delete_date_title'],
- 'autocomplete' => $strings['delete_date_title'],
- 'valid' => true,
- 'arg' => 'delete_time_zone',
- ];
- $response[] = [
- 'title' => $strings['base_pixels_title'],
- 'subtitle' => $strings['base_pixels_subtitle'],
- 'match' => $strings['base_pixels_title'],
- 'autocomplete' => $strings['base_pixels_title'],
- 'valid' => true,
- 'arg' => 'base_pixels',
- ];
- echo '{"items": ' . json_encode($response) . ' }';
- exit(0);
+ $response[] = [
+ 'title' => $strings['lang_title'],
+ 'subtitle' => $strings['lang_subtitle'] . ': ' . getSetting('language', 'en_EN', $settings),
+ 'valid' => true,
+ 'arg' => 'language',
+ 'match' => $strings['lang_title'],
+ 'autocomplete' => $strings['lang_title'],
+ 'autocomplete' => $curr_name,
+ ];
+ $response[] = [
+ 'title' => $strings['currency_title'],
+ 'subtitle' => $strings['currency_subtitle'] . ': ' . implode(', ', $currencies),
+ 'match' => $strings['currency_title'],
+ 'autocomplete' => $strings['currency_title'],
+ 'valid' => true,
+ 'arg' => 'add_base_currency',
+ ];
+ $response[] = [
+ 'title' => $strings['delete_currency_title'],
+ 'subtitle' => $strings['delete_currency_subtitle'],
+ 'match' => $strings['delete_currency_title'],
+ 'autocomplete' => $strings['delete_currency_title'],
+ 'valid' => true,
+ 'arg' => 'delete_base_currency',
+ ];
+ $response[] = [
+ 'title' => $strings['currency_locale_title'],
+ 'subtitle' => $strings['currency_locale_subtitle'] . ': ' . getSetting('locale_currency', 'en_US', $settings),
+ 'match' => $strings['currency_locale_title'],
+ 'autocomplete' => $strings['currency_locale_title'],
+ 'valid' => true,
+ 'arg' => 'locale_currency',
+ ];
+ $response[] = [
+ 'title' => $strings['crypto_title'],
+ 'subtitle' => $strings['crypto_subtitle'] . ': ' . getSetting('coinmarket_apikey', '', $settings),
+ 'match' => $strings['crypto_title'],
+ 'autocomplete' => $strings['crypto_title'],
+ 'valid' => true,
+ 'arg' => 'coinmarket_apikey',
+ ];
+ $response[] = [
+ 'title' => $strings['fixer_title'],
+ 'subtitle' => $strings['fixer_subtitle'] . ': ' . getSetting('fixer_apikey', '', $settings),
+ 'match' => $strings['fixer_title'],
+ 'autocomplete' => $strings['fixer_title'],
+ 'valid' => true,
+ 'arg' => 'fixer_apikey',
+ ];
+ $response[] = [
+ 'title' => $strings['measurement_title'],
+ 'subtitle' => $strings['measurement_subtitle'] . ': ' . getSetting('measurement_system', 'metric', $settings),
+ 'match' => $strings['measurement_title'],
+ 'autocomplete' => $strings['measurement_title'],
+ 'valid' => true,
+ 'arg' => 'measurement_system',
+ ];
+ $response[] = [
+ 'title' => $strings['vat_title'],
+ 'subtitle' => $strings['vat_subtitle'] . ': ' . getSetting('vat_percentage', '16%', $settings),
+ 'match' => $strings['vat_title'],
+ 'autocomplete' => $strings['vat_title'],
+ 'valid' => true,
+ 'arg' => 'vat_percentage',
+ ];
+ $response[] = [
+ 'title' => $strings['base_timezone_title'],
+ 'subtitle' => $strings['base_timezone_subtitle'] . ': ' . getSetting('time_zone', 'America/Los_Angeles', $settings),
+ 'match' => $strings['base_timezone_title'],
+ 'autocomplete' => $strings['base_timezone_title'],
+ 'valid' => true,
+ 'arg' => 'time_zone',
+ ];
+ $response[] = [
+ 'title' => $strings['add_date_title'],
+ 'subtitle' => $strings['add_date_subtitle'],
+ 'match' => $strings['add_date_title'],
+ 'autocomplete' => $strings['add_date_title'],
+ 'valid' => true,
+ 'arg' => 'add_time_zone',
+ ];
+ $response[] = [
+ 'title' => $strings['delete_date_title'],
+ 'subtitle' => $strings['delete_date_subtitle'],
+ 'match' => $strings['delete_date_title'],
+ 'autocomplete' => $strings['delete_date_title'],
+ 'valid' => true,
+ 'arg' => 'delete_time_zone',
+ ];
+ $response[] = [
+ 'title' => $strings['base_pixels_title'],
+ 'subtitle' => $strings['base_pixels_subtitle'] . ': ' . getSetting('base_pixels', '16px', $settings),
+ 'match' => $strings['base_pixels_title'],
+ 'autocomplete' => $strings['base_pixels_title'],
+ 'valid' => true,
+ 'arg' => 'base_pixels',
+ ];
+ echo '{"items": ' . json_encode($response) . ' }';
+ exit(0);
}
// Handle delete time zones
if ($param == 'delete_time_zone') {
- $response = [];
- $timezones = get_setting('timezones', [], $settings);
-
- if (empty($timezones)) {
- $response[] = [
- 'title' => '...',
- 'subtitle' => $strings['empty_date_formats'],
- 'valid' => false,
- ];
- echo '{"items": ' . json_encode($response) . ' }';
- exit(0);
- }
-
- foreach ($timezones as $key => $value) {
- $response[] = [
- 'variables' => [
- 'configure_key' => $param,
- 'configure_val' => $key
- ],
- 'title' => $value,
- 'subtitle' => $strings['enter_delete_date'],
- 'arg' => $value,
- ];
- }
- echo '{"items": ' . json_encode($response) . ' }';
- exit(0);
+ $response = [];
+ $timezones = getSetting('timezones', [], $settings);
+
+ if (empty($timezones)) {
+ $response[] = [
+ 'title' => '...',
+ 'subtitle' => $strings['empty_date_formats'],
+ 'valid' => false,
+ ];
+ echo '{"items": ' . json_encode($response) . ' }';
+ exit(0);
+ }
+
+ foreach ($timezones as $key => $value) {
+ $response[] = [
+ 'variables' => [
+ 'configure_key' => $param,
+ 'configure_val' => $key
+ ],
+ 'title' => $value,
+ 'subtitle' => $strings['enter_delete_date'],
+ 'arg' => $value,
+ ];
+ }
+ echo '{"items": ' . json_encode($response) . ' }';
+ exit(0);
}
-$value = get_var($argv, 2, '');
-$query = trim(get_var($argv, 1));
+// Handle delete currency
+if ($param == 'delete_base_currency') {
+ $response = [];
+ $stored_currencies = $currencies;
+
+ if (empty($stored_currencies)) {
+ $response[] = [
+ 'title' => '...',
+ 'subtitle' => $strings['empty_currency_formats'],
+ 'valid' => false,
+ ];
+ echo '{"items": ' . json_encode($response) . ' }';
+ exit(0);
+ }
+
+ foreach ($stored_currencies as $key => $value) {
+ $response[] = [
+ 'variables' => [
+ 'configure_key' => $param,
+ 'configure_val' => $key
+ ],
+ 'title' => $value,
+ 'subtitle' => $strings['enter_delete_base_currency'],
+ 'arg' => $value,
+ ];
+ }
+ echo '{"items": ' . json_encode($response) . ' }';
+ exit(0);
+}
+
+
+
+
+$value = getVar($argv, 2, '');
+$query = trim(getVar($argv, 1));
$param_name = ucfirst(str_replace('_', ' ', $param));
$config_value = $query;
echo '{"items": [
{
- "variables": {
- "configure_key": "'. $param . '",
- "configure_val": "' . $config_value . '"
- },
- "title": "'.$param_name.': ' . $query . '",
- "subtitle": "' . $strings['enter_save'] . '",
+ "variables": {
+ "configure_key": "' . $param . '",
+ "configure_val": "' . $config_value . '"
+ },
+ "title": "' . $param_name . ': ' . $query . '",
+ "subtitle": "' . $strings['enter_save'] . '",
"uid": "' . $query . '",
- "arg": "' . $query . '",
- "valid": true,
+ "arg": "' . $query . '",
+ "valid": true,
}
]}';
diff --git a/currency.php b/currency.php
deleted file mode 100644
index b2249fe..0000000
--- a/currency.php
+++ /dev/null
@@ -1,729 +0,0 @@
- "United Arab Emirates dirham",
- 'AFN' => "Afghan afghani",
- 'ALL' => "Albanian lek",
- 'AMD' => "Armenian dram",
- 'ANG' => "Netherlands Antillean guilder",
- 'AOA' => "Angolan kwanza",
- 'ARS' => "Argentine peso",
- 'AUD' => "Australian dollar",
- 'AWG' => "Aruban florin",
- 'AZN' => "Azerbaijani manat",
- 'BAM' => "Bosnia and Herzegovina convertible mark",
- 'BBD' => "Barbados dollar",
- 'BDT' => "Bangladeshi taka",
- 'BGN' => "Bulgarian lev",
- 'BHD' => "Bahraini dinar",
- 'BIF' => "Burundian franc",
- 'BMD' => "Bermudian dollar",
- 'BND' => "Brunei dollar",
- 'BOB' => "Boliviano",
- 'BRL' => "Brazilian real",
- 'BSD' => "Bahamian dollar",
- 'BTN' => "Bhutanese ngultrum",
- 'BWP' => "Botswana pula",
- 'BYN' => "New Belarusian ruble",
- 'BYR' => "Belarusian ruble",
- 'BZD' => "Belize dollar",
- 'CAD' => "Canadian dollar",
- 'CDF' => "Congolese franc",
- 'CHF' => "Swiss franc",
- 'CLF' => "Unidad de Fomento",
- 'CLP' => "Chilean peso",
- 'CNY' => "Renminbi|Chinese yuan",
- 'COP' => "Colombian peso",
- 'CRC' => "Costa Rican colon",
- 'CUC' => "Cuban convertible peso",
- 'CUP' => "Cuban peso",
- 'CVE' => "Cape Verde escudo",
- 'CZK' => "Czech koruna",
- 'DJF' => "Djiboutian franc",
- 'DKK' => "Danish krone",
- 'DOP' => "Dominican peso",
- 'DZD' => "Algerian dinar",
- 'EGP' => "Egyptian pound",
- 'ERN' => "Eritrean nakfa",
- 'ETB' => "Ethiopian birr",
- 'EUR' => "Euro",
- 'FJD' => "Fiji dollar",
- 'FKP' => "Falkland Islands pound",
- 'GBP' => "Pound sterling",
- 'GEL' => "Georgian lari",
- 'GHS' => "Ghanaian cedi",
- 'GIP' => "Gibraltar pound",
- 'GMD' => "Gambian dalasi",
- 'GNF' => "Guinean franc",
- 'GTQ' => "Guatemalan quetzal",
- 'GYD' => "Guyanese dollar",
- 'HKD' => "Hong Kong dollar",
- 'HNL' => "Honduran lempira",
- 'HRK' => "Croatian kuna",
- 'HTG' => "Haitian gourde",
- 'HUF' => "Hungarian forint",
- 'IDR' => "Indonesian rupiah",
- 'ILS' => "Israeli new shekel",
- 'INR' => "Indian rupee",
- 'IQD' => "Iraqi dinar",
- 'IRR' => "Iranian rial",
- 'ISK' => "Icelandic króna",
- 'JMD' => "Jamaican dollar",
- 'JOD' => "Jordanian dinar",
- 'JPY' => "Japanese yen",
- 'KES' => "Kenyan shilling",
- 'KGS' => "Kyrgyzstani som",
- 'KHR' => "Cambodian riel",
- 'KMF' => "Comoro franc",
- 'KPW' => "North Korean won",
- 'KRW' => "South Korean won",
- 'KWD' => "Kuwaiti dinar",
- 'KYD' => "Cayman Islands dollar",
- 'KZT' => "Kazakhstani tenge",
- 'LAK' => "Lao kip",
- 'LBP' => "Lebanese pound",
- 'LKR' => "Sri Lankan rupee",
- 'LRD' => "Liberian dollar",
- 'LSL' => "Lesotho loti",
- 'LYD' => "Libyan dinar",
- 'MAD' => "Moroccan dirham",
- 'MDL' => "Moldovan leu",
- 'MGA' => "Malagasy ariary",
- 'MKD' => "Macedonian denar",
- 'MMK' => "Myanmar kyat",
- 'MNT' => "Mongolian tögrög",
- 'MOP' => "Macanese pataca",
- 'MRO' => "Mauritanian ouguiya",
- 'MUR' => "Mauritian rupee",
- 'MVR' => "Maldivian rufiyaa",
- 'MWK' => "Malawian kwacha",
- 'MXN' => "Mexican peso",
- 'MXV' => "Mexican Unidad de Inversion",
- 'MYR' => "Malaysian ringgit",
- 'MZN' => "Mozambican metical",
- 'NAD' => "Namibian dollar",
- 'NGN' => "Nigerian naira",
- 'NIO' => "Nicaraguan córdoba",
- 'NOK' => "Norwegian krone",
- 'NPR' => "Nepalese rupee",
- 'NZD' => "New Zealand dollar",
- 'OMR' => "Omani rial",
- 'PAB' => "Panamanian balboa",
- 'PEN' => "Peruvian Sol",
- 'PGK' => "Papua New Guinean kina",
- 'PHP' => "Philippine peso",
- 'PKR' => "Pakistani rupee",
- 'PLN' => "Polish złoty",
- 'PYG' => "Paraguayan guaraní",
- 'QAR' => "Qatari riyal",
- 'RON' => "Romanian leu",
- 'RSD' => "Serbian dinar",
- 'RUB' => "Russian ruble",
- 'RWF' => "Rwandan franc",
- 'SAR' => "Saudi riyal",
- 'SBD' => "Solomon Islands dollar",
- 'SCR' => "Seychelles rupee",
- 'SDG' => "Sudanese pound",
- 'SEK' => "Swedish krona",
- 'SGD' => "Singapore dollar",
- 'SHP' => "Saint Helena pound",
- 'SLL' => "Sierra Leonean leone",
- 'SOS' => "Somali shilling",
- 'SRD' => "Surinamese dollar",
- 'SSP' => "South Sudanese pound",
- 'STD' => "São Tomé and Príncipe dobra",
- 'SVC' => "Salvadoran colón",
- 'SYP' => "Syrian pound",
- 'SZL' => "Swazi lilangeni",
- 'THB' => "Thai baht",
- 'TJS' => "Tajikistani somoni",
- 'TMT' => "Turkmenistani manat",
- 'TND' => "Tunisian dinar",
- 'TOP' => "Tongan paʻanga",
- 'TRY' => "Turkish lira",
- 'TTD' => "Trinidad and Tobago dollar",
- 'TWD' => "New Taiwan dollar",
- 'TZS' => "Tanzanian shilling",
- 'UAH' => "Ukrainian hryvnia",
- 'UGX' => "Ugandan shilling",
- 'USD' => "United States dollar",
- 'UYI' => "Uruguay Peso en Unidades Indexadas",
- 'UYU' => "Uruguayan peso",
- 'UZS' => "Uzbekistan som",
- 'VEF' => "Venezuelan bolívar",
- 'VND' => "Vietnamese đồng",
- 'VUV' => "Vanuatu vatu",
- 'WST' => "Samoan tala",
- 'XAF' => "Central African CFA franc",
- 'XCD' => "East Caribbean dollar",
- 'XOF' => "West African CFA franc",
- 'XPF' => "CFP franc",
- 'YER' => "Yemeni rial",
- 'ZAR' => "South African rand",
- 'ZMW' => "Zambian kwacha",
- 'ZWL' => "Zimbabwean dollar"
- ];
-}
-
-
-/**
- * Get units list
- * get a readable units list
- * to display to the user
- *
- * @return array
- */
-function get_currencies_list()
-{
- $translation = get_translation('currency');
- $units = get_available_currencies();
- $list = [];
- foreach ($units as $key => $value) {
- $curr = $key;
- $curr_name = (isset($translation[$curr]) ? $translation[$curr] : $curr);
-
- $list[] = [
- 'title' => $curr,
- 'subtitle' => $curr_name,
- 'arg' => $curr,
- 'match' => $curr . ' ' . $curr_name,
- 'autocomplete' => $curr_name,
- 'valid' => true,
- 'mods' => [
- 'cmd' => [
- 'valid' => true,
- 'arg' => $curr,
- 'subtitle' => get_text('action_copy'),
- ]
- ],
- 'icon' => [
- 'path' => "flags/{$curr}.png"
- ]
- ];
- }
-
- return $list;
-}
-
-
-
-/**
- * Regex
- * create a regex from the
- * available currencies array
- *
- * @return string
- */
-function available_currencies_regex()
-{
- $currencies = get_available_currencies();
- $params = implode('|', array_keys($currencies));
- $translated_currencies = translated_currencies();
- $params .= '|'. implode('|', array_keys($translated_currencies));
- $params = escape_currency_keywords($params);
-
- return '(' . $params . ')';
-}
-
-
-function escape_currency_keywords($key)
-{
- $key = str_replace('$', '\$', $key);
- $key = str_replace('/', '\/', $key);
- $key = str_replace('.', '\.', $key);
- return $key;
-}
-
-/**
- * Is valid
- * check if given currency
- * is valid and exists in the
- * currencies array
- *
- * @param string $val
- * @return boolean
- */
-function is_valid_currency($val)
-{
- $currencies = get_available_currencies();
- return isset($currencies[$val]);
-}
-
-/**
- * Is currency
- * validate if guven query
- * is a currency conversion string
- *
- * @param string $query
- * @return boolean
- */
-function is_currency($query)
-{
- $currencies = available_currencies_regex();
- $stopwords = currency_stopwords();
- $query = str_replace(',', '', $query);
- return preg_match('/^\d*\.?\d+ ?' . $currencies . ' ?'. $stopwords .'?/i', $query, $matches);
-}
-
-/**
- * Currency stop words
- * words that can be used in the query
- * when using natural languge like
- * 100usd to mxn - here the word "to" is a stop word
- *
- * @param mixed $sep
- * @return string
- */
-function currency_stopwords($sep = false)
-{
- $keys = get_extra_keywords('currency');
- $stop_words = get_stopwords_string($keys['stop_words'], $sep);
-
- return $stop_words;
-}
-
-
-/**
- * Process
- * process conversion
- *
- * @param string $query
- * @return mixed
- */
-function process_currency_conversion($query)
-{
- $amount = '';
- $from = '';
- $to = '';
- $settigs = get_settings();
- $fixer_api = get_setting('fixer_apikey', '', $settigs);
- $stopwords = currency_stopwords(' %s ');
- $query = str_replace(',', '', $query);
-
- preg_match('/^(\d*\.?\d+)[^\d]/i', $query, $amount_match);
- if (empty($amount_match)) {
- return false;
- }
-
- $amount = get_var($amount_match, 1);
- $amount = trim($amount);
- $string = str_replace($amount, '', $query);
- $string = trim($string);
-
- preg_match('/(.*).*' . $stopwords . '(.*)/i', $string, $matches);
-
- // Matches strings like 100 usd to mxn
- if (!empty($matches)) {
- $matches = array_values(array_filter($matches));
- $from = get_var($matches, 1);
- $to = get_var($matches, 3);
-
- // Default to stored currency
- if (empty($to)) {
- $to = get_setting('base_currency', 'USD', $settigs);
- }
-
- $from = translated_currencies($from);
- $to = translated_currencies($to);
- }
-
- // String is like 100 usd or 100 usd mxn
- elseif (empty($matches)) {
- $keywords = get_extra_keywords('currency');
-
- foreach ($keywords as $key => $value) {
- if (is_array($value)) {
- continue;
- }
- $key = escape_currency_keywords($key);
- $string = preg_replace('/(^|\W)' . $key . '(\W|$)/i', ' '. $value . ' ', $string);
- }
-
- $string = preg_replace('!\s+!', ' ', $string);
- $string = trim($string);
-
- $data = explode(' ', $string);
- $from = get_var($data, 0);
- $to = get_var($data, 1);
-
- // Default to stored currency
- if (empty($to)) {
- $to = get_setting('base_currency', 'USD', $settigs);
- }
-
- $from = strtoupper($from);
- $to = strtoupper($to);
- }
-
- if (!is_valid_currency($from) || !is_valid_currency($to)) {
- return false;
- }
-
- $amount = cleanup_number($amount);
- if ($amount <= 0) {
- return $amount . $to;
- }
-
- // Skip same currency conversions
- if ($from == $to) {
- $converted = ['total' => $amount, 'single' => '1.00', 'error' => false];
- }
-
- $locale = get_setting('locale_currency', 'en_US', $settigs);
- setlocale(LC_MONETARY, $locale);
-
- // Convert between currencies
- if ($from !== $to) {
- $converted = convert_currency([
- 'amount' => $amount,
- 'currency' => $from,
- 'to' => $to,
- 'fixer_api' => $fixer_api,
- ]);
- }
-
- if ($converted['error']) {
- return $converted['error'];
- }
-
- $total = money_format('%i', $converted['total']);
- $total = preg_replace("/[^0-9.,]/", '', $total);
- $single = $converted['single'];
- $single = format_number($converted['single']);
-
- $processed = [];
- $processed[$total] = "{$total}{$to}";
- $processed[$single] = "1{$from} = {$single}{$to}";
-
- return [
- 'data' => $processed,
- 'currency' => $to,
- ];
-}
-
-
-/**
- * Actual conversion
- * use the api to convert
- * the currencies
- *
- * @param int $amount
- * @param string $from
- * @param string $to
- * @return array
- */
-function convert_currency($data)
-{
- $amount = $data['amount'];
- $from = $data['currency'];
- $to = $data['to'];
- $fixer_apikey = $data['fixer_api'];
- $cache_seconds = 43200; // 12 hours in seconds
- $cache_dir = get_data_path('cache');
-
- create_dir($cache_dir);
-
- // Use Fixer io
- if (!empty($fixer_apikey)) {
- $cache_seconds = 7200;
- $cached = get_cache_fixer($from, $to, $cache_seconds);
- if ($cached) {
- $cached = (float) $cached;
- $value = $cached;
- }
-
- if (!$cached) {
- $exchange = get_fixer_rates($fixer_apikey, $cache_seconds);
-
- if (is_string($exchange)) {
- return ['total' => '', 'single' => '', 'error' => $exchange];
- }
-
- $base = $exchange['base'];
- $rates = $exchange['rates'];
- $default_base_currency = $rates[$base];
-
- $new_base_currency = $rates[$from]; //from currency
- $base_exchange = $default_base_currency / $new_base_currency;
- $value = ($rates[$to] * $base_exchange);
-
- cache_fixer($from, $to, $value);
- }
-
- return ['total' => $amount * $value, 'single' => $value, 'error' => false];
- }
-
-
- // Default convertions with to exchangeratesapi.io
- include_required_files();
-
- $converter = new CurrencyConverter\CurrencyConverter;
- $cache_adapter = new CurrencyConverter\Cache\Adapter\FileSystem($cache_dir);
- $cache_adapter->setCacheTimeout(DateInterval::createFromDateString($cache_seconds . ' second'));
- $converter->setCacheAdapter($cache_adapter);
-
- $value = '';
- $error = false;
-
- try {
- $value = $converter->convert($from, $to);
- } catch (\Throwable $th) {
- $error = true;
- $message = $th->getMessage();
- preg_match('/{(.*)}/', $message, $matches);
-
- if ($matches && !empty($matches)) {
- $value = $matches[1];
- }
- }
-
- if ($error) {
- return ['error' => $value];
- }
-
- return ['total' => $amount * $value, 'single' => $value, 'error' => false];
-}
-
-
-/**
- * Cached fixer
- *
- * @param string $from
- * @param string $to
- * @param string $value
- * @return void
- */
-function cache_fixer($from, $to, $value)
-{
- $dir = get_data_path('cache/fixer');
- $exists = create_dir($dir);
- $file = $dir . '/'. $from .'-'.$to.'.txt';
-
- file_put_contents($file, $value);
-}
-
-/**
- * Get fixer cache
- * return the cached rate
- *
- * @param string $from
- * @param string $to
- * @param int $cache_seconds
- * @return mixed
- */
-function get_cache_fixer($from, $to, $cache_seconds)
-{
- $dir = get_data_path('cache/fixer');
- $file = $dir . '/' . $from . '-' . $to . '.txt';
-
- create_dir($dir);
-
- if (!file_exists($file)) {
- return false;
- }
-
- $updated = filemtime($file);
- $time = time() - $updated;
-
- if ($time > $cache_seconds) { // cache already expired
- return false;
- }
-
- $val = file_get_contents($file);
- if (empty($val)) {
- return false;
- }
-
- return $val;
-}
-
-
-/**
- * Get rates from fixer
- * if already cached and cache
- * has not expired returns
- * the cached rates otherwise
- * it fetches the rates again
- *
- * @param string $key fixer api key
- * @param int $cache_seconds number of seconds before the cache expires
- * @return mixed array if sucess or string with error message
- */
-function get_fixer_rates($key, $cache_seconds)
-{
- $dir = get_data_path('cache/fixer');
- create_dir($dir);
-
- $file = $dir . '/rates.json';
- if (file_exists($file)) {
- $c = file_get_contents($file);
-
- if (!empty($c)) {
- $c = json_decode($c, true);
- $updated = $c['timestamp'];
- $time = time() - $updated;
-
- // Only return cached rates if cache
- // has not expired otherwise continue
- // to fetch the new rates
- if ($time < $cache_seconds) {
- return $c;
- }
- }
- }
-
- $c = file_get_contents("http://data.fixer.io/api/latest?access_key={$key}&format=1");
- if (empty($c)) {
- $strings = get_translation('currency');
- return $strings['fetch_error'];
- }
-
- file_put_contents($file, $c);
-
- return json_decode($c, true);
-}
-
-
-
-/**
- * Requred
- * includes requred files
- * only if conversion can be processed
- *
- * @return void
- */
-function include_required_files()
-{
- $dir = __DIR__ . DIRECTORY_SEPARATOR . 'currency' . DIRECTORY_SEPARATOR;
- include $dir . '/Cache/Adapter/CacheAdapterInterface.php';
- include $dir . '/Cache/Adapter/AbstractAdapter.php';
- include $dir . '/Cache/Adapter/FileSystem.php';
- include $dir . '/Cache/Adapter/ZendAdapter.php';
-
- include $dir . '/Provider/ProviderInterface.php';
- include $dir . '/Provider/ExchangeRatesIo.php';
- include $dir . '/Provider/FixerApi.php';
-
- include $dir . '/Exception/ExceptionInterface.php';
- include $dir . '/Exception/InvalidArgumentException.php';
- include $dir . '/Exception/RunTimeException.php';
- include $dir . '/Exception/UnsupportedCurrencyException.php';
-
- include $dir . '/guzzle/Exception/GuzzleException.php';
- include $dir . '/guzzle/Exception/TransferException.php';
- include $dir . '/guzzle/Exception/RequestException.php';
- include $dir . '/guzzle/Exception/BadResponseException.php';
- include $dir . '/guzzle/Exception/ConnectException.php';
- include $dir . '/guzzle/Exception/ClientException.php';
- include $dir . '/guzzle/Exception/InvalidArgumentException.php';
- include $dir . '/guzzle/Exception/SeekException.php';
- include $dir . '/guzzle/Exception/ServerException.php';
- include $dir . '/guzzle/Exception/TooManyRedirectsException.php';
- include $dir . '/guzzle/Handler/Proxy.php';
-
- include $dir . '/guzzle/Handler/EasyHandle.php';
- include $dir . '/guzzle/Handler/CurlMultiHandler.php';
- include $dir . '/guzzle/Handler/CurlHandler.php';
- include $dir . '/guzzle/Handler/CurlFactoryInterface.php';
- include $dir . '/guzzle/Handler/CurlFactory.php';
- include $dir . '/guzzle/Handler/StreamHandler.php';
- include $dir . '/guzzle/functions.php';
- include $dir . '/guzzle/PrepareBodyMiddleware.php';
- include $dir . '/guzzle/Middleware.php';
- include $dir . '/guzzle/RedirectMiddleware.php';
- include $dir . '/guzzle/RequestOptions.php';
- include $dir . '/guzzle/HandlerStack.php';
- include $dir . '/PSR7/functions.php';
- include $dir . '/PSR7/UriInterface.php';
- include $dir . '/PSR7/MessageInterface.php';
- include $dir . '/PSR7/Uri.php';
- include $dir . '/PSR7/MessageTrait.php';
- include $dir . '/PSR7/ResponseInterface.php';
- include $dir . '/PSR7/Response.php';
- include $dir . '/PSR7/RequestInterface.php';
- include $dir . '/PSR7/Request.php';
- include $dir . '/PSR7/StreamInterface.php';
- include $dir . '/PSR7/Stream.php';
-
- include $dir . '/guzzle/Promise/functions.php';
- include $dir . '/guzzle/Promise/TaskQueueInterface.php';
- include $dir . '/guzzle/Promise/TaskQueue.php';
- include $dir . '/guzzle/Promise/PromiseInterface.php';
- include $dir . '/guzzle/Promise/Promise.php';
- include $dir . '/guzzle/Promise/FulfilledPromise.php';
-
- include $dir . '/guzzle/ClientInterface.php';
- include $dir . '/guzzle/Client.php';
-
- include $dir . '/CurrencyConverterInterface.php';
- include $dir . '/CurrencyConverter.php';
- include $dir . '/CountryToCurrency.php';
-}
-
-
-/**
- * Translated currencies
- * Convert some keywords to
- * the actual currency so you can use
- * natual language to make conversions
- *
- * For example:
- * 100¥ to $
- * Will be converted to
- * 100JPY to usd
- *
- * Still the user can be able
- * to write 100jpy to usd
- *
- * The keywords list can be found in
- * /lang/{lang}-keys.php
- *
- * @param boolean $unit
- * @return array
- */
-function translated_currencies($val = false)
-{
- $val = mb_strtolower($val, 'UTF-8');;
- $keywords = get_extra_keywords('currency');
-
- if (!$val) {
- return $keywords;
- }
-
- if (is_valid_currency($val)) {
- return strtoupper($val);
- }
-
- foreach ($keywords as $key => $value) {
- if (is_array($value)) {
- continue;
- }
- $key = escape_currency_keywords($key);
- $val = preg_replace('/(^|\W)' . $key . '(\W|$)/i', ' '. $value . ' ', $val);
- }
-
- $val = strtoupper($val);
- $val = trim($val);
- $val = preg_replace('!\s+!', ' ', $val);
-
- return $val;
-}
diff --git a/flags/.DS_Store b/flags/.DS_Store
deleted file mode 100644
index 43ac91e..0000000
Binary files a/flags/.DS_Store and /dev/null differ
diff --git a/functions.php b/functions.php
deleted file mode 100644
index f267c73..0000000
--- a/functions.php
+++ /dev/null
@@ -1,340 +0,0 @@
-= 0) {
- if ($round) {
- return number_format($number, $decimals);
- }
- $number = bcdiv($number, 1, $decimals);
- return number_format($number, $decimals);
- }
-
- $decimals = 1;
- $string = ''. $number;
- $string = explode('.', $string);
- $string = str_split(end($string));
- $count = 1;
-
- // If string has 2 or more decimals make some cleanup
- if (count($string) >= 2) {
- $decimals = 2;
-
- foreach ($string as $order => $value) {
- $prev = (isset($string[$order - 1]) ? $string[$order - 1] : '');
- if ($value == '0') {
- $count += 1;
- continue;
- }
- if ($value !== '0' && $prev !== '0') {
- $count += 1;
- $end_digit = $value;
- break;
- }
- }
- $decimals = $count;
- }
-
- if ($round) {
- return number_format($number, $decimals);
- }
- $number = bcdiv($number, 1, $decimals);
- return number_format($number, $decimals);
- } else {
- return number_format($number);
- }
-}
-
-function cleanup_number($number)
-{
- return floatval(str_replace(',', '', $number));
-}
-
-function get_settings_path()
-{
- return get_data_path('settings.json');
-}
-
-function get_data_path($ipath = '')
-{
- $path = getenv('alfred_workflow_data');
- create_dir($path);
-
- if (!empty($ipath)) {
- return $path .'/'. $ipath;
- }
- return $path;
-}
-
-function create_dir($path)
-{
- if (!file_exists($path)) {
- mkdir($path);
- }
-
- return true;
-}
-
-function get_var($array, $key, $default = null)
-{
- if (is_array($array) && isset($array[$key]) && !empty($array[$key])) {
- return trim($array[$key]);
- }
- if (!is_null($default)) {
- return $default;
- }
-
- return '';
-}
-
-
-function get_translation($key = '', $lang = '')
-{
- if (defined('LANG_STRINGS')) {
- $strings = LANG_STRINGS;
- if (empty($key)) {
- return $strings;
- }
-
- if (isset($strings[$key])) {
- return $strings[$key];
- }
-
- return false;
- }
-
- $default_lang = default_lang();
- $lang = (empty($lang) ? get_setting('language', $default_lang) : $lang);
- $translations = load_translations_file($lang);
- if ($translations) {
- // Return default lang if translation error
- if (!is_array($translations) || empty($translations)) {
- return get_translation($key, $default_lang);
- }
-
- if (empty($key)) {
- return $translations;
- }
-
- if (isset($translations[$key])) {
- return $translations[$key];
- }
-
- return false;
- }
-
- return get_translation($key, $default_lang);
-}
-
-function load_translations_file($lang)
-{
- $file = __DIR__ . '/lang/' . $lang . '.php';
- if (file_exists($file)) {
- $data = include $file;
- return $data;
- }
- return false;
-}
-
-function get_text($key)
-{
- $strings = get_translation('general');
- if (!is_array($strings) || !isset($strings[$key])) {
- return '';
- }
- return $strings[$key];
-}
-
-function default_lang()
-{
- return 'en_EN';
-}
-
-function get_extra_keywords($key = '', $lang = '')
-{
- if (defined('LANG_KEYWORDS')) {
- $strings = LANG_KEYWORDS;
- if (empty($key)) {
- return $strings;
- }
-
- if (isset($strings[$key])) {
- return $strings[$key];
- }
-
- return false;
- }
-
- $default_lang = default_lang();
- $lang = (empty($lang) ? get_setting('language', $default_lang) : $lang);
- $file = __DIR__ . '/lang/' . $lang . '-keys.php';
-
- if (file_exists($file)) {
- $translations = include $file;
-
- // Return default lang if translation error
- if (!is_array($translations) || empty($translations)) {
- return get_extra_keywords($key, $default_lang);
- }
-
- // If language is different
- // from english, also load the en keys
- // so they are global
- if ($lang !== $default_lang) {
- $translations = merge_with_base_keywords($translations);
- }
-
- if (empty($key)) {
- return $translations;
- }
-
- if (isset($translations[$key])) {
- return $translations[$key];
- }
-
- return false;
- }
-
- return get_extra_keywords($key, $default_lang);
-}
-
-
-function merge_with_base_keywords($keywords)
-{
- $en_keys = get_en_keywords();
- foreach ($en_keys as $key => $value) {
- if (!isset($keywords[$key])) {
- $keywords[$key] = $value;
- continue;
- }
-
- foreach ($value as $k => $v) {
- if (isset($keywords[$key][$k]) && is_array($keywords[$key][$k])) {
- $mul = array_merge($keywords[$key][$k], $v);
- $keywords[$key][$k] = array_unique($mul);
- }
- elseif (!isset($keywords[$key][$k])) {
- $keywords[$key][$k] = $v;
- }
- }
- }
-
- return $keywords;
-}
-
-
-function get_en_keywords()
-{
- return include __DIR__ . '/lang/en_EN-keys.php';
-}
-
-
-function get_stopwords_string($words, $spaced = false)
-{
- $sep = ($spaced ? ' | ' : '|');
- if (is_bool($spaced)) {
- $str = implode($sep, $words);
- }
- if (is_string($spaced)) {
- $w = [];
- foreach ($words as $word) {
- $w[] = sprintf($spaced, $word);
- }
- $str = implode('|', $w);
- }
- return '(' . $str . ')';
-}
-
-
-function clean_query($query)
-{
- if (empty($query)) {
- return $query;
- }
- // $clean = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $tsts);
- // Fucking letter ñ this was the only
- // way i found to remove it without removing
- // the rest of the special characters in the string
- $clean = $query;
- $clean = urlencode($clean);
- $clean = str_replace('n%CC%83', 'n', $clean);
- $clean = str_replace('N%CC%83', 'n', $clean);
- $clean = str_replace('%C3%B3', 'o', $clean); // accented o
- $clean = str_replace('%CC%81', '', $clean); // accented i
-
- $clean = urldecode($clean);
- $clean = preg_replace('!\s+!', ' ', $clean);
- $clean = mb_strtolower($clean, 'UTF-8');
-
- return $clean;
-}
-
-
-function starts_with($haystack, $needle, $case = true)
-{
- if ($case) {
- return strpos($haystack, $needle, 0) === 0;
- }
-
- return stripos($haystack, $needle, 0) === 0;
-}
-
-function ends_with($haystack, $needle, $case = true)
-{
- $expectedPosition = strlen($haystack) - strlen($needle);
- if ($case) {
- return strrpos($haystack, $needle, 0) === $expectedPosition;
- }
-
- return strripos($haystack, $needle, 0) === $expectedPosition;
-}
diff --git a/info.plist b/info.plist
index d702fdd..a2a7791 100644
--- a/info.plist
+++ b/info.plist
@@ -8,19 +8,6 @@
Tools
connections
- 01F84077-BA5D-47E6-B9BD-5E6993AB2F98
-
-
- destinationuid
- 42330AA6-64D5-48A7-BCD2-0E63E0229052
- modifiers
- 0
- modifiersubtext
-
- vitoclose
-
-
-
02CB4726-DF90-475A-835E-0038EE019394
@@ -73,11 +60,11 @@
- 42330AA6-64D5-48A7-BCD2-0E63E0229052
+ 60604A23-2820-4DAE-A371-1F9A83C0D7FC
destinationuid
- BD95F30F-133D-4304-B9C9-A537320590B3
+ 7DA07E01-89C1-4B84-B76F-E8916DACF72F
modifiers
0
modifiersubtext
@@ -86,11 +73,11 @@
- 6B5446AC-124D-4626-92C3-D0CA1D94D27F
+ 65F736EC-3B8C-4B6D-BE2C-837A63AC34A3
destinationuid
- 7FE64D1B-CD91-40BC-903C-3F1BC17CEE02
+ ABA18E77-71E3-494A-8735-A60799F419B9
modifiers
0
modifiersubtext
@@ -99,7 +86,7 @@
- 6B6A13A8-C671-4033-A793-EE772BA1FB48
+ 6B5446AC-124D-4626-92C3-D0CA1D94D27F
destinationuid
@@ -112,11 +99,11 @@
- 73DD0CD0-28B0-4225-B29F-F7F44A44FAD0
+ 6B6A13A8-C671-4033-A793-EE772BA1FB48
destinationuid
- BD95F30F-133D-4304-B9C9-A537320590B3
+ 7FE64D1B-CD91-40BC-903C-3F1BC17CEE02
modifiers
0
modifiersubtext
@@ -152,18 +139,7 @@
7FE64D1B-CD91-40BC-903C-3F1BC17CEE02
-
-
- destinationuid
- BD95F30F-133D-4304-B9C9-A537320590B3
- modifiers
- 0
- modifiersubtext
-
- vitoclose
-
-
-
+
810259C6-0B1F-4440-99F4-7821B5226D70
@@ -181,7 +157,7 @@
destinationuid
- 73DD0CD0-28B0-4225-B29F-F7F44A44FAD0
+ ABA18E77-71E3-494A-8735-A60799F419B9
modifiers
0
modifiersubtext
@@ -256,18 +232,7 @@
ABA18E77-71E3-494A-8735-A60799F419B9
-
-
- destinationuid
- BD95F30F-133D-4304-B9C9-A537320590B3
- modifiers
- 0
- modifiersubtext
-
- vitoclose
-
-
-
+
BB2A4C00-3D9B-43BD-957F-AFF92B5A1DCB
@@ -285,7 +250,7 @@
destinationuid
- D310983D-CF37-4EA6-8557-05FCCF7AA40C
+ 60604A23-2820-4DAE-A371-1F9A83C0D7FC
modifiers
0
modifiersubtext
@@ -295,18 +260,7 @@
CAF3C560-8129-4EED-9555-4A3D5F56DE79
-
-
- destinationuid
- BD95F30F-133D-4304-B9C9-A537320590B3
- modifiers
- 0
- modifiersubtext
-
- vitoclose
-
-
-
+
D037BED7-D145-4DEB-8419-84840F408A48
@@ -370,7 +324,7 @@
queuemode
1
runningsubtext
- Calculating...
+
script
php ./process.php "0{query}"
scriptargtype
@@ -378,9 +332,9 @@
scriptfile
subtext
- Calculate
+
title
- Calculate
+
type
0
withspace
@@ -419,7 +373,7 @@
queuemode
1
runningsubtext
- Calculating...
+
script
php ./process.php "1{query}"
scriptargtype
@@ -427,9 +381,9 @@
scriptfile
subtext
- Calculate
+
title
- Calculate
+
type
0
withspace
@@ -468,7 +422,7 @@
queuemode
1
runningsubtext
- Calculating...
+
script
php ./process.php "2{query}"
scriptargtype
@@ -476,9 +430,9 @@
scriptfile
subtext
- Calculate
+
title
- Calculate
+
type
0
withspace
@@ -517,7 +471,7 @@
queuemode
1
runningsubtext
- Calculating...
+
script
php ./process.php "3{query}"
scriptargtype
@@ -525,9 +479,9 @@
scriptfile
subtext
- Calculate
+
title
- Calculate
+
type
0
withspace
@@ -566,7 +520,7 @@
queuemode
1
runningsubtext
- Calculating...
+
script
php ./process.php "4{query}"
scriptargtype
@@ -574,9 +528,9 @@
scriptfile
subtext
- Calculate
+
title
- Calculate
+
type
0
withspace
@@ -632,7 +586,7 @@
queuemode
1
runningsubtext
- Calculating...
+
script
php ./process.php "5{query}"
scriptargtype
@@ -640,9 +594,9 @@
scriptfile
subtext
- Calculate
+
title
- Calculate
+
type
0
withspace
@@ -681,7 +635,7 @@
queuemode
1
runningsubtext
- Calculating...
+
script
php ./process.php "6{query}"
scriptargtype
@@ -689,9 +643,9 @@
scriptfile
subtext
- Calculate
+
title
- Calculate
+
type
0
withspace
@@ -730,7 +684,7 @@
queuemode
1
runningsubtext
- Calculating...
+
script
php ./process.php "7{query}"
scriptargtype
@@ -738,9 +692,9 @@
scriptfile
subtext
- Calculate
+
title
- Calculate
+
type
0
withspace
@@ -779,7 +733,7 @@
queuemode
1
runningsubtext
- Calculating...
+
script
php ./process.php "8{query}"
scriptargtype
@@ -787,9 +741,9 @@
scriptfile
subtext
- Calculate
+
title
- Calculate
+
type
0
withspace
@@ -828,7 +782,7 @@
queuemode
1
runningsubtext
- Calculating...
+
script
php ./process.php "9{query}"
scriptargtype
@@ -836,9 +790,9 @@
scriptfile
subtext
- Calculate
+
title
- Calculate
+
type
0
withspace
@@ -851,23 +805,6 @@
version
3
-
- config
-
- autopaste
-
- clipboardtext
- {query}
- transient
-
-
- type
- alfred.workflow.output.clipboard
- uid
- 42330AA6-64D5-48A7-BCD2-0E63E0229052
- version
- 3
-
config
@@ -880,11 +817,11 @@
argumenttrimmode
0
argumenttype
- 1
+ 0
escaping
- 4
+ 0
keyword
- vat
+ iva
queuedelaycustom
3
queuedelayimmediatelyinitially
@@ -892,122 +829,47 @@
queuedelaymode
0
queuemode
- 1
+ 2
runningsubtext
- Calculating VAT...
+ Calculating...
script
- php ./vat.php "{query}"
+ php ./process.php $1 vat
scriptargtype
- 0
+ 1
scriptfile
subtext
- Calculate VAT
+ Enter a value to calculate VAT
title
Calculate VAT
type
0
withspace
-
+
type
alfred.workflow.input.scriptfilter
uid
- 01F84077-BA5D-47E6-B9BD-5E6993AB2F98
+ 65F736EC-3B8C-4B6D-BE2C-837A63AC34A3
version
3
config
- concurrently
+ autopaste
+
+ clipboardtext
+ {query}
+ transient
- escaping
- 0
- script
- # THESE VARIABLES MUST BE SET. SEE THE ONEUPDATER README FOR AN EXPLANATION OF EACH.
-# THESE VARIABLES MUST BE SET. SEE THE ONEUPDATER README FOR AN EXPLANATION OF EACH.
-readonly remote_info_plist="https://raw.githubusercontent.com/biati-digital/alfred-calculate-anything/master/info.plist"
-readonly workflow_url="https://github.com/biati-digital/alfred-calculate-anything/releases/latest/download/Calculate.Anything.alfredworkflow"
-readonly download_type='direct'
-readonly frequency_check='4'
-
-# FROM HERE ON, CODE SHOULD BE LEFT UNTOUCHED!
-function abort {
- echo "${1}" >&2
- exit 1
-}
-
-function url_exists {
- curl --silent --location --output /dev/null --fail --range 0-0 "${1}"
-}
-
-function notification {
- local -r notificator="$(find . -type d -name 'Notificator.app')"
- if [[ -n "${notificator}" ]]; then
- "${notificator}/Contents/Resources/Scripts/notificator" --message "${1}" --title "${alfred_workflow_name}" --subtitle 'A new version is available'
- return
- fi
-
- local -r terminal_notifier="$(find . -type f -name 'terminal-notifier')"
- if [[ -n "${terminal_notifier}" ]]; then
- "${terminal_notifier}" -title "${alfred_workflow_name}" -subtitle 'A new version is available' -message "${1}"
- return
- fi
-
- osascript -e "display notification \"${1}\" with title \"${alfred_workflow_name}\" subtitle \"A new version is available\""
-}
-
-# Local sanity checks
-readonly local_info_plist='info.plist'
-readonly local_version="$(/usr/libexec/PlistBuddy -c 'print version' "${local_info_plist}")"
-
-[[ -n "${local_version}" ]] || abort 'You need to set a workflow version in the configuration sheet.'
-[[ "${download_type}" =~ ^(direct|page|github_release)$ ]] || abort "'download_type' (${download_type}) needs to be one of 'direct', 'page', or 'github_release'."
-[[ "${frequency_check}" =~ ^[0-9]+$ ]] || abort "'frequency_check' (${frequency_check}) needs to be a number."
-
-# Check for updates
-if [[ $(find "${local_info_plist}" -mtime +"${frequency_check}"d) ]]; then
- if ! url_exists "${remote_info_plist}"; then abort "'remote_info_plist' (${remote_info_plist}) appears to not be reachable."; fi # Remote sanity check
-
- readonly tmp_file="$(mktemp)"
- curl --silent --location --output "${tmp_file}" "${remote_info_plist}"
- readonly remote_version="$(/usr/libexec/PlistBuddy -c 'print version' "${tmp_file}")"
-
- if [[ "${local_version}" == "${remote_version}" ]]; then
- touch "${local_info_plist}" # Reset timer by touching local file
- exit 0
- fi
-
- if [[ "${download_type}" == 'page' ]]; then
- notification 'Opening download page…'
- open "${workflow_url}"
- exit 0
- fi
-
- download_url="$([[ "${download_type}" == 'github_release' ]] && curl --silent "https://api.github.com/repos/${workflow_url}/releases/latest" | grep 'browser_download_url' | head -1 | sed -E 's/.*browser_download_url": "(.*)"/\1/' || echo "${workflow_url}")"
-
- if url_exists "${download_url}"; then
- notification 'Downloading and installing…'
- curl --silent --location --output "${HOME}/Downloads/${alfred_workflow_name}.alfredworkflow" "${download_url}"
- open "${HOME}/Downloads/${alfred_workflow_name}.alfredworkflow"
- else
- abort "'workflow_url' (${download_url}) appears to not be reachable."
- fi
-fi
- scriptargtype
- 1
- scriptfile
-
- type
- 0
type
- alfred.workflow.action.script
+ alfred.workflow.output.clipboard
uid
- BD95F30F-133D-4304-B9C9-A537320590B3
+ ABA18E77-71E3-494A-8735-A60799F419B9
version
- 2
+ 3
config
@@ -1037,9 +899,14 @@ fi
runningsubtext
Calculating Time...
script
- php ./time.php "{query}"
+ if [ -z "$1" ]
+then
+ php ./process.php now time
+else
+ php ./process.php "$1" time
+fi
scriptargtype
- 0
+ 1
scriptfile
subtext
@@ -1058,23 +925,6 @@ fi
version
3
-
- config
-
- autopaste
-
- clipboardtext
- {query}
- transient
-
-
- type
- alfred.workflow.output.clipboard
- uid
- ABA18E77-71E3-494A-8735-A60799F419B9
- version
- 3
-
config
@@ -1175,23 +1025,6 @@ php ./list.php "{query}"
version
3
-
- config
-
- autopaste
-
- clipboardtext
- {query}
- transient
-
-
- type
- alfred.workflow.output.clipboard
- uid
- 73DD0CD0-28B0-4225-B29F-F7F44A44FAD0
- version
- 3
-
config
@@ -1259,6 +1092,29 @@ php ./list.php "{query}"
version
3
+
+ config
+
+ concurrently
+
+ escaping
+ 102
+ script
+ php -f actions.php
+ scriptargtype
+ 0
+ scriptfile
+
+ type
+ 0
+
+ type
+ alfred.workflow.action.script
+ uid
+ 7E1D03F2-8A6E-4727-ACFB-0C5C77AA7BEE
+ version
+ 2
+
config
@@ -1308,29 +1164,6 @@ php ./list.php "{query}"
version
3
-
- config
-
- concurrently
-
- escaping
- 102
- script
- php -f actions.php
- scriptargtype
- 0
- scriptfile
-
- type
- 0
-
- type
- alfred.workflow.action.script
- uid
- 7E1D03F2-8A6E-4727-ACFB-0C5C77AA7BEE
- version
- 2
-
config
@@ -1376,80 +1209,11 @@ php ./list.php "{query}"
concurrently
escaping
- 0
+ 102
script
- # THESE VARIABLES MUST BE SET. SEE THE ONEUPDATER README FOR AN EXPLANATION OF EACH.
-# THESE VARIABLES MUST BE SET. SEE THE ONEUPDATER README FOR AN EXPLANATION OF EACH.
-readonly remote_info_plist="https://raw.githubusercontent.com/biati-digital/alfred-calculate-anything/master/info.plist"
-readonly workflow_url="https://github.com/biati-digital/alfred-calculate-anything/releases/latest/download/Calculate.Anything.alfredworkflow"
-readonly download_type='direct'
-readonly frequency_check='0'
-
-# FROM HERE ON, CODE SHOULD BE LEFT UNTOUCHED!
-function abort {
- echo "${1}" >&2
- exit 1
-}
-
-function url_exists {
- curl --silent --location --output /dev/null --fail --range 0-0 "${1}"
-}
-
-function notification {
- local -r notificator="$(find . -type d -name 'Notificator.app')"
- if [[ -n "${notificator}" ]]; then
- "${notificator}/Contents/Resources/Scripts/notificator" --message "${1}" --title "${alfred_workflow_name}" --subtitle 'A new version is available'
- return
- fi
-
- local -r terminal_notifier="$(find . -type f -name 'terminal-notifier')"
- if [[ -n "${terminal_notifier}" ]]; then
- "${terminal_notifier}" -title "${alfred_workflow_name}" -subtitle 'A new version is available' -message "${1}"
- return
- fi
-
- osascript -e "display notification \"${1}\" with title \"${alfred_workflow_name}\" subtitle \"A new version is available\""
-}
-
-# Local sanity checks
-readonly local_info_plist='info.plist'
-readonly local_version="$(/usr/libexec/PlistBuddy -c 'print version' "${local_info_plist}")"
-
-[[ -n "${local_version}" ]] || abort 'You need to set a workflow version in the configuration sheet.'
-[[ "${download_type}" =~ ^(direct|page|github_release)$ ]] || abort "'download_type' (${download_type}) needs to be one of 'direct', 'page', or 'github_release'."
-[[ "${frequency_check}" =~ ^[0-9]+$ ]] || abort "'frequency_check' (${frequency_check}) needs to be a number."
-
-# Check for updates
-if [[ $(find "${local_info_plist}" -mtime +"${frequency_check}"d) ]]; then
- if ! url_exists "${remote_info_plist}"; then abort "'remote_info_plist' (${remote_info_plist}) appears to not be reachable."; fi # Remote sanity check
-
- readonly tmp_file="$(mktemp)"
- curl --silent --location --output "${tmp_file}" "${remote_info_plist}"
- readonly remote_version="$(/usr/libexec/PlistBuddy -c 'print version' "${tmp_file}")"
-
- if [[ "${local_version}" == "${remote_version}" ]]; then
- touch "${local_info_plist}" # Reset timer by touching local file
- exit 0
- fi
-
- if [[ "${download_type}" == 'page' ]]; then
- notification 'Opening download page…'
- open "${workflow_url}"
- exit 0
- fi
-
- download_url="$([[ "${download_type}" == 'github_release' ]] && curl --silent "https://api.github.com/repos/${workflow_url}/releases/latest" | grep 'browser_download_url' | head -1 | sed -E 's/.*browser_download_url": "(.*)"/\1/' || echo "${workflow_url}")"
-
- if url_exists "${download_url}"; then
- notification 'Downloading and installing…'
- curl --silent --location --output "${HOME}/Downloads/${alfred_workflow_name}.alfredworkflow" "${download_url}"
- open "${HOME}/Downloads/${alfred_workflow_name}.alfredworkflow"
- else
- abort "'workflow_url' (${download_url}) appears to not be reachable."
- fi
-fi
+ php -f ./workflow/lib/updater.php
scriptargtype
- 1
+ 0
scriptfile
type
@@ -1458,7 +1222,7 @@ fi
type
alfred.workflow.action.script
uid
- D310983D-CF37-4EA6-8557-05FCCF7AA40C
+ 60604A23-2820-4DAE-A371-1F9A83C0D7FC
version
2
@@ -1483,6 +1247,27 @@ fi
version
1
+
+ config
+
+ lastpathcomponent
+
+ onlyshowifquerypopulated
+
+ removeextension
+
+ text
+ {query}
+ title
+ Calculate Anything
+
+ type
+ alfred.workflow.output.notification
+ uid
+ 7DA07E01-89C1-4B84-B76F-E8916DACF72F
+ version
+ 1
+
readme
Calcualte Anything
@@ -1491,13 +1276,6 @@ https://github.com/biati-digital/alfred-calculate-anything
Made with love by https://www.biati.digital
uidata
- 01F84077-BA5D-47E6-B9BD-5E6993AB2F98
-
- xpos
- 160
- ypos
- 1280
-
02CB4726-DF90-475A-835E-0038EE019394
xpos
@@ -1526,10 +1304,17 @@ Made with love by https://www.biati.digital
ypos
1120
- 42330AA6-64D5-48A7-BCD2-0E63E0229052
+ 60604A23-2820-4DAE-A371-1F9A83C0D7FC
xpos
- 430
+ 425
+ ypos
+ 1815
+
+ 65F736EC-3B8C-4B6D-BE2C-837A63AC34A3
+
+ xpos
+ 160
ypos
1280
@@ -1547,12 +1332,12 @@ Made with love by https://www.biati.digital
ypos
765
- 73DD0CD0-28B0-4225-B29F-F7F44A44FAD0
+ 7DA07E01-89C1-4B84-B76F-E8916DACF72F
xpos
- 800
+ 610
ypos
- 1560
+ 1815
7E1D03F2-8A6E-4727-ACFB-0C5C77AA7BEE
@@ -1627,9 +1412,9 @@ Made with love by https://www.biati.digital
ABA18E77-71E3-494A-8735-A60799F419B9
xpos
- 430
+ 800
ypos
- 1405
+ 1345
BB2A4C00-3D9B-43BD-957F-AFF92B5A1DCB
@@ -1641,18 +1426,9 @@ Made with love by https://www.biati.digital
BD4CD916-8307-4C87-A3B3-3F55F2D2B2FF
xpos
- 795
- ypos
- 1825
-
- BD95F30F-133D-4304-B9C9-A537320590B3
-
- colorindex
- 8
- xpos
- 1085
+ 160
ypos
- 1365
+ 1815
CAF3C560-8129-4EED-9555-4A3D5F56DE79
@@ -1668,15 +1444,6 @@ Made with love by https://www.biati.digital
ypos
305
- D310983D-CF37-4EA6-8557-05FCCF7AA40C
-
- colorindex
- 8
- xpos
- 1080
- ypos
- 1825
-
DEEA362A-4FDA-454E-9BC6-57B0F3C9FC10
xpos
@@ -1685,10 +1452,8 @@ Made with love by https://www.biati.digital
1405
- variablesdontexport
-
version
- 1.0.7
+ 2.0.0
webaddress
https://github.com/biati-digital/alfred-calculate-anything
diff --git a/lang/en_EN-keys.php b/lang/en_EN-keys.php
index 45dc271..483322a 100644
--- a/lang/en_EN-keys.php
+++ b/lang/en_EN-keys.php
@@ -85,7 +85,7 @@
'yesterday' => '-1 day',
'workdays' => 'weekdays',
'workday' => 'weekdays',
-
+ 'wd' => 'weekdays',
'until' => ['until'],
'between' => ['between'],
'start of' => ['start of'],
@@ -141,6 +141,13 @@
'stop_words' => ['to', '=', 'in'],
],
+ 'crypto_currency' => [
+ 'bitcoins' => 'BTC',
+ 'ether' => 'ETH',
+ 'etherium' => 'ETH', //mispelled
+
+ 'stop_words' => ['to', '=', 'in'],
+ ],
'percentage' => [
'plus' => '+',
'minus' => '-',
diff --git a/lang/en_EN.php b/lang/en_EN.php
index 53bfc70..11fa3cc 100644
--- a/lang/en_EN.php
+++ b/lang/en_EN.php
@@ -7,10 +7,16 @@
'config' => [
'lang_title' => 'Set base language',
'lang_subtitle' => 'Configure the base language',
- 'currency_title' => 'Set base currency',
- 'currency_subtitle' => 'Set your base currency',
+ 'currency_title' => 'Add base currency',
+ 'currency_subtitle' => 'Set a base currency',
+ 'delete_currency_title' => 'Delete base currency',
+ 'delete_currency_subtitle' => 'Delete configured base currency',
+ 'enter_delete_base_currency' => 'Press enter to delete this currency',
+ 'empty_currency_formats' => 'There are no stored base currency',
'currency_locale_title' => 'Set currency locale',
'currency_locale_subtitle' => 'Used to give format to the value using your country format',
+ 'crypto_title' => 'Set Coinmarketcap API',
+ 'crypto_subtitle' => 'Configure your coinmarketcap API Key',
'fixer_title' => 'Set Fixer API',
'fixer_subtitle' => 'Configure your fixer API Key',
'measurement_title' => 'Set System of Measurement',
@@ -65,6 +71,8 @@
'until_subtitle' => '%s until %s',
'notvalid_subtitle' => 'Enter a valid date',
'format_subtitle' => 'Format %s',
+ 'cmd' => 'Action this item to copy this number to the clipboard',
+ 'alt' => 'Action this item to copy the amount with no format',
],
'vat' => [
'result' => 'VAT of %s = %s',
@@ -73,11 +81,15 @@
'plus_subtitle' => '%s plus %s of VAT',
'minus' => '%s minus VAT = %s',
'minus_subtitle' => '%s minus %s of VAT',
+ 'cmd' => 'Action this item to copy this number to the clipboard',
+ 'alt' => 'Action this item to copy the amount with no format',
],
'percentage' => [
'result' => '%s is %s of %s',
'increase' => 'The percentage increase from %s to %s is %s',
'decrease' => 'The percentage decrease from %s to %s is %s',
+ 'cmd' => 'Action this item to copy this number to the clipboard',
+ 'alt' => 'Action this item to copy the amount with no format',
],
'units' => [
'length' => 'Length',
@@ -173,7 +185,9 @@
'mev' => 'Mega Electron Volt',
'belongs_to' => '%s is a %s unit',
- 'error' => 'Error, you can not convert from %s to %s'
+ 'error' => 'Error, you can not convert from %s to %s',
+ 'cmd' => 'Action this item to copy this number to the clipboard',
+ 'alt' => 'Action this item to copy the amount with no format',
],
'currency' => [
'AED' => "United Arab Emirates dirham",
@@ -338,6 +352,15 @@
'ZMW' => "Zambian kwacha",
'ZWL' => "Zimbabwean dollar",
- "fetch_error" => "Unable to get currencies data"
+ 'cmd' => 'Action this item to copy this number to the clipboard',
+ 'alt' => 'Action this item to copy the amount with no format',
+ 'fetch_error' => 'Unable to get currencies data'
+ ],
+ 'crypto_currency' => [
+ 'cmd' => 'Action this item to copy this number to the clipboard',
+ 'alt' => 'Action this item to copy the amount with no format',
+ 'fetch_error' => 'Unable to get currencies data',
+ 'noapikey_title' => 'You need to confugure the API key for cryptocurrencies',
+ 'noapikey_subtitle' => 'Please read the documentation for more information',
]
];
diff --git a/lang/es_ES-keys.php b/lang/es_ES-keys.php
index 7a67182..34f9d90 100644
--- a/lang/es_ES-keys.php
+++ b/lang/es_ES-keys.php
@@ -61,12 +61,24 @@
],
'time' => [
'ahora' => 'now',
+ 'más ' => '+',
+ 'mas ' => '+',
'más' => '+',
'mas' => '+',
+ 'menos ' => '-',
'menos' => '-',
'mañana' => '+1 day',
'ayer' => '-1 day',
-
+ 'días' => 'days',
+ 'dias' => 'days',
+ 'día' => 'days',
+ 'dia' => 'days',
+ 'horas' => 'hours',
+ 'hora' => 'hour',
+ 'semanas' => 'weeks',
+ 'semana' => 'week',
+ 'años' => 'years',
+ 'año' => 'year',
'until' => ['hasta'],
'between' => ['entre'],
@@ -79,6 +91,7 @@
'pesos mexicanos' => 'MXN',
'pesos argentinos' => 'ARS',
'pesos cubanos' => 'CUP',
+ 'pesos' => 'MXN',
'dolares canadienses' => 'CAD',
'$m' => 'MXN',
'mxm' => 'MXN',
@@ -90,6 +103,13 @@
'stop_words' => ['a', '='],
],
+ 'crypto_currency' => [
+ 'bitcoins' => 'BTC',
+ 'ether' => 'ETH',
+ 'etherium' => 'ETH', //mispelled
+
+ 'stop_words' => ['a', '='],
+ ],
'percentage' => [
'más' => '+',
'mas' => '+',
diff --git a/lang/es_ES.php b/lang/es_ES.php
index 9e552a0..5899f34 100644
--- a/lang/es_ES.php
+++ b/lang/es_ES.php
@@ -7,10 +7,16 @@
'config' => [
'lang_title' => 'Definir idioma principal',
'lang_subtitle' => 'Configura el idioma principal',
- 'currency_title' => 'Definir típo de moneda',
+ 'currency_title' => 'Añadir típo de moneda',
'currency_subtitle' => 'Define tu típo de moneda por default',
+ 'delete_currency_title' => 'Eliminar típo de moneda',
+ 'delete_currency_subtitle' => 'Elimina típo de moneda configurado',
+ 'enter_delete_base_currency' => 'Presiona enter para eliminar el típo de moneda',
+ 'empty_currency_formats' => 'No hay típos de moneda configurados',
'currency_locale_title' => 'Configuración regional de moneda',
'currency_locale_subtitle' => 'Usada para dar formato a la cantidad usando el formato de tu país',
+ 'crypto_title' => 'Definir la API de Coinmarketcap',
+ 'crypto_subtitle' => 'Ingresa la API Key de Coinmarketcap',
'fixer_title' => 'Definir la API de Fixer',
'fixer_subtitle' => 'Ingresa la API Key de Fixer',
'measurement_title' => 'Definir Sistema de unidades',
@@ -63,6 +69,8 @@
'until_subtitle' => '%s faltantes hasta el %s',
'notvalid_subtitle' => 'Ingresa una fecha válida',
'format_subtitle' => 'Formato %s',
+ 'cmd' => 'Presiona enter para copiar el valor al clipboard',
+ 'alt' => 'Presiona enter para copiar el valor al clipboard sin formato',
],
'vat' => [
'result' => 'IVA de %s = %s',
@@ -71,11 +79,15 @@
'plus_subtitle' => '%s más %s de IVA',
'minus' => '%s menos IVA = %s',
'minus_subtitle' => '%s menos %s de IVA',
+ 'cmd' => 'Presiona enter para copiar el valor al clipboard',
+ 'alt' => 'Presiona enter para copiar el valor al clipboard sin formato',
],
'percentage' => [
'result' => '%s es %s de %s',
'increase' => 'El incremento en porcentaje de %s a %s es de %s',
'decrease' => 'El decremento en porcentaje de %s a %s es de %s',
+ 'cmd' => 'Presiona enter para copiar el valor al clipboard',
+ 'alt' => 'Presiona enter para copiar el valor al clipboard sin formato',
],
'units' => [
'length' => 'Length',
@@ -171,7 +183,9 @@
'mev' => 'Mega Electron Volt',
'belongs_to' => '%s pertenece a %s',
- 'error' => 'Error, no puedes convertir de %s a %s'
+ 'error' => 'Error, no puedes convertir de %s a %s',
+ 'cmd' => 'Presiona enter para copiar el valor al clipboard',
+ 'alt' => 'Presiona enter para copiar el valor al clipboard sin formato',
],
'currency' => [
'AED' => "Dirham Emiratos Arabes Unidos",
@@ -336,6 +350,15 @@
'ZMW' => "Kwacha Zambiano",
'ZWL' => "Dólar Zimbawense",
- "fetch_error" => "Error al importar la información de cambio"
+ 'cmd' => 'Presiona enter para copiar el valor al clipboard',
+ 'alt' => 'Presiona enter para copiar el valor al clipboard sin formato',
+ 'fetch_error' => 'Error al importar la información de cambio'
+ ],
+ 'crypto_currency' => [
+ 'cmd' => 'Presiona enter para copiar el valor al clipboard',
+ 'alt' => 'Presiona enter para copiar el valor al clipboard sin formato',
+ 'fetch_error' => 'Error al importar la información de cambio',
+ 'noapikey_title' => 'Debes de configurar la API key para cryptomonedas',
+ 'noapikey_subtitle' => 'Por favor lee la documentación para más información',
]
];
diff --git a/list.php b/list.php
index 6f977e4..baad8e8 100644
--- a/list.php
+++ b/list.php
@@ -8,8 +8,8 @@
* name, etc.
*/
-require_once(__DIR__ . '/units.php');
-require_once(__DIR__ . '/currency.php');
+require_once __DIR__ . '/workflow/lib/functions.php';
+require_once __DIR__ . '/workflow/calculateanything.php';
$list = getenv('list');
$response = [];
@@ -21,6 +21,12 @@
'valid' => true,
'arg' => 'currency',
];
+ $response[] = [
+ 'title' => 'List Available Cryptocurrencies',
+ 'subtitle' => 'Display the list of cryptocurrencies',
+ 'valid' => true,
+ 'arg' => 'cryptocurrency',
+ ];
$response[] = [
'title' => 'List Available Units',
'subtitle' => 'Display the list of units',
@@ -31,8 +37,10 @@
exit(0);
}
-$items = ($list == 'units' ? get_units_list() : []);
-$items = ($list == 'currency' ? get_currencies_list() : $items);
+$calculate = new Workflow\CalculateAnything();
+$items = ($list == 'units' ? $calculate->getCalculator('units')->listAvailable() : []);
+$items = ($list == 'currency' ? $calculate->getCalculator('currency')->listAvailable() : $items);
+$items = ($list == 'cryptocurrency' ? $calculate->getCalculator('cryptocurrency')->listAvailable() : $items);
foreach ($items as $item) {
$response[] = $item;
diff --git a/percentages.php b/percentages.php
deleted file mode 100644
index df42084..0000000
--- a/percentages.php
+++ /dev/null
@@ -1,148 +0,0 @@
- percentage_of($query), 'process' => true];
- }
-
- // Total plus percentage
- // 100 + 16% = 116
- elseif (preg_match('/^\d*\.?\d+ ?\+ ?\d*\.?\d*%$/', $query, $matches)) {
- return ['value' => total_plus_percentage($query), 'process' => true];
- }
-
- // Total minus percentage
- // 116 - 16% = 100
- elseif (preg_match('/^\d*\.?\d+ ?- ?\d*\.?\d*%$/', $query, $matches)) {
- return ['value' => total_minus_percentage($query), 'process' => true];
- }
-
- // Calculates `a` percent of `b` is what percent?
- // 30 % 40 = 75%
- // So 30 is 75% of 40.
- elseif (preg_match('/^\d+ +?\%.+?\d+/', $query, $matches)) {
- return ['value' => percent_of_two_numbers($query), 'process' => true];
- }
-
- return false;
-}
-
-
-function percentage_of($query)
-{
- $query = preg_replace("/[^0-9.%]/", ' ', $query);
- $query = preg_replace('!\s+!', ' ', $query);
- $data = explode(' ', $query);
-
- if (count($data) < 2) {
- return false;
- }
- $percent = cleanup_number($data[0]);
- $amount = cleanup_number($data[1]);
-
- return format_number(($percent / 100) * $amount);
-}
-
-
-function total_plus_percentage($query)
-{
- $query = preg_replace('/\s+/', '', $query);
- $query = preg_replace("/ +?\+ +?/", ' ', $query);
- $data = explode('+', $query);
-
- if (count($data) < 2) {
- return false;
- }
-
- $amount = cleanup_number($data[0]);
- $percent = cleanup_number($data[1]);
-
- return format_number($amount + (($percent / 100) * $amount));
-}
-
-
-function total_minus_percentage($query)
-{
- $query = preg_replace('/\s+/', '', $query);
- $data = explode('-', $query);
-
- if (count($data) < 2) {
- return false;
- }
-
- $amount = cleanup_number($data[0]);
- $percent = cleanup_number($data[1]);
- $percent_min = ($percent / 100) * $amount;
-
- $result = $percent == 100 ? '0.00' : format_number($amount - $percent_min);
- $saved = $amount - cleanup_number($result);
- $famount = format_number($amount);
- $saved = format_number($saved);
-
- $values = [];
- $values[$result] = $famount .' - '. $data[1] .' = '. $result;
- $values[$saved] = $famount .' - '. $result .' = '. $saved;
- return $values;
-}
-
-
-function percent_of_two_numbers($query)
-{
- $query = preg_replace("/ +?\% +?/", ' ', $query);
- $query = preg_replace('!\s+!', ' ', $query);
- $data = explode(' ', $query);
-
- if (count($data) < 2) {
- return false;
- }
-
- $val1 = cleanup_number($data[0]);
- $val2 = cleanup_number($data[1]);
- $percentage = ($val1 / $val2) * 100;
- $percentage = format_number($percentage);
-
- $pincrease = ($val2 - $val1) / $val1 * 100;
- $pincrease = format_number($pincrease);
-
- $pdecrease = ($val2 - $val1) / $val2 * 100;
- $pdecrease = format_number($pdecrease);
- $lang_strings = LANG_STRINGS['percentage'];
-
- $values = [];
- $values["{$percentage}%"] = sprintf($lang_strings['result'], $val1, "{$percentage}%", $val2);
- $values["{$pincrease}%"] = sprintf($lang_strings['increase'], $val1, $val2, "{$pincrease}%");
- $values["{$pdecrease}%"] = sprintf($lang_strings['decrease'], $val2, $val1, "{$pdecrease}%");
-
- return $values;
-}
diff --git a/process.php b/process.php
index 6635b36..477f80a 100644
--- a/process.php
+++ b/process.php
@@ -2,96 +2,39 @@
/**
* Process
- * init file when processing
- * units, currency and percentages conversion
- * time and tax has their own files
*
- * Queries will only be processed it
- * it has at least 3 characters
+ * Copyright (c) 2020 biati digital
+ * This software is released under the MIT License.
+ * https://opensource.org/licenses/MIT
+ *
+ * @author biati digital
+ * @version 2.0.0
+ * @create date 12-11-2019
+ * @modify date 13-05-2020
*/
-require_once(__DIR__ . '/units.php');
-require_once(__DIR__ . '/currency.php');
-require_once(__DIR__ . '/percentages.php');
-require_once(__DIR__ . '/px-em-rem.php');
-require_once(__DIR__ . '/functions.php');
-
-$query = clean_query(get_var($argv, 1));
-
-if (strlen($query) < 3) {
- echo '{"items": []}';
- exit(0);
-}
-
-$lang = get_translation();
-$keywords = get_extra_keywords();
-
-define('LANG_STRINGS', $lang);
-define('LANG_KEYWORDS', $keywords);
-
-$result = '...';
-$icon = 'icon.png';
-$matches = [];
-$process = false;
-$value = false;
-$response = [];
-$should_check = strlen($query) >= 3;
-
-if ($should_check && strpos($query, '%') !== false) {
- $data = process_percentages($query);
- if ($data) {
- $value = $data['value'];
- $process = $data['process'];
- }
-}
-
-elseif ($should_check && is_unit($query)) {
- $value = process_unit_conversion($query);
- $process = true;
-}
-
-elseif (is_pxemrem($query)) {
- $value = process_pxemrem($query);
- $process = true;
-}
-
-elseif (strlen($query) >= 3 && is_currency($query)) {
- $value = process_currency_conversion($query);
- $process = true;
-
- if (is_array($value)) {
- $icon = 'flags/' . $value['currency'] . '.png';
- $value = $value['data'];
- }
-}
-
-$response[] = [
- 'title' => ($value ? $value : $result),
- 'subtitle' => get_text('action_copy'),
- 'arg' => ($value ? $value : $result),
- 'valid' => ($value ? true : false),
- 'icon' => [
- 'path' => $icon
- ]
-];
-
-if ($value && is_array($value)) {
- $response = [];
- foreach ($value as $key => $val) {
- $response[] = [
- 'title' => $val,
- 'subtitle' => get_text('action_copy'),
- 'arg' => $key,
- 'icon' => [
- 'path' => $icon
- ]
- ];
- }
+require_once __DIR__ . '/workflow/lib/functions.php';
+require_once __DIR__ . '/workflow/calculateanything.php';
+
+$query = cleanQuery(getVar($argv, 1));
+$action = getVar($argv, 2);
+$calculate = new Workflow\CalculateAnything($query);
+
+if (!empty($action)) {
+ if ($action == 'vat') {
+ $processed = $calculate->processVat();
+ }
+ if ($action == 'time') {
+ $processed = $calculate->processTime();
+ }
+} else {
+ $processed = $calculate->processQuery();
}
-if ($process) {
- echo '{"items": ' . json_encode($response).' }';
- exit(0);
+if ($processed) {
+ echo '{"items": ' . json_encode($processed) . ' }';
+ exit(0);
}
echo '{"items": []}';
+exit(0);
diff --git a/px-em-rem.php b/px-em-rem.php
deleted file mode 100644
index fb886e5..0000000
--- a/px-em-rem.php
+++ /dev/null
@@ -1,148 +0,0 @@
-= 3) {
- $from = cleanup_number($matches[1]);
- $from_unit = trim($matches[2]);
- }
-
- if ($total_inputs == 4) {
- if (strpos($matches[3], 'base') !== false) {
- $base = preg_replace('/[^0-9]/', '', $matches[3]);
- $base = cleanup_number($base);
- }
- else {
- $target = trim($matches[3]);
- }
- }
- elseif ($total_inputs == 5) {
- $target = trim($matches[3]);
- $base = preg_replace('/[^0-9]/', '', $matches[4]);
- $base = cleanup_number($base);
- }
-
- $result = [];
- $units = ['px', 'em', 'rem', 'pt'];
- $data = [
- 'from' => $from,
- 'from_unit' => $from_unit,
- 'to' => $target,
- 'base' => $base,
- ];
-
- if (empty($target)) {
- if (($key = array_search($from_unit, $units)) !== false) {
- unset($units[$key]);
- }
- foreach ($units as $key => $value) {
- $data['to'] = $value;
- $val = calculate_font_size($data);
- $result[$val] = $val;
- }
- } else {
- $val = calculate_font_size($data);
- $result[$val] = $val;
- }
-
- return $result;
-}
-
-
-/**
- * Do font calculations
- *
- * @param array $data
- * @return string
- */
-function calculate_font_size($data)
-{
- $result = 0;
- $from = $data['from'];
- $from_unit = $data['from_unit'];
- $to = $data['to'];
- $base = $data['base'];
- $emrem = ['em', 'rem'];
- $pt = 0.75;
-
- // exit if no required action
- if ($from_unit == $to || (in_array($from_unit, $emrem) && in_array($to, $emrem))) {
- return $from . $to;
- }
-
- // from px
- if ($from_unit == 'px') {
- if ($to == 'pt') {
- return ($from * $pt) . $to;
- }
- if (in_array($to, $emrem)) {
- return ($from / $base) . $to;
- }
- }
-
- // from pt
- if ($from_unit == 'pt') {
- if ($to == 'px') {
- return ($from / $pt) . $to;
- } elseif (in_array($to, $emrem)) {
- return (($from / $pt) / $base) . $to;
- }
- }
-
- // from em/rem
- if (in_array($from_unit, $emrem)) {
- if ($to == 'px') {
- return ($from * $base) . $to;
- }
- if ($to == 'pt') {
- return (($from * $base) * $pt) . $to;
- }
- }
-
- return $result;
-}
diff --git a/time.php b/time.php
deleted file mode 100644
index 4fb7890..0000000
--- a/time.php
+++ /dev/null
@@ -1,489 +0,0 @@
-= ~PHP_INT_MAX);
-}
-
-if (!empty($query) && is_timestamp($query)) {
- $query = (int) $query;
-}
-
-
-/**
- * Get date instance
- * return a date instance with
- * the specified time
- *
- * @param string $time
- * @return object
- */
-function get_date($time = 'now')
-{
- $d = false;
- try {
- $d = new Date($time, new DateTimeZone(TIME_ZONE));
- } catch (\Throwable $th) {
- throw $th;
- }
- return $d;
-}
-
-
-/**
- * Time diffrence
- * get the time difference between
- * to dates in the specified format
- *
- * @param string $time1
- * @param string $time2
- * @param string $format
- * @return string
- */
-function times_difference($time1, $time2, $format = 'hours')
-{
- $time1 = new Date($time1, new DateTimeZone(TIME_ZONE));
- $time2 = new Date($time2, new DateTimeZone(TIME_ZONE));
-
- if ($format == 'days') {
- $diff_hours = $time1->diffInHours($time2);
- if ($diff_hours < 24) {
- return $diff_hours;
- }
-
- return round($diff_hours / 24);
- }
- if ($format == 'hours') {
- return $time1->diffInHours($time2);
- }
- if ($format == 'minutes') {
- return $time1->diffInMinutes($time2);
- }
- if ($format == 'seconds') {
- return $time1->diffInSeconds($time2);
- }
- if ($format == 'milliseconds') {
- return $time1->diffInMilliseconds($time2);
- }
- if ($format == 'microseconds') {
- return $time1->diffInMicroseconds($time2);
- }
-}
-
-/**
- * Translate on begin
- * given a query like
- * +30 días
- * it need to be converted to
- * +30 days so the code can understand it
- *
- * @param string $query
- * @return string
- */
-function translate_date($query)
-{
- $query = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $query);
- $strs = STRINGS;
-
- if (is_numeric($query) || empty($strs)) {
- return $query;
- }
-
- $query = mb_strtolower($query, 'UTF-8');
- $keys = get_extra_keywords('time');
- foreach ($keys as $k => $value) {
- if (is_array($value)) {
- continue;
- }
- $query = str_replace($k, $value, $query);
- }
-
- $query = str_replace(
- array_values($strs),
- array_keys($strs),
- $query
- );
-
- return $query;
-}
-
-
-/**
- * Time keywords
- * some keywords used to
- * trigger diferent actions
- *
- * @param string $check
- * @return mixed
- */
-function time_keywords($check = '')
-{
- $data = [
- 'until' => [
- 'hasta'
- ],
- 'between' => [
- 'entre'
- ],
- 'start of' => [
- 'inicio de'
- ],
- 'end of' => [
- 'fin de'
- ],
- ];
-
- if (isset($data[$check])) {
- return $data[$check];
- }
-
- return $data;
-}
-
-
-/**
- * Regex
- * create a regex based on the keywords
- *
- * @param string $check
- * @return string
- */
-function available_time_keywords_regex($check)
-{
- $keys = time_keywords($check);
- $params = implode('|', array_values($keys));
- return '('. $check.'|' . $params . ')';
-}
-
-/**
- * Time Until
- * check if query ia a until query
- *
- * @param string $time
- * @return mixed
- */
-function date_is_untill($time)
-{
- $keys = available_time_keywords_regex('until');
- preg_match('/\w+ ' . $keys . ' .*/i', $time, $matches);
-
- if (!$matches || empty($matches)) {
- return false;
- }
-
- $time = preg_replace('/ ' . $keys . ' /i', '|', $time);
- $time = explode('|', $time);
- $time = array_filter($time);
-
- $k = (isset($time[0]) && !empty($time[0]) ? $time[0] : false);
- $date = (isset($time[1]) && !empty($time[1]) ? $time[1] : false);
- $valid_k = ['days', 'day', 'hours', 'hour', 'minutes', 'minutes', 'seconds', 'second'];
-
- if (!in_array($k, $valid_k) || !$date) {
- return false;
- }
-
- return ['get' => $k, 'time' => $date];
-}
-
-/**
- * Time end of
- * check if query ia a until query end
- *
- * @param string $time
- * @return mixed
- */
-function date_is_endof($time)
-{
- $keys = available_time_keywords_regex('end of');
- preg_match('/^' . $keys . ' .*/i', $time, $matches);
-
- if (!$matches || empty($matches)) {
- return false;
- }
-
- $time = preg_replace('/^' . $keys . '/i', '', $time);
- $time = trim($time);
- if ($time == 'year') {
- $year = get_date()->format('Y');
- $time = $year . '-12-31 23:59:59';
- }
- elseif (is_numeric($time) && strlen($time) == 4) {
- $time = $time . '-12-31 23:59:59';
- }
- return $time;
-}
-
-/**
- * Time start of
- * check if query ia a until query start
- *
- * @param string $time
- * @return mixed
- */
-function date_is_startof($time)
-{
- $keys = available_time_keywords_regex('start of');
- preg_match('/^' . $keys . ' .*/i', $time, $matches);
-
- if (!$matches || empty($matches)) {
- return false;
- }
-
- $time = preg_replace('/^' . $keys . '/i', '', $time);
- $time = trim($time);
- if ($time == 'year') {
- $time = 'first day of January this year';
- }
- else {
- if (is_numeric($time) && strlen($time) == 4) {
- $time = $time.'-01-01';
- }
- }
-
- return $time;
-}
-
-
-/**
- * Process
- * Process the query and perform
- * the correct action
- *
- * @param string $time
- * @return mixed array if success or false if not processed
- */
-function process_time($query)
-{
- Date::setLocale(LANGUAGE);
-
- // From user lang to en_us so time conversion
- // is able to understand some words
- $query = translate_date($query);
- $strings = STRINGS;
-
- // handle two dates like: 25 December, 2019 - 31 December, 2019
- if (strpos($query, ' - ') !== false) {
- $data = str_replace(' - ', '|', $query);
- $data = explode('|', $data);
- $data = array_filter($data);
-
- if (count($data) == 2) {
- $time1 = get_date(trim($data[0]));
- $time2 = get_date(trim($data[1]));
- $subtitle = sprintf($strings['difference_subtitle'], $time1, $time2);
-
- if ($time1 && $time2) {
- return [
- 'title' => $time1->timespan($time2),
- 'val' => $time1->timespan($time2),
- 'subtitle' => $subtitle,
- ];
- }
-
- return false;
- }
- }
-
- // Handle Until
- elseif ($until = date_is_untill($query)) {
- $utime = $until['time'];
- $get_tr = $until['get'];
- $check = times_difference('now', $utime, $until['get']);
- $title = $check . ' ' . $get_tr;
- $subtitle = sprintf($strings['until_subtitle'], $get_tr, $utime);
-
- $title = str_replace(
- array_keys($strings),
- array_values($strings),
- $title
- );
-
- $subtitle = str_replace(
- array_keys($strings),
- array_values($strings),
- $subtitle
- );
-
- if ($check) {
- return [
- 'title' => $title,
- 'val' => $check,
- 'subtitle' => $subtitle,
- ];
- }
-
- return false;
- }
-
- // Handle End of
- elseif ($endof = date_is_endof($query)) {
- $end = get_date($endof);
- if ($end) {
- return [
- 'instance' => $end
- ];
- }
-
- return false;
- }
-
- // Handle Start of
- elseif ($startof = date_is_startof($query)) {
- $start = get_date($startof);
- if ($start) {
- return [
- 'instance' => $start
- ];
- }
-
- return false;
- }
-
- elseif (is_timestamp($query)) {
- $query = (int) $query;
- }
-
- $processed = get_date($query);
- if ($processed) {
- return [
- 'instance' => $processed
- ];
- }
-
- return false;
-}
-
-
-
-$processed_date = process_time($query);
-if (!$processed_date) {
- $response[] = [
- "title" => '...',
- "subtitle" => $strings['notvalid_subtitle'],
- "valid" => false,
- ];
- echo '{"items": ' . json_encode($response) . ' }';
- exit(0);
-}
-
-$instance = (isset($processed_date['instance']) ? $processed_date['instance'] : false);
-
-if (!$instance) {
- $response[] = [
- "title" => $processed_date['title'],
- "subtitle" => $processed_date['subtitle'],
- "arg" => $processed_date['val'],
- "mods" => [
- "cmd" => [
- "valid" => true,
- "arg" => $processed_date['val'],
- "subtitle" => "Action this item to copy this value to the clipboard",
- ]
- ]
- ];
- echo '{"items": ' . json_encode($response) . ' }';
- exit(0);
-}
-
-
-$formats = get_setting('timezones', '', $settings);
-if (empty($formats)) {
- $formats = ['F jS, Y, g:i:s a'];
-}
-
-foreach ($formats as $key => $format) {
- $date = $instance->format($format);
- $response[] = [
- "title" => $date,
- "subtitle" => sprintf($strings['format_subtitle'], $format),
- "arg" => $date,
- "mods" => [
- "cmd" => [
- "valid" => true,
- "arg" => $date,
- "subtitle" => "Action this item to copy this value to the clipboard",
- ]
- ]
- ];
-}
-
-if ($query !== 'now') {
- $now = Date::now();
- if ($now > $instance) {
- $count = $instance->ago();
- } else {
- $count = $instance->timespan($now);
- }
-
- $response[] = [
- "title" => $count,
- "subtitle" => "Timespan",
- "arg" => $count,
- "mods" => [
- "cmd" => [
- "valid" => true,
- "arg" => $count,
- "subtitle" => "Action this item to copy this value to the clipboard",
- ]
- ]
- ];
-}
-
-
-$response[] = [
- "title" => $instance->getTimestamp(),
- "subtitle" => "Timestamp",
- "arg" => $instance->getTimestamp(),
- "mods" => [
- "cmd" => [
- "valid" => true,
- "arg" => $instance->getTimestamp(),
- "subtitle" => "Action this item to copy this value to the clipboard",
- ]
- ]
-];
-
-echo '{"items": ' . json_encode($response) . ' }';
diff --git a/units.php b/units.php
deleted file mode 100644
index 00e7994..0000000
--- a/units.php
+++ /dev/null
@@ -1,487 +0,0 @@
- [
- 'm',
- 'km',
- 'dm',
- 'cm',
- 'mm',
- 'μm',
- 'nm',
- 'pm',
- 'in',
- 'ft',
- 'yd',
- 'mi',
- 'h',
- 'ly',
- 'au',
- 'pc',
- ],
- 'area' => [
- 'm2',
- 'km2',
- 'cm2',
- 'mm2',
- 'ft2',
- 'mi2',
- 'ac',
- 'ha',
- 'ha',
- ],
- 'volume' => [
- 'dm3',
- 'l',
- 'ml',
- 'cm3',
- 'hl',
- 'kl',
- 'm3',
- 'pt',
- 'gal',
- 'qt',
- 'ft3',
- 'in3',
- ],
- 'weight' => [
- 'kg',
- 'g',
- 'mg',
- 'N',
- 'st',
- 'lb',
- 'oz',
- 't',
- 'ukt',
- 'ust',
- ],
- 'speed' => [
- 'mps',
- 'kph',
- 'mph',
- 'fps',
- ],
- 'rotation' => [
- 'deg',
- 'rad',
- ],
- 'temperature' => [
- 'k',
- 'c',
- 'f',
- ],
- 'pressure' => [
- 'pa',
- 'kpa',
- 'mpa',
- 'bar',
- 'mbar',
- 'psi',
- ],
- 'time' => [
- 's',
- 'year',
- 'month',
- 'week',
- 'day',
- 'hr',
- 'min',
- 'ms',
- 'μs',
- 'ns',
- ],
- 'energy' => [
- 'j',
- 'kj',
- 'mj',
- 'cal',
- 'Nm',
- 'ftlb',
- 'whr',
- 'kwhr',
- 'mwhr',
- 'mev',
- ],
- ];
-}
-
-
-/**
- * Get units list
- * get a readable units list
- * to display to the user
- *
- * @return array
- */
-function get_units_list()
-{
- $translation = get_translation('units');
- $units = available_units();
- $list = [];
- foreach ($units as $key => $value) {
- $type = $key;
- $type_name = (isset($translation[$type]) ? $translation[$type] : $type);
-
- foreach ($value as $val) {
- $unit = $val;
- $unit_name = (isset($translation[$val]) ? $translation[$val] : $val);
-
- $list[] = [
- 'title' => "$unit_name = $val",
- 'subtitle' => $key,
- 'subtitle' => sprintf($translation['belongs_to'], $val, $type_name),
- 'match' => $val . ' ' . $unit_name,
- 'autocomplete' => $unit_name,
- 'arg' => $val,
- 'valid' => true,
- 'mods' => [
- 'cmd' => [
- 'valid' => true,
- 'arg' => $val,
- 'subtitle' => get_text('action_copy'),
- ]
- ],
- ];
- }
- }
-
- return $list;
-}
-
-
-/**
- * Regex
- * get a scaped regex based on
- * the available units
- *
- * @return string
- */
-function available_units_regex()
-{
- $units = available_units();
- $params = [];
- foreach ($units as $key => $value) {
- $params[] = implode(' |', $value);
- }
-
- $translated_units = translated_units();
- $params[] = implode(' |', array_keys($translated_units));
-
- $params = implode('|', $params);
- $params = str_replace('$', '\$', $params);
- $params = str_replace('/', '\/', $params);
- $params = str_replace('.', '\.', $params);
-
- return '(' . $params . ')';
-}
-
-
-/**
- * is unit
- * check if given string is unit
- *
- * @param string $query
- * @return boolean
- */
-function is_unit($query)
-{
- $units = available_units_regex();
- $stopwords = unit_stopwords();
- $query = str_replace(',', '', $query);
- return preg_match('/^\d*\.?\d+ ?' . $units . ' ?' . $stopwords . '?/i', $query, $matches);
-}
-
-
-/**
- * Check if unit is valid
- *
- * @param string $unit
- * @return boolean
- */
-function is_valid_unit($unit)
-{
- $units = available_units();
- $found = false;
- foreach ($units as $key => $value) {
- if (in_array($unit, $value)) {
- $found = true;
- break;
- }
- }
- return $found;
-}
-
-
-/**
- * Unit stop words
- * words that can be used in the query
- * when using natural languge like
- * 100km to cm - here the word "to" is a stop word
- *
- * @param mixed $sep
- * @return string
- */
-function unit_stopwords($sep = false)
-{
- $keys = get_extra_keywords('units');
- $stop_words = get_stopwords_string($keys['stop_words'], $sep);
-
- return $stop_words;
-}
-
-
-/**
- * Unit type
- * return the type of the unit
- * for example km = length, kph = speed, etc.
- *
- * @param string $unit
- * @return mixed string if found
- */
-function get_unit_type($unit)
-{
- $unit = str_replace('**', '', $unit);
- $units = available_units();
- $found = false;
- foreach ($units as $key => $value) {
- if (in_array($unit, $value)) {
- $found = $key;
- break;
- }
- }
-
- return $found;
-}
-
-
-/**
- * Process
- * process conversion
- *
- * @param string $query
- * @return mixed
- */
-function process_unit_conversion($query)
-{
- $stopwords = unit_stopwords(' %s ');
- // $regex = available_units_regex();
- $query = str_replace(',', '', $query);
-
- preg_match('/^(\d*\.?\d+)[^\d]/i', $query, $amount_match);
- if (empty($amount_match)) {
- return false;
- }
-
- $amount = get_var($amount_match, 1);
- $amount = trim($amount);
- $string = str_replace($amount, '', $query);
- $string = trim($string);
-
- preg_match('/(.*).*' . $stopwords . '(.*)/i', $string, $matches);
-
- // Matches strings like 100 kilograms to ounces
- if (!empty($matches)) {
- $matches = array_values(array_filter($matches));
- $from = get_var($matches, 1);
- $to = get_var($matches, 3);
- }
-
- elseif (empty($matches)) {
- $keywords = get_extra_keywords('units');
-
- foreach ($keywords as $key => $value) {
- if (is_array($value)) {
- continue;
- }
- $key = escape_units_keywords($key);
- $string = preg_replace('/(^|\W)' . $key . '(\W|$)/i', ' ' . $value . ' ', $string);
- }
-
- $string = preg_replace('!\s+!', ' ', $string);
- $string = trim($string);
- $data = explode(' ', $string);
- $from = get_var($data, 0);
- $to = get_var($data, 1);
- }
-
- if (empty($from) || empty($to)) {
- return false;
- }
-
- if (empty($from) || empty($to)) {
- return false;
- }
-
- return make_unit_conversion([
- 'from_amount' => cleanup_number($amount),
- 'from_unit' => cleanup_unit($from),
- 'to' => cleanup_unit($to),
- ]);
-}
-
-
-/**
- * Make actual conversion
- *
- * @param array $data
- * @return mixed
- */
-function make_unit_conversion($data)
-{
- $from_amount = $data['from_amount'];
- $from_unit = $data['from_unit'];
- $to = $data['to'];
- $from_unit_type = get_unit_type($from_unit);
- $to_unit_type = get_unit_type($to);
-
- if (empty($from_unit_type) || empty($to_unit_type)) {
- return false;
- }
-
- if ($to_unit_type !== $from_unit_type) {
- $units_str = get_translation('units');
- return sprintf($units_str['error'], standard_unit($from_unit), standard_unit($to));
- }
-
- if ($from_unit == 'year' && $to == 'month') {
- $converted = $from_amount * 12;
- } else {
- $conversion_error = false;
- try {
- $convert = new Convertor($from_amount, $from_unit);
- $converted = $convert->to($to);
- } catch (\Throwable $th) {
- $conversion_error = $th->getMessage();
- }
-
- if ($conversion_error) {
- return $conversion_error;
- }
- }
-
- $decimals = -1;
- if ($from_unit_type == 'temperature') {
- $decimals = 1;
- }
-
- // Before displaying the result
- // Convert some units to readable human form
- if ($from_unit_type == 'time') {
- $time_human_units = ['seconds', 'years', 'months', 'weeks', 'days', 'hours', 'minutes', 'milliseconds'];
- if ($converted > 1) {
- $to = str_replace(
- ['s', 'year', 'month', 'week', 'day', 'hr', 'min', 'ms'],
- $time_human_units,
- $to
- );
- }
- $strings = get_translation('time');
- if (is_array($strings) && isset($strings[$to])) {
- $to = $strings[$to];
- }
- $to = ' ' . $to;
- }
-
- return format_number($converted, $decimals) . standard_unit($to);
-}
-
-
-
-/**
- * Clean unit
- * clean up the unit
- *
- * @param string $val
- * @return string
- */
-function cleanup_unit($val)
-{
- $unit = trim($val);
- $unit = translated_units($unit);
-
- return $unit;
-}
-
-
-/**
- * Translated units
- * Convert some keywords to
- * the actual unit so you can use
- * natual language to make conversions
- *
- * For example:
- * 100 kilometers to meters
- * Will be converted to
- * 100km to m
- *
- * Still the user can be able
- * to write 100hr to s
- *
- * The keywords list can be found in
- * /lang/{lang}-keys.php
- *
- * @param boolean $unit
- * @return array
- */
-function translated_units($unit = false)
-{
- $keywords = get_extra_keywords('units');
- if (!$unit) {
- return $keywords;
- }
-
- if (!is_valid_unit($unit)) {
- foreach ($keywords as $key => $value) {
- if (is_array($value)) {
- continue;
- }
- $key = escape_currency_keywords($key);
- $unit = preg_replace('/(^|\W)' . $key . '(\W|$)/i', ' ' . $value . ' ', $unit);
- }
- }
-
- $unit = trim($unit);
- $unit = preg_replace('!\s+!', ' ', $unit);
- if (ends_with($unit, '2')) {
- $unit = str_replace('2', '**2', $unit);
- }
-
- return $unit;
-}
-
-
-function standard_unit($unit)
-{
- return str_replace('**', '', $unit);
-}
-
-
-function escape_units_keywords($key)
-{
- $key = str_replace('$', '\$', $key);
- $key = str_replace('/', '\/', $key);
- $key = str_replace('.', '\.', $key);
- return $key;
-}
diff --git a/vat.php b/vat.php
deleted file mode 100644
index 1644611..0000000
--- a/vat.php
+++ /dev/null
@@ -1,66 +0,0 @@
- 0) {
- $processed = true;
- $plustaxt = $amount + $result;
- $minustax = $amount / ((float) "1.$percent");
-
- $amount = format_number($amount);
- $result = format_number($result);
- $plustaxt = format_number($plustaxt, -1, true);
- $minustax = format_number($minustax, -1, true);
- }
-}
-
-if (!$processed) {
- $response[] = [
- 'title' => $result,
- 'subtitle' => get_text('action_copy'),
- 'valid' => false,
- ];
- echo '{"items": ' . json_encode($response) . ' }';
- exit(0);
-}
-
-$response[] = [
- 'title' => sprintf($strings['result'], $amount, $result),
- 'subtitle' => sprintf($strings['subtitle'], "{$percent}%"),
- 'arg' => $result,
-];
-
-$response[] = [
- 'title' => sprintf($strings['plus'], $amount, $plustaxt),
- 'subtitle' => sprintf($strings['plus_subtitle'], $amount, "{$percent}%"),
- 'arg' => $plustaxt,
-];
-
-$response[] = [
- 'title' => sprintf($strings['minus'], $amount, $minustax),
- 'subtitle' => sprintf($strings['minus_subtitle'], $amount, "{$percent}%"),
- 'arg' => $minustax,
-];
-
-echo '{"items": ' . json_encode($response).' }';
diff --git a/workflow/calculateanything.php b/workflow/calculateanything.php
new file mode 100644
index 0000000..a66b9a4
--- /dev/null
+++ b/workflow/calculateanything.php
@@ -0,0 +1,499 @@
+getQuery();
+ $lenght = strlen($query);
+
+ // For all calculators that do not require a keyword
+ // the passed keyword must have at leats 3 characters
+ // being the first one a number
+ if ($lenght < 3 || !is_numeric($query[0])) {
+ return false;
+ }
+
+ self::$percentageCalculator = new Percentage($query);
+ self::$currencyCalculator = new Currency($query);
+ self::$cryptocurrencyCalculator = new Cryptocurrency($query);
+ self::$pxemremCalculator = new PXEmRem($query);
+ self::$unitsCalculator = new Units($query);
+
+ // Process query
+ $processed = $this->processByType();
+
+ if (!empty($processed)) {
+ workflowUpdater();
+ }
+
+ return $processed;
+ }
+
+ /**
+ * Process the query
+ * checking before if it's type
+ * is supported
+ *
+ * @return mixed
+ */
+ private function processByType()
+ {
+ $query = $this->getQuery();
+ $lenght = strlen($query);
+ $percentages = self::$percentageCalculator;
+ $cryptocurrency = self::$cryptocurrencyCalculator;
+ $currency = self::$currencyCalculator;
+ $pxemrem = self::$pxemremCalculator;
+ $units = self::$unitsCalculator;
+ $processed = [];
+
+ if ($percentages->shouldProcess($lenght)) {
+ $processed = $percentages->processQuery();
+ }
+
+ if ($pxemrem->shouldProcess($lenght)) {
+ $processed = $pxemrem->processQuery();
+ }
+
+ if ($units->shouldProcess($lenght)) {
+ $processed = [$units->processQuery()];
+ }
+
+ if ($cryptocurrency->shouldProcess($lenght)) {
+ $processed = $cryptocurrency->processQuery();
+ }
+
+ if ($currency->shouldProcess($lenght)) {
+ $processed = $currency->processQuery();
+ }
+
+ return $processed;
+ }
+
+
+ /**
+ * Process VAt
+ * handle vat calculations
+ *
+ * @return array|bool
+ */
+ public function processVat()
+ {
+ $vatCalculator = new Vat(self::$_query);
+ $data = $vatCalculator->processQuery();
+
+ return $data;
+ }
+
+ /**
+ * Process Time
+ * handle time calculations
+ *
+ * @return array|bool
+ */
+ public function processTime()
+ {
+ $timeCalculator = new Time(self::$_query);
+ $data = $timeCalculator->processQuery();
+
+ return $data;
+ }
+
+
+ /**
+ * Return a new instance
+ * of a calclulator
+ *
+ * @param string $id
+ * @param string $query
+ */
+ public function getCalculator($id, $query = '')
+ {
+ $calculator = false;
+ switch ($id) {
+ case 'percentage':
+ $calculator = new Percentage($query);
+ break;
+ case 'currency':
+ $calculator = new Currency($query);
+ break;
+ case 'cryptocurrency':
+ $calculator = new Cryptocurrency($query);
+ break;
+ case 'units':
+ $calculator = new Units($query);
+ break;
+ case 'pxemrem':
+ $calculator = new PXEmRem($query);
+ break;
+ default:
+ # code...
+ break;
+ }
+
+ return $calculator;
+ }
+
+
+ /**
+ * Translations
+ * get workflow translations
+ *
+ * @param string $key
+ * @return array
+ */
+ public function getTranslation(string $key = ''): array
+ {
+ return self::$translations[$key];
+ }
+
+ /**
+ * Translations get text
+ * get workflow translation text
+ *
+ * @param string $key
+ * @return string
+ */
+ public function getText(string $key)
+ {
+ $strings = $this->getTranslation('general');
+ if (!is_array($strings) || !isset($strings[$key])) {
+ return '';
+ }
+ return $strings[$key];
+ }
+
+ /**
+ * Keywords
+ * returns an array of jeywords
+ * that are used for natual language queries
+ * this keywords is an array of key value pairs
+ * the key is the keyword and the value is
+ * the end result for example
+ * 'Bitcoins' => 'BTC',
+ * If the user types Bitcoins the code will
+ * convert that word to BTC
+ * so the user can write thinks like
+ * minute, minutes, years, year, Kilometers, etc.
+ * and the code will convert those words in the query
+ *
+ * @param string $key
+ * @return array
+ */
+ protected function getKeywords(string $key = ''): array
+ {
+ return self::$langKeywords[$key];
+ }
+
+ /**
+ * Get stop words
+ * returns an array with all the stop
+ * words of the current calculator,
+ * this words are used to identify the composition
+ * of the string and then removed
+ * for example in the query
+ * 100 km to meters
+ * to is a stop word, they can be safetly removed
+ *
+ * @param string $key
+ * @return array
+ */
+ protected function getStopWords(string $key = ''): array
+ {
+ return self::$langKeywords[$key]['stop_words'];
+ }
+
+ /**
+ * Stop words string
+ * a string formed by the stop
+ * words, used in regex
+ *
+ * @param array $words
+ * @param string|boolean $spaced
+ * @return string
+ */
+ protected function getStopWordsString($words, $spaced = false): string
+ {
+ if (is_string($words)) {
+ $words = $this->getStopWords($words);
+ }
+
+ $sep = ($spaced ? ' | ' : '|');
+ if (is_bool($spaced)) {
+ $str = implode($sep, $words);
+ }
+ if (is_string($spaced)) {
+ $w = [];
+ foreach ($words as $word) {
+ $w[] = sprintf($spaced, $word);
+ }
+ $str = implode('|', $w);
+ }
+ return '(' . $str . ')';
+ }
+
+ /**
+ * Get query
+ * return the query the user provided in
+ *
+ * @return string
+ */
+ protected function getQuery(): string
+ {
+ return self::$_query;
+ }
+
+ /**
+ * Get settings
+ * returns the workflow stored settings
+ *
+ * @return array
+ */
+ public function getSettings()
+ {
+ return self::$settings;
+ }
+
+ /**
+ * Get setting
+ * return a specific workflow setting
+ *
+ * @param string $name
+ * @param mixed $default
+ * @return mixed
+ */
+ public function getSetting($name, $default = '')
+ {
+ return getSetting($name, $default, $this->getSettings());
+ }
+
+ /**
+ * Esaape words
+ * dos some escape for regex match
+ *
+ * @param string $key
+ * @return string
+ */
+ public function escapeKeywords($key)
+ {
+ $key = str_replace('$', '\$', $key);
+ $key = str_replace('/', '\/', $key);
+ $key = str_replace('.', '\.', $key);
+ return $key;
+ }
+
+ /**
+ * Translated keywords
+ * Convert some keywords to
+ * the actual value so you can use
+ * natual language to make conversions
+ *
+ * For example:
+ * 100¥ to $
+ * Will be converted to
+ * 100JPY to usd
+ *
+ * Still the user can be able
+ * to write 100jpy to usd
+ *
+ * The keywords list can be found in
+ * /lang/{lang}-keys.php
+ *
+ * @param boolean $unit
+ * @return array
+ */
+ public function keywordTranslation($word = false, $keywordsArray)
+ {
+ $val = mb_strtolower($word, 'UTF-8');
+ $keywords = $keywordsArray;
+
+ if (!$val) {
+ return $keywords;
+ }
+
+ foreach ($keywords as $key => $value) {
+ if (is_array($value)) {
+ continue;
+ }
+ $key = $this->escapeKeywords($key);
+ $val = preg_replace('/(^|\W)' . $key . '(\W|$)/i', ' ' . $value . ' ', $val);
+ }
+
+ $val = trim($val);
+ $val = preg_replace('!\s+!', ' ', $val);
+
+ return $val;
+ }
+
+
+ /**
+ * Cached conversion
+ *
+ * @param string $id
+ * @param string $from
+ * @param string $to
+ * @param string $value
+ * @return void
+ */
+ public function cacheConversion($id, $from, $to, $value)
+ {
+ $dir = getDataPath('cache/' . $id);
+ createDir($dir);
+
+ $file = $dir . '/' . $from . '-' . $to . '.txt';
+
+ file_put_contents($file, $value);
+ }
+
+
+ /**
+ * Get cached conversion
+ * return the cached rate
+ *
+ * @param string $from
+ * @param string $to
+ * @param int $cache_seconds
+ * @return mixed
+ */
+ public function getCachedConversion($id, $from, $to, $cache_seconds)
+ {
+ $cache_dir = getDataPath('cache');
+
+ createDir($cache_dir);
+
+ $dir = getDataPath('cache/' . $id);
+ $file = $dir . '/' . $from . '-' . $to . '.txt';
+
+ createDir($dir);
+
+ if (!file_exists($file)) {
+ return false;
+ }
+
+ $updated = filemtime($file);
+ $time = time() - $updated;
+
+ if ($time > $cache_seconds) { // cache already expired
+ return false;
+ }
+
+ $val = file_get_contents($file);
+ if (empty($val)) {
+ return false;
+ }
+
+ return $val;
+ }
+
+
+ /**
+ * Cleanup number
+ *
+ * @param string $number
+ * @return int
+ */
+ public function cleanupNumber($number)
+ {
+ return floatval(str_replace(',', '', $number));
+ }
+
+ /**
+ * Format number
+ * handle number format for the multiple
+ * converters and their specific rules
+ *
+ * @param int $number
+ * @param int $decimals
+ * @param bool $round
+ * @return int
+ */
+ public function formatNumber($number, $decimals = -1, $round = false)
+ {
+ if (fmod($number, 1) !== 0.00) {
+ if ($decimals >= 0) {
+ if ($round) {
+ return number_format($number, $decimals);
+ }
+ $number = bcdiv($number, 1, $decimals);
+ return number_format($number, $decimals);
+ }
+
+ $decimals = 1;
+ $string = '' . $number;
+ $string = explode('.', $string);
+ $string = str_split(end($string));
+ $count = 1;
+
+ // If string has 2 or more decimals make some cleanup
+ if (count($string) >= 2) {
+ $decimals = 2;
+
+ foreach ($string as $order => $value) {
+ $prev = (isset($string[$order - 1]) ? $string[$order - 1] : '');
+ if ($value == '0') {
+ $count += 1;
+ continue;
+ }
+ if ($value !== '0' && $prev !== '0') {
+ $count += 1;
+ $end_digit = $value;
+ break;
+ }
+ }
+ $decimals = $count;
+ }
+
+ if ($round) {
+ return number_format($number, $decimals);
+ }
+ $number = bcdiv($number, 1, $decimals);
+ return number_format($number, $decimals);
+ } else {
+ return number_format($number);
+ }
+ }
+}
diff --git a/currency/Cache/Adapter/AbstractAdapter.php b/workflow/lib/currency/Cache/Adapter/AbstractAdapter.php
similarity index 100%
rename from currency/Cache/Adapter/AbstractAdapter.php
rename to workflow/lib/currency/Cache/Adapter/AbstractAdapter.php
diff --git a/currency/Cache/Adapter/CacheAdapterInterface.php b/workflow/lib/currency/Cache/Adapter/CacheAdapterInterface.php
similarity index 100%
rename from currency/Cache/Adapter/CacheAdapterInterface.php
rename to workflow/lib/currency/Cache/Adapter/CacheAdapterInterface.php
diff --git a/currency/Cache/Adapter/Exception/CachePathNotFoundException.php b/workflow/lib/currency/Cache/Adapter/Exception/CachePathNotFoundException.php
similarity index 100%
rename from currency/Cache/Adapter/Exception/CachePathNotFoundException.php
rename to workflow/lib/currency/Cache/Adapter/Exception/CachePathNotFoundException.php
diff --git a/currency/Cache/Adapter/Exception/ExceptionInterface.php b/workflow/lib/currency/Cache/Adapter/Exception/ExceptionInterface.php
similarity index 100%
rename from currency/Cache/Adapter/Exception/ExceptionInterface.php
rename to workflow/lib/currency/Cache/Adapter/Exception/ExceptionInterface.php
diff --git a/currency/Cache/Adapter/FileSystem.php b/workflow/lib/currency/Cache/Adapter/FileSystem.php
similarity index 100%
rename from currency/Cache/Adapter/FileSystem.php
rename to workflow/lib/currency/Cache/Adapter/FileSystem.php
diff --git a/currency/Cache/Adapter/ZendAdapter.php b/workflow/lib/currency/Cache/Adapter/ZendAdapter.php
similarity index 100%
rename from currency/Cache/Adapter/ZendAdapter.php
rename to workflow/lib/currency/Cache/Adapter/ZendAdapter.php
diff --git a/currency/CountryToCurrency.php b/workflow/lib/currency/CountryToCurrency.php
similarity index 100%
rename from currency/CountryToCurrency.php
rename to workflow/lib/currency/CountryToCurrency.php
diff --git a/currency/CurrencyConverter.php b/workflow/lib/currency/CurrencyConverter.php
similarity index 100%
rename from currency/CurrencyConverter.php
rename to workflow/lib/currency/CurrencyConverter.php
diff --git a/currency/CurrencyConverterInterface.php b/workflow/lib/currency/CurrencyConverterInterface.php
similarity index 100%
rename from currency/CurrencyConverterInterface.php
rename to workflow/lib/currency/CurrencyConverterInterface.php
diff --git a/currency/Exception/ExceptionInterface.php b/workflow/lib/currency/Exception/ExceptionInterface.php
similarity index 100%
rename from currency/Exception/ExceptionInterface.php
rename to workflow/lib/currency/Exception/ExceptionInterface.php
diff --git a/currency/Exception/InvalidArgumentException.php b/workflow/lib/currency/Exception/InvalidArgumentException.php
similarity index 100%
rename from currency/Exception/InvalidArgumentException.php
rename to workflow/lib/currency/Exception/InvalidArgumentException.php
diff --git a/currency/Exception/RunTimeException.php b/workflow/lib/currency/Exception/RunTimeException.php
similarity index 100%
rename from currency/Exception/RunTimeException.php
rename to workflow/lib/currency/Exception/RunTimeException.php
diff --git a/currency/Exception/UnsupportedCurrencyException.php b/workflow/lib/currency/Exception/UnsupportedCurrencyException.php
similarity index 100%
rename from currency/Exception/UnsupportedCurrencyException.php
rename to workflow/lib/currency/Exception/UnsupportedCurrencyException.php
diff --git a/currency/PSR7/AggregateException.php b/workflow/lib/currency/PSR7/AggregateException.php
similarity index 100%
rename from currency/PSR7/AggregateException.php
rename to workflow/lib/currency/PSR7/AggregateException.php
diff --git a/currency/PSR7/AppendStream.php b/workflow/lib/currency/PSR7/AppendStream.php
similarity index 100%
rename from currency/PSR7/AppendStream.php
rename to workflow/lib/currency/PSR7/AppendStream.php
diff --git a/currency/PSR7/BufferStream.php b/workflow/lib/currency/PSR7/BufferStream.php
similarity index 100%
rename from currency/PSR7/BufferStream.php
rename to workflow/lib/currency/PSR7/BufferStream.php
diff --git a/currency/PSR7/CachingStream.php b/workflow/lib/currency/PSR7/CachingStream.php
similarity index 100%
rename from currency/PSR7/CachingStream.php
rename to workflow/lib/currency/PSR7/CachingStream.php
diff --git a/currency/PSR7/CancellationException.php b/workflow/lib/currency/PSR7/CancellationException.php
similarity index 100%
rename from currency/PSR7/CancellationException.php
rename to workflow/lib/currency/PSR7/CancellationException.php
diff --git a/currency/PSR7/Coroutine.php b/workflow/lib/currency/PSR7/Coroutine.php
similarity index 100%
rename from currency/PSR7/Coroutine.php
rename to workflow/lib/currency/PSR7/Coroutine.php
diff --git a/currency/PSR7/DroppingStream.php b/workflow/lib/currency/PSR7/DroppingStream.php
similarity index 100%
rename from currency/PSR7/DroppingStream.php
rename to workflow/lib/currency/PSR7/DroppingStream.php
diff --git a/currency/PSR7/EachPromise.php b/workflow/lib/currency/PSR7/EachPromise.php
similarity index 100%
rename from currency/PSR7/EachPromise.php
rename to workflow/lib/currency/PSR7/EachPromise.php
diff --git a/currency/PSR7/FnStream.php b/workflow/lib/currency/PSR7/FnStream.php
similarity index 100%
rename from currency/PSR7/FnStream.php
rename to workflow/lib/currency/PSR7/FnStream.php
diff --git a/currency/PSR7/FulfilledPromise.php b/workflow/lib/currency/PSR7/FulfilledPromise.php
similarity index 100%
rename from currency/PSR7/FulfilledPromise.php
rename to workflow/lib/currency/PSR7/FulfilledPromise.php
diff --git a/currency/PSR7/HttpFactory.php b/workflow/lib/currency/PSR7/HttpFactory.php
similarity index 100%
rename from currency/PSR7/HttpFactory.php
rename to workflow/lib/currency/PSR7/HttpFactory.php
diff --git a/currency/PSR7/InflateStream.php b/workflow/lib/currency/PSR7/InflateStream.php
similarity index 100%
rename from currency/PSR7/InflateStream.php
rename to workflow/lib/currency/PSR7/InflateStream.php
diff --git a/currency/PSR7/LazyOpenStream.php b/workflow/lib/currency/PSR7/LazyOpenStream.php
similarity index 100%
rename from currency/PSR7/LazyOpenStream.php
rename to workflow/lib/currency/PSR7/LazyOpenStream.php
diff --git a/currency/PSR7/LimitStream.php b/workflow/lib/currency/PSR7/LimitStream.php
similarity index 100%
rename from currency/PSR7/LimitStream.php
rename to workflow/lib/currency/PSR7/LimitStream.php
diff --git a/currency/PSR7/MessageInterface.php b/workflow/lib/currency/PSR7/MessageInterface.php
similarity index 100%
rename from currency/PSR7/MessageInterface.php
rename to workflow/lib/currency/PSR7/MessageInterface.php
diff --git a/currency/PSR7/MessageTrait.php b/workflow/lib/currency/PSR7/MessageTrait.php
similarity index 100%
rename from currency/PSR7/MessageTrait.php
rename to workflow/lib/currency/PSR7/MessageTrait.php
diff --git a/currency/PSR7/MultipartStream.php b/workflow/lib/currency/PSR7/MultipartStream.php
similarity index 100%
rename from currency/PSR7/MultipartStream.php
rename to workflow/lib/currency/PSR7/MultipartStream.php
diff --git a/currency/PSR7/NoSeekStream.php b/workflow/lib/currency/PSR7/NoSeekStream.php
similarity index 100%
rename from currency/PSR7/NoSeekStream.php
rename to workflow/lib/currency/PSR7/NoSeekStream.php
diff --git a/currency/PSR7/Promise.php b/workflow/lib/currency/PSR7/Promise.php
similarity index 100%
rename from currency/PSR7/Promise.php
rename to workflow/lib/currency/PSR7/Promise.php
diff --git a/currency/PSR7/PromiseInterface.php b/workflow/lib/currency/PSR7/PromiseInterface.php
similarity index 100%
rename from currency/PSR7/PromiseInterface.php
rename to workflow/lib/currency/PSR7/PromiseInterface.php
diff --git a/currency/PSR7/PromisorInterface.php b/workflow/lib/currency/PSR7/PromisorInterface.php
similarity index 100%
rename from currency/PSR7/PromisorInterface.php
rename to workflow/lib/currency/PSR7/PromisorInterface.php
diff --git a/currency/PSR7/PumpStream.php b/workflow/lib/currency/PSR7/PumpStream.php
similarity index 100%
rename from currency/PSR7/PumpStream.php
rename to workflow/lib/currency/PSR7/PumpStream.php
diff --git a/currency/PSR7/RejectedPromise.php b/workflow/lib/currency/PSR7/RejectedPromise.php
similarity index 100%
rename from currency/PSR7/RejectedPromise.php
rename to workflow/lib/currency/PSR7/RejectedPromise.php
diff --git a/currency/PSR7/RejectionException.php b/workflow/lib/currency/PSR7/RejectionException.php
similarity index 100%
rename from currency/PSR7/RejectionException.php
rename to workflow/lib/currency/PSR7/RejectionException.php
diff --git a/currency/PSR7/Request.php b/workflow/lib/currency/PSR7/Request.php
similarity index 100%
rename from currency/PSR7/Request.php
rename to workflow/lib/currency/PSR7/Request.php
diff --git a/currency/PSR7/RequestInterface.php b/workflow/lib/currency/PSR7/RequestInterface.php
similarity index 100%
rename from currency/PSR7/RequestInterface.php
rename to workflow/lib/currency/PSR7/RequestInterface.php
diff --git a/currency/PSR7/Response.php b/workflow/lib/currency/PSR7/Response.php
similarity index 100%
rename from currency/PSR7/Response.php
rename to workflow/lib/currency/PSR7/Response.php
diff --git a/currency/PSR7/ResponseInterface.php b/workflow/lib/currency/PSR7/ResponseInterface.php
similarity index 100%
rename from currency/PSR7/ResponseInterface.php
rename to workflow/lib/currency/PSR7/ResponseInterface.php
diff --git a/currency/PSR7/Rfc7230.php b/workflow/lib/currency/PSR7/Rfc7230.php
similarity index 100%
rename from currency/PSR7/Rfc7230.php
rename to workflow/lib/currency/PSR7/Rfc7230.php
diff --git a/currency/PSR7/ServerRequest.php b/workflow/lib/currency/PSR7/ServerRequest.php
similarity index 100%
rename from currency/PSR7/ServerRequest.php
rename to workflow/lib/currency/PSR7/ServerRequest.php
diff --git a/currency/PSR7/ServerRequestInterface.php b/workflow/lib/currency/PSR7/ServerRequestInterface.php
similarity index 100%
rename from currency/PSR7/ServerRequestInterface.php
rename to workflow/lib/currency/PSR7/ServerRequestInterface.php
diff --git a/currency/PSR7/Stream.php b/workflow/lib/currency/PSR7/Stream.php
similarity index 100%
rename from currency/PSR7/Stream.php
rename to workflow/lib/currency/PSR7/Stream.php
diff --git a/currency/PSR7/StreamDecoratorTrait.php b/workflow/lib/currency/PSR7/StreamDecoratorTrait.php
similarity index 100%
rename from currency/PSR7/StreamDecoratorTrait.php
rename to workflow/lib/currency/PSR7/StreamDecoratorTrait.php
diff --git a/currency/PSR7/StreamInterface.php b/workflow/lib/currency/PSR7/StreamInterface.php
similarity index 100%
rename from currency/PSR7/StreamInterface.php
rename to workflow/lib/currency/PSR7/StreamInterface.php
diff --git a/currency/PSR7/StreamWrapper.php b/workflow/lib/currency/PSR7/StreamWrapper.php
similarity index 100%
rename from currency/PSR7/StreamWrapper.php
rename to workflow/lib/currency/PSR7/StreamWrapper.php
diff --git a/currency/PSR7/TaskQueue.php b/workflow/lib/currency/PSR7/TaskQueue.php
similarity index 100%
rename from currency/PSR7/TaskQueue.php
rename to workflow/lib/currency/PSR7/TaskQueue.php
diff --git a/currency/PSR7/TaskQueueInterface.php b/workflow/lib/currency/PSR7/TaskQueueInterface.php
similarity index 100%
rename from currency/PSR7/TaskQueueInterface.php
rename to workflow/lib/currency/PSR7/TaskQueueInterface.php
diff --git a/currency/PSR7/UploadedFile.php b/workflow/lib/currency/PSR7/UploadedFile.php
similarity index 100%
rename from currency/PSR7/UploadedFile.php
rename to workflow/lib/currency/PSR7/UploadedFile.php
diff --git a/currency/PSR7/UploadedFileInterface.php b/workflow/lib/currency/PSR7/UploadedFileInterface.php
similarity index 100%
rename from currency/PSR7/UploadedFileInterface.php
rename to workflow/lib/currency/PSR7/UploadedFileInterface.php
diff --git a/currency/PSR7/Uri.php b/workflow/lib/currency/PSR7/Uri.php
similarity index 100%
rename from currency/PSR7/Uri.php
rename to workflow/lib/currency/PSR7/Uri.php
diff --git a/currency/PSR7/UriInterface.php b/workflow/lib/currency/PSR7/UriInterface.php
similarity index 100%
rename from currency/PSR7/UriInterface.php
rename to workflow/lib/currency/PSR7/UriInterface.php
diff --git a/currency/PSR7/UriNormalizer.php b/workflow/lib/currency/PSR7/UriNormalizer.php
similarity index 100%
rename from currency/PSR7/UriNormalizer.php
rename to workflow/lib/currency/PSR7/UriNormalizer.php
diff --git a/currency/PSR7/UriResolver.php b/workflow/lib/currency/PSR7/UriResolver.php
similarity index 100%
rename from currency/PSR7/UriResolver.php
rename to workflow/lib/currency/PSR7/UriResolver.php
diff --git a/currency/PSR7/functions.php b/workflow/lib/currency/PSR7/functions.php
similarity index 100%
rename from currency/PSR7/functions.php
rename to workflow/lib/currency/PSR7/functions.php
diff --git a/currency/PSR7/functions_include.php b/workflow/lib/currency/PSR7/functions_include.php
similarity index 100%
rename from currency/PSR7/functions_include.php
rename to workflow/lib/currency/PSR7/functions_include.php
diff --git a/currency/Provider/ExchangeRatesIo.php b/workflow/lib/currency/Provider/ExchangeRatesIo.php
similarity index 100%
rename from currency/Provider/ExchangeRatesIo.php
rename to workflow/lib/currency/Provider/ExchangeRatesIo.php
diff --git a/currency/Provider/FixerApi.php b/workflow/lib/currency/Provider/FixerApi.php
similarity index 100%
rename from currency/Provider/FixerApi.php
rename to workflow/lib/currency/Provider/FixerApi.php
diff --git a/currency/Provider/ProviderInterface.php b/workflow/lib/currency/Provider/ProviderInterface.php
similarity index 100%
rename from currency/Provider/ProviderInterface.php
rename to workflow/lib/currency/Provider/ProviderInterface.php
diff --git a/currency/guzzle/Client.php b/workflow/lib/currency/guzzle/Client.php
similarity index 100%
rename from currency/guzzle/Client.php
rename to workflow/lib/currency/guzzle/Client.php
diff --git a/currency/guzzle/ClientInterface.php b/workflow/lib/currency/guzzle/ClientInterface.php
similarity index 100%
rename from currency/guzzle/ClientInterface.php
rename to workflow/lib/currency/guzzle/ClientInterface.php
diff --git a/currency/guzzle/Cookie/CookieJar.php b/workflow/lib/currency/guzzle/Cookie/CookieJar.php
similarity index 100%
rename from currency/guzzle/Cookie/CookieJar.php
rename to workflow/lib/currency/guzzle/Cookie/CookieJar.php
diff --git a/currency/guzzle/Cookie/CookieJarInterface.php b/workflow/lib/currency/guzzle/Cookie/CookieJarInterface.php
similarity index 100%
rename from currency/guzzle/Cookie/CookieJarInterface.php
rename to workflow/lib/currency/guzzle/Cookie/CookieJarInterface.php
diff --git a/currency/guzzle/Cookie/FileCookieJar.php b/workflow/lib/currency/guzzle/Cookie/FileCookieJar.php
similarity index 100%
rename from currency/guzzle/Cookie/FileCookieJar.php
rename to workflow/lib/currency/guzzle/Cookie/FileCookieJar.php
diff --git a/currency/guzzle/Cookie/SessionCookieJar.php b/workflow/lib/currency/guzzle/Cookie/SessionCookieJar.php
similarity index 100%
rename from currency/guzzle/Cookie/SessionCookieJar.php
rename to workflow/lib/currency/guzzle/Cookie/SessionCookieJar.php
diff --git a/currency/guzzle/Cookie/SetCookie.php b/workflow/lib/currency/guzzle/Cookie/SetCookie.php
similarity index 100%
rename from currency/guzzle/Cookie/SetCookie.php
rename to workflow/lib/currency/guzzle/Cookie/SetCookie.php
diff --git a/currency/guzzle/Exception/BadResponseException.php b/workflow/lib/currency/guzzle/Exception/BadResponseException.php
similarity index 100%
rename from currency/guzzle/Exception/BadResponseException.php
rename to workflow/lib/currency/guzzle/Exception/BadResponseException.php
diff --git a/currency/guzzle/Exception/ClientException.php b/workflow/lib/currency/guzzle/Exception/ClientException.php
similarity index 100%
rename from currency/guzzle/Exception/ClientException.php
rename to workflow/lib/currency/guzzle/Exception/ClientException.php
diff --git a/currency/guzzle/Exception/ConnectException.php b/workflow/lib/currency/guzzle/Exception/ConnectException.php
similarity index 100%
rename from currency/guzzle/Exception/ConnectException.php
rename to workflow/lib/currency/guzzle/Exception/ConnectException.php
diff --git a/currency/guzzle/Exception/GuzzleException.php b/workflow/lib/currency/guzzle/Exception/GuzzleException.php
similarity index 100%
rename from currency/guzzle/Exception/GuzzleException.php
rename to workflow/lib/currency/guzzle/Exception/GuzzleException.php
diff --git a/currency/guzzle/Exception/InvalidArgumentException.php b/workflow/lib/currency/guzzle/Exception/InvalidArgumentException.php
similarity index 100%
rename from currency/guzzle/Exception/InvalidArgumentException.php
rename to workflow/lib/currency/guzzle/Exception/InvalidArgumentException.php
diff --git a/currency/guzzle/Exception/RequestException.php b/workflow/lib/currency/guzzle/Exception/RequestException.php
similarity index 100%
rename from currency/guzzle/Exception/RequestException.php
rename to workflow/lib/currency/guzzle/Exception/RequestException.php
diff --git a/currency/guzzle/Exception/SeekException.php b/workflow/lib/currency/guzzle/Exception/SeekException.php
similarity index 100%
rename from currency/guzzle/Exception/SeekException.php
rename to workflow/lib/currency/guzzle/Exception/SeekException.php
diff --git a/currency/guzzle/Exception/ServerException.php b/workflow/lib/currency/guzzle/Exception/ServerException.php
similarity index 100%
rename from currency/guzzle/Exception/ServerException.php
rename to workflow/lib/currency/guzzle/Exception/ServerException.php
diff --git a/currency/guzzle/Exception/TooManyRedirectsException.php b/workflow/lib/currency/guzzle/Exception/TooManyRedirectsException.php
similarity index 100%
rename from currency/guzzle/Exception/TooManyRedirectsException.php
rename to workflow/lib/currency/guzzle/Exception/TooManyRedirectsException.php
diff --git a/currency/guzzle/Exception/TransferException.php b/workflow/lib/currency/guzzle/Exception/TransferException.php
similarity index 100%
rename from currency/guzzle/Exception/TransferException.php
rename to workflow/lib/currency/guzzle/Exception/TransferException.php
diff --git a/currency/guzzle/Handler/CurlFactory.php b/workflow/lib/currency/guzzle/Handler/CurlFactory.php
similarity index 100%
rename from currency/guzzle/Handler/CurlFactory.php
rename to workflow/lib/currency/guzzle/Handler/CurlFactory.php
diff --git a/currency/guzzle/Handler/CurlFactoryInterface.php b/workflow/lib/currency/guzzle/Handler/CurlFactoryInterface.php
similarity index 100%
rename from currency/guzzle/Handler/CurlFactoryInterface.php
rename to workflow/lib/currency/guzzle/Handler/CurlFactoryInterface.php
diff --git a/currency/guzzle/Handler/CurlHandler.php b/workflow/lib/currency/guzzle/Handler/CurlHandler.php
similarity index 100%
rename from currency/guzzle/Handler/CurlHandler.php
rename to workflow/lib/currency/guzzle/Handler/CurlHandler.php
diff --git a/currency/guzzle/Handler/CurlMultiHandler.php b/workflow/lib/currency/guzzle/Handler/CurlMultiHandler.php
similarity index 100%
rename from currency/guzzle/Handler/CurlMultiHandler.php
rename to workflow/lib/currency/guzzle/Handler/CurlMultiHandler.php
diff --git a/currency/guzzle/Handler/EasyHandle.php b/workflow/lib/currency/guzzle/Handler/EasyHandle.php
similarity index 100%
rename from currency/guzzle/Handler/EasyHandle.php
rename to workflow/lib/currency/guzzle/Handler/EasyHandle.php
diff --git a/currency/guzzle/Handler/MockHandler.php b/workflow/lib/currency/guzzle/Handler/MockHandler.php
similarity index 100%
rename from currency/guzzle/Handler/MockHandler.php
rename to workflow/lib/currency/guzzle/Handler/MockHandler.php
diff --git a/currency/guzzle/Handler/Proxy.php b/workflow/lib/currency/guzzle/Handler/Proxy.php
similarity index 100%
rename from currency/guzzle/Handler/Proxy.php
rename to workflow/lib/currency/guzzle/Handler/Proxy.php
diff --git a/currency/guzzle/Handler/StreamHandler.php b/workflow/lib/currency/guzzle/Handler/StreamHandler.php
similarity index 100%
rename from currency/guzzle/Handler/StreamHandler.php
rename to workflow/lib/currency/guzzle/Handler/StreamHandler.php
diff --git a/currency/guzzle/HandlerStack.php b/workflow/lib/currency/guzzle/HandlerStack.php
similarity index 100%
rename from currency/guzzle/HandlerStack.php
rename to workflow/lib/currency/guzzle/HandlerStack.php
diff --git a/currency/guzzle/MessageFormatter.php b/workflow/lib/currency/guzzle/MessageFormatter.php
similarity index 100%
rename from currency/guzzle/MessageFormatter.php
rename to workflow/lib/currency/guzzle/MessageFormatter.php
diff --git a/currency/guzzle/Middleware.php b/workflow/lib/currency/guzzle/Middleware.php
similarity index 100%
rename from currency/guzzle/Middleware.php
rename to workflow/lib/currency/guzzle/Middleware.php
diff --git a/currency/guzzle/Pool.php b/workflow/lib/currency/guzzle/Pool.php
similarity index 100%
rename from currency/guzzle/Pool.php
rename to workflow/lib/currency/guzzle/Pool.php
diff --git a/currency/guzzle/PrepareBodyMiddleware.php b/workflow/lib/currency/guzzle/PrepareBodyMiddleware.php
similarity index 100%
rename from currency/guzzle/PrepareBodyMiddleware.php
rename to workflow/lib/currency/guzzle/PrepareBodyMiddleware.php
diff --git a/currency/guzzle/Promise/AggregateException.php b/workflow/lib/currency/guzzle/Promise/AggregateException.php
similarity index 100%
rename from currency/guzzle/Promise/AggregateException.php
rename to workflow/lib/currency/guzzle/Promise/AggregateException.php
diff --git a/currency/guzzle/Promise/CancellationException.php b/workflow/lib/currency/guzzle/Promise/CancellationException.php
similarity index 100%
rename from currency/guzzle/Promise/CancellationException.php
rename to workflow/lib/currency/guzzle/Promise/CancellationException.php
diff --git a/currency/guzzle/Promise/Coroutine.php b/workflow/lib/currency/guzzle/Promise/Coroutine.php
similarity index 100%
rename from currency/guzzle/Promise/Coroutine.php
rename to workflow/lib/currency/guzzle/Promise/Coroutine.php
diff --git a/currency/guzzle/Promise/EachPromise.php b/workflow/lib/currency/guzzle/Promise/EachPromise.php
similarity index 100%
rename from currency/guzzle/Promise/EachPromise.php
rename to workflow/lib/currency/guzzle/Promise/EachPromise.php
diff --git a/currency/guzzle/Promise/FulfilledPromise.php b/workflow/lib/currency/guzzle/Promise/FulfilledPromise.php
similarity index 100%
rename from currency/guzzle/Promise/FulfilledPromise.php
rename to workflow/lib/currency/guzzle/Promise/FulfilledPromise.php
diff --git a/currency/guzzle/Promise/Promise.php b/workflow/lib/currency/guzzle/Promise/Promise.php
similarity index 100%
rename from currency/guzzle/Promise/Promise.php
rename to workflow/lib/currency/guzzle/Promise/Promise.php
diff --git a/currency/guzzle/Promise/PromiseInterface.php b/workflow/lib/currency/guzzle/Promise/PromiseInterface.php
similarity index 100%
rename from currency/guzzle/Promise/PromiseInterface.php
rename to workflow/lib/currency/guzzle/Promise/PromiseInterface.php
diff --git a/currency/guzzle/Promise/PromisorInterface.php b/workflow/lib/currency/guzzle/Promise/PromisorInterface.php
similarity index 100%
rename from currency/guzzle/Promise/PromisorInterface.php
rename to workflow/lib/currency/guzzle/Promise/PromisorInterface.php
diff --git a/currency/guzzle/Promise/RejectedPromise.php b/workflow/lib/currency/guzzle/Promise/RejectedPromise.php
similarity index 100%
rename from currency/guzzle/Promise/RejectedPromise.php
rename to workflow/lib/currency/guzzle/Promise/RejectedPromise.php
diff --git a/currency/guzzle/Promise/RejectionException.php b/workflow/lib/currency/guzzle/Promise/RejectionException.php
similarity index 100%
rename from currency/guzzle/Promise/RejectionException.php
rename to workflow/lib/currency/guzzle/Promise/RejectionException.php
diff --git a/currency/guzzle/Promise/TaskQueue.php b/workflow/lib/currency/guzzle/Promise/TaskQueue.php
similarity index 100%
rename from currency/guzzle/Promise/TaskQueue.php
rename to workflow/lib/currency/guzzle/Promise/TaskQueue.php
diff --git a/currency/guzzle/Promise/TaskQueueInterface.php b/workflow/lib/currency/guzzle/Promise/TaskQueueInterface.php
similarity index 100%
rename from currency/guzzle/Promise/TaskQueueInterface.php
rename to workflow/lib/currency/guzzle/Promise/TaskQueueInterface.php
diff --git a/currency/guzzle/Promise/functions.php b/workflow/lib/currency/guzzle/Promise/functions.php
similarity index 100%
rename from currency/guzzle/Promise/functions.php
rename to workflow/lib/currency/guzzle/Promise/functions.php
diff --git a/currency/guzzle/Promise/functions_include.php b/workflow/lib/currency/guzzle/Promise/functions_include.php
similarity index 100%
rename from currency/guzzle/Promise/functions_include.php
rename to workflow/lib/currency/guzzle/Promise/functions_include.php
diff --git a/currency/guzzle/RedirectMiddleware.php b/workflow/lib/currency/guzzle/RedirectMiddleware.php
similarity index 100%
rename from currency/guzzle/RedirectMiddleware.php
rename to workflow/lib/currency/guzzle/RedirectMiddleware.php
diff --git a/currency/guzzle/RequestOptions.php b/workflow/lib/currency/guzzle/RequestOptions.php
similarity index 100%
rename from currency/guzzle/RequestOptions.php
rename to workflow/lib/currency/guzzle/RequestOptions.php
diff --git a/currency/guzzle/RetryMiddleware.php b/workflow/lib/currency/guzzle/RetryMiddleware.php
similarity index 100%
rename from currency/guzzle/RetryMiddleware.php
rename to workflow/lib/currency/guzzle/RetryMiddleware.php
diff --git a/currency/guzzle/TransferStats.php b/workflow/lib/currency/guzzle/TransferStats.php
similarity index 100%
rename from currency/guzzle/TransferStats.php
rename to workflow/lib/currency/guzzle/TransferStats.php
diff --git a/currency/guzzle/UriTemplate.php b/workflow/lib/currency/guzzle/UriTemplate.php
similarity index 100%
rename from currency/guzzle/UriTemplate.php
rename to workflow/lib/currency/guzzle/UriTemplate.php
diff --git a/currency/guzzle/functions.php b/workflow/lib/currency/guzzle/functions.php
similarity index 100%
rename from currency/guzzle/functions.php
rename to workflow/lib/currency/guzzle/functions.php
diff --git a/currency/guzzle/functions_include.php b/workflow/lib/currency/guzzle/functions_include.php
similarity index 100%
rename from currency/guzzle/functions_include.php
rename to workflow/lib/currency/guzzle/functions_include.php
diff --git a/workflow/lib/functions.php b/workflow/lib/functions.php
new file mode 100644
index 0000000..3ab4fe1
--- /dev/null
+++ b/workflow/lib/functions.php
@@ -0,0 +1,295 @@
+ $value) {
+ if (!isset($keywords[$key])) {
+ $keywords[$key] = $value;
+ continue;
+ }
+
+ foreach ($value as $k => $v) {
+ if (isset($keywords[$key][$k]) && is_array($keywords[$key][$k])) {
+ $mul = array_merge($keywords[$key][$k], $v);
+ $keywords[$key][$k] = array_unique($mul);
+ } elseif (!isset($keywords[$key][$k])) {
+ $keywords[$key][$k] = $v;
+ }
+ }
+ }
+
+ return $keywords;
+}
+
+function getEnglishKeywords()
+{
+ return include dirname(__DIR__, 2) . '/lang/en_EN-keys.php';
+}
+
+function cleanQuery($query)
+{
+ if (empty($query)) {
+ return $query;
+ }
+ // $clean = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $tsts);
+ // Fucking letter ñ this was the only
+ // way i found to remove it without removing
+ // the rest of the special characters in the string
+ $clean = $query;
+ $clean = urlencode($clean);
+ $clean = str_replace('n%CC%83', 'n', $clean);
+ $clean = str_replace('N%CC%83', 'n', $clean);
+ $clean = str_replace('%C3%B3', 'o', $clean); // accented o
+ $clean = str_replace('%CC%81', '', $clean); // accented i
+
+ $clean = urldecode($clean);
+ $clean = preg_replace('!\s+!', ' ', $clean);
+ $clean = mb_strtolower($clean, 'UTF-8');
+
+ return $clean;
+}
+
+
+function startsWith(string $haystack, string $needle, bool $case = true): bool
+{
+ if ($case) {
+ return strpos($haystack, $needle, 0) === 0;
+ }
+
+ return stripos($haystack, $needle, 0) === 0;
+}
+
+function endsWith(string $haystack, string $needle, bool $case = true): bool
+{
+ $expectedPosition = strlen($haystack) - strlen($needle);
+ if ($case) {
+ return strrpos($haystack, $needle, 0) === $expectedPosition;
+ }
+
+ return strripos($haystack, $needle, 0) === $expectedPosition;
+}
+
+
+function workflowUpdater($config = [])
+{
+ require_once dirname(__DIR__, 2) . '/alfred/updater.php';
+
+ $default = [
+ 'plist_url' => 'https://raw.githubusercontent.com/biati-digital/alfred-calculate-anything/master/info.plist',
+ 'workflow_url' => 'https://github.com/biati-digital/alfred-calculate-anything/releases/latest/download/Calculate.Anything.alfredworkflow',
+ 'force_check' => false,
+ 'force_download' => false,
+ 'download_type' => 'async',
+ ];
+ $updater = new Alfred\Updater(array_merge($default, $config));
+
+ return $updater->checkUpdates();
+}
+
+
+spl_autoload_register(function ($class) {
+ $class = str_replace('_', '-', $class);
+ $class = str_replace('\\CalculateAnything\\', '\\tools\\', $class);
+ $class = str_replace('\\', DIRECTORY_SEPARATOR, $class);
+ $class = strtolower($class) . '.php';
+
+ $dir = dirname(__DIR__, 2);
+ $file = $dir . '/' . $class;
+
+ if (is_readable($file)) {
+ include_once $file;
+ } else {
+ $libs = [
+ 'olifolkerd/convertor/convertor.php' => $dir . '/workflow/lib/units/Convertor.php',
+ 'olifolkerd/convertor/exceptions/convertorinvalidunitexception.php' => $dir . '/workflow/lib/units/Exceptions/ConvertorInvalidUnitException.php',
+ ];
+
+ if (isset($libs[$class]) && is_readable($libs[$class])) {
+ include_once $libs[$class];
+ }
+ }
+});
diff --git a/units/Config/BaseUnits.php b/workflow/lib/units/Config/BaseUnits.php
similarity index 100%
rename from units/Config/BaseUnits.php
rename to workflow/lib/units/Config/BaseUnits.php
diff --git a/units/Config/Units.php b/workflow/lib/units/Config/Units.php
similarity index 100%
rename from units/Config/Units.php
rename to workflow/lib/units/Config/Units.php
diff --git a/units/Convertor.php b/workflow/lib/units/Convertor.php
similarity index 100%
rename from units/Convertor.php
rename to workflow/lib/units/Convertor.php
diff --git a/units/Exceptions/ConvertorDifferentTypeException.php b/workflow/lib/units/Exceptions/ConvertorDifferentTypeException.php
similarity index 100%
rename from units/Exceptions/ConvertorDifferentTypeException.php
rename to workflow/lib/units/Exceptions/ConvertorDifferentTypeException.php
diff --git a/units/Exceptions/ConvertorException.php b/workflow/lib/units/Exceptions/ConvertorException.php
similarity index 100%
rename from units/Exceptions/ConvertorException.php
rename to workflow/lib/units/Exceptions/ConvertorException.php
diff --git a/units/Exceptions/ConvertorInvalidUnitException.php b/workflow/lib/units/Exceptions/ConvertorInvalidUnitException.php
similarity index 100%
rename from units/Exceptions/ConvertorInvalidUnitException.php
rename to workflow/lib/units/Exceptions/ConvertorInvalidUnitException.php
diff --git a/units/Exceptions/FileNotFoundException.php b/workflow/lib/units/Exceptions/FileNotFoundException.php
similarity index 100%
rename from units/Exceptions/FileNotFoundException.php
rename to workflow/lib/units/Exceptions/FileNotFoundException.php
diff --git a/workflow/lib/updater.php b/workflow/lib/updater.php
new file mode 100644
index 0000000..28203df
--- /dev/null
+++ b/workflow/lib/updater.php
@@ -0,0 +1,19 @@
+ true,
+ 'download_type' => 'sync',
+]);
+
+// No title if there are updates as the updater
+// will display the notifications
+$notification = (!$updates ? 'You have the latest version.' : '');
+
+echo $notification;
diff --git a/workflow/tools/calculatorinterface.php b/workflow/tools/calculatorinterface.php
new file mode 100644
index 0000000..135aca9
--- /dev/null
+++ b/workflow/tools/calculatorinterface.php
@@ -0,0 +1,36 @@
+query = str_replace(',', '', $query);
+ $this->lang = $this->getTranslation('crypto_currency');
+ $this->keywords = $this->getKeywords('crypto_currency');
+ $this->stop_words = $this->getStopWords('crypto_currency');
+ $this->symbolsList = $this->symbols();
+ $this->apikey = $this->getSetting('coinmarket_apikey', '');
+ }
+
+
+ /**
+ * Symbols
+ * Cryptocurrency Symbols
+ *
+ * @return array
+ */
+ private function symbols()
+ {
+ return [
+ 'BTC' => 'bitcoin',
+ 'ETH' => 'ethereum',
+ 'XRP' => 'xrp',
+ 'USDT' => 'tether',
+ 'BCH' => 'bitcoin cash',
+ 'BSV' => 'bitcoin sv',
+ 'LTC' => 'litecoin',
+ 'BNB' => 'binance coin',
+ 'EOS' => 'eos',
+ 'XTZ' => 'tezos',
+ 'XLM' => 'stellar',
+ 'ADA' => 'cardano',
+ 'LINK' => 'chainlink',
+ 'LEO' => 'unus sed leo',
+ 'CRO' => 'crypto.com coin',
+ 'XMR' => 'monero',
+ 'TRX' => 'tron',
+ 'HT' => 'huobi token',
+ 'USDC' => 'usd coin',
+ 'ETC' => 'ethereum classic',
+ 'NEO' => 'neo',
+ 'DASH' => 'dash',
+ 'HEDG' => 'hedgetrade',
+ 'MIOTA' => 'iota',
+ 'ATOM' => 'cosmos',
+ 'ZEC' => 'zcash',
+ 'XEM' => 'nem',
+ 'MKR' => 'maker',
+ 'DOGE' => 'dogecoin',
+ 'ONT' => 'ontology',
+ 'OKB' => 'okb',
+ 'BAT' => 'basic attention token',
+ 'FTT' => 'ftx token',
+ 'PAX' => 'paxos standard',
+ 'DGB' => 'digibyte',
+ 'ZRX' => '0x',
+ 'VET' => 'vechain',
+ 'BUSD' => 'binance usd',
+ 'BTG' => 'bitcoin gold',
+ 'REP' => 'augur',
+ 'DCR' => 'decred',
+ 'SNX' => 'synthetix network token',
+ 'HBAR' => 'hedera hashgraph',
+ 'TUSD' => 'trueusd',
+ 'ICX' => 'icon',
+ 'HYN' => 'hyperion',
+ 'QTUM' => 'qtum',
+ 'ALGO' => 'algorand',
+ 'THETA' => 'theta',
+ 'LSK' => 'lisk',
+ 'ENJ' => 'enjin coin',
+ 'SNT' => 'status',
+ 'RVN' => 'ravencoin',
+ 'DAI' => 'multi-collateral dai',
+ 'KNC' => 'kyber network',
+ 'ZB' => 'zb token',
+ 'BCD' => 'bitcoin diamond',
+ 'WAVES' => 'waves',
+ 'OMG' => 'omisego',
+ 'HIVE' => 'hive',
+ 'ABBC' => 'abbc coin',
+ 'NRG' => 'energi',
+ 'MCO' => 'mco',
+ 'FXC' => 'flexacoin',
+ 'LEND' => 'aave',
+ 'MONA' => 'monacoin',
+ 'DX' => 'dxchain token',
+ 'HOT' => 'holo',
+ 'NANO' => 'nano',
+ 'SC' => 'siacoin',
+ 'DGD' => 'digixdao',
+ 'ZIL' => 'zilliqa',
+ 'NMR' => 'numeraire',
+ 'KCS' => 'kucoin shares',
+ 'CKB' => 'nervos network',
+ 'BTM' => 'bytom',
+ 'RDD' => 'reddcoin',
+ 'KMD' => 'komodo',
+ 'STEEM' => 'steem',
+ 'REN' => 'ren',
+ 'CRPT' => 'crypterium',
+ 'NEXO' => 'nexo',
+ 'BTT' => 'bittorrent',
+ 'LUNA' => 'terra',
+ 'MATIC' => 'matic network',
+ 'QNT' => 'quant',
+ 'XVG' => 'verge',
+ 'SEELE' => 'seele-n',
+ 'ZEN' => 'horizen',
+ 'BTS' => 'bitshares',
+ 'DATA' => 'streamr',
+ 'RCN' => 'ripio credit network',
+ 'MANA' => 'decentraland',
+ 'BHT' => 'bhex token',
+ 'BCN' => 'bytecoin',
+ 'HC' => 'hypercash',
+ 'VSYS' => 'v.systems',
+ 'MAID' => 'maidsafecoin',
+ 'PAXG' => 'pax gold',
+ 'UBT' => 'unibright',
+ ];
+ }
+
+
+ /**
+ * shouldProcess
+ *
+ * @param string $query
+ * @param integer $strlenght
+ * @return bool
+ */
+ public function shouldProcess(int $strlenght = 0)
+ {
+ if ($strlenght <= 3) {
+ return false;
+ }
+
+ $currencies = $this->matchRegex();
+ $stopwords = $this->getStopWordsString($this->stop_words);
+ $query = $this->query;
+ return preg_match('/^\d*\.?\d+ ?' . $currencies . ' ?' . $stopwords . '?/i', $query, $matches);
+ }
+
+
+ /**
+ * Process query
+ *
+ * @return string|array
+ */
+ public function processQuery()
+ {
+ $query = $this->query;
+ $data = $this->extractQueryData($query);
+
+ if (!$data) {
+ return false;
+ }
+
+ if (empty($this->apikey)) {
+ return $this->output(['noapi' => true]);
+ }
+
+ if ($data['amount'] <= 0 || $data['from'] == $data['to']) {
+ return $data['amount'] . $data['to'];
+ }
+
+ $locale = $this->getSetting('locale_currency', 'en_US');
+ setlocale(LC_MONETARY, $locale);
+
+ $converted = $this->convert($data);
+
+ if ($converted['error']) {
+ return $converted['error'];
+ }
+
+ $data['converted'] = [];
+ foreach ($converted as $key => $value) {
+ $data['converted'][$key] = [];
+
+ $total = money_format('%i', $value['total']);
+ $total = preg_replace("/[^0-9.,]/", '', $total);
+ $single = $value['single'];
+ $single = $this->formatNumber($value['single']);
+
+ $data['converted'][$key]['total'] = ['value' => $total, 'formatted' => "{$total}{$key}"];
+ $data['converted'][$key]['single'] = ['value' => $single, 'formatted' => "1{$data['from']} = {$single}{$key}"];
+ }
+
+ return $this->output($data);
+ }
+
+
+ /**
+ * Output
+ * build the output the way
+ * it should be displayed by Alfred
+ *
+ * @param array $result
+ * @return array
+ */
+ public function output($result)
+ {
+ $items = [];
+
+ if ($result['noapi']) {
+ $items[] = [
+ 'title' => $this->lang['noapikey_title'],
+ 'subtitle' => $this->lang['noapikey_subtitle'],
+ 'valid' => false,
+ ];
+ return $items;
+ }
+
+ $converted = $result['converted'];
+
+ foreach ($converted as $key => $value) {
+ $total = $value['total'];
+ $single = $value['single'];
+ $icon = 'flags/' . $key . '.png';
+
+ $items[] = [
+ 'title' => $total['formatted'],
+ 'subtitle' => $single['formatted'],
+ 'arg' => $total['value'],
+ 'icon' => ['path' => $icon],
+ 'mods' => [
+ 'cmd' => [
+ 'valid' => true,
+ 'arg' => $total['value'],
+ 'subtitle' => $this->lang['cmd'],
+ ],
+ 'alt' => [
+ 'valid' => true,
+ 'arg' => $this->cleanupNumber($total['value']),
+ 'subtitle' => $this->lang['alt'],
+ ],
+ ]
+ ];
+ }
+
+ return $items;
+ }
+
+
+ /**
+ * Handle conversion
+ *
+ * @param array $data
+ * @return array
+ */
+ public function convert($data)
+ {
+ $converted = [];
+ $amount = $data['amount'];
+ $from = $data['from'];
+ $to = $data['to'];
+ $use_cache = (isset($data['use_cache']) ? $data['use_cache'] : true);
+ $cache_seconds = ($use_cache ? 14400 : 0);
+
+ if (is_string($to)) {
+ $to = [$to];
+ }
+
+ foreach ($to as $currency) {
+ $conversion = $this->coinmarketConversion($amount, $from, $currency, $cache_seconds);
+
+ if (isset($conversion['error']) && !empty($conversion['error'])) {
+ $converted['error'] = $conversion['error'];
+ }
+ $converted[$currency] = $conversion;
+ }
+
+
+ return $converted;
+ }
+
+ /**
+ * Fixer conversion
+ *
+ * @param int $amount
+ * @param string $from
+ * @param string $to
+ * @param int $cache_seconds
+ * @return array
+ */
+ private function coinmarketConversion($amount, $from, $to, $cache_seconds)
+ {
+ $cached = $this->getCachedConversion('coinmarketcap', $from, $to, $cache_seconds);
+ if ($cached) {
+ $cached = (float) $cached;
+ $value = $cached;
+ $total = $amount * $value;
+ }
+
+ if (!$cached) {
+ $exchange = $this->getRates($cache_seconds);
+
+ if (is_string($exchange)) {
+ return ['total' => '', 'single' => '', 'error' => $exchange];
+ }
+
+ $exchange = $exchange['data'];
+ $crypto_data = $this->getRate($from, $exchange);
+
+ if (!$crypto_data) {
+ return false;
+ }
+
+ $crypto_value = $crypto_data['quote']['USD']['price'];
+ $to_type = $this->isValidSymbol($to) ? 'cryptocurrency' : 'currency';
+
+ // Check if doing a converstion from crypto currency to crypto currency
+ if ($to_type == 'cryptocurrency') {
+ $crypto_to_data = $this->getRate($to, $exchange);
+ if (!$crypto_to_data) {
+ return false;
+ }
+
+ $to_value = $crypto_to_data['quote']['USD']['price'];
+ $to_value = str_replace(',', '', $to_value);
+
+ $convert = floatval($crypto_value) / floatval($to_value);
+ $value = $convert;
+ $total = $convert * $amount;
+ }
+
+ if ($to_type == 'currency') {
+ if ($to == 'USD') {
+ // Rates are already in USD
+ $crypto_value = str_replace(',', '', $crypto_value);
+ $total = floatval($crypto_value) * $amount;
+ } elseif (self::$currencyCalculator->isValidCurrency($to)) {
+ $currency_conversion = self::$currencyCalculator->convert([
+ 'from' => 'USD',
+ 'to' => $to,
+ 'amount' => $crypto_value,
+ ]);
+
+ if ($currency_conversion && isset($currency_conversion[$to])) {
+ $total = str_replace(',', '', $currency_conversion[$to]['total']);
+ $total = floatval($total) * $crypto_value;
+ $value = floatval($total) / $crypto_value;
+ }
+ }
+ }
+
+ $this->cacheConversion('coinmarketcap', $from, $to, $value);
+ }
+
+ return ['total' => $total, 'single' => $value, 'error' => false];
+ }
+
+
+
+ /**
+ * Extract query data
+ * extract the values from and to
+ * from the query typed by the user
+ * it returns from, to and amount
+ */
+ private function extractQueryData($query)
+ {
+ $amount = '';
+ $from = '';
+ $to = '';
+ $default_currency = self::$currencyCalculator->getBaseCurrency();
+ $stopwords = $this->getStopWordsString($this->stop_words, ' %s ');
+
+ preg_match('/^(\d*\.?\d+)[^\d]/i', $query, $amount_match);
+ if (empty($amount_match)) {
+ return false;
+ }
+
+ $amount = getVar($amount_match, 1);
+ $amount = trim($amount);
+ $string = str_replace($amount, '', $query);
+ $string = trim($string);
+
+ preg_match('/(.*).*' . $stopwords . '(.*)/i', $string, $matches);
+
+ // Matches strings like 100 usd to mxn
+ if (!empty($matches)) {
+ $matches = array_values(array_filter($matches));
+ $from = getVar($matches, 1);
+ $to = getVar($matches, 3);
+ }
+ // String is like 100 usd or 100 usd mxn
+ elseif (empty($matches)) {
+ $keywords = $this->keywords;
+
+ foreach ($keywords as $key => $value) {
+ if (is_array($value)) {
+ continue;
+ }
+ $key = $this->escapeKeywords($key);
+ $string = preg_replace('/(^|\W)' . $key . '(\W|$)/i', ' ' . $value . ' ', $string);
+ }
+
+ $string = preg_replace('!\s+!', ' ', $string);
+ $string = trim($string);
+
+
+ $data = explode(' ', $string);
+ $from = getVar($data, 0);
+ $to = getVar($data, 1);
+ }
+
+ $from = strtoupper($from);
+ $to = (!empty($to) ? strtoupper($to) : $default_currency);
+
+ $from = $this->getCorrectSymbol($from);
+ $to = (is_string($to) ? $this->getCorrectSymbol($to) : $to);
+
+ if (!$from || !$to) {
+ return false;
+ }
+
+ return [
+ 'from' => $from,
+ 'to' => $to,
+ 'amount' => $this->cleanupNumber($amount),
+ ];
+ }
+
+
+ /**
+ * Get rates
+ * if already cached and cache
+ * has not expired returns
+ * the cached rates otherwise
+ * it fetches the rates again
+ *
+ * @param int $cache_seconds number of seconds before the cache expires
+ * @return mixed array if sucess or string with error message
+ */
+ private function getRates($cache_seconds)
+ {
+ $dir = getDataPath('cache/coinmarketcap');
+ createDir($dir);
+
+ $file = $dir . '/rates.json';
+ if (file_exists($file)) {
+ $c = file_get_contents($file);
+
+ if (!empty($c)) {
+ $c = json_decode($c, true);
+ $updated = $c['timestamp'];
+ $time = time() - $updated;
+
+ // Only return cached rates if cache
+ // has not expired otherwise continue
+ // to fetch the new rates
+ if ($time < $cache_seconds) {
+ return $c;
+ }
+ }
+ }
+
+ $apikey = $this->apikey;
+ if (empty($apikey)) {
+ throw new Exception('No API Key provided');
+ }
+
+ $c = file_get_contents("https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?CMC_PRO_API_KEY={$apikey}");
+ if (empty($c)) {
+ return $this->lang['fetch_error'];
+ }
+
+ file_put_contents($file, $c);
+
+ return json_decode($c, true);
+ }
+
+
+ /**
+ * Get single rate
+ * from the $exchange_rates list
+ *
+ * @param string $symbol
+ * @param array $exchange_rates
+ * @return array
+ */
+ public function getRate($symbol, $exchange_rates)
+ {
+ $crypto_data = false;
+
+ foreach ($exchange_rates as $value) {
+ if ($value['symbol'] == $symbol) {
+ $crypto_data = $value;
+ break;
+ }
+ }
+
+ return $crypto_data;
+ }
+
+
+ /**
+ * Get correct symbol
+ * searching in translations and symbols array
+ *
+ * @param string $val
+ * @return string|bool
+ */
+ private function getCorrectSymbol($val)
+ {
+ if ($this->isValidSymbol($val)) {
+ return $val;
+ }
+
+ // $val = strtolower($val);
+ $val = mb_strtolower($val);
+ $val = $this->keywordTranslation($val, $this->keywords);
+
+ if (($key = array_search($val, $this->symbolsList)) !== false) {
+ return $key;
+ }
+
+ // Check if instead of a cryptocurrency is a regular currency
+ $is_currency = self::$currencyCalculator->getCorrectCurrency($val);
+ if ($is_currency) {
+ return $is_currency;
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Regex
+ * create a regex from the
+ * available currencies array
+ *
+ * @return string
+ */
+ private function matchRegex()
+ {
+ $currencies = $this->symbolsList;
+ $params = implode('|', array_keys($currencies));
+ $params .= '|' . implode('|', array_values($currencies));
+ $translation_keywords = $this->keywords;
+
+ if (!empty($translation_keywords)) {
+ $params .= '|' . implode('|', array_keys($translation_keywords));
+ }
+ $params = $this->escapeKeywords($params);
+
+ return '(' . $params . ')';
+ }
+
+
+ /**
+ * Is valid
+ * check if given symbols
+ * is valid and exists in the
+ * symbols array
+ *
+ * @param string $val
+ * @return bool
+ */
+ private function isValidSymbol($val)
+ {
+ return isset($this->symbolsList[$val]);
+ }
+
+
+ /**
+ * Get list
+ * get a readable units list
+ * to display to the user
+ *
+ * @return array
+ */
+ function listAvailable()
+ {
+ $units = $this->symbolsList;
+ $list = [];
+ foreach ($units as $key => $value) {
+ $list[] = [
+ 'title' => $key,
+ 'subtitle' => ucwords($value),
+ 'arg' => $key,
+ 'match' => $key . ' ' . $value,
+ 'autocomplete' => $value,
+ 'valid' => true,
+ 'mods' => [
+ 'cmd' => [
+ 'valid' => true,
+ 'arg' => $key,
+ 'subtitle' => $this->getText('action_copy'),
+ ]
+ ],
+ 'icon' => [
+ 'path' => "flags/{$key}.png"
+ ]
+ ];
+ }
+
+ return $list;
+ }
+}
diff --git a/workflow/tools/currency.php b/workflow/tools/currency.php
new file mode 100644
index 0000000..431d55c
--- /dev/null
+++ b/workflow/tools/currency.php
@@ -0,0 +1,765 @@
+query = str_replace(',', '', $query);
+ $this->lang = $this->getTranslation('currency');
+ $this->keywords = $this->getKeywords('currency');
+ $this->stop_words = $this->getStopWords('currency');
+ $this->currencyList = $this->currencies();
+ }
+
+
+ /**
+ * currencies
+ * Cryptocurrency currencies
+ *
+ * @return array
+ */
+ private function currencies()
+ {
+ return [
+ 'AED' => 'United Arab Emirates dirham',
+ 'AFN' => 'Afghan afghani',
+ 'ALL' => 'Albanian lek',
+ 'AMD' => 'Armenian dram',
+ 'ANG' => 'Netherlands Antillean guilder',
+ 'AOA' => 'Angolan kwanza',
+ 'ARS' => 'Argentine peso',
+ 'AUD' => 'Australian dollar',
+ 'AWG' => 'Aruban florin',
+ 'AZN' => 'Azerbaijani manat',
+ 'BAM' => 'Bosnia and Herzegovina convertible mark',
+ 'BBD' => 'Barbados dollar',
+ 'BDT' => 'Bangladeshi taka',
+ 'BGN' => 'Bulgarian lev',
+ 'BHD' => 'Bahraini dinar',
+ 'BIF' => 'Burundian franc',
+ 'BMD' => 'Bermudian dollar',
+ 'BND' => 'Brunei dollar',
+ 'BOB' => 'Boliviano',
+ 'BRL' => 'Brazilian real',
+ 'BSD' => 'Bahamian dollar',
+ 'BTN' => 'Bhutanese ngultrum',
+ 'BWP' => 'Botswana pula',
+ 'BYN' => 'New Belarusian ruble',
+ 'BYR' => 'Belarusian ruble',
+ 'BZD' => 'Belize dollar',
+ 'CAD' => 'Canadian dollar',
+ 'CDF' => 'Congolese franc',
+ 'CHF' => 'Swiss franc',
+ 'CLF' => 'Unidad de Fomento',
+ 'CLP' => 'Chilean peso',
+ 'CNY' => 'Renminbi|Chinese yuan',
+ 'COP' => 'Colombian peso',
+ 'CRC' => 'Costa Rican colon',
+ 'CUC' => 'Cuban convertible peso',
+ 'CUP' => 'Cuban peso',
+ 'CVE' => 'Cape Verde escudo',
+ 'CZK' => 'Czech koruna',
+ 'DJF' => 'Djiboutian franc',
+ 'DKK' => 'Danish krone',
+ 'DOP' => 'Dominican peso',
+ 'DZD' => 'Algerian dinar',
+ 'EGP' => 'Egyptian pound',
+ 'ERN' => 'Eritrean nakfa',
+ 'ETB' => 'Ethiopian birr',
+ 'EUR' => 'Euro',
+ 'FJD' => 'Fiji dollar',
+ 'FKP' => 'Falkland Islands pound',
+ 'GBP' => 'Pound sterling',
+ 'GEL' => 'Georgian lari',
+ 'GHS' => 'Ghanaian cedi',
+ 'GIP' => 'Gibraltar pound',
+ 'GMD' => 'Gambian dalasi',
+ 'GNF' => 'Guinean franc',
+ 'GTQ' => 'Guatemalan quetzal',
+ 'GYD' => 'Guyanese dollar',
+ 'HKD' => 'Hong Kong dollar',
+ 'HNL' => 'Honduran lempira',
+ 'HRK' => 'Croatian kuna',
+ 'HTG' => 'Haitian gourde',
+ 'HUF' => 'Hungarian forint',
+ 'IDR' => 'Indonesian rupiah',
+ 'ILS' => 'Israeli new shekel',
+ 'INR' => 'Indian rupee',
+ 'IQD' => 'Iraqi dinar',
+ 'IRR' => 'Iranian rial',
+ 'ISK' => 'Icelandic króna',
+ 'JMD' => 'Jamaican dollar',
+ 'JOD' => 'Jordanian dinar',
+ 'JPY' => 'Japanese yen',
+ 'KES' => 'Kenyan shilling',
+ 'KGS' => 'Kyrgyzstani som',
+ 'KHR' => 'Cambodian riel',
+ 'KMF' => 'Comoro franc',
+ 'KPW' => 'North Korean won',
+ 'KRW' => 'South Korean won',
+ 'KWD' => 'Kuwaiti dinar',
+ 'KYD' => 'Cayman Islands dollar',
+ 'KZT' => 'Kazakhstani tenge',
+ 'LAK' => 'Lao kip',
+ 'LBP' => 'Lebanese pound',
+ 'LKR' => 'Sri Lankan rupee',
+ 'LRD' => 'Liberian dollar',
+ 'LSL' => 'Lesotho loti',
+ 'LYD' => 'Libyan dinar',
+ 'MAD' => 'Moroccan dirham',
+ 'MDL' => 'Moldovan leu',
+ 'MGA' => 'Malagasy ariary',
+ 'MKD' => 'Macedonian denar',
+ 'MMK' => 'Myanmar kyat',
+ 'MNT' => 'Mongolian tögrög',
+ 'MOP' => 'Macanese pataca',
+ 'MRO' => 'Mauritanian ouguiya',
+ 'MUR' => 'Mauritian rupee',
+ 'MVR' => 'Maldivian rufiyaa',
+ 'MWK' => 'Malawian kwacha',
+ 'MXN' => 'Mexican peso',
+ 'MXV' => 'Mexican Unidad de Inversion',
+ 'MYR' => 'Malaysian ringgit',
+ 'MZN' => 'Mozambican metical',
+ 'NAD' => 'Namibian dollar',
+ 'NGN' => 'Nigerian naira',
+ 'NIO' => 'Nicaraguan córdoba',
+ 'NOK' => 'Norwegian krone',
+ 'NPR' => 'Nepalese rupee',
+ 'NZD' => 'New Zealand dollar',
+ 'OMR' => 'Omani rial',
+ 'PAB' => 'Panamanian balboa',
+ 'PEN' => 'Peruvian Sol',
+ 'PGK' => 'Papua New Guinean kina',
+ 'PHP' => 'Philippine peso',
+ 'PKR' => 'Pakistani rupee',
+ 'PLN' => 'Polish złoty',
+ 'PYG' => 'Paraguayan guaraní',
+ 'QAR' => 'Qatari riyal',
+ 'RON' => 'Romanian leu',
+ 'RSD' => 'Serbian dinar',
+ 'RUB' => 'Russian ruble',
+ 'RWF' => 'Rwandan franc',
+ 'SAR' => 'Saudi riyal',
+ 'SBD' => 'Solomon Islands dollar',
+ 'SCR' => 'Seychelles rupee',
+ 'SDG' => 'Sudanese pound',
+ 'SEK' => 'Swedish krona',
+ 'SGD' => 'Singapore dollar',
+ 'SHP' => 'Saint Helena pound',
+ 'SLL' => 'Sierra Leonean leone',
+ 'SOS' => 'Somali shilling',
+ 'SRD' => 'Surinamese dollar',
+ 'SSP' => 'South Sudanese pound',
+ 'STD' => 'São Tomé and Príncipe dobra',
+ 'SVC' => 'Salvadoran colón',
+ 'SYP' => 'Syrian pound',
+ 'SZL' => 'Swazi lilangeni',
+ 'THB' => 'Thai baht',
+ 'TJS' => 'Tajikistani somoni',
+ 'TMT' => 'Turkmenistani manat',
+ 'TND' => 'Tunisian dinar',
+ 'TOP' => 'Tongan paʻanga',
+ 'TRY' => 'Turkish lira',
+ 'TTD' => 'Trinidad and Tobago dollar',
+ 'TWD' => 'New Taiwan dollar',
+ 'TZS' => 'Tanzanian shilling',
+ 'UAH' => 'Ukrainian hryvnia',
+ 'UGX' => 'Ugandan shilling',
+ 'USD' => 'United States dollar',
+ 'UYI' => 'Uruguay Peso en Unidades Indexadas',
+ 'UYU' => 'Uruguayan peso',
+ 'UZS' => 'Uzbekistan som',
+ 'VEF' => 'Venezuelan bolívar',
+ 'VND' => 'Vietnamese đồng',
+ 'VUV' => 'Vanuatu vatu',
+ 'WST' => 'Samoan tala',
+ 'XAF' => 'Central African CFA franc',
+ 'XCD' => 'East Caribbean dollar',
+ 'XOF' => 'West African CFA franc',
+ 'XPF' => 'CFP franc',
+ 'YER' => 'Yemeni rial',
+ 'ZAR' => 'South African rand',
+ 'ZMW' => 'Zambian kwacha',
+ 'ZWL' => 'Zimbabwean dollar'
+ ];
+ }
+
+
+ /**
+ * shouldProcess
+ *
+ * @param string $query
+ * @param integer $strlenght
+ * @return bool
+ */
+ public function shouldProcess(int $strlenght = 0)
+ {
+ if ($strlenght <= 3) {
+ return false;
+ }
+
+ $query = $this->query;
+ $currencies = $this->matchRegex();
+ $stopwords = $this->getStopWordsString($this->stop_words);
+
+ return preg_match('/^\d*\.?\d+ ?' . $currencies . ' ?' . $stopwords . '?/i', $query, $matches);
+ }
+
+
+ /**
+ * Process query
+ *
+ * @return string|array
+ */
+ public function processQuery()
+ {
+ $query = $this->query;
+ $data = $this->extractQueryData($query);
+
+ if (!$data) {
+ return false;
+ }
+
+ if ($data['amount'] <= 0 || $data['from'] == $data['to']) {
+ return $data['amount'] . $data['to'];
+ }
+
+ $locale = $this->getSetting('locale_currency', 'en_US');
+ setlocale(LC_MONETARY, $locale);
+
+ $converted = $this->convert($data);
+
+ if ($converted['error']) {
+ return $converted['error'];
+ }
+
+ $data['converted'] = [];
+ foreach ($converted as $key => $value) {
+ $data['converted'][$key] = [];
+
+ $total = money_format('%i', $value['total']);
+ $total = preg_replace("/[^0-9.,]/", '', $total);
+ $single = $value['single'];
+ $single = $this->formatNumber($value['single']);
+
+ $data['converted'][$key]['total'] = ['value' => $total, 'formatted' => "{$total}{$key}"];
+ $data['converted'][$key]['single'] = ['value' => $single, 'formatted' => "1{$data['from']} = {$single}{$key}"];
+ }
+
+ return $this->output($data);
+ }
+
+
+ /**
+ * Output
+ * build the output the way
+ * it should be displayed by Alfred
+ *
+ * @param array $result
+ * @return array
+ */
+ public function output($result)
+ {
+ $items = [];
+ $converted = $result['converted'];
+
+ foreach ($converted as $key => $value) {
+ $total = $value['total'];
+ $single = $value['single'];
+ $icon = 'flags/' . $key . '.png';
+
+ $items[] = [
+ 'title' => $total['formatted'],
+ 'subtitle' => $single['formatted'],
+ 'arg' => $total['value'],
+ 'icon' => ['path' => $icon],
+ 'mods' => [
+ 'cmd' => [
+ 'valid' => true,
+ 'arg' => $total['value'],
+ 'subtitle' => $this->lang['cmd'],
+ ],
+ 'alt' => [
+ 'valid' => true,
+ 'arg' => $this->cleanupNumber($total['value']),
+ 'subtitle' => $this->lang['alt'],
+ ],
+ ]
+ ];
+ }
+
+ return $items;
+ }
+
+
+ /**
+ * Handle conversion
+ *
+ * @param array $data
+ * @return array
+ */
+ public function convert($data)
+ {
+ $converted = [];
+ $amount = $data['amount'];
+ $from = $data['from'];
+ $to = $data['to'];
+ $use_cache = (isset($data['use_cache']) ? $data['use_cache'] : true);
+ $cache_seconds = ($use_cache ? 14400 : 0);
+ $fixer_apikey = $this->getSetting('fixer_apikey');
+
+ if (is_string($to)) {
+ $to = [$to];
+ }
+
+ foreach ($to as $currency) {
+ // Use Fixer io
+ if (!empty($fixer_apikey)) {
+ $cache_seconds = ($use_cache ? 7200 : 0);
+ $conversion = $this->fixerConversion($amount, $from, $currency, $cache_seconds);
+ } else {
+ $conversion = $this->exchangeRatesConversion($amount, $from, $currency, $cache_seconds);
+ }
+
+ if (isset($conversion['error']) && !empty($conversion['error'])) {
+ $converted['error'] = $conversion['error'];
+ }
+ $converted[$currency] = $conversion;
+ }
+
+ return $converted;
+ }
+
+
+ /**
+ * Fixer conversion
+ *
+ * @param int $amount
+ * @param string $from
+ * @param string $to
+ * @param int $cache_seconds
+ * @return array
+ */
+ private function fixerConversion($amount, $from, $to, $cache_seconds)
+ {
+ $cached = $this->getCachedConversion('fixer', $from, $to, $cache_seconds);
+ if ($cached) {
+ $cached = (float) $cached;
+ $value = $cached;
+ $total = $amount * $value;
+ }
+
+ if (!$cached) {
+ $exchange = $this->getRates($cache_seconds);
+
+ if (is_string($exchange)) {
+ return ['total' => '', 'single' => '', 'error' => $exchange];
+ }
+
+ $base = $exchange['base'];
+ $rates = $exchange['rates'];
+ $default_base_currency = $rates[$base];
+
+ $new_base_currency = $rates[$from]; //from currency
+ $base_exchange = $default_base_currency / $new_base_currency;
+ $value = ($rates[$to] * $base_exchange);
+ $total = $amount * $value;
+
+ $this->cacheConversion('fixer', $from, $to, $value);
+ }
+
+ return ['total' => $total, 'single' => $value, 'error' => false];
+ }
+
+
+ /**
+ * exchangeratesapi.io
+ *
+ * @param int $amount
+ * @param string $from
+ * @param string $to
+ * @param int $cache_seconds
+ * @return array
+ */
+ public function exchangeRatesConversion($amount, $from, $to, $cache_seconds)
+ {
+ $cached = $this->getCachedConversion('exchangerates', $from, $to, $cache_seconds);
+ if ($cached) {
+ $cached = (float) $cached;
+ $value = $cached;
+ }
+
+ if (!$cached) {
+ $this->required();
+
+ $converter = new \CurrencyConverter\CurrencyConverter;
+ $value = '';
+ $error = false;
+
+ try {
+ $value = $converter->convert($from, $to);
+ $this->cacheConversion('exchangerates', $from, $to, $value);
+ } catch (\Throwable $th) {
+ $error = true;
+ $message = $th->getMessage();
+ preg_match('/{(.*)}/', $message, $matches);
+
+ if ($matches && !empty($matches)) {
+ $value = $matches[1];
+ }
+ }
+
+ if ($error) {
+ return ['error' => $value];
+ }
+ }
+
+ return ['total' => $amount * $value, 'single' => $value, 'error' => false];
+ }
+
+
+ /**
+ * Extract query data
+ * extract the values from and to
+ * from the query typed by the user
+ * it returns from, to and amount
+ */
+ private function extractQueryData($query)
+ {
+ $amount = '';
+ $from = '';
+ $to = '';
+ $default_currency = $this->getBaseCurrency();
+ $stopwords = $this->getStopWordsString($this->stop_words, ' %s ');
+
+ preg_match('/^(\d*\.?\d+)[^\d]/i', $query, $amount_match);
+ if (empty($amount_match)) {
+ return false;
+ }
+
+ $amount = getVar($amount_match, 1);
+ $amount = trim($amount);
+ $string = str_replace($amount, '', $query);
+ $string = trim($string);
+
+ preg_match('/(.*).*' . $stopwords . '(.*)/i', $string, $matches);
+
+ // Matches strings like 100 usd to mxn
+ if (!empty($matches)) {
+ $matches = array_values(array_filter($matches));
+ $from = getVar($matches, 1);
+ $to = getVar($matches, 3);
+ }
+ // String is like 100 usd or 100 usd mxn
+ elseif (empty($matches)) {
+ $keywords = $this->keywords;
+
+ foreach ($keywords as $key => $value) {
+ if (is_array($value)) {
+ continue;
+ }
+ $key = $this->escapeKeywords($key);
+ $string = preg_replace('/(^|\W)' . $key . '(\W|$)/i', ' ' . $value . ' ', $string);
+ }
+
+ $string = preg_replace('!\s+!', ' ', $string);
+ $string = trim($string);
+
+
+ $data = explode(' ', $string);
+ $from = getVar($data, 0);
+ $to = getVar($data, 1);
+ }
+
+ $from = strtoupper($from);
+ $to = (!empty($to) ? strtoupper($to) : $default_currency);
+
+ $from = $this->getCorrectCurrency($from);
+ $to = (is_string($to) ? $this->getCorrectCurrency($to) : $to);
+
+ if (!$from || !$to) {
+ return false;
+ }
+
+ return [
+ 'from' => $from,
+ 'to' => $to,
+ 'amount' => $this->cleanupNumber($amount),
+ ];
+ }
+
+
+ /**
+ * Get rates
+ * if already cached and cache
+ * has not expired returns
+ * the cached rates otherwise
+ * it fetches the rates again
+ *
+ * @param int $cache_seconds number of seconds before the cache expires
+ * @return mixed array if sucess or string with error message
+ */
+ private function getRates($cache_seconds)
+ {
+ $dir = getDataPath('cache/fixer');
+ createDir($dir);
+
+ $file = $dir . '/rates.json';
+ if (file_exists($file)) {
+ $c = file_get_contents($file);
+
+ if (!empty($c)) {
+ $c = json_decode($c, true);
+ $updated = $c['timestamp'];
+ $time = time() - $updated;
+
+ // Only return cached rates if cache
+ // has not expired otherwise continue
+ // to fetch the new rates
+ if ($time < $cache_seconds) {
+ return $c;
+ }
+ }
+ }
+
+ $apikey = $this->getSetting('fixer_apikey');
+ if (empty($apikey)) {
+ throw new Exception('No API Key provided');
+ }
+
+ $c = file_get_contents("http://data.fixer.io/api/latest?access_key={$apikey}&format=1");
+ if (empty($c)) {
+ return $this->lang['fetch_error'];
+ }
+
+ file_put_contents($file, $c);
+
+ return json_decode($c, true);
+ }
+
+
+
+
+ /**
+ * Get correct currency
+ * the user can enter for example ¥
+ * and this function should return JPY
+ * so it will search if the key exists in the
+ * currencies list and translation keywords
+ *
+ * @param string $val
+ * @return string|bool
+ */
+ public function getCorrectCurrency($val)
+ {
+ if ($this->isValidCurrency($val)) {
+ return $val;
+ }
+
+ // $val = strtolower($val);
+ $val = mb_strtolower($val);
+ $val = $this->keywordTranslation($val, $this->keywords);
+ $val = strtoupper($val);
+ if ($this->isValidCurrency($val)) {
+ return $val;
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Regex
+ * create a regex from the
+ * available currencies array
+ *
+ * @return string
+ */
+ private function matchRegex()
+ {
+ $currencies = $this->currencyList;
+ $params = implode('|', array_keys($currencies));
+ $params .= '|' . implode('|', array_values($currencies));
+ $translation_keywords = $this->keywords;
+
+ if (!empty($translation_keywords)) {
+ $params .= '|' . implode('|', array_keys($translation_keywords));
+ }
+ $params = $this->escapeKeywords($params);
+
+ return '(' . $params . ')';
+ }
+
+
+ /**
+ * Is valid
+ * check if given srting
+ * is valid and exists in the
+ * available array
+ *
+ * @param string $val
+ * @return bool
+ */
+ public function isValidCurrency($val)
+ {
+ return isset($this->currencyList[$val]);
+ }
+
+
+ /**
+ * Get the base currency
+ * defined in the workflow configuration
+ *
+ * @return array
+ */
+ public function getBaseCurrency()
+ {
+ $currency = $this->getSetting('base_currency', 'USD');
+ if (is_string($currency)) { // convert old setting to array
+ $currency = [$currency];
+ }
+
+ return $currency;
+ }
+
+
+ /**
+ * Get list
+ * get a readable units list
+ * to display to the user
+ *
+ * @return array
+ */
+ function listAvailable()
+ {
+ $translation = $this->getTranslation('currency');
+ $units = $this->currencyList;
+ $list = [];
+ foreach ($units as $key => $value) {
+ $curr = $key;
+ $curr_name = (isset($translation[$curr]) ? $translation[$curr] : $curr);
+
+ $list[] = [
+ 'title' => $curr,
+ 'subtitle' => $curr_name,
+ 'arg' => $curr,
+ 'match' => $curr . ' ' . $curr_name,
+ 'autocomplete' => $curr_name,
+ 'valid' => true,
+ 'mods' => [
+ 'cmd' => [
+ 'valid' => true,
+ 'arg' => $curr,
+ 'subtitle' => $this->getText('action_copy'),
+ ]
+ ],
+ 'icon' => [
+ 'path' => "flags/{$curr}.png"
+ ]
+ ];
+ }
+
+ return $list;
+ }
+
+ /**
+ * Requred
+ * includes requred files
+ * only if conversion can be processed
+ *
+ * @return void
+ */
+ private function required()
+ {
+
+ // $dir = __DIR__ . DIRECTORY_SEPARATOR . 'currency' . DIRECTORY_SEPARATOR;
+ $dir = dirname(__DIR__, 1) . '/lib/currency';
+
+ include $dir . '/Cache/Adapter/CacheAdapterInterface.php';
+ include $dir . '/Cache/Adapter/AbstractAdapter.php';
+ include $dir . '/Cache/Adapter/FileSystem.php';
+ include $dir . '/Cache/Adapter/ZendAdapter.php';
+
+ include $dir . '/Provider/ProviderInterface.php';
+ include $dir . '/Provider/ExchangeRatesIo.php';
+ include $dir . '/Provider/FixerApi.php';
+
+ include $dir . '/Exception/ExceptionInterface.php';
+ include $dir . '/Exception/InvalidArgumentException.php';
+ include $dir . '/Exception/RunTimeException.php';
+ include $dir . '/Exception/UnsupportedCurrencyException.php';
+
+ include $dir . '/guzzle/Exception/GuzzleException.php';
+ include $dir . '/guzzle/Exception/TransferException.php';
+ include $dir . '/guzzle/Exception/RequestException.php';
+ include $dir . '/guzzle/Exception/BadResponseException.php';
+ include $dir . '/guzzle/Exception/ConnectException.php';
+ include $dir . '/guzzle/Exception/ClientException.php';
+ include $dir . '/guzzle/Exception/InvalidArgumentException.php';
+ include $dir . '/guzzle/Exception/SeekException.php';
+ include $dir . '/guzzle/Exception/ServerException.php';
+ include $dir . '/guzzle/Exception/TooManyRedirectsException.php';
+ include $dir . '/guzzle/Handler/Proxy.php';
+
+ include $dir . '/guzzle/Handler/EasyHandle.php';
+ include $dir . '/guzzle/Handler/CurlMultiHandler.php';
+ include $dir . '/guzzle/Handler/CurlHandler.php';
+ include $dir . '/guzzle/Handler/CurlFactoryInterface.php';
+ include $dir . '/guzzle/Handler/CurlFactory.php';
+ include $dir . '/guzzle/Handler/StreamHandler.php';
+ include $dir . '/guzzle/functions.php';
+ include $dir . '/guzzle/PrepareBodyMiddleware.php';
+ include $dir . '/guzzle/Middleware.php';
+ include $dir . '/guzzle/RedirectMiddleware.php';
+ include $dir . '/guzzle/RequestOptions.php';
+ include $dir . '/guzzle/HandlerStack.php';
+ include $dir . '/PSR7/functions.php';
+ include $dir . '/PSR7/UriInterface.php';
+ include $dir . '/PSR7/MessageInterface.php';
+ include $dir . '/PSR7/Uri.php';
+ include $dir . '/PSR7/MessageTrait.php';
+ include $dir . '/PSR7/ResponseInterface.php';
+ include $dir . '/PSR7/Response.php';
+ include $dir . '/PSR7/RequestInterface.php';
+ include $dir . '/PSR7/Request.php';
+ include $dir . '/PSR7/StreamInterface.php';
+ include $dir . '/PSR7/Stream.php';
+
+ include $dir . '/guzzle/Promise/functions.php';
+ include $dir . '/guzzle/Promise/TaskQueueInterface.php';
+ include $dir . '/guzzle/Promise/TaskQueue.php';
+ include $dir . '/guzzle/Promise/PromiseInterface.php';
+ include $dir . '/guzzle/Promise/Promise.php';
+ include $dir . '/guzzle/Promise/FulfilledPromise.php';
+
+ include $dir . '/guzzle/ClientInterface.php';
+ include $dir . '/guzzle/Client.php';
+
+ include $dir . '/CurrencyConverterInterface.php';
+ include $dir . '/CurrencyConverter.php';
+ include $dir . '/CountryToCurrency.php';
+ }
+}
diff --git a/workflow/tools/percentage.php b/workflow/tools/percentage.php
new file mode 100644
index 0000000..af54ede
--- /dev/null
+++ b/workflow/tools/percentage.php
@@ -0,0 +1,245 @@
+query = $query;
+ $this->keywords = $this->getKeywords('percentage');
+ $this->stop_words = $this->getStopWords('percentage');
+ $this->lang = $this->getTranslation('percentage');
+ }
+
+
+ /**
+ * shouldProcess
+ *
+ * @param string $query
+ * @param integer $strlenght
+ * @return bool
+ */
+ public function shouldProcess(int $strlenght = 0)
+ {
+ if ($strlenght >= 3 && strpos($this->query, ' ') !== false && strpos($this->query, '%') !== false) {
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Process query
+ *
+ * @return string|array
+ */
+ public function processQuery()
+ {
+ $query = $this->query;
+ $keys = $this->keywords;
+ $stop_words = $this->getStopWordsString($this->stop_words);
+ $from = array_keys($keys);
+ $to = array_values($keys);
+
+ $from[] = 'plus';
+ $to[] = '+';
+ $from[] = 'minus';
+ $to[] = '-';
+
+ $query = str_replace(
+ $from,
+ $to,
+ $query
+ );
+
+ $this->query = $query;
+ $processed = '';
+
+ // Calculate Percentage of value
+ // 30% de 100 = 30
+ if (preg_match('/^\d*\.?\d*% ?' . $stop_words . '? ?(\d+)?/', $query, $matches)) {
+ $processed = $this->percentageOf();
+ }
+
+ // Total plus percentage
+ // 100 + 16% = 116
+ elseif (preg_match('/^\d*\.?\d+ ?\+ ?\d*\.?\d*%$/', $query, $matches)) {
+ $processed = $this->totalPlusPercentage();
+ }
+
+ // Total minus percentage
+ // 116 - 16% = 100
+ elseif (preg_match('/^\d*\.?\d+ ?- ?\d*\.?\d*%$/', $query, $matches)) {
+ $processed = $this->totalMinusPercentage();
+ }
+
+ // Calculates `a` percent of `b` is what percent?
+ // 30 % 40 = 75%
+ // So 30 is 75% of 40.
+ elseif (preg_match('/^\d+ +?\%.+?\d+/', $query, $matches)) {
+ $processed = $this->percentOfTwoNumbers();
+ }
+
+ return $this->output($processed);
+ }
+
+
+ /**
+ * Output
+ * build the output the way
+ * it should be displayed by Alfred
+ *
+ * @param array $result
+ * @return array
+ */
+ public function output($result)
+ {
+ $items = [];
+ $result = (is_array($result) ? $result : [$result]);
+
+ foreach ($result as $key => $val) {
+ $items[] = [
+ 'title' => $val,
+ 'arg' => $val,
+ 'subtitle' => $this->getText('action_copy'),
+ 'mods' => [
+ 'alt' => [
+ 'valid' => true,
+ 'arg' => $this->cleanupNumber($val),
+ 'subtitle' => $this->lang['alt'],
+ ],
+ ]
+ ];
+ }
+
+ return $items;
+ }
+
+
+ /**
+ * Percentage of
+ * 30% of 100 = 30
+ *
+ * @return string
+ */
+ private function percentageOf()
+ {
+ $query = $this->query;
+ $query = preg_replace("/[^0-9.%]/", ' ', $query);
+ $query = preg_replace('!\s+!', ' ', $query);
+ $data = explode(' ', $query);
+
+ if (count($data) < 2) {
+ return false;
+ }
+ $percent = $this->cleanupNumber($data[0]);
+ $amount = $this->cleanupNumber($data[1]);
+
+ return $this->formatNumber(($percent / 100) * $amount);
+ }
+
+
+ /**
+ * Total plus percetage
+ * 100 + 16% = 116
+ *
+ * @return string
+ */
+ private function totalPlusPercentage()
+ {
+ $query = $this->query;
+ $query = preg_replace('/\s+/', '', $query);
+ $query = preg_replace("/ +?\+ +?/", ' ', $query);
+ $data = explode('+', $query);
+
+ if (count($data) < 2) {
+ return false;
+ }
+
+ $amount = $this->cleanupNumber($data[0]);
+ $percent = $this->cleanupNumber($data[1]);
+
+ return $this->formatNumber($amount + (($percent / 100) * $amount));
+ }
+
+ /**
+ * Total minus percetage
+ * 116 - 16% = 100
+ *
+ * @return array
+ */
+ private function totalMinusPercentage()
+ {
+ $query = $this->query;
+ $query = preg_replace('/\s+/', '', $query);
+ $data = explode('-', $query);
+
+ if (count($data) < 2) {
+ return false;
+ }
+
+ $amount = $this->cleanupNumber($data[0]);
+ $percent = $this->cleanupNumber($data[1]);
+ $percent_min = ($percent / 100) * $amount;
+
+ $result = $percent == 100 ? '0.00' : $this->formatNumber($amount - $percent_min);
+ $saved = $amount - $this->cleanupNumber($result);
+ $famount = $this->formatNumber($amount);
+ $saved = $this->formatNumber($saved);
+
+ $values = [];
+ $values[$result] = $famount . ' - ' . $data[1] . ' = ' . $result;
+ $values[$saved] = $famount . ' - ' . $result . ' = ' . $saved;
+ return $values;
+ }
+
+
+ /**
+ * Percent of two numbers
+ * 30 % 40 = 75%
+ * So 30 is 75% of 40.
+ *
+ * @return array
+ */
+ private function percentOfTwoNumbers()
+ {
+ $query = $this->query;
+ $query = preg_replace("/ +?\% +?/", ' ', $query);
+ $query = preg_replace('!\s+!', ' ', $query);
+ $data = explode(' ', $query);
+
+ if (count($data) < 2) {
+ return false;
+ }
+
+ $val1 = $this->cleanupNumber($data[0]);
+ $val2 = $this->cleanupNumber($data[1]);
+ $percentage = ($val1 / $val2) * 100;
+ $percentage = $this->formatNumber($percentage);
+
+ $pincrease = ($val2 - $val1) / $val1 * 100;
+ $pincrease = $this->formatNumber($pincrease);
+
+ $pdecrease = ($val2 - $val1) / $val2 * 100;
+ $pdecrease = $this->formatNumber($pdecrease);
+ $lang = $this->lang;
+
+ $values = [];
+ $values["{$percentage}%"] = sprintf($lang['result'], $val1, "{$percentage}%", $val2);
+ $values["{$pincrease}%"] = sprintf($lang['increase'], $val1, $val2, "{$pincrease}%");
+ $values["{$pdecrease}%"] = sprintf($lang['decrease'], $val2, $val1, "{$pdecrease}%");
+
+ return $values;
+ }
+}
diff --git a/workflow/tools/pxemrem.php b/workflow/tools/pxemrem.php
new file mode 100644
index 0000000..37bff2d
--- /dev/null
+++ b/workflow/tools/pxemrem.php
@@ -0,0 +1,206 @@
+query = str_replace(',', '', $query);
+ $this->keywords = $this->getKeywords('units');
+ $this->stop_words = $this->getStopWords('units');
+ }
+
+
+ /**
+ * shouldProcess
+ *
+ * @param string $query
+ * @param integer $strlenght
+ * @return bool
+ */
+ public function shouldProcess(int $strlenght = 0)
+ {
+ if ($strlenght <= 3) {
+ return false;
+ }
+
+ $query = $this->query;
+ return preg_match('/^\d*\.?\d+ ?(px|em|rem|pt) ?/', $query, $matches);
+ }
+
+
+ /**
+ * Process query
+ *
+ * @return string|array
+ */
+ public function processQuery()
+ {
+ $query = $this->query;
+ $stop_words = $this->getStopWordsString($this->stop_words, ' %s ');
+ $query = preg_replace("/ ?" . $stop_words . " ?/i", ' ', $query);
+ $query = preg_replace('!\s+!', ' ', $query);
+
+ preg_match('/^(\d*\.?\d+) ?(px|em|rem|pt) ?' . $stop_words . '? ?(px|em|rem|pt)? ?(base.*px$)?/', $query, $matches);
+ $matches = array_values(array_filter($matches));
+
+ $from = 0;
+ $from_unit = '';
+ $total_inputs = count($matches);
+ $target = '';
+ $base = $this->getSetting('base_pixels', '16px');
+ $base = $this->cleanupNumber($base);
+
+ if ($total_inputs >= 3) {
+ $from = $this->cleanupNumber($matches[1]);
+ $from_unit = trim($matches[2]);
+ }
+
+ if ($total_inputs == 4) {
+ if (strpos($matches[3], 'base') !== false) {
+ $base = preg_replace('/[^0-9]/', '', $matches[3]);
+ $base = $this->cleanupNumber($base);
+ } else {
+ $target = trim($matches[3]);
+ }
+ } elseif ($total_inputs == 5) {
+ $target = trim($matches[3]);
+ $base = preg_replace('/[^0-9]/', '', $matches[4]);
+ $base = $this->cleanupNumber($base);
+ }
+
+ $result = [];
+ $units = ['px', 'em', 'rem', 'pt'];
+ $data = [
+ 'from' => $from,
+ 'from_unit' => $from_unit,
+ 'to' => $target,
+ 'base' => $base,
+ ];
+
+ if (empty($target)) {
+ if (($key = array_search($from_unit, $units)) !== false) {
+ unset($units[$key]);
+ }
+ foreach ($units as $key => $value) {
+ $data['to'] = $value;
+ $val = $this->calculateFontSize($data);
+ $result[$val] = $val;
+ }
+ } else {
+ $val = $this->calculateFontSize($data);
+ $result[$val] = $val;
+ }
+
+ return $this->output($result);
+ }
+
+
+
+ /**
+ * Output
+ * build the output the way
+ * it should be displayed by Alfred
+ *
+ * @param array $result
+ * @return array
+ */
+ public function output($result)
+ {
+ $items = [];
+ $result = (is_array($result) ? $result : [$result]);
+
+ foreach ($result as $key => $val) {
+ $items[] = [
+ 'title' => $val,
+ 'arg' => $key,
+ 'subtitle' => $this->getText('action_copy'),
+ ];
+ }
+
+ return $items;
+ }
+
+
+
+ /**
+ * Do font calculations
+ *
+ * @param array $data
+ * @return string
+ */
+ private function calculateFontSize($data)
+ {
+ $result = 0;
+ $from = $data['from'];
+ $from_unit = $data['from_unit'];
+ $to = $data['to'];
+ $base = $data['base'];
+ $emrem = ['em', 'rem'];
+ $pt = 0.75;
+
+ // exit if no required action
+ if ($from_unit == $to || (in_array($from_unit, $emrem) && in_array($to, $emrem))) {
+ return $from . $to;
+ }
+
+ // from px
+ if ($from_unit == 'px') {
+ if ($to == 'pt') {
+ return ($from * $pt) . $to;
+ }
+ if (in_array($to, $emrem)) {
+ return ($from / $base) . $to;
+ }
+ }
+
+ // from pt
+ if ($from_unit == 'pt') {
+ if ($to == 'px') {
+ return ($from / $pt) . $to;
+ } elseif (in_array($to, $emrem)) {
+ return (($from / $pt) / $base) . $to;
+ }
+ }
+
+ // from em/rem
+ if (in_array($from_unit, $emrem)) {
+ if ($to == 'px') {
+ return ($from * $base) . $to;
+ }
+ if ($to == 'pt') {
+ return (($from * $base) * $pt) . $to;
+ }
+ }
+
+ return $result;
+ }
+}
diff --git a/workflow/tools/time.php b/workflow/tools/time.php
new file mode 100644
index 0000000..3f25efb
--- /dev/null
+++ b/workflow/tools/time.php
@@ -0,0 +1,502 @@
+query = (!empty($query) ? $query : 'now');
+ if ($this->isTimestamp($this->query)) {
+ $this->query = (int) $this->query;
+ }
+
+ $this->lang = $this->getTranslation('time');
+ $this->keywords = $this->getKeywords('time');
+ $this->timezone = getVar($argv, 3, $this->getSetting('time_zone', 'America/Los_Angeles'));
+ $this->display_formats = $this->getSetting('timezones', ['F jS, Y, g:i:s a']);
+ $this->display_language = $this->getSetting('language', defaultLang());
+ }
+
+
+ /**
+ * shouldProcess
+ *
+ * @param string $query
+ * @param integer $strlenght
+ * @return bool
+ */
+ public function shouldProcess(int $strlenght = 0)
+ {
+ if ($strlenght >= 3) {
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Process query
+ *
+ * @return string|array
+ */
+ public function processQuery()
+ {
+ Date::setLocale($this->display_language);
+
+ if (!$this->shouldProcess(strlen($this->query))) {
+ return false;
+ }
+
+ // From user lang to en_us so time conversion
+ // is able to understand some words
+ $query = $this->translateDate($this->query);
+ $data = $this->extractQueryData($query);
+
+ return $this->output($data);
+ }
+
+
+ /**
+ * Output
+ * build the output the way
+ * it should be displayed by Alfred
+ *
+ * @param array $result
+ * @return array
+ */
+ public function output($processed)
+ {
+ $strings = $this->lang;
+ $items = [];
+
+ if (!$processed) {
+ $items[] = [
+ 'title' => '...',
+ 'subtitle' => $strings['notvalid_subtitle'],
+ 'valid' => false,
+ ];
+ return $items;
+ }
+
+ // $instance = getVar($processed, 'instance', false);
+ $instance = (isset($processed['instance']) ? $processed['instance'] : false);
+
+ if (!$instance) {
+ $items[] = [
+ 'title' => $processed['title'],
+ 'subtitle' => $processed['subtitle'],
+ 'arg' => $processed['val'],
+ ];
+ return $items;
+ }
+
+
+ foreach ($this->display_formats as $format) {
+ $date = $instance->format($format);
+ $items[] = [
+ 'title' => $date,
+ 'subtitle' => sprintf($strings['format_subtitle'], $format),
+ 'arg' => $date
+ ];
+ }
+
+ if ($this->query !== 'now') {
+ $now = Date::now();
+ if ($now > $instance) {
+ $count = $instance->ago();
+ } else {
+ $count = $instance->timespan($now);
+ }
+
+ $items[] = [
+ 'title' => $count,
+ 'subtitle' => "Timespan",
+ 'arg' => $count,
+ ];
+ }
+
+ $items[] = [
+ 'title' => $instance->getTimestamp(),
+ 'subtitle' => "Timestamp",
+ 'arg' => $instance->getTimestamp()
+ ];
+
+ return $items;
+ }
+
+
+
+ /**
+ * Extract query data
+ * extract the values from and to
+ * from the query typed by the user
+ * it returns from, to and amount
+ */
+ private function extractQueryData($query)
+ {
+ $data = [];
+ $strings = $this->lang;
+
+ // handle two dates like: 25 December, 2019 - 31 December, 2019
+ if (strpos($query, ' - ') !== false) {
+ $data = str_replace(' - ', '|', $query);
+ $data = explode('|', $data);
+ $data = array_filter($data);
+
+ if (count($data) == 2) {
+ $time1 = $this->getDate(trim($data[0]));
+ $time2 = $this->getDate(trim($data[1]));
+ $subtitle = sprintf($strings['difference_subtitle'], $time1, $time2);
+
+ if ($time1 && $time2) {
+ return [
+ 'title' => $time1->timespan($time2),
+ 'val' => $time1->timespan($time2),
+ 'subtitle' => $subtitle,
+ ];
+ }
+
+ return false;
+ }
+ }
+
+ // Handle Until
+ if ($until = $this->dateIsUntill($query)) {
+ $utime = $until['time'];
+ $get_tr = $until['get'];
+ $check = $this->timesDifference('now', $utime, $until['get']);
+ $title = $check . ' ' . $get_tr;
+ $subtitle = sprintf($strings['until_subtitle'], $get_tr, $utime);
+
+ $title = str_replace(
+ array_keys($strings),
+ array_values($strings),
+ $title
+ );
+
+ $subtitle = str_replace(
+ array_keys($strings),
+ array_values($strings),
+ $subtitle
+ );
+
+ if ($check) {
+ return [
+ 'title' => $title,
+ 'val' => $check,
+ 'subtitle' => $subtitle,
+ ];
+ }
+
+ return false;
+ }
+
+ // Handle End of
+ if ($endof = $this->dateIsEndOf($query)) {
+ $end = $this->getDate($endof);
+ if ($end) {
+ return [
+ 'instance' => $end
+ ];
+ }
+
+ return false;
+ }
+
+ // Handle Start of
+ if ($startof = $this->dateIsStartOf($query)) {
+ $start = $this->getDate($startof);
+ if ($start) {
+ return [
+ 'instance' => $start
+ ];
+ }
+
+ return false;
+ } elseif ($this->isTimestamp($query)) {
+ $query = (int) $query;
+ }
+
+ $processed = $this->getDate($query);
+ if ($processed) {
+ return [
+ 'instance' => $processed
+ ];
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Translate on begin
+ * given a query like
+ * +30 días
+ * it need to be converted to
+ * +30 days so the code can understand it
+ *
+ * @param string $query
+ * @return string
+ */
+ function translateDate($query)
+ {
+ $query = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $query);
+ $strs = $this->lang;
+
+ if ($query == 'time') {
+ return 'now';
+ }
+
+ if (is_numeric($query) || empty($strs)) {
+ return $query;
+ }
+
+ $query = mb_strtolower($query, 'UTF-8');
+ $keys = $this->keywords;
+ foreach ($keys as $k => $value) {
+ if (is_array($value)) {
+ continue;
+ }
+ $query = str_replace($k, $value, $query);
+ }
+
+ $query = str_replace(
+ array_values($strs),
+ array_keys($strs),
+ $query
+ );
+
+ return $query;
+ }
+
+
+ /**
+ * Get date instance
+ * return a date instance with
+ * the specified time
+ *
+ * @param string $time
+ * @return object
+ */
+ private function getDate($time = 'now')
+ {
+ $d = false;
+ try {
+ $d = new Date($time, new DateTimeZone($this->timezone));
+ } catch (\Throwable $th) {
+ throw $th;
+ }
+ return $d;
+ }
+
+ /**
+ * Time diffrence
+ * get the time difference between
+ * to dates in the specified format
+ *
+ * @param string $time1
+ * @param string $time2
+ * @param string $format
+ * @return string
+ */
+ function timesDifference($time1, $time2, $format = 'hours')
+ {
+ $time1 = new Date($time1, new DateTimeZone($this->timezone));
+ $time2 = new Date($time2, new DateTimeZone($this->timezone));
+
+ if ($format == 'days') {
+ $diff_hours = $time1->diffInHours($time2);
+ if ($diff_hours < 24) {
+ return $diff_hours;
+ }
+
+ return round($diff_hours / 24);
+ }
+ if ($format == 'hours') {
+ return $time1->diffInHours($time2);
+ }
+ if ($format == 'minutes') {
+ return $time1->diffInMinutes($time2);
+ }
+ if ($format == 'seconds') {
+ return $time1->diffInSeconds($time2);
+ }
+ if ($format == 'milliseconds') {
+ return $time1->diffInMilliseconds($time2);
+ }
+ if ($format == 'microseconds') {
+ return $time1->diffInMicroseconds($time2);
+ }
+ }
+
+
+ /**
+ * Is timestamp
+ * check if passed query is timestamp
+ *
+ * @param mixed $timestamp
+ * @return boolean
+ */
+ public function isTimestamp($timestamp)
+ {
+ if (!is_numeric($timestamp)) {
+ return false;
+ }
+ return ((string) (int) $timestamp === $timestamp)
+ && ($timestamp <= PHP_INT_MAX)
+ && ($timestamp >= ~PHP_INT_MAX);
+ }
+
+
+ /**
+ * Time Until
+ * check if query ia a until query
+ *
+ * @param string $time
+ * @return mixed
+ */
+ private function dateIsUntill($time)
+ {
+ $keys = $this->timeKeywordsRegex('until');
+ preg_match('/\w+ ' . $keys . ' .*/i', $time, $matches);
+
+ if (!$matches || empty($matches)) {
+ return false;
+ }
+
+ $time = preg_replace('/ ' . $keys . ' /i', '|', $time);
+ $time = explode('|', $time);
+ $time = array_filter($time);
+
+ $k = (isset($time[0]) && !empty($time[0]) ? $time[0] : false);
+ $date = (isset($time[1]) && !empty($time[1]) ? $time[1] : false);
+ $valid_k = ['days', 'day', 'hours', 'hour', 'minutes', 'minutes', 'seconds', 'second'];
+
+ if (!in_array($k, $valid_k) || !$date) {
+ return false;
+ }
+
+ return ['get' => $k, 'time' => $date];
+ }
+
+
+ /**
+ * Time end of
+ * check if query ia a until query end
+ *
+ * @param string $time
+ * @return mixed
+ */
+ private function dateIsEndOf($time)
+ {
+ $keys = $this->timeKeywordsRegex('end of');
+ preg_match('/^' . $keys . ' .*/i', $time, $matches);
+
+ if (!$matches || empty($matches)) {
+ return false;
+ }
+
+ $time = preg_replace('/^' . $keys . '/i', '', $time);
+ $time = trim($time);
+ if ($time == 'year') {
+ $year = $this->getDate()->format('Y');
+ $time = $year . '-12-31 23:59:59';
+ } elseif (is_numeric($time) && strlen($time) == 4) {
+ $time = $time . '-12-31 23:59:59';
+ }
+ return $time;
+ }
+
+
+ /**
+ * Time start of
+ * check if query ia a until query start
+ *
+ * @param string $time
+ * @return mixed
+ */
+ private function dateIsStartOf($time)
+ {
+ $keys = $this->timeKeywordsRegex('start of');
+ preg_match('/^' . $keys . ' .*/i', $time, $matches);
+
+ if (!$matches || empty($matches)) {
+ return false;
+ }
+
+ $time = preg_replace('/^' . $keys . '/i', '', $time);
+ $time = trim($time);
+ if ($time == 'year') {
+ $time = 'first day of January this year';
+ } else {
+ if (is_numeric($time) && strlen($time) == 4) {
+ $time = $time . '-01-01';
+ }
+ }
+
+ return $time;
+ }
+
+
+ /**
+ * Time keywords
+ * some keywords used to
+ * trigger diferent actions
+ *
+ * @param string $check
+ * @return mixed
+ */
+ private function timeKeywords($check = '')
+ {
+ $data = [
+ 'until' => [
+ 'hasta'
+ ],
+ 'between' => [
+ 'entre'
+ ],
+ 'start of' => [
+ 'inicio de'
+ ],
+ 'end of' => [
+ 'fin de'
+ ],
+ ];
+
+ if (isset($data[$check])) {
+ return $data[$check];
+ }
+
+ return $data;
+ }
+
+
+ /**
+ * Regex
+ * create a regex based on the keywords
+ *
+ * @param string $check
+ * @return string
+ */
+ private function timeKeywordsRegex($check)
+ {
+ $keys = $this->timeKeywords($check);
+ $params = implode('|', array_values($keys));
+ return '(' . $check . '|' . $params . ')';
+ }
+}
diff --git a/workflow/tools/units.php b/workflow/tools/units.php
new file mode 100644
index 0000000..6611677
--- /dev/null
+++ b/workflow/tools/units.php
@@ -0,0 +1,481 @@
+query = str_replace(',', '', $query);
+ $this->lang = $this->getTranslation('units');
+ $this->keywords = $this->getKeywords('units');
+ $this->stop_words = $this->getStopWords('units');
+ $this->unitsList = $this->availableUnits();
+ }
+
+ /**
+ * List of available units
+ *
+ * @return array
+ */
+ private function availableUnits()
+ {
+ return [
+ 'length' => [
+ 'm',
+ 'km',
+ 'dm',
+ 'cm',
+ 'mm',
+ 'μm',
+ 'nm',
+ 'pm',
+ 'in',
+ 'ft',
+ 'yd',
+ 'mi',
+ 'h',
+ 'ly',
+ 'au',
+ 'pc',
+ ],
+ 'area' => [
+ 'm2',
+ 'km2',
+ 'cm2',
+ 'mm2',
+ 'ft2',
+ 'mi2',
+ 'ac',
+ 'ha',
+ 'ha',
+ ],
+ 'volume' => [
+ 'dm3',
+ 'l',
+ 'ml',
+ 'cm3',
+ 'hl',
+ 'kl',
+ 'm3',
+ 'pt',
+ 'gal',
+ 'qt',
+ 'ft3',
+ 'in3',
+ ],
+ 'weight' => [
+ 'kg',
+ 'g',
+ 'mg',
+ 'N',
+ 'st',
+ 'lb',
+ 'oz',
+ 't',
+ 'ukt',
+ 'ust',
+ ],
+ 'speed' => [
+ 'mps',
+ 'kph',
+ 'mph',
+ 'fps',
+ ],
+ 'rotation' => [
+ 'deg',
+ 'rad',
+ ],
+ 'temperature' => [
+ 'k',
+ 'c',
+ 'f',
+ ],
+ 'pressure' => [
+ 'pa',
+ 'kpa',
+ 'mpa',
+ 'bar',
+ 'mbar',
+ 'psi',
+ ],
+ 'time' => [
+ 's',
+ 'year',
+ 'month',
+ 'week',
+ 'day',
+ 'hr',
+ 'min',
+ 'ms',
+ 'μs',
+ 'ns',
+ ],
+ 'energy' => [
+ 'j',
+ 'kj',
+ 'mj',
+ 'cal',
+ 'Nm',
+ 'ftlb',
+ 'whr',
+ 'kwhr',
+ 'mwhr',
+ 'mev',
+ ],
+ ];
+ }
+
+
+ /**
+ * shouldProcess
+ *
+ * @param string $query
+ * @param integer $strlenght
+ * @return bool
+ */
+ public function shouldProcess(int $strlenght = 0)
+ {
+ if ($strlenght <= 3) {
+ return false;
+ }
+
+ $query = $this->query;
+ $units = $this->matchRegex();
+ $stopwords = $this->getStopWordsString($this->stop_words);
+
+ return preg_match('/^\d*\.?\d+ ?' . $units . ' ?' . $stopwords . '?/i', $query, $matches);
+ }
+
+
+ /**
+ * Process query
+ *
+ * @return string|array
+ */
+ public function processQuery()
+ {
+ $query = $this->query;
+ $data = $this->extractQueryData($query);
+
+ return $this->output($this->convert($data));
+ }
+
+ /**
+ * Output
+ * build the output the way
+ * it should be displayed by Alfred
+ *
+ * @param array $result
+ * @return array
+ */
+ public function output($result)
+ {
+ $items = [
+ 'title' => $result,
+ 'arg' => $result,
+ 'subtitle' => $this->getText('action_copy'),
+ 'mods' => [
+ 'cmd' => [
+ 'valid' => true,
+ 'arg' => $result,
+ 'subtitle' => $this->lang['cmd'],
+ ],
+ 'alt' => [
+ 'valid' => true,
+ 'arg' => $this->cleanupNumber($result),
+ 'subtitle' => $this->lang['alt'],
+ ],
+ ]
+ ];
+ return $items;
+ }
+
+ /**
+ * Make actual conversion
+ *
+ * @param array $data
+ * @return mixed
+ */
+ public function convert($data)
+ {
+ $from = $data['from'];
+ $to = $data['to'];
+ $amount = $data['amount'];
+ $from_type = $this->getUnitType($from);
+ $to_type = $this->getUnitType($to);
+
+ if (empty($from_type) || empty($to_type)) {
+ return false;
+ }
+
+ if ($to_type !== $from_type) {
+ $units_str = $this->getTranslation('units');
+ return sprintf($units_str['error'], $this->standardUnit($from), $this->standardUnit($to));
+ }
+
+ if ($from == 'year' && $to == 'month') {
+ $converted = $amount * 12;
+ } else {
+ $conversion_error = false;
+ try {
+ $convert = new Convertor($amount, $from);
+ $converted = $convert->to($to);
+ } catch (\Throwable $th) {
+ $conversion_error = $th->getMessage();
+ }
+
+ if ($conversion_error) {
+ return $conversion_error;
+ }
+ }
+
+ $decimals = -1;
+ if ($from_type == 'temperature') {
+ $decimals = 1;
+ }
+
+ // Before displaying the result
+ // Convert some units to readable human form
+ if ($from_type == 'time') {
+ $time_human_units = ['seconds', 'years', 'months', 'weeks', 'days', 'hours', 'minutes', 'milliseconds'];
+ if ($converted > 1) {
+ $to = str_replace(
+ ['s', 'year', 'month', 'week', 'day', 'hr', 'min', 'ms'],
+ $time_human_units,
+ $to
+ );
+ }
+ $strings = $this->getTranslation('time');
+ if (is_array($strings) && isset($strings[$to])) {
+ $to = $strings[$to];
+ }
+ $to = ' ' . $to;
+ }
+
+ return $this->formatNumber($converted, $decimals) . $this->standardUnit($to);
+ }
+
+
+
+ /**
+ * Extract query data
+ * extract the values from and to
+ * from the query typed by the user
+ * it returns from, to and amount
+ */
+ private function extractQueryData($query)
+ {
+ preg_match('/^(\d*\.?\d+)[^\d]/i', $query, $amount_match);
+ if (empty($amount_match)) {
+ return false;
+ }
+
+ $stopwords = $this->getStopWordsString($this->stop_words, ' %s ');
+ $amount = getVar($amount_match, 1);
+ $amount = trim($amount);
+ $string = str_replace($amount, '', $query);
+ $string = trim($string);
+
+ preg_match('/(.*).*' . $stopwords . '(.*)/i', $string, $matches);
+
+ // Matches strings like 100 kilograms to ounces
+ if (!empty($matches)) {
+ $matches = array_values(array_filter($matches));
+ $from = getVar($matches, 1);
+ $to = getVar($matches, 3);
+ } elseif (empty($matches)) {
+ $keywords = $this->keywords;
+
+ foreach ($keywords as $key => $value) {
+ if (is_array($value)) {
+ continue;
+ }
+ $key = $this->escapeKeywords($key);
+ $string = preg_replace('/(^|\W)' . $key . '(\W|$)/i', ' ' . $value . ' ', $string);
+ }
+
+ $string = preg_replace('!\s+!', ' ', $string);
+ $string = trim($string);
+ $data = explode(' ', $string);
+ $from = getVar($data, 0);
+ $to = getVar($data, 1);
+ }
+
+ if (empty($from) || empty($to)) {
+ return false;
+ }
+
+ return [
+ 'amount' => $this->cleanupNumber($amount),
+ 'from' => $this->cleanupUnit($from),
+ 'to' => $this->cleanupUnit($to),
+ ];
+ }
+
+
+ /**
+ * Unit type
+ * return the type of the unit
+ * for example km = length, kph = speed, etc.
+ *
+ * @param string $unit
+ * @return mixed string if found
+ */
+ private function getUnitType($unit)
+ {
+ $unit = str_replace('**', '', $unit);
+ $units = $this->unitsList;
+ $found = false;
+ foreach ($units as $key => $value) {
+ if (in_array($unit, $value)) {
+ $found = $key;
+ break;
+ }
+ }
+
+ return $found;
+ }
+
+
+ /**
+ * Check if unit is valid
+ *
+ * @param string $unit
+ * @return boolean
+ */
+ private function isValidUnit($unit)
+ {
+ $units = $this->unitsList;
+ $found = false;
+ foreach ($units as $value) {
+ if (in_array($unit, $value)) {
+ $found = true;
+ break;
+ }
+ }
+ return $found;
+ }
+
+
+ /**
+ * Regex
+ * create a regex from the
+ * available units array
+ *
+ * @return string
+ */
+ private function matchRegex()
+ {
+ $units = $this->unitsList;
+ $params = [];
+ foreach ($units as $value) {
+ $params[] = implode(' |', $value);
+ }
+
+ $translation_keywords = $this->keywords;
+ if (!empty($translation_keywords)) {
+ $params[] = implode(' |', array_keys($translation_keywords));
+ }
+
+ $params = implode('|', $params);
+ $params = $this->escapeKeywords($params);
+
+ return '(' . $params . ')';
+ }
+
+ /**
+ * Clean unit
+ * clean up the unit
+ *
+ * @param string $val
+ * @return string
+ */
+ private function cleanupUnit($val)
+ {
+ $unit = trim($val);
+ $unit = $this->keywordTranslation($unit, $this->keywords);
+
+ $unit = trim($unit);
+ $unit = preg_replace('!\s+!', ' ', $unit);
+ if (endsWith($unit, '2')) {
+ $unit = str_replace('2', '**2', $unit);
+ }
+
+ return $unit;
+ }
+
+ private function standardUnit($unit)
+ {
+ return str_replace('**', '', $unit);
+ }
+
+
+ /**
+ * Get units list
+ * get a readable units list
+ * to display to the user
+ *
+ * @return array
+ */
+ public function listAvailable()
+ {
+ $translation = $this->getTranslation('units');
+ $units = $this->unitsList;
+ $list = [];
+ foreach ($units as $key => $value) {
+ $type = $key;
+ $type_name = (isset($translation[$type]) ? $translation[$type] : $type);
+
+ foreach ($value as $val) {
+ $unit = $val;
+ $unit_name = (isset($translation[$val]) ? $translation[$val] : $val);
+
+ $list[] = [
+ 'title' => "$unit_name = $val",
+ 'subtitle' => $key,
+ 'subtitle' => sprintf($translation['belongs_to'], $val, $type_name),
+ 'match' => $val . ' ' . $unit_name,
+ 'autocomplete' => $unit_name,
+ 'arg' => $val,
+ 'valid' => true,
+ 'mods' => [
+ 'cmd' => [
+ 'valid' => true,
+ 'arg' => $val,
+ 'subtitle' => $this->getText('action_copy'),
+ ]
+ ],
+ ];
+ }
+ }
+
+ return $list;
+ }
+}
diff --git a/workflow/tools/vat.php b/workflow/tools/vat.php
new file mode 100644
index 0000000..340beb1
--- /dev/null
+++ b/workflow/tools/vat.php
@@ -0,0 +1,159 @@
+query = preg_replace('/[^\\d.]+/', '', $query);
+ $this->lang = $this->getTranslation('vat');
+ }
+
+
+ /**
+ * shouldProcess
+ *
+ * @param string $query
+ * @param integer $strlenght
+ * @return bool
+ */
+ public function shouldProcess(int $strlenght = 0)
+ {
+ if ($strlenght >= 1) {
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Process query
+ *
+ * @return string|array
+ */
+ public function processQuery()
+ {
+ $query = $this->query;
+ $percent = $this->getSetting('vat_percentage', '16%');
+ $processed = false;
+
+ if (empty($query)) {
+ return $this->output($processed);
+ }
+
+ $percent = (int) $percent;
+ $amount = $this->cleanupNumber($query);
+
+ $result = ($percent / 100) * $amount;
+ $result = (fmod($result, 1) !== 0.00 ? bcdiv($result, 1, 2) : $result);
+
+ if ($result && $result > 0) {
+ $processed = true;
+ $plustaxt = $amount + $result;
+ $minustax = $amount / ((float) "1.$percent");
+
+ $processed = [
+ 'amount' => $this->formatNumber($amount),
+ 'result' => $this->formatNumber($result),
+ 'plustaxt' => $this->formatNumber($plustaxt, -1, true),
+ 'minustax' => $this->formatNumber($minustax, -1, true),
+ 'defined_percentage' => $percent
+ ];
+ }
+
+ return $this->output($processed);
+ }
+
+
+ /**
+ * Output
+ * build the output the way
+ * it should be displayed by Alfred
+ *
+ * @param array $result
+ * @return array
+ */
+ public function output($processed)
+ {
+ if (!$processed) {
+ return [
+ 'title' => '...',
+ 'subtitle' => $this->getText('action_copy'),
+ 'valid' => false,
+ ];
+ }
+
+ $items = [];
+ $amount = $processed['amount'];
+ $result = $processed['result'];
+ $plustaxt = $processed['plustaxt'];
+ $minustax = $processed['minustax'];
+ $percent = $processed['defined_percentage'];
+ $lang = $this->lang;
+
+ $items[] = [
+ 'title' => sprintf($lang['result'], $amount, $result),
+ 'subtitle' => sprintf($lang['subtitle'], "{$percent}%"),
+ 'arg' => $result,
+ 'mods' => [
+ 'cmd' => [
+ 'valid' => true,
+ 'arg' => $amount,
+ 'subtitle' => $this->lang['cmd'],
+ ],
+ 'alt' => [
+ 'valid' => true,
+ 'arg' => $this->cleanupNumber($amount),
+ 'subtitle' => $this->lang['alt'],
+ ],
+ ]
+ ];
+
+ $items[] = [
+ 'title' => sprintf($lang['plus'], $amount, $plustaxt),
+ 'subtitle' => sprintf($lang['plus_subtitle'], $amount, "{$percent}%"),
+ 'arg' => $plustaxt,
+ 'mods' => [
+ 'cmd' => [
+ 'valid' => true,
+ 'arg' => $plustaxt,
+ 'subtitle' => $this->lang['cmd'],
+ ],
+ 'alt' => [
+ 'valid' => true,
+ 'arg' => $this->cleanupNumber($plustaxt),
+ 'subtitle' => $this->lang['alt'],
+ ],
+ ]
+ ];
+
+ $items[] = [
+ 'title' => sprintf($lang['minus'], $amount, $minustax),
+ 'subtitle' => sprintf($lang['minus_subtitle'], $amount, "{$percent}%"),
+ 'arg' => $minustax,
+ 'mods' => [
+ 'cmd' => [
+ 'valid' => true,
+ 'arg' => $minustax,
+ 'subtitle' => $this->lang['cmd'],
+ ],
+ 'alt' => [
+ 'valid' => true,
+ 'arg' => $this->cleanupNumber($minustax),
+ 'subtitle' => $this->lang['alt'],
+ ],
+ ]
+ ];
+
+ return $items;
+ }
+}