Monday, September 23, 2013

Knockout ES5 track nested objects - (JavaScript version)


See updated version on github: https://github.com/CarlosOnline/knockout.es5.mapping

/* Knockout ES5 track nested objects: JavaScript version - compiled via TypeScript.  Original: http://carlosonlineprogramming.blogspot.com/2013/09/knockout-es5-track-nested-objects.html

Solves the problem of ko.track not traversing into nested objects.
  • Calls ko.es5.track on nested objects: allowing tracking of strings, numbers, & arrays.
  • Use ko.es5.computed(function() ...) to mark computed functions.  ko.es5.track will call ko.defineProperty on these marked functions, after calling ko.track on the primitive members.
  • Excludes nested functions/objects with named constructors.  Idea is that TypeScript classes with constructors can call ko.es5.track themselves.  This exclusion can be removed.
  • Does not traverse into nested classes/objects with named constructors will not be traversed.  These constructors should call ko.es5.track themselves.
*/

var koES5;
(function (koES5) {
    function getType(x) {
        if ((x) && (typeof (x) === "object")) {
            if (x.constructor === Date)
                return "date";
            if (x.constructor === Array)
                return "array";
        }
        return typeof x;
    }
 
    var Track = (function () {
        function Track(rootObject) {
            this.rootObject = rootObject;
            this.mapped = [];
            this.track(rootObject);
            this.clearAllMapped();
        }
        Track.prototype.track = function (source, name) {
            if (typeof name === "undefined") { name = null; }
            var _this = this;
            if (source == null || this.isMapped(source))
                return;
            if (name == null)
                name = this.name(source);
 
            var keys = [];
            var computed = [];
            this.setMapped(source);
 
            for (var key in source) {
                var value = source[key];
                var type = getType(value);
 
                switch (type) {
                    case "array":
                    case "string":
                    case "number":
                        //console.log(name + "." + key, type);
                        keys.push(key);
                        break;
 
                    case "function":
                        if (this.isComputed(value)) {
                            //console.log("f> " + name + "." + key, type);
                            computed.push({
                                name: key,
                                fn: value
                            });
                        }
                        break;
 
                    case "object":
                        if (value == null || this.isMapped(value) || !this.isTrackable(value) || !this.isTrackableField(key))
                            continue;
 
                        //console.log("o> " + name + "." + key, type);
                        this.track(value, key);
                        break;
                }
            }
 
            if (keys.length > 0) {
                ko.track(source, keys);
            }
 
            if (computed.length > 0) {
                computed.forEach(function (item) {
                    _this.makeComputed(source, item.name, item.fn);
                });
            }
        };
 
        Track.prototype.name = function (value) {
            var xtor = value.__proto__.constructor;
            return xtor !== undefined && xtor.name != undefined ? xtor.name : "";
        };
 
        Track.prototype.isTrackable = function (value) {
            var xtor = value.__proto__.constructor;
            return xtor.name === "Object";
        };
 
        Track.prototype.isTrackableField = function (key) {
            return key != "__ko_mapping__";
        };
 
        Track.prototype.isMapped = function (value) {
            return (value.__tracked__ === true);
        };
 
        Track.prototype.setMapped = function (value) {
            if (this.isMapped(value))
                return;
            value.__tracked__ = true;
            this.mapped.push(value);
        };
 
        Track.prototype.clearAllMapped = function () {
            this.mapped.forEach(function (value) {
                delete value["__tracked__"];
            });
            this.mapped.unshift();
        };
 
        Track.prototype.isComputed = function (fn) {
            return (fn["__ko_es5_computed__"] === true);
        };
 
        Track.prototype.makeComputed = function (container, name, fn) {
            var nameOverride = fn["__ko_es5_computed_name__"];
            if (nameOverride !== undefined && nameOverride !== "") {
                name = nameOverride;
                delete fn["__ko_es5_computed_name__"];
            }
 
            if (name === undefined || name == "") {
                console.log("Error. Function missing name", fn);
                return;
            }
            ko.defineProperty(container, name, fn);
            delete fn["__ko_es5_computed__"];
        };
        return Track;
    })();
    koES5.Track = Track;
 
    function track(root) {
        new Track(root);
    }
    koES5.track = track;
 
    function computed(fn, name) {
        if (typeof name === "undefined") { name = null; }
        fn["__ko_es5_computed__"] = true;
        if (name || false) {
            fn["__ko_es5_computed_name__"] = true;
        }
        return fn;
    }
    koES5.computed = computed;
 
    ko.es5 = {
        computed: koES5.computed,
        track: koES5.track
    };
})(koES5 || (koES5 = {}));

Knockout ES5 track nested objects

See updated version on github:
https://github.com/CarlosOnline/knockout.es5.mapping

/* Knockout ES5 track nested objects: TypeScript version.
Solves the problem of ko.track not traversing into nested objects.
  • Calls ko.es5.track on nested objects: allowing tracking of strings, numbers, & arrays.
  • Use ko.es5.computed(function() ...) to mark computed functions.  ko.es5.track will call ko.defineProperty on these marked functions, after calling ko.track on the primitive members.
  • Excludes nested functions/objects with named constructors.  Idea is that TypeScript classes with constructors can call ko.es5.track themselves.  This exclusion can be removed.
  • Does not traverse into nested classes/objects with named constructors will not be traversed.  These constructors should call ko.es5.track themselves.
  • Does not handle null valued variables.  Cannot deduce the type from these variables.
*/

interface KnockoutStatic {
    es5: {
        computed: Function;
        track: Function;
    };
}
 
module koES5 {
    function getType(x) {
        if ((x) && (typeof (x) === "object")) {
            if (x.constructor === Date) return "date";
            if (x.constructor === Array) return "array";
        }
        return typeof x;
    }
 
    export class Track {
        mapped = [];
 
        constructor(private rootObject: any) {
            this.track(rootObject);
            this.clearAllMapped();
        }
 
        track(source, name: string = null) {
            if (source == null || this.isMapped(source))
                return;
            if (name == null)
                name = this.name(source);
 
            var keys = [];
            var computed = [];
            this.setMapped(source);
 
            for (var key in source) {
                var value = source[key];
                var type = getType(value);
 
                switch (type) {
                    case "array":
                    case "string":
                    case "number":
                        //console.log(name + "." + key, type);
                        keys.push(key);
                        break;
 
                    case "function":
                        if (this.isComputed(value)) {
                            //console.log("f> " + name + "." + key, type);
                            computed.push({
                                name: key,
                                fn: value
                            });
                        }
                        break;
 
                    case "object":
                        if (value == null || this.isMapped(value) || !this.isTrackable(value) || !this.isTrackableField(key))
                            continue;
 
                        //console.log("o> " + name + "." + key, type);
                        this.track(value, key);
                        break;
                }
            }
 
            if (keys.length > 0) {
                ko.track(source, keys);
            }
 
            if (computed.length > 0) {
                computed.forEach((item) => {
                    this.makeComputed(source, item.name, item.fn);
                });
            }
        }
 
        private name(value) {
            var xtor = value.__proto__.constructor;
            return xtor !== undefined && xtor.name != undefined ? xtor.name : "";
        }
 
        private isTrackable(value) {
            var xtor = value.__proto__.constructor;
            return xtor.name === "Object";
        }
 
        private isTrackableField(key: string) {
            return key != "__ko_mapping__";
        }
 
        private isMapped(value: any) {
            return (value.__tracked__ === true);
        }
 
        private setMapped(value: any) {
            if (this.isMapped(value))
                return;
            value.__tracked__ = true;
            this.mapped.push(value);
        }
 
        private clearAllMapped() {
            this.mapped.forEach((value) => {
                delete value["__tracked__"];
            });
            this.mapped.unshift();
        }
 
        private isComputed(fn: Function) {
            return (fn["__ko_es5_computed__"] === true);
        }
 
        private makeComputed(container: any, name: string, fn: Function) {
            var nameOverride = fn["__ko_es5_computed_name__"];
            if (nameOverride !== undefined && nameOverride !== "") {
                name = nameOverride;
                delete fn["__ko_es5_computed_name__"];
            }
 
            if (name === undefined || name == "") {
                console.log("Error. Function missing name", fn);
                return;
            }
            ko.defineProperty(container, name, fn);
            delete fn["__ko_es5_computed__"];
        }
    }
 
    export function track(root: any) {
        new Track(root);
    }
 
    export function computed(fn: Function, name: string = null) {
        fn["__ko_es5_computed__"] = true;
        if (name || false) {
            fn["__ko_es5_computed_name__"] = true;
        }
        return fn;
    }
 
    ko.es5 = {
        computed: koES5.computed,
        track: koES5.track,
    };
}

Monday, January 14, 2013

SetColor.cmd


@if "%_echo%"=="" echo off
setlocal EnableDelayedExpansion
set _BatDir=%~dp0
set _BatchFile=%0
set _StartTime=%TIME%
:: echo %0 %*
cls

REM Color Names Array
set ColorNames[0]=Black
set ColorNames[1]=Blue
set ColorNames[2]=Green
set ColorNames[3]=Aqua
set ColorNames[4]=Red
set ColorNames[5]=Purple
set ColorNames[6]=Yellow
set ColorNames[7]=White
set ColorNames[8]=Gray
set ColorNames[9]=Light Blue
set ColorNames[A]=Light Green
set ColorNames[B]=Light Aqua
set ColorNames[C]=Light Red
set ColorNames[D]=Light Purple
set ColorNames[E]=Light Yellow
set ColorNames[F]=Bright White

REM Color Values Array
set ColorValues[Black]=0
set ColorValues[Blue]=1
set ColorValues[Green]=2
set ColorValues[Aqua]=3
set ColorValues[Red]=4
set ColorValues[Purple]=5
set ColorValues[Yellow]=6
set ColorValues[White]=7
set ColorValues[Gray]=8
set ColorValues[Light Blue]=9
set ColorValues[Light Green]=A
set ColorValues[Light Aqua]=B
set ColorValues[Light Red]=C
set ColorValues[Light Purple]=D
set ColorValues[Light Yellow]=E
set ColorValues[Bright White]=F

if "%BackColor%" EQU "" set BackColor=1
if "%ForeColor%" EQU "" set ForeColor=F

call :array_getitem BackColor ColorValues %1
call :array_getitem ForeColor ColorValues %2
call :array_getitem BackName ColorNames %1
call :array_getitem ForeName ColorNames %2
if "%3" NEQ "" echo color %BackColor%%ForeColor% %BackName% %ForeName%

call color %BackColor%%ForeColor%

goto :EOF

:array_setitem <Array> <Index> <Value>
    set Index=%2
    call :LowerCase Index
    set %1[%2]=%3
goto :EOF

:array_getitem <Result> <Array> <Index>
    set outputvar=%1
    set array.name=%2
    set array.index=%3
    call :LowerCase array.index
    call :DeQuote array.index %array.index%
    echo searching %array.name% for '%array.index%'
    for /f "delims=[=] tokens=1,2,3" %%a in ('set %array.name%[') do (
        rem echo getitem %%a '%array.index%' == '%%b' %%c
        if /I "%%b"=="%array.index%" set %outputvar%=%%c
        if /I "%%b"=="%array.index%" echo #### found %%a %3 == %%b %%c
    )
goto :eof

:LowerCase
    :: Subroutine to convert a variable VALUE to all lower case.
    :: The argument for this subroutine is the variable NAME.
    FOR %%i IN ("A=a" "B=b" "C=c" "D=d" "E=e" "F=f" "G=g" "H=h" "I=i" "J=j" "K=k" "L=l" "M=m" "N=n" "O=o" "P=p" "Q=q" "R=r" "S=s" "T=t" "U=u" "V=v" "W=w" "X=x" "Y=y" "Z=z") DO CALL SET "%1=%%%1:%%~i%%"
goto :eof

:UpperCase
    :: Subroutine to convert a variable VALUE to all UPPER CASE.
    :: The argument for this subroutine is the variable NAME.
    FOR %%i IN ("a=A" "b=B" "c=C" "d=D" "e=E" "f=F" "g=G" "h=H" "i=I" "j=J" "k=K" "l=L" "m=M" "n=N" "o=O" "p=P" "q=Q" "r=R" "s=S" "t=T" "u=U" "v=V" "w=W" "x=X" "y=Y" "z=Z") DO CALL SET "%1=%%%1:%%~i%%"
goto :eof

:TitleCase
    :: Subroutine to convert a variable VALUE to Title Case.
    :: The argument for this subroutine is the variable NAME.
    FOR %%i IN (" a= A" " b= B" " c= C" " d= D" " e= E" " f= F" " g= G" " h= H" " i= I" " j= J" " k= K" " l= L" " m= M" " n= N" " o= O" " p= P" " q= Q" " r= R" " s= S" " t= T" " u= U" " v= V" " w= W" " x= X" " y= Y" " z= Z") DO CALL SET "%1=%%%1:%%~i%%"
goto :eof

:DeQuote <Result> <String>
    for /f "useback tokens=*" %%a in ('%2') do set %1=%%~a
goto :EOF

Useful Batch functions


@if "%_echo%"=="" echo off
setlocal EnableDelayedExpansion
set _BatDir=%~dp0
set _BatchFile=%0
set _StartTime=%TIME%
echo %0 %*

goto :EOF

:array_setitem <Array> <Index> <Value>
    set Index=%2
    call :LowerCase Index
    set %1[%2]=%3
goto :EOF

:array_getitem <Result> <Array> <Index>
    set outputvar=%1
    set array.name=%2
    set array.index=%3
    call :LowerCase array.index
    call :DeQuote array.index %array.index%
    REM echo searching %array.name% for '%array.index%'
    for /f "delims=[=] tokens=1,2,3" %%a in ('set %array.name%[') do (
        REM echo getitem %%a '%array.index%' == '%%b' %%c
        if /I "%%b"=="%array.index%" set %outputvar%=%%c
        REM if /I "%%b"=="%array.index%" echo #### found %%a %3 == %%b %%c
    )
goto :eof

:LowerCase <String>
    FOR %%i IN ("A=a" "B=b" "C=c" "D=d" "E=e" "F=f" "G=g" "H=h" "I=i" "J=j" "K=k" "L=l" "M=m" "N=n" "O=o" "P=p" "Q=q" "R=r" "S=s" "T=t" "U=u" "V=v" "W=w" "X=x" "Y=y" "Z=z") DO CALL SET "%1=%%%1:%%~i%%"
goto :eof

:UpperCase <String>
    FOR %%i IN ("a=A" "b=B" "c=C" "d=D" "e=E" "f=F" "g=G" "h=H" "i=I" "j=J" "k=K" "l=L" "m=M" "n=N" "o=O" "p=P" "q=Q" "r=R" "s=S" "t=T" "u=U" "v=V" "w=W" "x=X" "y=Y" "z=Z") DO CALL SET "%1=%%%1:%%~i%%"
goto :eof

:TitleCase <String>
    FOR %%i IN (" a= A" " b= B" " c= C" " d= D" " e= E" " f= F" " g= G" " h= H" " i= I" " j= J" " k= K" " l= L" " m= M" " n= N" " o= O" " p= P" " q= Q" " r= R" " s= S" " t= T" " u= U" " v= V" " w= W" " x= X" " y= Y" " z= Z") DO CALL SET "%1=%%%1:%%~i%%"
goto :eof

:DeQuote <String>
    for /f "useback tokens=*" %%a in ('%2') do set %1=%%~a
goto :EOF

:FillColorArrays
    :: Color Names Array
    set ColorNames[0]=Black
    set ColorNames[1]=Blue
    set ColorNames[2]=Green
    set ColorNames[3]=Aqua
    set ColorNames[4]=Red
    set ColorNames[5]=Purple
    set ColorNames[6]=Yellow
    set ColorNames[7]=White
    set ColorNames[8]=Gray
    set ColorNames[9]=Light Blue
    set ColorNames[A]=Light Green
    set ColorNames[B]=Light Aqua
    set ColorNames[C]=Light Red
    set ColorNames[D]=Light Purple
    set ColorNames[E]=Light Yellow
    set ColorNames[F]=Bright White

    ::  Color Values Array
    set ColorValues[Black]=0
    set ColorValues[Blue]=1
    set ColorValues[Green]=2
    set ColorValues[Aqua]=3
    set ColorValues[Red]=4
    set ColorValues[Purple]=5
    set ColorValues[Yellow]=6
    set ColorValues[White]=7
    set ColorValues[Gray]=8
    set ColorValues[Light Blue]=9
    set ColorValues[Light Green]=A
    set ColorValues[Light Aqua]=B
    set ColorValues[Light Red]=C
    set ColorValues[Light Purple]=D
    set ColorValues[Light Yellow]=E
    set ColorValues[Bright White]=F
goto :EOF

Arrays in Batch scripts


@if "%_echo%"=="" echo off
setlocal EnableDelayedExpansion
set _BatDir=%~dp0
set _BatchFile=%0
set _StartTime=%TIME%
echo %0 %*

REM Populate Array
for %%G in (c:\temp\*) do (
    set /a count+=1
    call :array_setitem myArray !count! %%~nG
)


REM Read Array
for /L %%X in (1,1,%count%) do (
    call :array_getitem last myArray %%X
    echo myArray[%%X] == !last!
)

goto :EOF

:array_setitem <Array> <Index> <Value>
    echo Set %1[%2] = %3
    set %1[%2]=%3
goto :EOF

:array_getitem <Result> <Array> <Index>
    set array.name=%2
    set array.index=%3
    set outputvar=%1
    for /f "delims=[=] tokens=1,2,3" %%a in ('set %array.name%[') do (
        if %%b==%array.index% set %outputvar%=%%c
    )
goto :eof