cURL – Latest Chromium Downloader

echo off
::stuff you can modify.
::--------------------------------------------------------------------------------
::                          http|https
set PROTOCOL=http
::                          snapshots|continuous
set BRANCH=snapshots
::                          Android|Arm|Linux|LinuxGit|LinuxGit_x64|Linux_ARM_Cross-Compile|Linux_ChromiumOS|Linux_ChromiumOS_Full|Linux_x64|Mac|MacGit|Win|WinGit|Win_x64|chromium-full-linux-chromeos
set OS=Win_x64
::                          mini_installer.exe|REVISIONS|chrome-win32-syms.zip|chrome-win32.zip|pnacl.zip|content-shell.zip|gcapi.zip|metrics-metadata.zip|remoting-host.msi|remoting-me2me-host-win.zip|devtools_frontend.zip|chrome-linux.zip|changelog.xml|chrome-android.zip
set FILE=mini_installer.exe
::--------------------------------------------------------------------------------


::stuff you should keep as is.
::--------------------------------------------------------------------------------
set URL_LASTCHANGE=%PROTOCOL%://storage.googleapis.com/chromium-browser-%BRANCH%/%OS%/LAST_CHANGE

set COMMAND_CURL_FORVERSION=curl.exe --silent --http2 --ipv4 --anyauth --insecure --location-trusted --ssl-allow-beast --ssl-no-revoke --url "%URL_LASTCHANGE%"

set VERSION=
for /f "tokens=*" %%a in ('call %COMMAND_CURL_FORVERSION% 2^>^&1') do (set VERSION=%%a)
::error handling
if ["%VERSION%"] == [""] ( goto NOVERSION )
set URL_DOWNLOAD=%PROTOCOL%://storage.googleapis.com/chromium-browser-%BRANCH%/%OS%/%VERSION%/%FILE%

echo.
echo Got Latest-Version: ^>%VERSION%^< ^[Branch:%BRANCH%/OS:%OS%^]
::--------------------------------------------------------------------------------


::you should enable one-of-your-prefered downloaders.
::goto DOWNLOAD_CURL
::goto DOWNLOAD_WGET
::goto DOWNLOAD_ORBITDOWNLOADER
goto DOWNLOAD_ARIA2C



::you should not reach here, unless
::you've forgot to enable one of the "downloader lines" (above)...
goto NODOWNLOADER


::--------------------------------------------------------------------------------
:NOVERSION
  echo ERROR: could not get the latest version...
  goto EXIT

:NODOWNLOADER
  echo ERROR: please enable one of the downloader lines..
  goto EXIT


:DOWNLOAD_CURL
  echo Start Download using cURL...
  call curl.exe --verbose --http2 --ipv4 --ignore-content-length ^
                --anyauth --insecure --location-trusted          ^
                --ssl-allow-beast --ssl-no-revoke --tcp-fastopen ^
                --tcp-nodelay --use-ascii --url "%URL_DOWNLOAD%"
  goto EXIT


:DOWNLOAD_WGET
  echo Start Download using wGET...
  call wget.exe --directory-prefix="." --debug --user-agent="Mozilla/5.0 Chrome" --continue ^
                --server-response --no-check-certificate --secure-protocol=auto  "%URL_DOWNLOAD%"
  goto EXIT


:DOWNLOAD_ARIA2C
  echo Start Download using Aria2C...
  call aria2c.exe --allow-overwrite=true         --auto-file-renaming=false         --check-certificate=false        ^
                  --check-integrity=false        --connect-timeout=120              --console-log-level=notice       ^
                  --continue=true                --dir="."                          --disable-ipv6=true              ^
                  --enable-http-keep-alive=true  --enable-http-pipelining=true      --file-allocation=prealloc       ^
                  --http-auth-challenge=false    --human-readable=true              --max-concurrent-downloads=16    ^
                  --max-connection-per-server=16 --max-tries=3                      --min-split-size=1M              ^
                  --retry-wait=1                 --rpc-secure=false                 --split=8                        ^
                  --timeout=120                  --user-agent="Mozilla/5.0 Chrome"  "%URL_DOWNLOAD%"
  goto EXIT


:DOWNLOAD_ORBITDOWNLOADER
  echo Start Download using OrbitDownloader...
  call "C:\Program Files (x86)\Orbitdownloader\orbitdm.exe" "%URL_DOWNLOAD%"
  goto EXIT


:EXIT  
  echo.
  echo Done.
  pause


::--------------------------------------------------------------------------------------------
::--------------------------------------------------------------------------------------------
::
::   - "http"           - http protocol is faster to connect and download with OrbitDownloader and wGet, it also means your PC won't preform certificate exchange with the remote machine.
::   - "snapshots"      - snapshots is newer, "continuous" is more stable (but might be very old).
::   - "OS" and "FILE"  - are what you want to download
::
::
:: snapshots    - newest (unstable) newest code-changes - passed unit-tests + compilation.
:: continuous   - old    (stable)                       - passed unit-tests + compilation + test-suits.
:: ------------------------------------------------------------------------------------------------------
::   branch   |  branch description           |  version-based build
:: ___________|_______________________________|_____________________________________________________________________________________________
::   Win_x64  |  Chromium Installer (64-bit)  |  storage.googleapis.com/chromium-browser-continuous/Win_x64/{version}/mini_installer.exe  
::   Win_x64  |  Chromium Package (64-bit)    |  storage.googleapis.com/chromium-browser-continuous/Win_x64/{version}/chrome-win32.zip    
::   Win      |  Chromium Installer (32-bit)  |  storage.googleapis.com/chromium-browser-continuous/Win/{version}/mini_installer.exe
::   Win      |  Chromium Package (64-bit)    |  storage.googleapis.com/chromium-browser-continuous/Win/{version}/chrome-win32.zip    
::
::
::------------------------------------------------------
::- SCRIPT WAS WRITTEN AND MAINTAINED BY ELAD KARAKO.  -
::- LAST UPDATED: JULY, 2017. FREE TO USE UNDER GNU.   -
::------------------------------------------------------

Download tools..

JavaScript – Dynamic Resources Loading-Error Handling

Some time ago I’ve wrote the article HTML5 Image Loading Error Handling, although quite simple solution, foucsing on self-contained, more simple solution using an inline event-handling to hide images with specific width/height set, to avoid dirtying up the UI with “empty squares”.

I’ve recently embedded a YouTube video (which uses an iframe), I’ve noticed that for some reason it wasn’t shown on mobile devices, although it should show the “poster”-of the video the very least, nothing was shown but an empty frame,

So I’ve thought I could reuse the previous solution for iframe-elements too,
and to make it easier to use I’ll make it dynamic,
– but will KEEP using an inline-event-handlers instead of settings various handlers to the DOM/node representative of the element.

Continue reading

Solved: ThinApp Registry Export

Say you want to convert VMWare’s ThinApp (formerly known as Thinstall) Registry,
From the sanbox-virtual format (those text files in your capture/project-name folder) To something a human can be easily read, say, a windows registry file.

Why? well.. maybe you’ve just captured a setup process in-order to check what has been changed on your operation-system.

A really common reason to use ThinApp without actually building anything at the end, at least among the VM-savvy engineers is for the sake of tracking the changes to the operation-system, in hope of simplifying installations, in cases all you may need is a pair of exe-and-reg files and no need for an overkill of sandboxing an entire application + virtualapp-engine.
~
ThinApp does a very good job of capturing even the deepest registry changes (including those of permission limitation or ones which does not “really exist”, such as soft symbolic-linked keys for example under HKEY_USERS (which are common enough).

Another way of comparing registry changes including dumping the entire registry (before and after..) and comparing the the .REG files using a program such as BeyondCompare.

A similar method but somehow slightly easier is the usage of Registry-Workshop, and the “before” and “after” snapshots feature, following the built-in compare-engine which is pretty much a nice wrap around the same thing (above) except using the program’s internal-compare engine which also allows to jump-into the inspected values, sync changes, etc…

So..

I’ve captured a nice little freeware called foxit PDF
and got the familiar folder structure (before building anything!)

icompile.eladkarako.com_thinapp_thinstall_virtual_registry_tvr_convert_to_reg_key1

If you’ll have a look (just a look, don’t worry..)
inside the build.bat batch-file,
You’ll see part of the command we’ll going to use, which is actually part of creating the virtual-sandbox,
in-particularity- the REGISTRY part:

icompile.eladkarako.com_thinapp_thinstall_virtual_registry_tvr_convert_to_reg_key2_batch_look

After the hint, it is time for the solution walk-through:

  1. Under your ThinApp folder (same level where you’ll find the Setup Capture.exe file create a new folder, named reg_convert.
    icompile.eladkarako.com_thinapp_thinstall_virtual_registry_tvr_convert_to_reg_key_folder1
  2. Under reg_convert create two folder named in and out
    icompile.eladkarako.com_thinapp_thinstall_virtual_registry_tvr_convert_to_reg_key_folder2
  3. Under in folder, copy the Package.ini from your captured-package,
    or use this generic, minimal Package.ini

    you only need the part related to setup-capture, mostly the versioning of the capturing engine of ThinApp, and the code-page [language] of 1033 [English] might be useful in-case you have registry keys with foreign-characters, which in this case you might want to have a look at the values of one of your original captured Package.ini files, or Google it.. 1037 is Hebrew :]

    [Compression]
    CompressionType=None
    
    [Isolation]
    DirectoryIsolationMode=Merged
    
    [BuildOptions]
    ;-------- Parameters used only during Setup Capture  ----------
    AccessDeniedMsg=You are not currently authorized to run this application. Please contact your administrator.
    CapturedUsingVersion=5.1.0-2079447
    CaptureProcessorArchitecture=0
    CapturePlatformVersion=0501
    CaptureOSArchitecture=32
    CaptureOSMajorVersion=5
    CaptureOSMinorVersion=1
    CaptureOSSuite=256
    CaptureOSProductType=1
    CaptureOSCSDVersion=Service Pack 3
    CaptureOSProcessorCoreCount=2
    CaptureOSRemoteSession=0
    CaptureOSVMwareVM=1
    OutDir=bin
    
    AnsiCodePage=1255
    LocaleIdentifier=1033
    
    AltArchitectureShortcut=0
    QualityReportingEnabled=0
    

    icompile.eladkarako.com_thinapp_thinstall_virtual_registry_tvr_convert_to_reg_key_folder3

  4. Still under the in folder, you should now copy (just) the registry files (.TXT) from your captured project.

    You do not have to copy them all, and you are well encouraged to make them as small and lite as possible by removing values that are not needed. The smaller and fewer they’ll be, the faster the entire process will be completed.

    For example I’ll going to remove the following “keys/values/data” since they are not needed or even related to the package itself, even more than that, those can collide with the operation-system’s more recent-values (in-case I’ll be building the project later..)
    icompile.eladkarako.com_thinapp_thinstall_virtual_registry_tvr_convert_to_reg_key_can_make_registry_txt_files_smaller

  5. At this point, we will generate a virtual-sandbox holding just the registry values (no files), using the vregtool.exe command.

    1. open up CMD and navigate to where you have your vregtool.exe
      (same place you’ll have reg_convert).
      cd c:\.......\ThinApp\
    2. run vregtool.exe reg_convert\out\reg.tvr ImportDir reg_convert\in\,
      icompile.eladkarako.com_thinapp_thinstall_virtual_registry_tvr_convert_to_reg_key_generate_tvr_of_just_registry_values2
      You may ignore warnings, or remove any extra-empty lines at the bottom of the txt files,
      It will take few seconds, and you’ll find the tvr file under the out folder.
      icompile.eladkarako.com_thinapp_thinstall_virtual_registry_tvr_convert_to_reg_key_generate_tvr_result
      1. Almost done, we will, now, extract the actual registry key (in the standard windows format) from the virtual-sandbox, exporting it to the same out folder.

        run:vregtool.exe reg_convert\out\reg.tvr ExportReg reg_convert\out\registry.reg
        icompile.eladkarako.com_thinapp_thinstall_virtual_registry_tvr_convert_to_reg_key_generate_final_registry_key_result
        You’ll find your result file under the out folder as well as the old tvr file.
        icompile.eladkarako.com_thinapp_thinstall_virtual_registry_tvr_convert_to_reg_key_generate_final_registry_key_result_in_folder

      2. Done.

        naturally a cleanup will be required in-order to use the same method of converting txttvrreg,
        remove the txt files under in folder, keep Package.ini file there to be used the next time.
        you can safely remove the entire content of the out folder (the reg.tvr, and once you’ll be done with it- the registry.reg file).

        Naturally a batch file can quite easily be generated,
        You can make one to drag&drop the entire captured-folder, allowing automated copy, generating the copying back the result to your captured-project, in same place as the txt files, to keep things organised by project. :)

      Hope it helps ! :]

      Happy engineering day :]]

JavaScript Ninja – Unicode Aware Base64

I think examples are more effective than a long explanation.

Have a look at the code below:

(function(window, document, username_placeover_query, password_placeover_query, USERNAME, PASSWORD){
  "use strict";
  
/*┌─────────────────────────────────────────────────────────────────────────────┐
  │ NOTE.                                                                       │
  ╞═════════════════════════════════════════════════════════════════════════════╡
  │- unescape-encodeURIComponent breaks Unicode to binary-string where each     │
  │   character is less than 256 bytes.                                         │
  │- decodeURIComponent-escape brings back binary-string to Unicode.            │
  │- both are consider "hacks/non-official tricks" but are extreamly efficient, │
  │   and 100% compatible with non-Unicode characters.                          │
  └─────────────────────────────────────────────────────────────────────────────┘*/

/*┌─────┐
  │ LIB │
  └─────┘*/
  NodeList.prototype.forEach = Array.prototype.forEach;

  /*
  function base64_encode(str){
    str = unescape(encodeURIComponent(str));
    return btoa(str);
  };
  */

  function base64_decode(str){
    str = decodeURIComponent(escape(str));
    return atob(str);
  };

/*┌──────┐
  │ MAIN │
  └──────┘*/
  document.querySelectorAll(username_placeover_query).forEach(function(element){ element.value = base64_decode(USERNAME); });
  document.querySelectorAll(password_placeover_query).forEach(function(element){ element.value = base64_decode(PASSWORD); });


/*┌─────┐
  │ END │
  └─────┘*/
}(
   top
,  document
,  "#username"
,  "#password"
,  "bXlfdXNlcm5hbWU="   /*= my_username */
,  "bXlfcGFzc3dvcmQ="   /*= my_password */
));

This was intended to be used (after minified) as a bookmarklet,
After you will edit the USERNAME‘s value and PASSWORD‘s value,
and the placeover query,
you could (in a click) fill up username and password in websites that does not support Google Chrome’s authentication sync (or if for example this is the first time after installing new browser, with no previous profile existing on the current OS, or if you just have bookmarks in a html backup file… anyway.. I’ve used it from time to time [make sure to keep this stuff secure though..])

Anyway.. clicking the bookmarklet will fill up the data in the field’s value attribute.

naturally you can provide a better mechanisem by generating mouse click/down/up event or even a char-by-char keyboard “typing” with focus but I’ve skipped this part to simplify the example..

Base64’s btoa and atob methods are supported by mostly all of the new browsers,
which makes the entire solution much shorter than before,

just fyi.. those were the code segment previously required (old browsers) to do the same *thing*:

function base64_decode(data) {
  var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  var o1, o2, o3, h1, h2, h3, h4, bits, i = 0
    , ac = 0
    , dec = ""
    , tmp_arr = [];
  if (!data) {
    return data;
  }
  data += '';

  do {
    h1 = b64.indexOf(data.charAt(i++));
    h2 = b64.indexOf(data.charAt(i++));
    h3 = b64.indexOf(data.charAt(i++));
    h4 = b64.indexOf(data.charAt(i++));
    bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
    o1 = bits >> 16 & 0xff;
    o2 = bits >> 8 & 0xff;
    o3 = bits & 0xff;
    if (h3 == 64) {
      tmp_arr[ac++] = String.fromCharCode(o1);
    } else if (h4 == 64) {
      tmp_arr[ac++] = String.fromCharCode(o1, o2);
    } else {
      tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
    }
  } while (i < data.length);
  dec = tmp_arr.join('');
  dec = this.utf8_decode(dec);
  return dec;
}

function utf8_decode(str_data) {
  var tmp_arr = []
    , i = 0
    , ac = 0
    , c1 = 0
    , c2 = 0
    , c3 = 0;
  str_data += '';
  while (i < str_data.length) {
    c1 = str_data.charCodeAt(i);
    if (c1 < 128) {
      tmp_arr[ac++] = String.fromCharCode(c1);
      i++;
    } else if (c1 > 191 && c1 < 224) {
      c2 = str_data.charCodeAt(i + 1);
      tmp_arr[ac++] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
      i += 2;
    } else {
      c2 = str_data.charCodeAt(i + 1);
      c3 = str_data.charCodeAt(i + 2);
      tmp_arr[ac++] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
      i += 3;
    }
  }
  return tmp_arr.join('');
}

function utf8_encode(argString) {
  if (argString === null || typeof argString === "undefined") {
    return "";
  }
  var string = (argString + '');
  var utftext = ""
    , start, end, stringl = 0;
  start = end = 0;
  stringl = string.length;
  for (var n = 0; n < stringl; n++) {
    var c1 = string.charCodeAt(n);
    var enc = null;
    if (c1 < 128) {
      end++;
    } else if (c1 > 127 && c1 < 2048) {
      enc = String.fromCharCode((c1 >> 6) | 192) + String.fromCharCode((c1 & 63) | 128);
    } else {
      enc = String.fromCharCode((c1 >> 12) | 224) + String.fromCharCode(((c1 >> 6) & 63) | 128) + String.fromCharCode((c1 & 63) | 128);
    }
    if (enc !== null) {
      if (end > start) {
        utftext += string.slice(start, end);
      }
      utftext += enc;
      start = end = n + 1;
    }
  }
  if (end > start) {
    utftext += string.slice(start, stringl);
  }
  return utftext;
}

function base64_encode(data) {
  var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  var o1, o2, o3, h1, h2, h3, h4, bits, i = 0
    , ac = 0
    , enc = ""
    , tmp_arr = [];
  if (!data) {
    return data;
  }
  data = this.utf8_encode(data + '');
  do {
    o1 = data.charCodeAt(i++);
    o2 = data.charCodeAt(i++);
    o3 = data.charCodeAt(i++);
    bits = o1 << 16 | o2 << 8 | o3;
    h1 = bits >> 18 & 0x3f;
    h2 = bits >> 12 & 0x3f;
    h3 = bits >> 6 & 0x3f;
    h4 = bits & 0x3f;
    tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
  } while (i < data.length);
  enc = tmp_arr.join('');
  var r = data.length % 3;
  return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);
}

by the way here is a binary string – Unicode examples

unescape(encodeURIComponent("א"))  -to- "א"   to binary string (length=1 -to- lengh=2)
decodeURIComponent(escape("א"))    -to- "א"   from binary string (length=2 - to lengh=1)

Unix Snippet – Get File’s “Touch Date-Format” One-Liner

Pipelining the modification date from stat into an inline-date-phrase using the date --date="@... directive (using read to locate the pipeline’s output in the middle of the date-phrase),
And you’ve got yourself a nice little converter, which you can customise, to use *any* date-format,
I’ve using the Universal Time Coordinated standard.

file name:          my_file.txt
unix time-format:   1458036575     (Asia/Jerusalem)
touch time-format:  201603151009   (UTC)
stat --format=%Y "my_file.txt" | { read -r tmp_date; date --utc --date="@$tmp_date" "+%C%y%m%d%H%M"; }

*Ps.
You can easily use the touch-date-format with touch command using the following directive:
touch -t 201603151009 --time=mtime my_file.txt;
but make sure first you set the local-timezone to same timezone it was generated on.
TZ=UTC will help you do that,
TZ=UTC; touch -t 201603151009 --time=mtime my_file.txt; will apply it to the current line.
and you can also make sure of it is done right by using:
export TZ=UTC at the start of the script, and at the end of it just re-run
export TZ=Asia/Jerusalem (or whatever your TimeZone is) to come-back to your TimeZone,

Nice!

CMD Ninja – Relative To Fully Qualified Path And Other File Properties Without Directing Relative Path To Another CMD-File As An Argument

Given the following directory-tree
icompile.eladkarako.com_cmd_file_path_properties_short_relative_file_name_directory_folder_without_sending_to_another_cmd_file_as_argument_args

I want to get some information on apktool_2.0.2.jar
for example fully-qualified short (old dos 8.3 format that is compatible with old Java argument’ing) path,

here is a snippet containing it,
this ‘_’ prefix is advisable, the double ‘%’ is to escape the loop’s variable, the ‘~’ is to strip any \’ \” wrapping chars around the path (best practice, always use it), the reason ‘_drive’ phrase has (also) ‘s’ in it is to make the result more letter-case consistent (the 8.3 format uses Upper-Case while if you’ll run CMD console and browse or change-dir using a Lower-Case drive letter it will be embodied in the result).

@echo off
set relative=.\apktool_2.0.2.jar

for /f %%a in ("%relative%")do (set "_full=%%~fa"     )
for /f %%a in ("%relative%")do (set "_full83=%%~fsa"  )
for /f %%a in ("%relative%")do (set "_drive=%%~dsa"   )
for /f %%a in ("%relative%")do (set "_path=%%~pa"     )
for /f %%a in ("%relative%")do (set "_path83=%%~psa"  )
for /f %%a in ("%relative%")do (set "_name=%%~na"     )
for /f %%a in ("%relative%")do (set "_name83=%%~nsa"  )
for /f %%a in ("%relative%")do (set "_ext=%%~xa"      )
for /f %%a in ("%relative%")do (set "_ext83=%%~xsa"   )
for /f %%a in ("%relative%")do (set "_att=%%~aa"      )
for /f %%a in ("%relative%")do (set "_time=%%~ta"     )
for /f %%a in ("%relative%")do (set "_size=%%~za"     )

echo %_full%
echo %_full83%
echo %_drive%
echo %_path%
echo %_path83%
echo %_name%
echo %_name83%
echo %_ext%
echo %_ext83%
echo %_att%
echo %_time%
echo %_size%

::------------------------------------------------------------::
::  relative  < ->  .\apktool_2.0.2.jar                        ::
::------------------------------------------------------------::
::  _full     < ->  D:\DOS\android\bin\apktool_2.0.2.jar       ::
::  _full83   < ->  D:\DOS\android\bin\APKTOO~1.JAR            ::
::  _drive    < ->  D:                                         ::
::  _path     < ->  \DOS\android\bin\                          ::
::  _path83   < ->  \DOS\android\bin\                          ::
::  _name     < ->  apktool_2.0.2                              ::
::  _name83   < ->  APKTOO~1                                   ::
::  _ext      < ->  .jar                                       ::
::  _ext83    < ->  .JAR                                       ::
::  _att      < ->  --a------                                  ::
::  _time     < ->  10/14/2015 02:06 PM                        ::
::  _size     < ->  6329931                                    ::
::------------------------------------------------------------::

PHP Snippet – A Self-Called Function Just Like JavaScript

This is the parallel of PHP to one of JavaScript’s most usable work-in design-pattern

self-called, with arguments, anonymous method are commonly used to /block/ a code-segment,
(/block/ as in “put something in a code-block” and not to block something…)
this essentially keeps your code builds as a smaller building blocks, rather then a one continues code-block.

As a coding design pattern I’ve highly recommend to use,
I’ve already converted tens of modules to the following writing-pattern,
and all of my clients practically fell-in love with this new pattern, mostly accepted it as company standards.

var 
  external_variable1 = ... ,
  external_variable2 = ... ,
  ...

var result = (function(variable1, variable2, ...){
  ...
  ...
  return ...
}(external_variable1, external_variable2, ...));

here is the parallel of PHP’s universe:

$external_variable1 = ... ,
$external_variable2 = ... ,
  ...

$result = call_user_func_array(function($variable1, $variable2, ...){
  ...
  ...
  return ...
}, array($external_variable1, $external_variable2, ...));

lovely how those too play nicely together P.S. this is no coincidence those pattern are cross-languages-similar!

PHP Snippet – Accept-Language HTTP header, Browser Settings, Native Language and PHP Parsing Of $SERVER[‘HTTP_ACCEPT_LANGUAGE’]

One of the header being sent in a request done by standard (new-age) browsers is the Accept-Language, which derive from the definition of the languages-settings in the browser, its quite reliable, usually used to specify what your preferred language should be, in-case the target page support multi-lingual content,
its also used in Google-Translate, while browsing a language different from your own set.

most common is the ‘en’, ‘en-US’ and ‘en_US’, those do not have actual difference, unless the user have installed a specific MUI of Windows, or a non-default versions of Chrome or Firefox, those en* “default” will be send.

Both Firefox and Chrome (and Chrome based-on) browsers will also support regional/different language builds, which other then provide native-language front, will also alter the sent requests to specify that the currently used language is “more important” then the other (usually the en will still be sent).
the quality of “stronger” vs. “weaker” measured by the q=___ added to each of the languages reported.

for example my Google-Chrome browser, will send ‘English’ and also ‘Hebrew’:
2014-07-19_2312481

On Google Chrome, I’ve navigated to chrome://settings/languages page,
and set those values (in that order..)

2014-07-19_232957

As can be seen, the evaluation is same as in the request, the higher the number, the higher the priority.

On client-side, many statistics-collecting products such as Google-Analytics will use the very same value (derived from browser’s settings) by accessing navigator.language value (or window.navigator.userLanguage on IE)

On server-side PHP, do the same by accessing $SERVER['HTTP_ACCEPT_LANGUAGE'] and parsing the string for example: en,en-US;q=0.8,he;q=0.6 into something useful, probably an array that is ordered by the q= value,

although as a rule-of-thumb this is not really necessary, since the standard is to send the first-used language first, simply cut out the first cell and return it.

the following also provides a fallback.

$languages_header = isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? $_SERVER['HTTP_ACCEPT_LANGUAGE'] : 'en_US';
$languages_header = explode(',', $languages_header);
echo $languages_header[0]

I did made a small collection of supported languages, there is three formats that are being used as part of the i18n standard.

  1. regional underscore – en_US
  2. regional hyphen – en-US
  3. non regional – en (…Note that I’ve made those names.. so don’t try Google it around..)

the following list might come-in handy, since group [1] and [2] (regional) provided more information, the list is sorted a-z, in those 3 groups (in the order above, first is regional underscore, regional hyphen then non regional)

I’m using the PHP’s array format..

$languages_supported = array('af_ZA', 'ar_AE', 'ar_BH', 'ar_DZ', 'ar_EG', 'ar_IQ', 'ar_JO', 'ar_KW', 'ar_LB', 'ar_LY', 'ar_MA', 'ar_OM', 'ar_QA', 'ar_SA', 'ar_SY', 'ar_TN', 'ar_YE', 'az_AZ', 'az_IR', 'be_BY', 'bg_BG', 'bn_BD', 'bn_IN', 'bs_BA', 'ca_AD', 'ca_ES', 'ca_FR', 'ca_IT', 'cs_CZ', 'cy_GB', 'da_dk', 'da_DK', 'de_at', 'de_AT', 'de_ch', 'de_CH', 'de_de', 'de_DE', 'de_LI', 'de_LU', 'dv_MV', 'el_gr', 'el_GR', 'en_au', 'en_AU', 'en_BZ', 'en_ca', 'en_CA', 'en_CB', 'en_gb', 'en_GB', 'en_IE', 'en_in', 'en_JM', 'en_NZ', 'en_PH', 'en_sg', 'en_TT', 'en_us', 'en_US', 'en_ZA', 'en_ZW', 'es_419', 'es_ar', 'es_AR', 'es_BO', 'es_cl', 'es_CL', 'es_CO', 'es_CR', 'es_DO', 'es_EC', 'es_es', 'es_ES', 'es_GT', 'es_HN', 'es_LA', 'es_mx', 'es_MX', 'es_NI', 'es_PA', 'es_pe', 'es_PE', 'es_PR', 'es_PY', 'es_SV', 'es_UY', 'es_VE', 'et_EE', 'eu_ES', 'fa_AF', 'fa_IR', 'fi_fi', 'fi_FI', 'fo_FO', 'fr_BE', 'fr_CA', 'fr_CH', 'fr_fr', 'fr_FR', 'fr_FX', 'fr_LU', 'fr_MC', 'fy_NL', 'gl_ES', 'gu_IN', 'he_il', 'he_IL', 'hi_IN', 'hr_BA', 'hr_HR', 'hu_hu', 'hu_HU', 'hy_AM', 'id_ID', 'is_IS', 'it_CH', 'it_it', 'it_IT', 'ja_jp', 'ja_JP', 'jv_ID', 'ka_GE', 'kk_KZ', 'km_KH', 'kn_IN', 'ko_kr', 'ko_KR', 'kok_IN', 'ky_KG', 'lt_LT', 'lv_LV', 'mi_NZ', 'mk_MK', 'ml_IN', 'mn_MN', 'mr_IN', 'mr_MR', 'ms_BN', 'ms_MY', 'mt_MT', 'my_MM', 'nb_NO', 'nl_BE', 'nl_nl', 'nl_NL', 'nn_NO', 'no_NO', 'ns_ZA', 'pa_IN', 'pl_pl', 'pl_PL', 'ps_AR', 'pt_br', 'pt_BR', 'pt_pt', 'pt_PT', 'qu_BO', 'qu_EC', 'qu_PE', 'ro_RO', 'ru_ru', 'ru_RU', 'ru_UA', 'sa_IN', 'se_FI', 'se_NO', 'se_SE', 'si_LK', 'sk_SK', 'sl_SI', 'sq_AL', 'sr_BA', 'sr_SP', 'sr_YU', 'su_ID', 'sv_FI', 'sv_se', 'sv_SE', 'sw_KE', 'syr_SY', 'ta_IN', 'ta_LK', 'te_IN', 'th_th', 'th_TH', 'tl_PH', 'tn_ZA', 'to_ID', 'tr_tr', 'tr_TR', 'tt_RU', 'ug_CN', 'uk_UA', 'ur_PK', 'uz_UZ', 'vi_VI', 'vi_VN', 'xh_ZA', 'zh_cn', 'zh_CN', 'zh_hk', 'zh_HK', 'zh_MO', 'zh_SG', 'zh_tw', 'zh_TW', 'zu_ZA', 'af-ZA', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-OM', 'ar-QA', 'ar-SA', 'ar-SY', 'ar-TN', 'ar-YE', 'az-AZ', 'be-BY', 'bg-BG', 'bs-BA', 'ca-ES', 'cs-CZ', 'cy-GB', 'da-dk', 'da-DK', 'de-at', 'de-AT', 'de-ch', 'de-CH', 'de-de', 'de-DE', 'de-LI', 'de-LU', 'dv-MV', 'el-gr', 'el-GR', 'en-au', 'en-AU', 'en-BZ', 'en-ca', 'en-CA', 'en-CB', 'en-gb', 'en-GB', 'en-IE', 'en-in', 'en-JM', 'en-NZ', 'en-PH', 'en-sg', 'en-TT', 'en-us', 'en-US', 'en-ZA', 'en-ZW', 'es-419', 'es-ar', 'es-AR', 'es-BO', 'es-cl', 'es-CL', 'es-CO', 'es-CR', 'es-DO', 'es-EC', 'es-es', 'es-ES', 'es-GT', 'es-HN', 'es-mx', 'es-MX', 'es-NI', 'es-PA', 'es-pe', 'es-PE', 'es-PR', 'es-PY', 'es-SV', 'es-UY', 'es-VE', 'et-EE', 'eu-ES', 'fa-IR', 'fi-fi', 'fi-FI', 'fo-FO', 'fr-BE', 'fr-CA', 'fr-CH', 'fr-fr', 'fr-FR', 'fr-LU', 'fr-MC', 'gl-ES', 'gu-IN', 'he-il', 'he-IL', 'hi-IN', 'hr-BA', 'hr-HR', 'hu-hu', 'hu-HU', 'hy-AM', 'id-ID', 'is-IS', 'it-CH', 'it-it', 'it-IT', 'ja-jp', 'ja-JP', 'ka-GE', 'kk-KZ', 'kn-IN', 'kok-IN', 'ko-kr', 'ko-KR', 'ky-KG', 'lt-LT', 'lv-LV', 'mi-NZ', 'mk-MK', 'mn-MN', 'mr-IN', 'ms-BN', 'ms-MY', 'mt-MT', 'nb-NO', 'nl-BE', 'nl-nl', 'nl-NL', 'nn-NO', 'ns-ZA', 'pa-IN', 'pl-pl', 'pl-PL', 'ps-AR', 'pt-br', 'pt-BR', 'pt-pt', 'pt-PT', 'qu-BO', 'qu-EC', 'qu-PE', 'ro-RO', 'ru-ru', 'ru-RU', 'sa-IN', 'se-FI', 'se-NO', 'se-SE', 'sk-SK', 'sl-SI', 'sq-AL', 'sr-BA', 'sr-SP', 'sv-FI', 'sv-se', 'sv-SE', 'sw-KE', 'syr-SY', 'ta-IN', 'te-IN', 'th-th', 'th-TH', 'tl-PH', 'tn-ZA', 'tr-tr', 'tr-TR', 'tt-RU', 'uk-UA', 'ur-PK', 'uz-UZ', 'vi-VN', 'xh-ZA', 'zh-cn', 'zh-CN', 'zh-hk', 'zh-HK', 'zh-MO', 'zh-SG', 'zh-tw', 'zh-TW', 'zu-ZA', 'ab', 'af', 'am', 'an', 'ang', 'ar', 'as', 'ast', 'ay', 'az', 'be', 'bem', 'bg', 'bi', 'bn', 'bo', 'br', 'bs', 'byn', 'ca', 'co', 'crh', 'cs', 'csb', 'cv', 'cy', 'cz', 'da', 'de', 'dk', 'dv', 'dz', 'el', 'en', 'eo', 'es', 'et', 'eu', 'fa', 'fi', 'fil', 'fo', 'fr', 'frp', 'fur', 'fy', 'ga', 'gd', 'gez', 'gl', 'gn', 'gu', 'gv', 'ha', 'he', 'hi', 'hne', 'hr', 'ht', 'hu', 'hy', 'ia', 'id', 'ig', 'io', 'is', 'it', 'ja', 'jv', 'ka', 'kab', 'kk', 'km', 'kn', 'ko', 'kok', 'ks', 'ku', 'kw', 'ky', 'la', 'lb', 'lg', 'li', 'ln', 'lo', 'lt', 'lv', 'mai', 'mg', 'mi', 'mk', 'ml', 'mn', 'mr', 'ms', 'mt', 'mus', 'my', 'na', 'nb', 'nds', 'ne', 'new', 'ng', 'nl', 'nn', 'no', 'nr', 'ns', 'nso', 'oc', 'or', 'os', 'pa', 'pl', 'pms', 'ps', 'pt', 'qu', 'rm', 'ro', 'ru', 'rw', 'sa', 'sc', 'sd', 'se', 'si', 'sk', 'sl', 'sm', 'so', 'sp', 'sq', 'sr', 'st', 'su', 'sv', 'sw', 'syr', 'ta', 'te', 'tg', 'th', 'ti', 'tig', 'tk', 'tl', 'tlh', 'tn', 'to', 'tr', 'ts', 'tt', 'ug', 'uk', 'ur', 'uz', 've', 'vi', 'wa', 'wal', 'wo', 'xh', 'yi', 'yo', 'zh', 'zu');

Just in-case we would like to break the Accept-Language string, I’ve wrote a nice way of doing it..

$all_languages_eval = array();

foreach($languages_header as $language_header){
  $language_name = preg_replace('/([^;]+);.*$/', '${1}', $language_header); //extract actuall language string

  $language_significance = preg_replace('/^[^q]*q=([^\,]+)*$/', '${1}', $language_header);      //extract evaluation as string
  $language_significance = is_numeric($language_significance) ? floatval($language_significance) : 1.0; //parse evaluation, if no q=__ (first language is like "1"), then we will use 1.0

  $all_languages_eval[ $language_name ] = $language_significance;
}

array_multisort($all_languages_eval, SORT_DESC , SORT_NATURAL);//SORT_DESC: higher-value is on top (vs. SORT_ASC).

var_dump($all_languages_eval);

languages without value specification are (by definition) have q=1 (and usually put ahead as the main language to be using).

you’ll be parsing the string into an associative array using ‘language string’ as key, and its proper float-significance as value, then its a matter of applying some array_multisort with SORT_DESC flag to really make usage of the fact we’ve parsed so much…
But Heck! You Can’t Put A Price-Tag On Accuracy!! (well.. actually.. you really can.. nevermind..)

well.. our example from before: en,en-US;q=0.8,he;q=0.6 will be show this output:
2014-07-20_003053

naturally you may take the first element’s key name, which is the “higher” value language, which you should be using to determine the user’s language.

to get back just the array of languages ordered by most significant to least $arr = array_keys($all_languages_eval); will output en,en-US,he, for just the string of the first one array_keys($all_languages_eval)[0] its OK to address specific cell since we’ve got fallback to “en”, which is also the native result: en

Protected: צבע אדום API

This content is password protected. To view it please enter your password below:

Enter your password to view comments.

JavaScript Map-Reduce – Simple Reduce Example

[1,2,3,4].reduce(function(lastReturn, currentCell, index, array){
  return lastReturn + currentCell;
});

outputs:

10

the idea is that you’ll be travelling along the array, but will keep maintaining a return result along the way,
each iteration may return a result, which will be stored in the lastReturn (in the example above), so you’ll might want to chain any math calculation, which will be eventually be returned as the lastReturn of the callback, in this case a single number.

at the first time running naturally the lastReturn will be undefined, so using ‘+’ (in math) will be treated as 0.

here is another example, but now, let us work with some DOM objects:
walking on all the elements, that has href with value (meaning non-empty href), lets find the with as much characters in its href-URL.

Array.prototype.reduce.call(document.querySelectorAll('a[href]:not([href=""]'), function(lastReturn, currentCell, index, array){
  return (index === 1) ? currentCell : //handling first case, where lastReturn is undefined...
    currentCell.href.length > lastReturn.href.length ? currentCell : lastReturn;
});

on my website it will probably return something like this..
<a href="http://icompile.eladkarako.com/javascript-code-segment-piggybackdata-the-proper-object-oriented-way-to-overcome-known-problem-of-callback-hell-and-chained-callbacks-with-single-data-parameter/" rel="bookmark" title="Permanent link to this post">JavaScript Code-Segment: PiggybackData : The Proper Object-Oriented Way to Overcome Known Problem Of "Callback-Hell" And Chained Callbacks With Single Data Parameter</a>
 

better than a loop? Yes!
Browser’s JS-engines (especially SpiderMonkey and V8) will implement map/reduce in a more efficient way then normal “free-written-loops” (for and while), the rule of thumb is simple “always prefer using predicted-behavior methods (iterated prototypes)”

PHP snippet – string of X latest modified files in a JS supported format

I am using this one, along with selenium, and an automated screen-capture programs I’ve made in C#,
to write screenshots of tests to Apache hdocs folder, so it will be available as a page resource.
my program does not overrides the image name, it keeps couple of screenshots in a folder and the php snippet attached here, selects the 3 (can be more) of the last modified files,
puts a nice string in a format suitable for the later usage in page’s JS code,
for example, to be used in a roller or something..


Force Save Password

Allowing the browser’s password-manager to save the login data, meaning user-name and password, even if it is in a SSL (for example https secured websites).

it is done by setting the autocomplete attribute of forms and password field to “on”.

put easily as a bookmark button (known as ‘snippet’).

add this to your bookmark (or favorites) toolbar:

javascript:(function (){var a,b,c,d,e,f,g,h,i;a=b=c=0;d=document.forms;for(f=0;f

You can look at the formatted code for better understanding:
(it is slightly different)

(function () {
    var ca, cea, cs, df, dfe, i, j, x, y;

    function n(i, what) {
        return i + " " + what + ((i == 1) ? "" : "s")
    }
    ca = cea = cs = 0;
    df = document.forms;
    for (i = 0; i < df.length; ++i) {
        x = df[i];
        dfe = x.elements;
        if (x.onsubmit) {
            x.onsubmit = "";
            ++cs;
        }
        if (x.attributes["autocomplete"]) {
            x.attributes["autocomplete"].value = "on";
            ++ca;
        }
        for (j = 0; j < dfe.length; ++j) {
            y = dfe[j];
            if (y.attributes["autocomplete"]) {
                y.attributes["autocomplete"].value = "on";
                ++cea;
            }
        }
    }
    alert("Auto-Complete Has Been Re-Applied To:\n-----------------------------------------------\nForms: " + n(ca, "form") + " .\nForms Elements: " + n(cea, "form element") + " .\nOn-Submits: " + n(cs, "form") + " .");
})();

Example Using PayPal