COOKIES! This blog uses cookies!
I am completely out of control of cookies here, otherwise I would have disabled them (it is controlled by the platform).
If you don't like cookies and being tracked please leave this blog immediately.

Showing posts with label JavaScript. Show all posts
Showing posts with label JavaScript. Show all posts

Wednesday, 10 July 2019

Spaghetti code

There's a million of articles explaining why one should not write the Spaghetti code, however software implementations with all kinds of rotten Spaghetti are really numerous, the function spaghetti code in particular.

What is the difference of function spaghetti code from usual spaghetti code? Whereas a classic spaghetti code consists of a combination of goto, conditions, if-clauses and so on; the function spaghetti kind of code usually consists of single source file filled with all possible kinds of functions which do absolutely anything. The source file of 4000 (four thousands) lines is kind of a norm in the software development industry nowadays, I personally seen source files with more than 15000 (fifteen thousands lines of code).

It is very common for JavaScript, not because the language is particularly bad, just because a lot of development is done with JavaScript, and it's often done not by best developers. Nevertheless, the Java classes with hundreds of methods, python files with heaps of functions irrelevant to each other, the PHP files doing everything are also quite common.

There's absolutely no excuse to keep these giants on the back-end, so back-enders simply make a sad face and admit that they write bad code. JS developers are often more persistent in protecting their noodles, and have many excuses to dig these enormously deep function draw-wells, some excuses them are:

  • We don't want many separate JavaScript files because single one loads faster;
  • We don't want another build tool which would transpile our JavaScript into single or a few build files;
  • We don't want to use NPM;
  • Our company proxy blocking NPM downloads;
  • These are just a tiny little functions, all irrelevant to each others, let's keep 'em in one place;
  • We don't know how to write proper JS. 
All these excuses are just rubbish, if you are capable to install packages with NPM this problem is easily solved with such things as Rollup, Webpack, Babel and Browserify.

If use of NPM is for some mysterious reason is absolutely impossible, these functions anyway can be somehow grouped by the subject and split into a few files, those could be fed to the browser as they are, or can be simply concatenated into one file with any kind of build tool.

Friday, 29 July 2016

Take a screenshot of whole app in the Electron

First you need to enable usermedia-screen-capturing in your Chromium Electron,
add the following string into your main.js:
app.commandLine.appendSwitch('enable-usermedia-screen-capturing');

After that you can use the following function to take a PNG blob
/**
 * A simplified function which takes a screenshot with webkitGetUserMedia
 * and returns this screenshot as a PNG blob into the callback
 * @param callback (pngData: Blob) => void
 * @returns void
 */
function takeScreenShot (callback) {
    let screenConstraints = {
        mandatory: {
            chromeMediaSource: "screen",
            maxHeight: 1080,
            maxWidth: 1920,
            minAspectRatio: 1.77
        },
        optional: []
    };

    let session = {
        audio: false,
        video: screenConstraints
    };

    let streaming = false;
    let canvas = document.createElement("canvas");
    let video = document.createElement("video");
    document.body.appendChild(canvas);
    document.body.appendChild(video);
    let width = screen.width;
    let height = 0;

    video.addEventListener("canplay", function(){
        if (!streaming) {
            height = video.videoHeight / (video.videoWidth / width);

            if (isNaN(height)) {
                height = width / (4 / 3);
            }

            video.setAttribute("width", width.toString());
            video.setAttribute("height", height.toString());
            canvas.setAttribute("width", width.toString());
            canvas.setAttribute("height", height.toString());
            streaming = true;

            let context = canvas.getContext("2d");
            if (width && height) {
                canvas.width = width;
                canvas.height = height;
                context.drawImage(video, 0, 0, width, height);

                canvas.toBlob(function (data) {
                    video.pause();
                    video.src = "";
                    document.body.removeChild(video);
                    document.body.removeChild(canvas);
                    callback(data); // here the png blob returned to the callback
                });
            }
        }
    }, false);

    navigator.webkitGetUserMedia(session, function (stream) {
        video.src = window.webkitURL.createObjectURL(stream);
        video.play();
    }, function () {
        console.error("Can't take a screenshot");
    });
}

Wednesday, 6 July 2016

Electron and ReactJS performance hint

If process.env.NODE_ENV is not set to 'production' react will do some performance consuming debug stuff. Set process.env.NODE_ENV to 'production' and it will bump the app performance.

Tuesday, 26 April 2016

Call Rust from NodeJS via cross-platform C ABI with RuNo bridge

The RuNo bridge is a command line tool which generates C++ code for NodeJS addon from Rust code or from JSON definition (with JSON definition it should work with any C ABI compatible library, of course when implemented functionality is enough).

I've implemeted this tool after my last research on calling Rust from Node JS.

The parser of RuNo bridge does not do magical deep analysis of code, it just detects the following signatures in your code:

#[no_mangle]

pub extern "C" fn ...


it does not require any C++ knowledge from developer if you use primitives mentioned above and your Rust ABI interface complies with simple requirements:

  • All your ABI functoins should be listed in one Rust file;
  • Your library should use crate libc;
  • Each ABI function should be preceeded with #[no_mangle];
  • Each ABI function should be prefixed with pub extern "C";
  • ABI Functions should only take params of c_int,c_float,c_double or *c_char (as a C string with EOF);
  • ABI Functions should return either one of c_int,c_float,c_double or *c_char (as a C string with EOF)
It is tested on Windows, Mac OS and Ubuntu, however it has some limitations developer should know:


The package itself does not need Rust or C++ with node-gyp, it just emits a C++ source file.

However in order to build the source code, rust and C++ compiler should be compatible with NodeJS version installed. It is particularly important on Windows, where Rust target should be MSVC not GNU. For example, if one using 32 bit NodeJS on Windows this one should use target i686-pc-windows-msvc, if 64 bit Node then Rust should be configured with x86_64-pc-windows-msvc compile target. The same about C++: Everything is mostrly smooth on platforms with GCC, and a bit painful with MS Visual C++, please refer to node-gyp installation instructions for details.

You can find simple usage examples on the github: https://github.com/andruhon/runo-bridge-example

I will appreciate any comments or contribution.

Tuesday, 15 September 2015

Explaining closure pattern name

Live and learn. Indeed.

I'm currently reading a book about Scala and found that "closure" "design pattern" which I've been using in JavaScript for ages is actually from functional programming world, and this closure is not a whole thing, but a variable closing so called "open term".

Imagine the function:
function myFunc(a) {
  return a + b
}

The "a" variable here is a "bound variable", and it makes sense in context of myFunc. The "b" variable is a "free variable" and it is senseless in this context. The "a + b" expression here is an "open term". On the other hand if we replace the expression in myFunc with something like "a + 2" it will be a "closed term".

The name "closure" arises from the act of "closing" the function literal with the open term(s) by "capturing" bindings of its free variables.

var b = 2 //this b variable is actually a closure!
function myFunc(a) {
  return a + b
}

That's it. This variable is the "closure". And the thing is the "function literal" with the "open term".

Saturday, 25 July 2015

Calculate particular weekdays in the range of dates in JavaScript

I've created a plugin for Moment JS to calculate particular weekdays in the range of dates:
https://github.com/andruhon/moment-weekday-calc

For example it would work if you need to calculate quantity business days in current financial year. The exclusion option to mind public holidays is also available.

The plugin is available in Bower and NPM as moment-weekday-calc.
npm install moment-weekday-calc
bower install moment-weekday-calc

Wednesday, 14 January 2015

Alfresco Javascript webscript Debuggers

Just found that there are actually two of them:

One for the Alfresco repository debugging:
/alfresco/service/api/javascript/debugger
(may be http://127.0.0.1:8080/alfresco/service/api/javascript/debugger)

And one for the Alfresco share debugging:
/share/page/api/javascript/debugger
(may be http://127.0.0.1:8081/share/page/api/javascript/debugger)

Webscript indexes accordingly are:
/alfresco/service/index
(http://127.0.0.1:8080/alfresco/service/index)

and

/share/service/index
(http://127.0.0.1:8081/share/service/index)


Debuggers sometimes won't catch webscripts evaluated: you may need to clean up the project and do "clear dependency caches" and "refresh web scripts" on webscript indexes service pages.

Tuesday, 4 November 2014

JWT usage example

Good, easy to understand JSON web token (JWT) usage example, which does not contain much unrelated stuff:
https://github.com/auth0/angular-token-auth

Article with explanation:
https://auth0.com/blog/2014/01/07/angularjs-authentication-with-cookies-vs-token/

Listings just in case if it suddenly disappear:

auth.server.js
var express = require('express');
var bodyParser = require('body-parser');

var jwt = require('jsonwebtoken');  //https://npmjs.org/package/node-jsonwebtoken
var expressJwt = require('express-jwt'); //https://npmjs.org/package/express-jwt


var secret = 'this is the secret secret secret 12356';

var app = express();

// We are going to protect /api routes with JWT
app.use('/api', expressJwt({secret: secret}));

app.use(bodyParser.json());
app.use('/', express.static(__dirname + '/'));

app.use(function(err, req, res, next){
  if (err.constructor.name === 'UnauthorizedError') {
    res.status(401).send('Unauthorized');
  }
});

app.post('/authenticate', function (req, res) {
  //TODO validate req.body.username and req.body.password
  //if is invalid, return 401
  if (!(req.body.username === 'john.doe' && req.body.password === 'foobar')) {
    res.status(401).send('Wrong user or password');
    return;
  }

  var profile = {
    first_name: 'John',
    last_name: 'Doe',
    email: 'john@doe.com',
    id: 123
  };

  // We are sending the profile inside the token
  var token = jwt.sign(profile, secret, { expiresInMinutes: 60*5 });

  res.json({ token: token });
});

app.get('/api/restricted', function (req, res) {
  console.log('user ' + req.user.email + ' is calling /api/restricted');
  res.json({
    name: 'foo'
  });
});

app.listen(8080, function () {
  console.log('listening on http://localhost:8080');
});


auth.client.js
var myApp = angular.module('myApp', []);

//this is used to parse the profile
function url_base64_decode(str) {
  var output = str.replace('-', '+').replace('_', '/');
  switch (output.length % 4) {
    case 0:
      break;
    case 2:
      output += '==';
      break;
    case 3:
      output += '=';
      break;
    default:
      throw 'Illegal base64url string!';
  }
  return window.atob(output); //polifyll https://github.com/davidchambers/Base64.js
}

myApp.controller('UserCtrl', function ($scope, $http, $window) {
  $scope.user = {username: 'john.doe', password: 'foobar'};
  $scope.isAuthenticated = false;
  $scope.welcome = '';
  $scope.message = '';

  $scope.submit = function () {
    $http
      .post('/authenticate', $scope.user)
      .success(function (data, status, headers, config) {
        $window.sessionStorage.token = data.token;
        $scope.isAuthenticated = true;
        var encodedProfile = data.token.split('.')[1];
        var profile = JSON.parse(url_base64_decode(encodedProfile));
        $scope.welcome = 'Welcome ' + profile.first_name + ' ' + profile.last_name;
      })
      .error(function (data, status, headers, config) {
        // Erase the token if the user fails to log in
        delete $window.sessionStorage.token;
        $scope.isAuthenticated = false;

        // Handle login errors here
        $scope.error = 'Error: Invalid user or password';
        $scope.welcome = '';
      });
  };

  $scope.logout = function () {
    $scope.welcome = '';
    $scope.message = '';
    $scope.isAuthenticated = false;
    delete $window.sessionStorage.token;
  };

  $scope.callRestricted = function () {
    $http({url: '/api/restricted', method: 'GET'})
    .success(function (data, status, headers, config) {
      $scope.message = $scope.message + ' ' + data.name; // Should log 'foo'
    })
    .error(function (data, status, headers, config) {
      alert(data);
    });
  };

});

myApp.factory('authInterceptor', function ($rootScope, $q, $window) {
  return {
    request: function (config) {
      config.headers = config.headers || {};
      if ($window.sessionStorage.token) {
        config.headers.Authorization = 'Bearer ' + $window.sessionStorage.token;
      }
      return config;
    },
    responseError: function (rejection) {
      if (rejection.status === 401) {
        // handle the case where the user is not authenticated
      }
      return $q.reject(rejection);
    }
  };
});

myApp.config(function ($httpProvider) {
  $httpProvider.interceptors.push('authInterceptor');
});


index.html
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>Angular Authentication</title>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.min.js"></script>
    <script src="./auth.client.js"></script>
  </head>
  <body ng-app="myApp">
    <div ng-controller="UserCtrl">
      <span ng-show="isAuthenticated">{{welcome}}</span>
      <form ng-show="!isAuthenticated" ng-submit="submit()">
        <input ng-model="user.username" type="text" name="user" placeholder="Username" />
        <input ng-model="user.password" type="password" name="pass" placeholder="Password" />
        <input type="submit" value="Login" />
      </form>
      <div>{{error}}</div>
      <div ng-show="isAuthenticated">
        <a ng-click="callRestricted()" href="">Shh, this is private!</a>
        <br>
        <div>{{message}}</div>
        <a ng-click="logout()" href="">Logout</a>
      </div>
    </div>
  </body>
</html>


package.json
{
  "name": "angular-token-auth",
  "version": "0.1.0",
  "dependencies": {
    "body-parser": "^1.9.0",
    "express": "~4.9.0",
    "express-jwt": "~0.2.1",
    "jsonwebtoken": "~0.4.0"
  },
  "description": "Example of Token-based authentication in [AngularJS](http://angularjs.org) with [Express](http://expressjs.com).",
  "main": "auth.server.js",
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/auth0/angular-token-auth.git"
  },
  "keywords": [
    "angular",
    "auth",
    "jwt",
    "express"
  ],
  "author": "",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/auth0/angular-token-auth/issues"
  },
  "homepage": "https://github.com/auth0/angular-token-auth"
}

Wednesday, 10 September 2014

Sencha: Catch all events of Observable

Very useful pattern to catch all events of Observable object in Sencha:
Ext.util.Observable.capture(object, function(){
    console.log(arguments);
});


This object might be Store, View, or whatever using Observable mixin. This may be useful to learn how the object work or to catch events before any other on/addListener listeners.

Arguments may wary but it contains at least two objects:
0: {String} always event name
1: {Object} object itself

This may contain other arguments such as Action, changed Model (for Store), String name of the action caused event, Array of changed fields (for Model and Store)

Store usage:
Ext.util.Observable.capture(store, function(){
    if (arguments[0]=='update') {
        /*eventName,store,record,operation,modifiedFields*/
        // Your fancy code here
        store.suspendEvent('update');
    }
});

Thursday, 7 August 2014

Getting rid of annoying Uncaught TypeError: Cannot read property 'isGroupHeader' of null

"Uncaught TypeError: Cannot read property 'isGroupHeader' of null" in sencha 4.x might be caused by nested grids usage. For example if you have grid with RowExpander rendering another grid into expander.

This happens because grid cell or cell editor event (mouseover, click or whatever) is fired in context of another grid (parent or ancestor).

This override might help (it works fine on 4.2.2 for me):

Ext.define('SystemFox.overrides.view.Table', {
    override: 'Ext.view.Table',
    checkThatContextIsParentGridView: function(e){
        var target = Ext.get(e.target);
        var parentGridView = target.up('.x-grid-view');
        if (this.el != parentGridView) {
            /* event of different grid caused by grids nesting */
            return false;
        } else {
            return true;
        }
    },
    processItemEvent: function(record, row, rowIndex, e) {
        if (e.target && !this.checkThatContextIsParentGridView(e)) {
            return false;
        } else {
            return this.callParent([record, row, rowIndex, e]);
        }
    }
});