Showing posts with label Javascript. Show all posts
Showing posts with label Javascript. Show all posts

Sunday 12 November 2023

Getting a parent object by name in parent scope chain in AngularJs

I still work with some legacy solutions in AngularJs. I want to look in parent scope for a object which I know the name of, but it is some levels up by calling multiple $parent calls to get to the correct parent scope. Here is a small util method I wrote the other day to access a variable inside parent scopes by known name. Note : Select an element via F12 Developer tools and access its AngularJs scope. In the browser this is done by running in the console : angular.element($0).scope() Here is the helper method I wrote :


angular.element($0).scope().findParentObjByName = function($scope, objName) {
 var curScope = $scope;
var parentLevel = 0;
 //debugger
 while ((curScope = curScope.$parent) != null && !curScope.hasOwnProperty(objName) && parentLevel < 15){
     parentLevel++;
 }
 return curScope.hasOwnProperty(objName) ? curScope[objName] : null;  
}



We can then look for a property in the parent scopes like in this example :

angular.element($0).scope().findParentObjByName($scope, 'list')

This returns the object, if found and you can further work on it , for example in this particular example I used :

angular.element($0).scope().findParentObjByName($scope, 'list').listData[0]

Thursday 28 May 2020

Creating an AngularJs directive for a horizontal scroller at top and bottom of HTML container element

I made an AngularJs directive today that adds a horizontal scroller at top and bottom of an HTML container element, such as text area, table or div. The AngularJs directive uses the link function of AngularJs to prepend and wrap the necessary scrolling mechanism and add some Javascript scroll event handlers using jQuery.

import angular from 'angular';

var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, $compile) {
  $scope.name = 'Dual wielded horizontal scroller';
});

app.directive('doubleHscroll', function($compile) {
  return {
    restrict: 'C',
    link: function(scope, elem, attr){

      var elemWidth = parseInt(elem[0].clientWidth);

      elem.wrap(`<div id='wrapscroll' style='width:${elemWidth}px;overflow:scroll'></div>`); 
      //note the top scroll contains an empty space as a 'trick' 
      $('#wrapscroll').before(`<div id='topscroll' style='height:20px; overflow:scroll;width:${elemWidth}px'><div style='min-width:${elemWidth}px'> </div></div>`);

      $(function(){
        $('#topscroll').scroll(function(){
          $("#wrapscroll").scrollLeft($("#topscroll").scrollLeft());
        });
        $('#wrapscroll').scroll(function() {
          $("#topscroll").scrollLeft($("#wrapscroll").scrollLeft());
        });

      });  

    }

  };


});


The HTML that uses this directive, restricted to 'C' (class) is then simply using the class 'double-Hscroll' following AngularJs 'snake escaping' naming convention of capitalization and dashes.

<!DOCTYPE html>

<html>
  <head>
    <link rel="stylesheet" href="lib/style.css" />
    <script src="lib/script.js"></script>
    <script
  src="https://code.jquery.com/jquery-3.5.1.js"
  integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc="
  crossorigin="anonymous"></script>
  </head>

  <body ng-app="plunker" ng-cloak>
    <div ng-controller="MainCtrl">
      <h1>Hello {{name}}</h1>
      <p>Dual horizontal scroll top and below a text area.</p>
      <textarea noresize class="double-hscroll" rows="10" cols="30">
        lorem ipsum dolores  lorem ipsum dolores
      lorem ipsum dolores
      lorem ipsum dolores sit amen
      lorem ipsum dolores
      lorem ipsum dolores sit amen
      lorem ipsum dolores
      lorem ipsum dolores amen sit
     
      </textarea>
    </div>
  </body>
</html>

Saturday 4 January 2020

Implementing GetPropertyNames in Typescript

I am currently working on a Linq-like library for Typescript and wanted to implement something like GetProperties of C# in Typescript / Javascript. The more I work with Typescript and generics, the clearer picture I get of that you usually have to have an instantiated object with intialized properties to get any useful information out at runtime about properties of a class. But it would be nice to retrieve information anyways just from the constructor function object, or an array of objects and be flexible about this. I was following a question thread on Stack Overflow and found a good answer that helped me out: https://stackoverflow.com/questions/40636292/get-properties-of-a-class/59586570#59586570 Here is what I ended up with for now. First off, I define Array prototype method ('extension method' for you C# developers).

export { } //creating a module of below code
declare global {
  interface Array>T< {
    GetProperties>T<(TClass: Function, sortProps: boolean): string[];
} }
The GetProperties method then looks like this, inspired by madreasons answer.

if (!Array.prototype.GetProperties) {
  Array.prototype.GetProperties = function >T<(TClass: any = null, sortProps: boolean = false): string[] {
    if (TClass === null || TClass === undefined) {
      if (this === null || this === undefined || this.length === 0) {
        return []; //not possible to find out more information - return empty array
      }
    }
    // debugger
    if (TClass !== null && TClass !== undefined) {
      if (this !== null && this !== undefined) {
        if (this.length < 0) {
          let knownProps: string[] = Describer.describe(this[0]).Where(x =< x !== null && x !== undefined);
          if (sortProps && knownProps !== null && knownProps !== undefined) {
            knownProps = knownProps.OrderBy(p =< p);
          }
          return knownProps;
        }
        if (TClass !== null && TClass !== undefined) {
          let knownProps: string[] = Describer.describe(TClass).Where(x =< x !== null && x !== undefined);
          if (sortProps && knownProps !== null && knownProps !== undefined) {
            knownProps = knownProps.OrderBy(p =< p);
          }
          return knownProps;
        }
      }
    }
    return []; //give up..
  }
}

The describer method is about the same as madreason's answer on Stack Overflow concerning this. It can handle both class Function and if you get an object instead. It will then use Object.getOwnPropertyNames if no class Function is given (i.e. the class 'type' for C# developers).

class Describer {
  private static FRegEx = new RegExp(/(?:this\.)(.+?(?= ))/g);
  static describe(val: any, parent = false): string[] {
    let isFunction = Object.prototype.toString.call(val) == '[object Function]';
    if (isFunction) {
      let result = [];
      if (parent) {
        var proto = Object.getPrototypeOf(val.prototype);
        if (proto) {
          result = result.concat(this.describe(proto.constructor, parent));
        }
      }
      result = result.concat(val.toString().match(this.FRegEx));
      result = result.Where(r =< r !== null && r !== undefined);
      return result;
    }
    else {
      if (typeof val == "object") {
        let knownProps: string[] = Object.getOwnPropertyNames(val);
        return knownProps;
      }
    }
    return val !== null ? [val.tostring()] : [];
  }
}

Here you see two specs for testing this out with Jasmine.

class Hero {
  name: string;
  gender: string;
  age: number;
  constructor(name: string = "", gender: string = "", age: number = 0) {
    this.name = name;
    this.gender = gender;
    this.age = age;
  }
}

class HeroWithAbility extends Hero {
  ability: string;
  constructor(ability: string = "") {
    super();
    this.ability = ability;
  }
}

describe('Array Extensions tests for TsExtensions Linq esque library', () =< {

  it('can retrieve props for a class items of an array', () =< {
    let heroes: Hero[] = [>Hero<{ name: "Han Solo", age: 44, gender: "M" }, >Hero<{ name: "Leia", age: 29, gender: "F" }, >Hero<{ name: "Luke", age: 24, gender: "M" }, >Hero<{ name: "Lando", age: 47, gender: "M" }];
    let foundProps = heroes.GetProperties(Hero, false);
    //debugger
    let expectedArrayOfProps = ["name", "age", "gender"];
    expect(foundProps).toEqual(expectedArrayOfProps);
    expect(heroes.GetProperties(Hero, true)).toEqual(["age", "gender", "name"]);
  });

  it('can retrieve props for a class only knowing its function', () =< {
    let heroes: Hero[] = [];
    let foundProps = heroes.GetProperties(Hero, false);
    let expectedArrayOfProps = ["this.name", "this.gender", "this.age"];
    expect(foundProps).toEqual(expectedArrayOfProps);
    let foundPropsThroughClassFunction = heroes.GetProperties(Hero, true);
    //debugger
    expect(foundPropsThroughClassFunction.SequenceEqual(["this.age", "this.gender", "this.name"])).toBe(true);
  });

..

And as madreason mentioned, you have to initialize the props to get any information out from just the class Function itself, or else it is stripped away when Typescript code is turned into Javascript code. Typescript 3.7 is very good with Generics, but coming from a C# and Reflection background, some fundamental parts of Typescript and generics still feels somewhat loose and unfinished business. Like my code here, but at least I got out the information I wanted - a list of property names for a given class or instance of objects.

Tuesday 31 December 2019

DistinctBy operator written in Typescript

I am extended my Linq library for Typescript with many more methods! Here is my implementation of DistinctBy.

if (!Array.prototype.DistinctBy) {
  Array.prototype.DistinctBy = function <T>(property: (keyof T)): T[] {
    if (this === null || this === undefined) {
      return [];
    }
    let filteringArray = this.Select(property).map(n => n[property]);

    let distinctRunOnArray = this.filter((value, index, array) => {
      let valueProperty = value[property];
      return filteringArray.indexOf(valueProperty) === index;
    });
    return distinctRunOnArray;
  }
}

This Jasmine test can test this operator out.
describe('TSLinq Jasmine tests', () => {

  it('can filter out duplicates using DistinctBy on array of items of objects', () => {
    let someArray: Student[] = [];
    someArray.push(<Student>{ StudentID: 1, StudentName: "John", Age: 13 });
    someArray.push(<Student>{ StudentID: 2, StudentName: "Moin", Age: 21 });
    someArray.push(<Student>{ StudentID: 2, StudentName: "Moin", Age: 21 });
    someArray.push(<Student>{ StudentID: 4, StudentName: "Ram", Age: 20 });
    someArray.push(<Student>{ StudentID: 5, StudentName: "Ron", Age: 15 });
    let expectedArray: Student[] = [];
    expectedArray.push(<Student>{ StudentID: 1, StudentName: "John", Age: 13 });
    expectedArray.push(<Student>{ StudentID: 2, StudentName: "Moin", Age: 21 });
    expectedArray.push(<Student>{ StudentID: 4, StudentName: "Ram", Age: 20 });
    expectedArray.push(<Student>{ StudentID: 5, StudentName: "Ron", Age: 15 });
    let result = someArray.DistinctBy<Student>("StudentID");
    expect(result).toEqual(expectedArray);
  });


});
The Student class is simple:

class Student {
  StudentID: number;
  StudentName: string;
  Age: number;
}

Implementing OfType in Typescript

I am working on my Linq library for Typescript and wanted to implement OfType. Turns out, this is hard because the generic type arguments in Typescript usually requires a value, i.e. an object instance of type T to get any shape information at run-time. So I ended up passing in a vanilla object setting default property values instead. Here is how my implementation ended up:

function isOfSimilarShape<T>(input: any, compareObject: T): boolean {
  if (input === undefined || input === null || compareObject === undefined || compareObject === null)
    return false;

  let propsOfInput = Object.keys(input);
  let propsOfCompareObject = Object.keys(compareObject);
  //debugger
  let sameShapeOfInputAndCompareObject = propsOfInput.EqualTo(propsOfCompareObject);
  return sameShapeOfInputAndCompareObject;
}

if (!Array.prototype.OfType) {
  Array.prototype.OfType = function <T>(compareObject: T): T[] {
    let result: T[] = [];
    this.forEach(el => {
      //debugger
      let t: T = null;
      if (isOfSimilarShape(el, compareObject))
        result.push(el);
    });
    return result;
  }
}

The following Jasmine test shows its usage:

describe('Array Extensions tests', () => {

  it('can find desired items using OfType of type T', () => {
    let someMixedArray: any[] = [];
    someMixedArray.push(<SomeClass>{ Name: "Foo", Num: 1 });
    someMixedArray.push(<SomeOtherClass>{ SomeName: "BarBazBaze", SomeOtherNum: 813 });
    someMixedArray.push(<SomeClass>{ Name: "FooBaz", Num: 4 });
    someMixedArray.push(<SomeOtherClass>{ SomeName: "BarBaze", SomeOtherNum: 13 });
    someMixedArray.push(<SomeClass>{ Name: "AllyoBaze", Num: 7 });

    let compareObject = <SomeClass>{ Name: "", Num: 0 };
    let filteredArrayBySpecifiedType = someMixedArray.OfType(compareObject);
    console.log(filteredArrayBySpecifiedType);

    expect(filteredArrayBySpecifiedType.All(item => <SomeClass>item !== undefined)).toBe(true, "Expected only items of type SomeOtherClass in the filtered array after running OfType of SomeOtherClass on it.");
  });

It would be nice if we did not have to pass in a vanilla object and populate its properties, but I could not find any tips online or in the Typescript documentation for how to implement extracting type information from generic arguments of Typescript. This is very easy in C#, but while Typescript gives compilation type information, getting runtime information from generic arguments in the Javascript code that Typescript compiles into turned much harder.

Friday 27 December 2019

Implementing Linq methods on arrays with Typescript for Angular 8

This article will look into implementing Linq methods on array with Typescript for Angular 8. First off, I have created a repo for this article on Github. Simple Linq Library written with Typescript for Angular 8 This only implements FirstOrDefault and Where Linq operators on arrays. We first need to define our Array prototype methods. Since we use Angular, first we define an empty module using export {} and then declare global { .. } Inside our declare global we define our type predicate and our methods Where and FirstOrDefault. Then we define our two methods if they do not exist yet on Array.prototype. The special syntax above is adaptions for Typescript and Angular. I have tested this with Angular 8. Here is the Typescript code I ended up with:

export { } //creating a module of below code
declare global {
  type predicate<T> = (arg: T) => boolean;
  interface Array<T> {
    FirstOrDefault<T>(condition: predicate<T>): T;
    Where<T>(condition: predicate<T>): T[];
  }
}

if (!Array.prototype.FirstOrDefault) {
  Array.prototype.FirstOrDefault = function <T>(condition: predicate<T>): T {
    let matchingItems: T[] = this.filter((item: T) => {

      if (condition(item)) {
        return item;
      }
    });
    if (matchingItems.length > 0) {
      return matchingItems[0];
    }
    return null;
  }
}

if (!Array.prototype.Where) {
  Array.prototype.Where = function <T>(condition: predicate<T>): T[] {

    let matchingItems: T[] = this.filter((item: T) => {

      if (condition(item)) {
        return true;
      }
    });
    return matchingItems;
  }
}

Let us define some input data - an array to work on and pass into predicates where we can test out FirstOrDefault and Where methods !

import { Movie } from './movie';

export const StarWarsMovies : Array>Movie< =
 [{
      "title" : "Star Wars: Episode I - The Phantom Menace",
      "episode_number" : "1",
      "main_characters" : ["Qui-Gon Jinn", "Obi-Wan Kenobi", "Anakin Skywalker", "Padmé Amidala", "Jar Jar Binks", "Darth Maul"],
      "description" : "The evil Trade Federation, led by Nute Gunray is planning to take over the peaceful world of Naboo. Jedi Knights Qui-Gon Jinn and Obi-Wan Kenobi are sent to confront the leaders. But not everything goes to plan. The two Jedi escape, and along with their new Gungan friend, Jar Jar Binks head to Naboo to warn Queen Amidala, but droids have already started to capture Naboo and the Queen is not safe there. Eventually, they land on Tatooine, where they become friends with a young boy known as Anakin Skywalker. Qui-Gon is curious about the boy, and sees a bright future for him. The group must now find a way of getting to Coruscant and to finally solve this trade dispute, but there is someone else hiding in the shadows. Are the Sith really extinct? Is the Queen really who she says she is? And what's so special about this young boy?",
      "poster" : "star_wars_episode_1_poster.png",
      "hero_image" : "star_wars_episode_1_hero.jpg"
    },

    {
      "title" : "Star Wars: Episode II - Attack of the Clones",
      "episode_number" : "2",
      "main_characters" : ["Obi-Wan Kenobi", "Anakin Skywalker", "Count Dooku", "Padmé Amidala", "Mace Windu", "Yoda", "Jango Fett", "Supreme Chancellor Palpatine"],
      "description" : "Ten years after the 'Phantom Menace' threatened the planet Naboo, Padmé Amidala is now a Senator representing her homeworld. A faction of political separatists, led by Count Dooku, attempts to assassinate her. There are not enough Jedi to defend the Republic against the threat, so Chancellor Palpatine enlists the aid of Jango Fett, who promises that his army of clones will handle the situation. Meanwhile, Obi-Wan Kenobi continues to train the young Jedi Anakin Skywalker, who fears that the Jedi code will forbid his growing romance with Amidala.",
      "poster" : "star_wars_episode_2_poster.png",
      "hero_image" : "star_wars_episode_2_hero.jpg"
    },

    {
      "title" : "Star Wars: Episode III - Revenge of the Sith",
      "episode_number" : "3",
      "main_characters" : ["Obi-Wan Kenobi", "Anakin Skywalker", "Count Dooku", "Padmé Amidala", "Mace Windu", "Yoda", "C-3PO", "Supreme Chancellor Palpatine"],
      "description" : "Three years after the onset of the Clone Wars; the noble Jedi Knights are spread out across the galaxy leading a massive clone army in the war against the Separatists. After Chancellor Palpatine is kidnapped, Jedi Master Obi-Wan Kenobi and his former Padawan, Anakin Skywalker, are dispatched to eliminate the evil General Grievous. Meanwhile, Anakin's friendship with the Chancellor arouses suspicion in the Jedi Order, and dangerous to the Jedi Knight himself. When the sinister Sith Lord, Darth Sidious, unveils a plot to take over the galaxy, the fate of Anakin, the Jedi order, and the entire galaxy is at stake. Upon his return, Anakin Skywalker's wife Padme Amidala is pregnant, but he is having visions of her dying in childbirth. Anakin Skywalker ultimately turns his back on the Jedi, thus completing his journey to the dark side and his transformation into Darth Vader. Obi-Wan Kenobi must face his former apprentice in a ferocious lightsaber duel on the fiery world of Mustafar.",
      "poster" : "star_wars_episode_3_poster.png",
      "hero_image" : "star_wars_episode_3_hero.jpg"
    },

    {
      "title" : "Star Wars: Episode IV - A New Hope",
      "episode_number" : "4",
      "main_characters" : ["Luke Skywalker", "Han Solo", "Princess Leia Organa", "Ben Kenobi", "Darth Vader", "C-3P0", "R2-D2", "Chewbacca"],
      "description" : "Part IV in George Lucas' epic, Star Wars: A New Hope opens with a Rebel ship being boarded by the tyrannical Darth Vader. The plot then follows the life of a simple farm boy, Luke Skywalker, as he and his newly met allies (Han Solo, Chewbacca, Obi-Wan Kenobi, C-3PO, R2-D2) attempt to rescue a Rebel leader, Princess Leia, from the clutches of the Empire. The conclusion is culminated as the Rebels, including Skywalker and flying ace Wedge Antilles make an attack on the Empire's most powerful and ominous weapon, the Death Star.",
      "poster" : "star_wars_episode_4_poster.png",
      "hero_image" : "star_wars_episode_4_hero.jpg"
    },

    {
      "title" : "Star Wars: Episode V - The Empire Strikes Back",
      "episode_number" : "5",
      "main_characters" : ["Luke Skywalker", "Han Solo", "Princess Leia Organa", "Darth Vader", "C-3P0", "R2-D2", "Chewbacca", "Lando Calrissian", "Boba Fett"],
      "description" : "Fleeing the evil Galactic Empire, the Rebels abandon their new base in an assault with the Imperial AT-AT walkers on the ice world of Hoth. Princess Leia, Han Solo, Chewbacca and the droid C-3PO escape in the Millennium Falcon, but are later captured by Darth Vader on Bespin. Meanwhile, Luke Skywalker and the droid R2-D2 follows Obi-Wan Kenobi's posthumous command, and receives Jedi training from Master Yoda on the swamp world of Dagobah. Will Skywalker manage to rescue his friends from the Dark Lord?",
      "poster" : "star_wars_episode_5_poster.png",
      "hero_image" : "star_wars_episode_5_hero.jpg"
    },

    {
      "title" : "Star Wars: Episode VI - Return of the Jedi",
      "episode_number" : "6",
      "main_characters" : ["Luke Skywalker", "Han Solo", "Princess Leia Organa", "Darth Vader", "C-3P0", "Chewbacca", "The Emperor", "Boba Fett"],
      "description" : "Darth Vader and the Empire are building a new, indestructible Death Star. Meanwhile, Han Solo has been imprisoned, and Luke Skywalker has sent R2-D2 and C-3PO to try and free him. Princess Leia - disguised as a bounty hunter - and Chewbacca go along as well. The final battle takes place on the moon of Endor, with its natural inhabitants, the Ewoks, lending a hand to the Rebels. Will Darth Vader and the Dark Side overcome the Rebels and take over the universe?",
      "poster" : "star_wars_episode_6_poster.png",
      "hero_image" : "star_wars_episode_6_hero.jpg"
    }];


The Movie class looks like this:

export class Movie {
  title: string;
  episode_number: string;
  main_characters: string[];
  description: string;
  poster: string;
  hero_image: string;
}

Now we can test out these predicate methods FirstOrDefault and Where ! Note that in case you have worked with C# and Linq before, the syntax and the Intellisense will make you feel more at home with this kind of code - not only do you have type checking, but you can also pass in strongly typed predicate methods using Typescript, Generics and the Array prototype techniques discussed here. Also note how I define a predicate type here defining an arrow function, being the predicate.

import { Component, Inject } from '@angular/core';
import { StarWarsMovies } from './starwarsmovies';
import { Movie } from './movie';
import './array-extensions';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'Linq TsExtensions demo';

  firstMovieWithBoba: Movie;
  allMoviesWithLeia: Movie[];
  starwarsMovies: string;

  constructor() {
    this.starwarsMovies = JSON.stringify(StarWarsMovies);
    this.firstMovieWithBoba = 
StarWarsMovies.FirstOrDefault<Movie>(m => m.main_characters.indexOf('Boba Fett') > 0);
    this.allMoviesWithLeia = 
StarWarsMovies.Where<Movie>(m => m.main_characters.indexOf('Princess Leia Organa') > 0);
    console.log(this.firstMovieWithBoba);
    console.log(this.allMoviesWithLeia);
  }

}


We now can work on our strongly typed arrays and define easily our filtering predicates for Where and FirstOrDefault implementation. It should be easy to extend this into more methods, such as SingleOrDefault and Any and so on. The screen shot below shows the results after running ng serve -o:

Monday 2 December 2019

Eslint Standalone tool

I have created a standalone Eslint tool the last weeks! This tool is available through Npmjs here: https://www.npmjs.com/package/eslint-standalone The source code is available here: https://github.com/toreaurstadboss/eslint-standalone To clone the repo, you can run this command:

git clone https://github.com/toreaurstadboss/eslint-standalone.git

This animated gif shows how the tools shows linting capabilities. The loaded .eslintrc.js file errors if there are ES6 syntax, which does not work well in Internet Explorer. This is done by specifying env->es6 set to true and parserOptions->ecmaVersion set to '5'. Why would you even consider supporting Internet Explorer as a browser for your products ? Well, in the real world, some customers still use Internet Explorer due to company restrictions and compability reasons. So this standalone tool together with .eslintrc.js file below should help you check files conforming to ES5 Syntax and thereby supporting Internet Explorer.

module.exports = {
  "plugins": ["ie11"],
  "env": {
    "browser": true,
    "node": true,
    "es6": false
  },
  "parserOptions": {
    "ecmaVersion": 5,
  },
  "rules": {
    "ie11/no-collection-args": ["error"],
    "ie11/no-for-in-const": ["error"],
    //"ie11/no-loop-func": ["warn"],
    "ie11/no-weak-collections": ["error"]
  }
};

To check that your Javascript code works using this tool, add the .eslinrc.js file above in the folder of your project (or any parent folder on that media disk volume). Then run the command eslint-standalone.exe after adding the .eslintrc.js file. You should then have outputted to the command line the errors (or warnings found) of ES6 Syntax, which IE does not support. Note that I have not added functionality for '--fix' with this ESLint tool yet. You must inspect the warnings and errors reported and manually adjust/fix the Javascript source code. Also note that this tool only supports looking at .js and .htm and .html files. I tried adding .cshtml files in the list of file globs supported, but the tool could not understand Razor syntax. Feel free to give me some tips here if you know how to add this as a support. Also note that it is additional documentation to be found for this tool on the Npmjs.org site and also in the GitHub repository.
eslint-standalone.exe 
The sample screen shot shows how to run the tool from the command line (simple command). I deliberately added an arrow method in a Javascript file and the tool quickly spots this issue. For a medium sized project the tool takes only 5-10 seconds to execute.

Friday 15 November 2019

Sorting array word-wise in Javascript

Sometimes it is nice to sort arrays word-wise in Javascript. This means sorting not the entire column value,

/**
 * Summary: Sorts alphabetaically word-wise to be used in e.g. a Select2Js control. Specify the n-word to start sorting with. 
 *
 * Alphabetically sorts by each word
 * @param {Number}   startWordIndex The 'n-word' to begin sorting with  *
 * @param {boolean}  isAscending The direction of sorting - true means ascending, false means descending
 * @return {type} Return sort comparison value. If 0, the sorting is tie, if < 0 the element a preceeds b
 */
(function () {
    var sortFunction = function sortFunctionTemplate(isAscending, startWordIndex, valueSelector, a, b) {
        var aValue = valueSelector(a);
        var bValue = valueSelector(b);
        var astripped = aValue.split(' ');
        var bstripped = bValue.split(' ');
        var wordLength = Math.min(astripped.length, bstripped.length);

        var compareValue = 0;
        for (var i = startWordIndex; i < wordLength; i++) {
            compareValue = astripped[i].localeCompare(bstripped[i]);
            if (compareValue !== 0) {
                break;
            }
        }
        if (compareValue === 0) {
            if (astripped.length > bstripped.length) {
                compareValue = 1;
            } else if (astripped.length < bstripped.length) {
                compareValue = 1;
            }
        }
        return compareValue * (isAscending ? 1 : -1);
    };
    if (typeof (Array.prototype.sortWordwise) === 'undefined') {
        // ReSharper disable once NativeTypePrototypeExtending
        Array.prototype.sortWordwise = function sortWordwise(startWordIndex, isAscending, valueSelector) {
            if (valueSelector === null) {
                valueSelector = function (item) {
                    return item;
                };
            }
            //console.log('sorterer word-wise... ');

            return this.sort(sortFunction.bind(this, isAscending, startWordIndex, valueSelector));

        };

    }
})();

Example how to use this method: Here we use the Select2.js jQuery plugin to do our custom sorting to sort word-wise. I only consider the text after the ':' in my specific use case and I supply the starting index - sorting by the nth-word 1 (i.e. second word and so on) and supply a value selector function also. Select2.Js also retrieves a matcher function to specify the matching to be done case-insensitive (the find item in list function of the select 2 js control in this specific case).

$('.stentGraftfabrikatpicker').select2({
      width: "element",
            sortResults: function(data) {
                var velgVerdi = data[0];
                var ret = [velgVerdi];
                var dataToBeSorted = data.splice(1);
                return ret.concat(
                    dataToBeSorted.sortWordwise(1, true, function(item) {
                        var value = item.text;
                        return value.indexOf(':') >= 0 ? value.substring(value.indexOf(':') + 1) : value;
                    })
                );
            },
      matcher: function(term, text) {
       return text.toUpperCase().indexOf(term.toUpperCase()) >= 0;
      },
      allowClear: true
    });

AngularJS Directive for Focus trap for modal popups

If you use Bootstrap modal popup, chances are that hitting TAB enough types on the keyboard will navigate out of the modal popup and back to the background web page, the DOM elements "beneath" the modal popup. This AngularJS directive should fix up that, wrapping everything in an IFE (Immediately invoking Function Expression). Note that this only works if your module is called 'app' (the default name).

(function() {
angular.module('app')
 .directive('modal', trapFocus)
 
 function trapFocus() {
        return {
            restrict: 'C',
            priority: 1,
            link: function (scope, element, attr) {
                if (typeof (scope.registerFocusTrap) === 'undefined') {
                    scope.registerFocusTrap = registerFocusTrap;
                } else {
                    for (var i = 0; i < element.length; i++) {
                        registerFocusTrap(element.get(i));
 
                    }
                }
 
            }
        };
    }
 
    function registerFocusTrap(element) {
        var focusableEls = element.querySelectorAll(
            'a[href]:not([disabled]), button:not([disabled]), textarea:not([disabled]), input[type="text"]:not([disabled]), input[type="radio"]:not([disabled]), input[type="checkbox"]:not([disabled]), select:not([disabled])');
        var firstFocusableEl = focusableEls[0];
        var lastFocusableEl = focusableEls[focusableEls.length - 1];
        // ReSharper disable once InconsistentNaming
        var KEYCODE_TAB = 9;
 
        $(element).on('keydown',
            function (e) {
                console.log('inside registerFocusTrap keydown');
                var isTabPressed = (e.key === 'Tab' || e.keyCode === KEYCODE_TAB);
 
                if (!isTabPressed) {
                    return;
                }
 
                if (e.shiftKey) /* shift + tab */ {
                    if (document.activeElement === firstFocusableEl) {
                        lastFocusableEl.focus();
                        e.preventDefault();
                    }
                } else /* tab */ {
                    if (document.activeElement === lastFocusableEl) {
                        firstFocusableEl.focus();
                        e.preventDefault();
                    }
                }
            }
        );
    }
  
}); 
 

Note that the name of registered modules in AngularJs is not supported in AngularJs by itself. This polyfill should tough fix up this.

(function(orig) {
    angular.modules = [];
    angular.module = function() {
        if (arguments.length > 1) {
            angular.modules.push(arguments[0]);
        }
        return orig.apply(null, arguments);
    }
})(angular.module);

Sunday 10 November 2019

Implementing projection in Javascript

Github page for source code in this article:
https://github.com/toreaurstadboss/JsLinqSimpleProjection
https://www.npmjs.com/package/jslinqsimpleprojection
Npm package for source code in this article: Since I started working with Linq in C#, I missed a good way of doing much of the same functionality in Javascript. Today, there are several Linq libraries for Javascript and Typescript, and libraries such as Backbone.Js or Lodash also containing a lot of helpful operators or utility methods. As an educational exercise, I was looking into a simple way of doing a projection method in pure Javascript (no es6 syntax). Here is what I made. First off, we need to be able to project an array of objects by listing up properties. In ES6 Javascript we could use arrow functions. But I wanted to support pure Javascript. So I choose to use a comma separated list of property or field values to dive into the array object, written in Json notation of course. Consider first this array as an example:

        var someCountries = [
          { country: "Norway", population: 5.2, code: "NO" },
          { country: "Finland", population: 5.5, code: "SU" },
          { country: "Iceland", population: 0.4, code: "IC" },
          { country: "Sweden", population: 10.2, code: "SW" }
        ];

We want to project this array using a new method select on the array of which we use the Array.prototype to achieve. Note that this will immediately add methods to all array objects immediately in the global scope. Now consider a projection of just the 'country' and the 'population' fields of the Json structure. Given a method call of just these two properties, we want to create a select projection method. First consider this lightweight linqmodule implementation, using an IFE (Immediately invoked function expression) and using the revealing module pattern. We expose the method dump to this module.

var linqmodule = (function() {
  projection = function(members) {
    var membersArray = members.replace(/s/g, "").split(",");
    var projectedObj = {};

    for (var i = 0; i < this.length; i++) {
      for (var j = 0; j < membersArray.length; j++) {
        var key = membersArray[j];
        if (j === 0) {
          projectedObj[i] = {};
        }
        projectedObj[i][key] = this[i][key];
      }
    }

    return projectedObj;
  };
  Array.prototype.select = projection;

  dumpmethod = function(arrayobj) {
    var result = "";
    result += "[";

    for (var i = 0; i < Object.keys(arrayobj).length; i++) {
      var membersArray = Object.keys(arrayobj[i]);
      for (var j = 0; j < membersArray.length; j++) {
        if (j === 0) {
          result += "{";
        }
        var key = membersArray[j];
        result +=
          "key: " +
          key +
          " , value: " +
          arrayobj[i][key] +
          (j < membersArray.length - 1 ? " , " : "");
        if (j === membersArray.length - 1) {
          result +=
            "}" + (i < Object.keys(arrayobj).length - 1 ? "," : "") + "\n";
        }
      }
    }
    result += "]";

    return result;
  };

  return {
    dump: dumpmethod
  };
})();


Now it is easy to dump the contents of our projected array (which is copied into a new object) using the dump method:

 result = someNums.select("country,population");
         document.getElementById("result").innerText = linqmodule.dump(result);

        console.log(result);

Note that our new object contains only the country and population fields in the Json structure, not the code. We have created a simple projection mechanism in Javascript in a self contained module!

Arithmetic parser in Javascript

I just added an arithmetic parser in Javascript code sample on Github, check out the following repo: JsSimpleParser The parser is also available through Npm: https://www.npmjs.com/package/simplejsparsermatharithmetic Installation: npm i simplejsparsermatharithmetic Supported expressions (examples): 2+3 should evaluate to 5 12 * 5–(5 * (32 + 4)) + 3 should evalute to -117 This is a great example of how to write a parser of your own.

Tuesday 24 September 2019

Getting started with tests on controllers in AngularJs

Some notes - I had to work with AngularJs tests today and needed to look into Jasmine and mocking enough and import enough to have running tests. I use the Chutzpah test runner to run the Jasmine Tests. The unit test below should get you started writing tests for controllers in AngularJs. The key concepts is to import jQuery, Bootstrap, Angular Animate, Angular-Mocks and your module and controller through using the reference path syntax at the top and then define a beforeEach that capture the _$controller_ and $_rootScope_ variables and using $rootScope.$new() to create a scope. But in my case I also had to specify a provided factory 'bootstrappedData' since my controller reads the 'model' property inside that. By specifying the value this provided factory returns at the top of each tests, I got the amount of DRY I needed to get started testing. I had to this since my controller got the data in an indirect manner, using he factory. I then create a new instance of the controller after updating the 'bootstrappedData' factory.

/// 
/// 
/// 
/// 
/// 
/// 
/// 

describe('someController works', function () {
    beforeEach(module('app'));
    var $scope;
    var $rootScope;
    var $controller;
    var $bootstrappedData;
    var $repository;
    var ctrl;
    var provide;

    beforeEach(module(function ($provide) {
        provide = $provide;
      
    }));

    beforeEach(module(function ($provide) {
        $provide.factory('repository', function () {
            return {
                model: {
                }
            };
        });
    }));

    beforeEach(inject(function (_$controller_, _$rootScope_) {
        $controller = _$controller_;
        $rootScope = _$rootScope_;
        scope = $rootScope.$new();

    }));

    it('Creates the AngularJs controller someController', function () {
        provide.factory('bootstrappedData', function () {
            return {
                model: {
                }
            };
        });

        ctrl = $controller('someController', { $scope: scope });
        expect(ctrl).not.toBe(null);

    });

    it('Method someproperty returns expected', function () {
        provide.factory('bootstrappedData', function () {
            return {
                model: {
                    SomeProperty: '3'
                }
            };
        });
        ctrl = $controller('KontrollskjemaController', { $scope: scope });
        var someprop = scope.isSomeConditionalPropertyReturningTrueIfSomePropertyIsThree;
        expect(someprop).toBe(true);
    });

});


A tip is to add a file called Chutzpah.json and ignoring well known Javascript libraries to only run code coverage on your own code:

{
  "CodeCoverageExcludes": [ "*\\jquery.js", "*\\angular.js", "*\\bootstrap.min.js", "*\\jquery*", "*\\angular-animate.min.js" ]
  //"CodeCoverageIncludes": [ "*\\*Spec.js" ]
} 

Sunday 8 September 2019

Building Angular apps with source maps and vendor chunk

Here is a quick tip about controlling the generation of source maps and vendor chunks in Angular 8 apps. Sourcemaps are built default in Angular according to the documentation @ https://angular.io/cli/build To be specific, the following command worked for me:


ng build --prod --sourceMap

In addition, the vendor chunk is now baked into the main chunk. To create a separate vendor chunk, run this: Angular 8 will put the vendor chunk into the main for optimizing the js code.
ng build --prod --sourceMap --vendor-chunk=true
In addition, it is recommended to analyze the bundle size with for example Webpack Bundle Analyzer like this: npm install -g webpack-bundle-analyzer Then add the following Npm run scripts to package.json:
    "buildwithstats": "ng b --sourceMap --prod --stats-json",
    "analyze": "webpack-bundle-analyzer --port 9901 dist/stats.json",
Now we have an interactive TreeMap view we can zoom into and see what is taking up space in our bundle!

Tuesday 11 June 2019

Deselecting all options in HTML SELECT using Jquery plugin

I created my first jQuery plugin today. This plugin will deselect all select option elements after a given timeout when checking an associated checkbox which will in our plugin represent the $(this) object. It can be utilized when you use the data-target attribute. The data-target attribute will automatically select the first option when the checkbox is unchecked. This plugin, however - deselects all options after a given timeout of milliseconds using the setTimeout function. It will keep a pointer to the select object by applying the $() function on a passed in DOM element identifier. Example usage:
     <script type="text/javascript">
                $(function() {
                    $("[name=@SearchParametersDataContract.SomeDOMElementCheckbox]").deselectAfterSelectCheckbox("#@SomeDOMElementSelect",100);
                });
            </script>
The jQuery plugin looks like this:

        <script type="text/javascript">
            $.fn.deselectAllAfterUnselectCheckbox= function(targetSelect, timeout) {
                $(this).on("click",
                    function() {
                        if (timeout === null || timeout == undefined)
                            timeout = 50;
                        $targetSelect = $(targetSelect);
                        if (this.checked === false) {
                            setTimeout(function() {
                                 $targetSelect.children("option").attr("selected", false);
                            }, timeout)
                        };
                    });
            }
        </script>

Wednesday 5 June 2019

Random selection of a HTML SELECT element

Just made a JsFiddle of a random selection control for HTML SELECT tag. The JsFiddle is available here: Random selecting combobox (JsFiddle)
  • The control should use Bootstrap to function properly. It needs jQuery library for Javascript bit.
  • To turn on hiding the selected option, set the data-hide-selectedoption HTML 5 data attribute to "true".

Random selecting HTML SELECT control


<!-- 
  Bootstrap docs: https://getbootstrap.com/docs
-->

<div class="container">

  <div class="input-group mb-3">
    <div class="input-group-prepend">
      <span class="input-group-text" id="basic-addon1"><img style="cursor:pointer" title="Choose random value" alt="Choose random value" width="24" height="24" src="https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-shuffle-512.png" /></span>
    </div>
    <select data-hide-selectedoption="true" disabled id="ComboBoxIceCreamBar" class="form-control" placeholder="Username" aria-label="Username" aria-describedby="basic-addon1">
 <option selected hidden>Choose icecream</option>
 <option value="Vanilla">Vanilla</option>
 <option value="Strawberry">Strawberry</option>
 <option value="Pineapple">Pineapple</option>
 <option value="Mint chocolate">Mint chocolate</option>
 <option value="Mango ice cream">Mango ice cream</option>
 <option value="Rasperry Ripple">Raspberry Ripple</option>
 <option value="Pistachio">Pistachio</option>
 <option value="Cookie dough">Cookie dough</option>
 <option value="Lemon">Lemon</option>
 <option value="Mango">Mango</option>
 </select>
    <!-- <input type="text" class="form-control" placeholder="Username" aria-label="Username" aria-describedby="basic-addon1"> -->
  </div>

</div>

<script type="text/javascript">
  $("#basic-addon1").on("click", function() {
    var selectcontrol = $(this).parent().siblings('select:first');
    var optionslength = selectcontrol.children('option').length;
    var randomIndex = Math.max(1, parseInt(Math.random() * optionslength));
  var optionElementToSelect = selectcontrol.children('option')[randomIndex];
    optionElementToSelect.selected = true;
  //debugger
  if (selectcontrol.data('hide-selectedoption')){
       optionElementToSelect.innerText = 'Selected value hidden.';   
  } 
  console.log("Selected ice cream: " + selectcontrol.val());
  });

</script>

Saturday 5 January 2019

Debugging Create React App javascript and tests in Visual Studio Code

This is a handy collection of configurations for debugging your Create React App javscript code (launching Chrome) and also tests generated with Create React App (CRA). Note that the last one does not work probably if you have ejected the CRA. Here is the .vscode/launch.json file:

{
 // Use IntelliSense to learn about possible attributes.
 // Hover to view descriptions of existing attributes.
 // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
 "version": "0.2.0",
 "configurations": [
  {
   "name": "Chrome debug 3000",
   "type": "chrome",
   "request": "launch",
   "url": "https://localhost:3000",
   "webRoot": "${workspaceRoot}/src"
  },
  {
   "name": "Debug CRA Tests",
   "type": "node",
   "request": "launch",
   "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/react-scripts",
   "args": [
    "test",
    "--runInBand",
    "--no-cache"
   ],
   "cwd": "${workspaceRoot}",
   "protocol": "inspector",
   "console": "integratedTerminal",
   "internalConsoleOptions": "neverOpen"
  }
 ]
}

Friday 28 December 2018

Debugging http Node.js server requests and responses from shell

Here is a cool tip to inspect Node.js running an http process / server. First off, we start a simple http server in Node.js like this:

const http = require('http');

const server = http.createServer((req, res) => {
    res.end("

Why hello world!

\n"); }); server.listen(4242, () => { console.log("Server is running..."); });
This sets up a http server in Node.js. The http module is built-in in Node.js, we only have to require it (import in ES6). Now, just enter the following (I use Windows Subsystem for Linux - WSL in this case) in your shell to export NODE_DEBUG environment variable: export NODE_DEBUG=http We can now see requests and responses in our Node.js server! (Image below uses Wsl-Terminal as our terminal against WSL).

Sunday 23 December 2018

Canceling Promise in Javascript

Canceling Promise in Js is a often sough after functionality, that can be provided by wrapping the Promise async function and provide canceling abilities. This will in functionality be similar to what we can do in C# with a CancellationTokenSource using in System.Threading.Task objects. We can invoke asynchronous function in Js with Promise, but if the user navigates away from a View or Page in for example React Native component, clicking a button to go to another Component, we must tidy up already started Promise operations such as fetch and here is the code to achieve that. First off, we define and export a makeCancelable method to be able to cancel a Promise.

/**
 * Wraps a promise into a cancelable promise, allowing it to be canceled. Useful in scenarios such as navigating away from a view or page and a fetch is already started.
 * @param {Promise}   promise           Promise object to cancel.
 * @return {Object with wrapped promise and a cancel function}
 */
export const makeCancelable = (promise) => {
    let hasCanceled = false;

    const wrappedPromise = new Promise((resolve, reject) => {
        promise.then(value => hasCanceled ? reject({ isCanceled: true }) : resolve(value),
            error => hasCanceled ? reject({ isCanceled: true }) : reject(error)
        );
    });

    return {
        promise: wrappedPromise,
        cancel() {
            hasCanceled: true;
        }
    };

};

The promise is wrapped with additional logic to check a boolean flag in a variable hasCanceled that either rejects the Promise if it is canceled or resolves the Promise (fullfils the async operation). Returned is an object in Js with the Promise itself in a primise attribute and the function cancel() which sets the boolean flag hasCanceled to true, effectively rejecting the Promise and rejecting it. Example usage below:

'use strict';

import React, { Component } from 'react';
import { TextInput, Text, View, StyleSheet, Image, TouchableHighlight, ActivityIndicator, FlatList, AsyncStorage } from 'react-native';
import AuthService from './AuthService';
import { makeCancelable } from './Util';

const styles = StyleSheet.create({
    container: {
        backgroundColor: '#F5FCFF',
        flex: 1,
        paddingTop: 40,
        alignItems: 'center'
    },
    heading: {
        fontSize: 30,
        fontWeight: '100',
        marginTop: 20
    },
    input: {
        height: 50,
        marginTop: 10,
        padding: 4,
        margin: 2,
        alignSelf: 'stretch',
        fontSize: 18,
        borderWidth: 1,
        borderColor: '#48bbec'
    },
    button: {
        height: 50,
        backgroundColor: '#48bbec',
        alignSelf: 'stretch',
        marginTop: 10,
        justifyContent: 'center'
    },
    buttonText: {
        fontSize: 22,
        color: '#FFF',
        alignSelf: 'center'
    },
    error: {
        fontWeight: '300',
        fontSize: 20,
        color: 'red',
        paddingTop: 10
    }
});

const cancelableSearchRepositoriesPromiseFetch = makeCancelable(fetch('https://api.github.com/search/repositories?q=react'));

class LoginForm extends Component {

    constructor(props) {
        super(props);

        this.state = {
            showProgress: false,
            username: '',
            password: '',
            repos: [],
            badCredentials: false,
            unknownError: false,
        };


    }

    onLoginPressed() {
        this.setState({ showProgress: true });

        var reposFound = [];

        var authService = new AuthService();

        authService.login({
            username: this.state.username, password: this.state.password
        }, (results) => {
            this.setState(Object.assign({ showProgress: false }, results));

            if (this.state.success && this.props.onLogin) {
                this.props.onLogin();
            }

        });




        cancelableSearchRepositoriesPromiseFetch.promise.then((response) => { return response.json(); })
            .then((results) => {
                results.items.forEach(item => {
                    reposFound.push(item);
                });
                this.setState({ repos: reposFound, showProgress: false });
            });
    }

    componentWillMount() {
        this._retrieveLastCredentials();
        cancelableSearchRepositoriesPromiseFetch.cancel();
    }

    _retrieveLastCredentials = async () => {

        var lastusername = await AsyncStorage.getItem("GithubDemo:LastUsername");
        var lastpassword = await AsyncStorage.getItem("GithubDemo:LastPassword");
        this.setState({ username: lastusername, password: lastpassword });
    }

    _saveLastUsername = async (username) => {
        if (username != null) {
            await AsyncStorage.setItem("GithubDemo:LastUsername", username);
        }
    }

    _savePassword = async (password) => {
        if (password != null) {
            await AsyncStorage.setItem("GithubDemo:LastPassword", password);
        }
    }

    componentWillUnmount() {

    }

    render() {


        var errorCtrl = <View />;

        if (!this.state.success && this.state.badCredentials) {
            errorCtrl = <Text color='#FF0000' style={styles.error}>That username and password combination did not work</Text>
        }

        if (!this.state.success && this.state.unknownError) {
            errorCtrl = <Text color='#FF0000' style={styles.error}>Unexpected error while logging in. Try again later</Text>
        }

        return (
            <View style={styles.container}>
                <Image style={{ width: 66, height: 55 }} source={require('./assets/Octocat.png')} />
                <Text style={styles.heading}>Github browser</Text>
                <TextInput value={this.state.username} onChangeText={(text) => { this._saveLastUsername(text); this.setState({ username: text }); }} style={styles.input} placeholder='Github username' />
                <TextInput value={this.state.password} textContentType={'password'} multiline={false} secureTextEntry={true} onChangeText={(text) => { this._savePassword(text); this.setState({ password: text }); }} style={styles.input} placeholder='Github password' />
                <TouchableHighlight style={styles.button} onPress={this.onLoginPressed.bind(this)}>
                    <Text style={styles.buttonText}>Log in</Text>
                </TouchableHighlight>
                {errorCtrl}
                <ActivityIndicator animating={this.state.showProgress} size={"large"} />
                <FlatList keyExtractor={item => item.id} data={this.state.repos} renderItem={({ item }) => <Text>{item.full_name}</Text>} />

            </View>
        );

    }


}

export default LoginForm; 


Sunday 5 August 2018

Consuming video from WCF as a Base64 encoded strings

This article follows up our quest of loading up video byte arrays from WCF efficiently, but now the byte array is converted into a Base64 encoded string. You can start by cloning the repository I have prepared from here:
git clone https://toreaurstad@bitbucket.org/toreaurstad/wcfaudiostreamdemo.git git fetch && git checkout VideBase64String_05082018
Base64 encoded strings take six bits from the byte array and designates is as a char where the char can be one of 64 characters, in MIME implementation it is [A-z][0-9] and + and /, which is 64 different characters that the six bytes are encoded into. This means that 24 bits can be represented as three Base64 encodeded characters, that is 3 bytes in our byte array can be represented as FOUR base64 encoded chars. This 3:4 ratio is the rationale behind the MTOM optimization in WCF (to be discussed later). However, recap from the previous article where we downloaded a video sizing 4 MB on disk and Fiddler reporting it to be about 3 times larger. This is bloated data that I want to explore if we can fix up a bit. The bloated data is because I have managed not to truly enforce the XMLHttpRequest to send the data through the WCF REST binding (webHttpBinding) as true binary data - binary data is still sent as a string object to Javascript. This is sad, and I want to try to speed this up and avoid bloated data. To get this size down, we can start by using a Base64 encoded string instead in our XmlHttpRequest. First off, we define a service contract operation to return our string with Base64 data:
        [OperationContract]
        [WebGet(UriTemplate = "mediabytes/{videofile}")]
        string GetVideoAsBase64(string videofile);
Now our Service Implementation looks like this:

using System;
using System.IO;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Web;
using WcfStreamAudioDemo.Common;

namespace WcfStreamAudioDemo.Host
{

    [AspNetCompatibilityRequirements (RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class VideoService : IVideoServiceContract
    {

        public Stream GetVideo(string videofile)
        {
            return GetVideoStream(videofile, ".mp4");
        }

        public string GetVideoAsBase64(string videofile)
        {
            Stream videoStream = GetVideoStream(videofile, ".mp4");

            byte[] buffer = new byte[32*1024];
            int count;
            var memoryStream = new MemoryStream();

            while ((count = videoStream.Read(buffer, 0, buffer.Length)) > 0)
            {
                memoryStream.Write(buffer, 0, count);
            }

            return Convert.ToBase64String(memoryStream.ToArray()); 
        }

        private Stream GetVideoStream(string videofile, string extension)
        {
            string mediaFolder = HttpContext.Current.Server.MapPath("~/media");
            string videoFileFullPath = Path.Combine(mediaFolder, videofile) + extension;
            return File.OpenRead(videoFileFullPath);
        }

    }
}


The client side code will now retrieve a Base64Encoded string. The following client side scripts loads up our video:

 <script type="text/javascript">

        window.onload = init;
      
        var source; //Video buffer 

        function init() {
            loadByteArray('http://myserver/WcfStreamAudioDemo.Host/VideoService.svc/mediabytes/sintel_trailer-480p');
        }

        function base64ToArrayBuffer(base64) {
            var binaryString =  window.atob(base64);
            var len = binaryString.length;
            var bytes = new Uint8Array( len );
            for (var i = 0; i < len; i++)        {
                bytes[i] = binaryString.charCodeAt(i);
            }
            return bytes.buffer;
        }

        function loadByteArray(url) {
            
            var request = new XMLHttpRequest();

            //request.overrideMimeType('text\/plain; charset=x-user-defined');
            //request.responseType = 'blob';

            request.open('GET', url, true);
            //request.setRequestHeader("Content-Type", "video/mp4");


            request.onload = function() {
                //console.log(request.response);
                debugger;

                var responseData = request.response;
                if (responseData === undefined || responseData === null)
                    return;

                responseData = responseData.slice(0, -9);
                responseData = responseData.substring(68);

                var videoByteArrayView = base64ToArrayBuffer(responseData);
            
                var blob = new Blob([videoByteArrayView], { type: "text/plain;charset=utf-8" });

                var blobUrl = URL.createObjectURL(blob);

                var videoCtrlFedByByteArray = document.getElementById("videoCtrlFedByByteArray");

                videoCtrlFedByByteArray.setAttribute("src", blobUrl);


            } //request.onload 

            request.send();

        }

    </script>

There is still a lot of manual Js scripting here to prepare the video here, I will try to explain. We have now managed to drastically reduce the bloated data of our original 4.4 MB video down from 15+ MB to just 5.8 MB using Base64 encoded string. The retrieved Base64 encoded string is first decoded with the Js atob() function ("ASCII to BINARY"). A Uint8Array is initialized with the length of this decoded binary string and the binary string is iterated by using the charCodeAt method, returning the ArrayBuffer inside the constructed Uint8Array. Note that I got the Base64 encoded string as a XML first, which is why I do some chopping off the string using slice and substring. Anyways, after getting an ArrayBuffer from the Uint8Array's buffer property, we can construct a Blob object (Binary large object) and create an object url and then set the "src" attribute of our HTML5 Video control to this blobUrl. This is still not very elegant, I have seen examples using "arraybuffer" as responseType of the XmlHttpRequest object in Js to retrieve binary data, but I got garbled data back trying to use it with WCF REST. So in this article you have seen a way to send binary data from a WCF method by using a Base64 encoded string as an intermediary between the Server and the client. We got the file size of the request according to Fiddler inspection down to much less than the bloated binary array transfer that actually sent the byte array as a text string back to the client.

Wcf byte array + HTML 5 Video + Custombinding

Again, this article looks at different ways to load byte array from WCF representing video data to web clients, this approach will return a byte array and use a CustomBinding in WCF. You can start by cloning the repository I have prepared from here:
git clone https://toreaurstad@bitbucket.org/toreaurstad/wcfaudiostreamdemo.git git fetch && git checkout VideBase64String_05082018
The following method is added to our WCF service contract, note that now we leave the webHttpBinding and use a CustomBinding.

     [OperationContract]
     byte[] GetVideoBytes(string videofile);

There is no [WebGet] attribute this time, as noted we will not use WCF REST but SOAP instead. The web.config of the host website for the WCF services exposes this CustomBinding:

<system.serviceModel>

    <services>
      
      <service name="WcfStreamAudioDemo.Host.AudioService">
        <endpoint behaviorConfiguration="RestBehaviorConfig" binding="webHttpBinding" bindingConfiguration="HttpStreaming" contract="WcfStreamAudioDemo.Common.IAudioServiceContract" />
      </service>
      
      <service name="WcfStreamAudioDemo.Host.VideoService">
        <endpoint behaviorConfiguration="RestBehaviorConfig" binding="webHttpBinding" bindingConfiguration="HttpStreaming" contract="WcfStreamAudioDemo.Common.IVideoServiceContract" />
        <endpoint address="custom" binding="customBinding" bindingConfiguration="CustomBinding" contract="WcfStreamAudioDemo.Common.IVideoServiceContract" />
      </service> 

    </services>
    

    <bindings>
      
     <customBinding>
        <binding name="CustomBinding">
          <binaryMessageEncoding>
            <readerQuotas maxArrayLength="100000000" maxStringContentLength="100000000"/>
          </binaryMessageEncoding>
          <httpTransport />
        </binding>
      </customBinding>

      <webHttpBinding>
        <binding name="HttpStreaming" transferMode="Streamed" maxReceivedMessageSize="1000000000" maxBufferPoolSize="100000000">
          <readerQuotas maxArrayLength="100000000" maxStringContentLength="100000000"/>
        </binding>
      </webHttpBinding>
    </bindings>
    
    <behaviors>

      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
     
      <endpointBehaviors>
        <behavior name="RestBehaviorConfig">
          <webHttp />
        </behavior>
      </endpointBehaviors>

    </behaviors>
    <protocolMapping>
        <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>    
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>

Note that the custom binding allows us to set up a binaryMessageEncoding. The next step is to go to the client project and add a service reference to the host project containing the WCF service. The app.config file is then updated, relevant parts shown here:
 <system.webServer>
    <directoryBrowse enabled="true" />
    <security>
      <requestFiltering>
        <requestLimits maxAllowedContentLength="1073741824" />
      </requestFiltering>
    </security>
        <staticContent>
            <mimeMap fileExtension=".mp4" mimeType="video/mp4" />
        </staticContent>
  </system.webServer>


  <system.serviceModel>
    <bindings>
      
      <customBinding>
        <binding name="CustomBinding">
          <binaryMessageEncoding>
            <readerQuotas maxArrayLength="100000000" maxStringContentLength="100000000" />
          </binaryMessageEncoding>
          <httpTransport maxReceivedMessageSize="100000000" />
        </binding>
      </customBinding>

    </bindings>
    <client>
      <endpoint address="http://he139920.helsemn.no/WcfStreamAudioDemo.Host/VideoService.svc/custom" contract="VideoService.IVideoServiceContract" binding="customBinding" bindingConfiguration="CustomBinding" />
    </client>
    
  </system.serviceModel>
Note the changes of setting up a MIME map for .mp4 files and setting up requestlimits. I will explain this soon. The client script now actually consists both of a server side script run in ASP.NET that sets up the video control in two ways. One way is to construct a HTML5 Data Url. This clearly was demanding for the browser to cope with and is not recommended. The video is actually embed on the page as a base64 encoded string! For a small video such as our video, it is actually possible still. It is of course fascinating that we can embed an entire video on our ASPX web page just as a Base64 encoded string, like it or not. The way to do this anyways is like this:

<script runat="server">

    private void btnInvokeWcfService_OnClick(object sender, EventArgs e)
    {

        using (var proxy = new VideoServiceContractClient())
        {
            byte[] payload = proxy.GetVideoBytes("sintel_trailer-480p");

            SetVideoSourceToHtmlDataUri(payload);

        }
    }


    private void SetVideoSourceToHtmlDataUri(byte[] payload)
    {
        //Set a base64 Html data uri

        videoCtrlFedByByteArrayThroughProxy.Attributes["type"] = "video/mp4";

        string base64String = Convert.ToBase64String(payload);

        videoCtrlFedByByteArrayThroughProxy.Attributes["src"] = "data:video/mp4;base64," + base64String;
    }

</script>

The following image shows how this actually works! Of course, this gives rendering and performance issues, as the web page now got very large content - the entire video is embedded into the page! Another way is to write the byte array to a temporary file on the server, and set the src attribute to this temporary file.

<script runat="server">

    private void btnInvokeWcfService_OnClick(object sender, EventArgs e)
    {

        using (var proxy = new VideoServiceContractClient())
        {
            byte[] payload = proxy.GetVideoBytes("sintel_trailer-480p");

            SetVideoSourceToTempFile(payload);

        }
    }

    private void SetVideoSourceToTempFile(byte[] payload)
    {
        //write to a temp file 
        string tempfile = Path.GetRandomFileName() + ".mp4";
        string tempfilePathForWebServer = HttpContext.Current.Server.MapPath("media/") + tempfile;
        File.WriteAllBytes(tempfilePathForWebServer, payload);

        videoCtrlFedByByteArrayThroughProxy.Attributes["src"] = "media/" + tempfile;
    }

</script>

Note that in these two samples, I have adjusted the HTML5 video control to be accessible to ASP.NET like this:

   <asp:Button runat="server" ID="btnInvokeWcfService" Text="Load Video" OnClick="btnInvokeWcfService_OnClick"/>
        
        <video id="videoCtrlFedByByteArrayThroughProxy" type="video/mp4"  runat="server"  controls width="320" height="240">         
            <p>Your browser doesn't support HTML5 video. Here is a <a href="http://wcfaudiodemohost.azurewebsites.net/VideoService.svc/mediabytes/sintel_trailer-480p">link to the video</a> instead.</p> 
        </video>


This method of retrieving video from WCF is the quickest way, it only fetches the original byte array data and it loads quickly. An adjusted version would not write to a file directly accessible on the web server, but use for example IsolatedStorage instead. I will look into that in the future. Hope you found this article interesting. CustomBindings in WCF gives you a lot of flexibility!