<commit_before>( function ( ctx ) {
    "use strict";

    var params = {
        endWithComma: false
    };

    var router = ctx.router = {
        on404: function ( rt, e ) {
            throw new Error( [
                'Path' + router.resolvePath.apply( router, rt ) + ' not found.',
                'You can handle the error by overriding this function.'
            ].join( '\n' ) );
        },
        route: ctx.ko.observableArray(),
        root: Page( {
            title: ctx.document.title
        } ),
        resolvePath: function resolvePath() {
            var args = Array.from( arguments );
            var url = args.map( function ( r ) {
                if ( r.slice( -1 ) !== '/' ) r += '/';
                return r;
            } ).reduce( function ( url, path ) {
                return new ctx.URL( path, url.href );
            }, new ctx.URL( ctx.location.origin ) );
            return ( params.endWithComma ? url.pathname : url.pathname.slice( 0, -1 ) ) + url.search + url.hash;
        },
        navigate: function navigate( href ) {
            href = router.resolvePath( href );
            return guards( path( href ) ).then( function () {
                ctx.history.pushState( {}, '', href );
                router.route( path() );
            } );
        },
        start: function start( opts ) {
            params = Object.assign( params, opts );
            return ( ctx.onpopstate = handlePop )();
        },
        loader: {
            start: function () {},
            done: function () {}
        }
    };
    router.Page = Page;

    ctx.ko.bindingHandlers[ 'page' ] = {
        init: function ( element, valueAccessor, allBindingsAccessor, viewModel, bindingContext ) {
            var value = valueAccessor();
            if ( !value.route ) throw new Error( 'route required' );
            var parent = bindingContext._page || router.root;
            var page = parent.to[ value.route ] = parent.to[ value.route ] || Page( {
                route: value.route
            } );
            Object.assign( page, {
                element: element,
                src: value.src,
                title: value.title || parent.title,
                template: value.template,
                guard: value.guard,
                onclose: value.onclose,
                context: bindingContext
            } );
            page.close();
            return {
                controlsDescendantBindings: true
            };
        }
    };

    ctx.ko.bindingHandlers[ 'nav' ] = {
        update: function ( element, valueAccessor, allBindingsAccessor, viewModel, bindingContext ) {
            var value = valueAccessor();
            var page = bindingContext._page || {};
            var route = page.path ? page.path() : '/';
            if ( element.tagName === 'A' ) element.href = router.resolvePath( route, value );
            element.onclick = function ( ev ) {
                var href = this.href || router.resolvePath( route, value );
                if ( ev.button !== 0 || ( ev.ctrlKey && ev.button === 0 ) ) {
                    if ( this.tagName !== 'A' ) window.open( href, '_blank' ).focus();
                    return true;
                };
                router.navigate( href );
                return false;
            };
        }
    };

    function Page( data ) {
        if ( !( this instanceof Page ) ) return new Page( data );

        data = data || {};
        this.element = data.element;
        this.route = data.route;
        this.title = data.title;
        this.template = data.template;
        this.guard = data.guard;
        this.onclose = data.onclose;
        this.current = '';
        this.src = data.src;
        this.context = data.context;
        this.to = {};
    };
    Page.prototype.check = function check( route ) {
        return wrap( this.guard, this, [ route ] ).then( function () {
            return this;
        }.bind( this ) );
    };
    Page.prototype.ensureTemplate = function ensureTemplate() {
        var tmplname = this.template.name || this.template;
        var tmpl = ctx.document.querySelector( 'script#' + tmplname );
        if ( !tmpl && !this.src ) return Promise.reject( new Error( 'No template or source supplied' ) );
        if ( !tmpl && this.src ) return ctx.fetch( encodeURI( this.src ) ).then( function ( response ) {
            if ( response.status !== 200 ) {
                var e = new Error( response.statusText );
                e.response = response;
                throw e;
            };
            return response.text();
        } ).then( function ( text ) {
            var tmpl = document.createElement( 'div' );
            tmpl.innerHTML = text;
            tmpl = tmpl.firstChild;
            if ( !tmpl || tmpl.tagName !== 'SCRIPT' || tmpl.id !== tmplname ) throw new Error( 'Wrong source' );
            ctx.document.body.appendChild( tmpl );
        } );
    };
    Page.prototype.open = function open( current ) {
        this.current = current;
        if ( !this.template || !this.element ) return this;

        ctx.ko.applyBindingsToNode( this.element, {
            template: this.template
        }, this.context.extend( {
            _page: this
        } ) );
        this.element.style.display = '';
        return this;
    };
    Page.prototype.close = function close() {
        this.current = '';
        var children = Array.from( this.element.children );
        var ready = Promise.resolve();

        if ( children.length ) ready = wrap( this.onclose, this );

        return ready.then( function () {
            children.map( function ( c ) {
                c.remove();
            } );
            this.element.style.display = 'none';
            return this;
        }.bind( this ) );
    };
    Page.prototype.closeChildren = function closeChildren() {
        return Promise.all( Object.keys( this.to ).map( function ( t ) {
            return this.to[ t ].close();
        }.bind( this ) ) );
    };
    Page.prototype.closeSiblings = function closeSiblings() {
        var parent = this.parent();
        if ( !parent ) return;
        return Promise.all( Object.keys( parent.to ).map( function ( t ) {
            if ( parent.to[ t ] !== this ) return parent.to[ t ].close();
        }.bind( this ) ) );
    };
    Page.prototype.next = function next( route ) {
        if ( !route && this.to[ '/' ] ) return this.to[ '/' ];
        if ( route && this.to[ route ] ) return this.to[ route ];
        if ( route && this.to[ '*' ] ) return this.to[ '*' ];
    };
    Page.prototype.path = function path() {
        var path = [];
        var parent = this;
        do {
            path.unshift( parent.current );
            parent = parent.parent();
        } while ( parent )
        return path.join( '/' );
    };
    Page.prototype.parent = function parent() {
        if ( this === router.root ) return;
        return Object.path( this, 'context._page' ) || router.root;
    };

    function guards( rt ) {
        router.loader.start();
        console.log( 'GUARD', rt );
        return router.root.check( rt ).then( workGuard.bind( this, {
            rt: Array.from( rt ),
            page: router.root
        } ) ).then( function ( page ) {
            ctx.document.title = page.title || router.root.title;
            router.loader.done();
            return page;
        } ).catch( function ( e ) {
            if ( e instanceof Promise ) return e;
            if ( e instanceof Error && e.message === 'Page not found' )
                return router.on404( rt, e );
            throw e;
            router.loader.done();
        } );
    };

    function workGuard( cur ) {
        var r = cur.rt.shift();
        var next = cur.page.next( r );
        if ( !next ) return cur.page.closeChildren().then( function () {
            if ( r ) return Promise.reject( new Error( 'Page not found' ) );
            return Promise.resolve( cur.page );
        }.bind( this ) );
        return Promise.all( [ next.check( r ), next.ensureTemplate() ] ).then( function ( res ) {
            cur.page = res[ 0 ];
            if ( cur.page.current === r ) return;
            return cur.page.closeSiblings().then( function () {
                cur.page.open( r );
            } );
        } ).then( workGuard.bind( this, cur ) );
    };

    function handlePop() {
        return guards( path() ).then( function () {
            router.route( path() );
        } );
    };

    function path( pathname ) {
        return ( pathname || ctx.location.pathname ).slice( 1 ).split( '/' ).filter( function ( r ) {
            return !!r;
        } );
    };

    function wrap( target, reciever, args ) {
        if ( target instanceof Error ) return Promise.reject( target );
        if ( !( target instanceof Function ) ) return Promise.resolve( target );
        if ( !Array.isArray( args ) ) args = [ args ];
        try {
            var r = ( reciever || args ) ? target.apply( reciever, args ) : target.call();
            if ( !( r instanceof Promise ) ) return Promise.resolve( r );
            return r;
        } catch ( e ) {
            return Promise.reject( e );
        };
    };

} )( window );
<commit_msg>fix #4<commit_after>( function ( ctx ) {
    "use strict";

    var router = ctx.router = {
        on404: function ( rt, e ) {
            throw new Error( [
                'Path' + router.resolvePath.apply( router, rt ) + ' not found.',
                'You can handle the error by overriding this function.'
            ].join( '\n' ) );
        },
        route: ctx.ko.observableArray(),
        root: Page( {
            title: ctx.document.title
        } ),
        resolvePath: function resolvePath() {
            var args = Array.from( arguments );
            var url = args.map( function ( r, i, s ) {
                if ( r.slice( -1 ) !== '/' && i !== s.length - 1 ) r += '/';
                return r;
            } ).reduce( function ( url, path ) {
                return new ctx.URL( path, url.href );
            }, new ctx.URL( ctx.location.origin ) );
            return url.pathname + url.search + url.hash;
        },
        navigate: function navigate( href ) {
            href = router.resolvePath( href );
            return guards( path( href ) ).then( function () {
                ctx.history.pushState( {}, '', href );
                router.route( path() );
            } );
        },
        start: function start( opts ) {
            return ( ctx.onpopstate = handlePop )();
        },
        loader: {
            start: function () {},
            done: function () {}
        }
    };
    router.Page = Page;

    ctx.ko.bindingHandlers[ 'page' ] = {
        init: function ( element, valueAccessor, allBindingsAccessor, viewModel, bindingContext ) {
            var value = valueAccessor();
            if ( !value.route ) throw new Error( 'route required' );
            var parent = bindingContext._page || router.root;
            var page = parent.to[ value.route ] = parent.to[ value.route ] || Page( {
                route: value.route
            } );
            Object.assign( page, {
                element: element,
                src: value.src,
                title: value.title || parent.title,
                template: value.template,
                guard: value.guard,
                onclose: value.onclose,
                context: bindingContext
            } );
            page.close();
            return {
                controlsDescendantBindings: true
            };
        }
    };

    ctx.ko.bindingHandlers[ 'nav' ] = {
        update: function ( element, valueAccessor, allBindingsAccessor, viewModel, bindingContext ) {
            var value = valueAccessor();
            var page = bindingContext._page || {};
            var route = page.path ? page.path() : '/';
            if ( element.tagName === 'A' ) element.href = router.resolvePath( route, value );
            element.onclick = function ( ev ) {
                var href = this.href || router.resolvePath( route, value );
                if ( ev.button !== 0 || ( ev.ctrlKey && ev.button === 0 ) ) {
                    if ( this.tagName !== 'A' ) window.open( href, '_blank' ).focus();
                    return true;
                };
                router.navigate( href );
                return false;
            };
        }
    };

    function Page( data ) {
        if ( !( this instanceof Page ) ) return new Page( data );

        data = data || {};
        this.element = data.element;
        this.route = data.route;
        this.title = data.title;
        this.template = data.template;
        this.guard = data.guard;
        this.onclose = data.onclose;
        this.current = '';
        this.src = data.src;
        this.context = data.context;
        this.to = {};
    };
    Page.prototype.check = function check( route ) {
        return wrap( this.guard, this, [ route ] ).then( function () {
            return this;
        }.bind( this ) );
    };
    Page.prototype.ensureTemplate = function ensureTemplate() {
        var tmplname = this.template.name || this.template;
        var tmpl = ctx.document.querySelector( 'script#' + tmplname );
        if ( !tmpl && !this.src ) return Promise.reject( new Error( 'No template or source supplied' ) );
        if ( !tmpl && this.src ) return ctx.fetch( encodeURI( this.src ) ).then( function ( response ) {
            if ( response.status !== 200 ) {
                var e = new Error( response.statusText );
                e.response = response;
                throw e;
            };
            return response.text();
        } ).then( function ( text ) {
            var tmpl = document.createElement( 'div' );
            tmpl.innerHTML = text;
            tmpl = tmpl.firstChild;
            if ( !tmpl || tmpl.tagName !== 'SCRIPT' || tmpl.id !== tmplname ) throw new Error( 'Wrong source' );
            ctx.document.body.appendChild( tmpl );
        } );
    };
    Page.prototype.open = function open( current ) {
        this.current = current;
        if ( !this.template || !this.element ) return this;

        ctx.ko.applyBindingsToNode( this.element, {
            template: this.template
        }, this.context.extend( {
            _page: this
        } ) );
        this.element.style.display = '';
        return this;
    };
    Page.prototype.close = function close() {
        this.current = '';
        var children = Array.from( this.element.children );
        var ready = Promise.resolve();

        if ( children.length ) ready = wrap( this.onclose, this );

        return ready.then( function () {
            children.map( function ( c ) {
                c.remove();
            } );
            this.element.style.display = 'none';
            return this;
        }.bind( this ) );
    };
    Page.prototype.closeChildren = function closeChildren() {
        return Promise.all( Object.keys( this.to ).map( function ( t ) {
            return this.to[ t ].close();
        }.bind( this ) ) );
    };
    Page.prototype.closeSiblings = function closeSiblings() {
        var parent = this.parent();
        if ( !parent ) return;
        return Promise.all( Object.keys( parent.to ).map( function ( t ) {
            if ( parent.to[ t ] !== this ) return parent.to[ t ].close();
        }.bind( this ) ) );
    };
    Page.prototype.next = function next( route ) {
        if ( !route && this.to[ '/' ] ) return this.to[ '/' ];
        if ( route && this.to[ route ] ) return this.to[ route ];
        if ( route && this.to[ '*' ] ) return this.to[ '*' ];
    };
    Page.prototype.path = function path() {
        var path = [];
        var parent = this;
        do {
            path.unshift( parent.current );
            parent = parent.parent();
        } while ( parent )
        return path.join( '/' );
    };
    Page.prototype.parent = function parent() {
        if ( this === router.root ) return;
        return Object.path( this, 'context._page' ) || router.root;
    };

    function guards( rt ) {
        router.loader.start();
        console.log( 'GUARD', rt );
        return router.root.check( rt ).then( workGuard.bind( this, {
            rt: Array.from( rt ),
            page: router.root
        } ) ).then( function ( page ) {
            ctx.document.title = page.title || router.root.title;
            router.loader.done();
            return page;
        } ).catch( function ( e ) {
            if ( e instanceof Promise ) return e;
            if ( e instanceof Error && e.message === 'Page not found' )
                return router.on404( rt, e );
            throw e;
            router.loader.done();
        } );
    };

    function workGuard( cur ) {
        var r = cur.rt.shift();
        var next = cur.page.next( r );
        if ( !next ) return cur.page.closeChildren().then( function () {
            if ( r ) return Promise.reject( new Error( 'Page not found' ) );
            return Promise.resolve( cur.page );
        }.bind( this ) );
        return Promise.all( [ next.check( r ), next.ensureTemplate() ] ).then( function ( res ) {
            cur.page = res[ 0 ];
            if ( cur.page.current === r ) return;
            return cur.page.closeSiblings().then( function () {
                cur.page.open( r );
            } );
        } ).then( workGuard.bind( this, cur ) );
    };

    function handlePop() {
        return guards( path() ).then( function () {
            router.route( path() );
        } );
    };

    function path( pathname ) {
        return ( pathname || ctx.location.pathname ).slice( 1 ).split( '/' ).filter( function ( r ) {
            return !!r;
        } );
    };

    function wrap( target, reciever, args ) {
        if ( target instanceof Error ) return Promise.reject( target );
        if ( !( target instanceof Function ) ) return Promise.resolve( target );
        if ( !Array.isArray( args ) ) args = [ args ];
        try {
            var r = ( reciever || args ) ? target.apply( reciever, args ) : target.call();
            if ( !( r instanceof Promise ) ) return Promise.resolve( r );
            return r;
        } catch ( e ) {
            return Promise.reject( e );
        };
    };

} )( window );
<|endoftext|>


<commit_before>import {Component, Template, NgElement, Decorator} from 'angular2/angular2';
import {If} from 'angular2/angular2';
import {EditorIframe} from 'editorIframe';

@Component({
    selector: 'editor-app'  //TODO: default to camel-cased class name if not provided?
})
@Template({
    inline: `<ace-editor style="width:100%; height:100%; display:block;"></ace-editor>
            <button (click)="editor.setContent()"">foo</button>
            <editor-iframe #editor></editor-iframe>`,
    directives: [AceEditor, EditorIframe]
})
export class EditorApp {
}

@Component({
  selector: 'ace-editor'
})
@Template({
    inline: `<div class="ace-editor">Foo bar</div>`,
})
class AceEditor {
  constructor(el: NgElement) {
    this.editor = ace.edit(el.domElement);
  }

  getContent () {
      return this.editor.getValue();
  }
}
<commit_msg>feat: set content into iframe<commit_after>import {Component, Template, NgElement, Decorator} from 'angular2/angular2';
import {If} from 'angular2/angular2';
import {EditorIframe} from 'editorIframe';

@Component({
    selector: 'editor-app'  //TODO: default to camel-cased class name if not provided?
})
@Template({
    inline: `<ace-editor #ace style="width:100%; height:100%; display:block;"></ace-editor>
            <button (click)="editor.setContent(ace.getContent())"">foo</button>
            <editor-iframe #editor></editor-iframe>`,
    directives: [AceEditor, EditorIframe]
})
export class EditorApp {
}

@Component({
  selector: 'ace-editor'
})
@Template({
    inline: `<div class="ace-editor">Foo bar</div>`,
})
class AceEditor {
  constructor(el: NgElement) {
    this.editor = ace.edit(el.domElement);
  }

  getContent () {
      return this.editor.getValue();
  }
}
<|endoftext|>


<commit_before>var Window          = require('pex-sys/Window');
var PerspCamera     = require('pex-cam/PerspCamera');
var Arcball         = require('pex-cam/Arcball');
var createCube      = require('primitive-cube');
var glslify         = require('glslify-promise');
var isBrowser       = require('is-browser');
var random          = require('pex-random')
var fx              = require('pex-fx')
var SSAOv2          = require('./SSAO')
var Vec3            = require('pex-math/Vec3')
var MathUtils       = require('pex-math/Utils')
var R               = require('ramda')
var GUI             = require('pex-gui')
var fs              = require('fs')

var MAX_DEPTH = 20

function divide(parent, rects) {
    rects.push(parent);

    var depth = parent[4]

    var shouldDivide = random.chance(0.1 + 1/(depth*0.5+1));
    if (depth <= 1) {
        shouldDivide = true
    }

    if (depth >= MAX_DEPTH || !shouldDivide) {
        return rects
    }

    var numDivisions = random.int(2, 5)
    var horizontal = random.chance(0.5);
    if (depth == 0) horizontal = false;
    if (depth == 1) horizontal = true;

    for(var i=0; i<numDivisions; i++) {
        var child = null
        if (horizontal) {
            child = [
                parent[0] + parent[2] * i * 1 / numDivisions,
                parent[1],
                parent[2] * 1 / numDivisions,
                parent[3],
                depth + 1
            ]
        }
        else {
            child = [
                parent[0],
                parent[1] + parent[3] * i * 1 / numDivisions,
                parent[2],
                parent[3] * 1 / numDivisions,
                depth + 1
            ]
        }
        var offset = 0.002;
        child[0] += offset
        child[1] += offset
        child[2] -= 2*offset
        child[3] -= 2*offset
        divide(child, rects)
    }
    return rects
}

Window.create({
    settings: {
        width:  1280,
        height: 720,
        fullScreen: isBrowser ? true : false
    },
    resources: {
        showNormalsVert: { glsl: glslify(__dirname + '/assets/ShowNormals.vert') },
        showNormalsFrag: { glsl: glslify(__dirname + '/assets/ShowNormals.frag') },
        solidColorVert: { glsl: glslify(__dirname + '/assets/SolidColor.vert') },
        solidColorFrag: { glsl: glslify(__dirname + '/assets/SolidColor.frag') },
    },
    init: function() {
        var ctx = this.getContext();
        var res = this.getResources();

        this.gui = new GUI(ctx, this.getWidth(), this.getHeight())

        random.seed(1)

        var ssaoKernel = [];
        for(var i=0; i<64; i++) {
            var sample = [
                random.float() * 2 - 1,
                random.float() * 2 - 1,
                random.float(),
                1
            ]
            Vec3.normalize(sample)
            var scale = random.float()
            scale = MathUtils.lerp(0.1, 1.0, scale * scale);
            Vec3.scale(sample, scale)
            ssaoKernel.push(sample)
        }
        var ssaoKernelData = new Float32Array(R.flatten(ssaoKernel))

        var ssaoNoise = [];
        for(var i=0; i<64; i++) {
            var sample = [
                random.float() * 2 - 1,
                random.float() * 2 - 1,
                0,
                1
            ]
            ssaoNoise.push(sample)
        }
        var ssaoNoiseData = new Float32Array(R.flatten(ssaoNoise))

        this.depthMap = ctx.createTexture2D(null, this.getWidth(), this.getHeight(), { format: ctx.DEPTH_COMPONENT, type: ctx.UNSIGNED_SHORT });
        this.ssaoKernelMap = ctx.createTexture2D(ssaoKernelData, 8, 8, { format: ctx.RGBA, type: ctx.FLOAT, minFilter: ctx.NEAREST, magFilter: ctx.NEAREST, repeat: true });
        this.ssaoNoiseMap = ctx.createTexture2D(ssaoNoiseData, 4, 4, { format: ctx.RGBA, type: ctx.FLOAT, minFilter: ctx.NEAREST, magFilter: ctx.NEAREST, repeat: true });
        //this.rects = divide([-1, -0.5, 2, 1, 0], [])
        this.rects = divide([-2, -1, 4, 2, 0], [])
        this.rects.forEach(function(rect) {
            if (rect[4] <= 2) rect.push([0,1,0.5,1])
        })

        this.camera  = new PerspCamera(45,this.getAspectRatio(), 0.1, 10.0);
        this.camera.lookAt([0, 1, 3], [0, 0, 0]);
        ctx.setProjectionMatrix(this.camera.getProjectionMatrix());

        this.arcball = new Arcball(this.camera, this.getWidth(), this.getHeight());
        this.arcball.setDistance(3.0);
        this.addEventListener(this.arcball);

        this.showNormalsProgram = ctx.createProgram(res.showNormalsVert, res.showNormalsFrag);
        ctx.bindProgram(this.showNormalsProgram);

        this.solidColorProgram = ctx.createProgram(res.solidColorVert, res.solidColorFrag);
        ctx.bindProgram(this.solidColorProgram);

        var cube = createCube();
        var cubeAttributes = [
            { data: cube.positions, location: ctx.ATTRIB_POSITION },
            { data: cube.normals, location: ctx.ATTRIB_NORMAL }
        ];
        var cubeIndices = { data: cube.cells };
        this.cubeMesh = ctx.createMesh(cubeAttributes, cubeIndices, ctx.TRIANGLES);

        var cubeWireIndices = { data: cube.cells };
        this.cubeWireMesh = ctx.createMesh(cubeAttributes, cubeWireIndices, ctx.LINES);

        this.fx = fx(ctx)

        this.gui.addHeader('Settings')
        this.gui.addTexture2D('Kernel', this.ssaoKernelMap)
        this.gui.addTexture2D('Noise', this.ssaoNoiseMap)

        var prevCode = '';
        var shaderFile = __dirname + '/SSAO.frag';
        if (false)
        var vert = fs.readFileSync(__dirname + '/ScreenImage.vert', 'utf8');
        if (false)
        fs.watch(shaderFile, { persistent: true, recursive: false }, function(e, filename) {
            var frag = fs.readFileSync(shaderFile, 'utf8');
            if (frag != prevCode) {
                try {
                    console.log('COMPILE >>>> ')
                    var prop = ctx.createProgram(vert, frag)
                    prop.dispose()
                    this.fx.ssao.updateFrag(frag)
                }
                catch(e) {
                    console.log(e)
                }
            }
            prevCode = frag;
        }.bind(this));
    },
    drawScene: function() {
        var ctx = this.getContext()
        ctx.setClearColor(1, 0.2, 0.2, 1);
        ctx.clear(ctx.COLOR_BIT | ctx.DEPTH_BIT);
        ctx.setDepthTest(true);

        var levelHeight = 0.04;
        ctx.bindProgram(this.solidColorProgram);
        ctx.setLineWidth(2)
        this.rects.forEach(function(rect) {
            ctx.pushModelMatrix()
            ctx.translate([rect[0] + rect[2]/2, levelHeight*(1+rect[4])/2, rect[1] + rect[3]/2])
            ctx.scale([rect[2], levelHeight*(1+rect[4]), rect[3]])
            this.solidColorProgram.setUniform('uColor', rect[5] || [1,1,1,1])
            ctx.bindMesh(this.cubeMesh);
            ctx.drawMesh();
            this.solidColorProgram.setUniform('uColor', [0.5,0.5,0.5,1])
            ctx.bindMesh(this.cubeWireMesh);
            ctx.drawMesh();
            ctx.popModelMatrix()
        }.bind(this))
    },
    drawSceneNormals: function() {
        var ctx = this.getContext()
        ctx.setClearColor(1, 0.2, 0.2, 1);
        ctx.clear(ctx.COLOR_BIT | ctx.DEPTH_BIT);
        ctx.setDepthTest(true);

        var levelHeight = 0.04;
        ctx.bindProgram(this.showNormalsProgram);
        ctx.setLineWidth(2)
        this.rects.forEach(function(rect) {
            ctx.pushModelMatrix()
            ctx.translate([rect[0] + rect[2]/2, levelHeight*(1+rect[4])/2, rect[1] + rect[3]/2])
            ctx.scale([rect[2], levelHeight*(1+rect[4]), rect[3]])
            this.solidColorProgram.setUniform('uColor', [1,1,1,1])
            ctx.bindMesh(this.cubeMesh);
            ctx.drawMesh();
            ctx.popModelMatrix()
        }.bind(this))
    },
    draw: function() {
        var ctx = this.getContext();

        this.arcball.apply();
        ctx.setViewMatrix(this.camera.getViewMatrix());

        var W = this.getWidth();
        var H = this.getHeight()

        ctx.setViewport(0, 0, W, H);

        var root = this.fx.reset();
        var color = root.render({ drawFunc: this.drawScene.bind(this), depth: this.depthMap });
        var normals = root.render({ drawFunc: this.drawSceneNormals.bind(this), depth: this.depthMap });
        var ssao = root.ssao({ depthMap: this.depthMap, normalMap: normals, kernelMap: this.ssaoKernelMap, noiseMap: this.ssaoNoiseMap, camera: this.camera, strength: 1, offset: 0.2, width: W, height: H });//.blur3().blur3();
        var fin = color.mult(ssao);

        color.blit({x: 0, y: H/2, width: W/2, height: H/2});
        root.asFXStage(this.depthMap, 'bla').blit({x: W/2, y: H/2, width: W/2, height: H/2});
        normals.blit({x: W/2, y: H/2, width: W/2, height: H/2});
        ssao.blit({x: 0, y: 0, width:W/2, height: H/2});
        fin.blit({x: W/2, y: 0, width: W/2, height: H/2});

        //this.gui.draw();
    }
})
<commit_msg>blocks added blur to ssao<commit_after>var Window          = require('pex-sys/Window');
var PerspCamera     = require('pex-cam/PerspCamera');
var Arcball         = require('pex-cam/Arcball');
var createCube      = require('primitive-cube');
var glslify         = require('glslify-promise');
var isBrowser       = require('is-browser');
var random          = require('pex-random')
var fx              = require('pex-fx')
var SSAOv2          = require('./SSAO')
var Vec3            = require('pex-math/Vec3')
var MathUtils       = require('pex-math/Utils')
var R               = require('ramda')
var GUI             = require('pex-gui')
var fs              = require('fs')

var MAX_DEPTH = 20

function divide(parent, rects) {
    rects.push(parent);

    var depth = parent[4]

    var shouldDivide = random.chance(0.1 + 1/(depth*0.5+1));
    if (depth <= 1) {
        shouldDivide = true
    }

    if (depth >= MAX_DEPTH || !shouldDivide) {
        return rects
    }

    var numDivisions = random.int(2, 5)
    var horizontal = random.chance(0.5);
    if (depth == 0) horizontal = false;
    if (depth == 1) horizontal = true;

    for(var i=0; i<numDivisions; i++) {
        var child = null
        if (horizontal) {
            child = [
                parent[0] + parent[2] * i * 1 / numDivisions,
                parent[1],
                parent[2] * 1 / numDivisions,
                parent[3],
                depth + 1
            ]
        }
        else {
            child = [
                parent[0],
                parent[1] + parent[3] * i * 1 / numDivisions,
                parent[2],
                parent[3] * 1 / numDivisions,
                depth + 1
            ]
        }
        var offset = 0.002;
        child[0] += offset
        child[1] += offset
        child[2] -= 2*offset
        child[3] -= 2*offset
        divide(child, rects)
    }
    return rects
}

Window.create({
    settings: {
        width:  1280,
        height: 720,
        fullScreen: isBrowser ? true : false
    },
    resources: {
        showNormalsVert: { glsl: glslify(__dirname + '/assets/ShowNormals.vert') },
        showNormalsFrag: { glsl: glslify(__dirname + '/assets/ShowNormals.frag') },
        solidColorVert: { glsl: glslify(__dirname + '/assets/SolidColor.vert') },
        solidColorFrag: { glsl: glslify(__dirname + '/assets/SolidColor.frag') },
    },
    init: function() {
        var ctx = this.getContext();
        var res = this.getResources();

        this.gui = new GUI(ctx, this.getWidth(), this.getHeight())

        random.seed(1)

        var ssaoKernel = [];
        for(var i=0; i<64; i++) {
            var sample = [
                random.float() * 2 - 1,
                random.float() * 2 - 1,
                random.float(),
                1
            ]
            Vec3.normalize(sample)
            var scale = random.float()
            scale = MathUtils.lerp(0.1, 1.0, scale * scale);
            Vec3.scale(sample, scale)
            ssaoKernel.push(sample)
        }
        var ssaoKernelData = new Float32Array(R.flatten(ssaoKernel))

        var ssaoNoise = [];
        for(var i=0; i<64; i++) {
            var sample = [
                random.float() * 2 - 1,
                random.float() * 2 - 1,
                0,
                1
            ]
            ssaoNoise.push(sample)
        }
        var ssaoNoiseData = new Float32Array(R.flatten(ssaoNoise))

        this.depthMap = ctx.createTexture2D(null, this.getWidth(), this.getHeight(), { format: ctx.DEPTH_COMPONENT, type: ctx.UNSIGNED_SHORT });
        this.ssaoKernelMap = ctx.createTexture2D(ssaoKernelData, 8, 8, { format: ctx.RGBA, type: ctx.FLOAT, minFilter: ctx.NEAREST, magFilter: ctx.NEAREST, repeat: true });
        this.ssaoNoiseMap = ctx.createTexture2D(ssaoNoiseData, 4, 4, { format: ctx.RGBA, type: ctx.FLOAT, minFilter: ctx.NEAREST, magFilter: ctx.NEAREST, repeat: true });
        //this.rects = divide([-1, -0.5, 2, 1, 0], [])
        this.rects = divide([-2, -1, 4, 2, 0], [])
        this.rects.forEach(function(rect) {
            if (rect[4] <= 2) rect.push([0,1,0.5,1])
        })

        this.camera  = new PerspCamera(45,this.getAspectRatio(), 0.1, 10.0);
        this.camera.lookAt([0, 1, 3], [0, 0, 0]);
        ctx.setProjectionMatrix(this.camera.getProjectionMatrix());

        this.arcball = new Arcball(this.camera, this.getWidth(), this.getHeight());
        this.arcball.setDistance(3.0);
        this.addEventListener(this.arcball);

        this.showNormalsProgram = ctx.createProgram(res.showNormalsVert, res.showNormalsFrag);
        ctx.bindProgram(this.showNormalsProgram);

        this.solidColorProgram = ctx.createProgram(res.solidColorVert, res.solidColorFrag);
        ctx.bindProgram(this.solidColorProgram);

        var cube = createCube();
        var cubeAttributes = [
            { data: cube.positions, location: ctx.ATTRIB_POSITION },
            { data: cube.normals, location: ctx.ATTRIB_NORMAL }
        ];
        var cubeIndices = { data: cube.cells };
        this.cubeMesh = ctx.createMesh(cubeAttributes, cubeIndices, ctx.TRIANGLES);

        var cubeWireIndices = { data: cube.cells };
        this.cubeWireMesh = ctx.createMesh(cubeAttributes, cubeWireIndices, ctx.LINES);

        this.fx = fx(ctx)

        this.gui.addHeader('Settings')
        this.gui.addTexture2D('Kernel', this.ssaoKernelMap)
        this.gui.addTexture2D('Noise', this.ssaoNoiseMap)

        var prevCode = '';
        var shaderFile = __dirname + '/SSAO.frag';
        if (false)
        var vert = fs.readFileSync(__dirname + '/ScreenImage.vert', 'utf8');
        if (false)
        fs.watch(shaderFile, { persistent: true, recursive: false }, function(e, filename) {
            var frag = fs.readFileSync(shaderFile, 'utf8');
            if (frag != prevCode) {
                try {
                    console.log('COMPILE >>>> ')
                    var prop = ctx.createProgram(vert, frag)
                    prop.dispose()
                    this.fx.ssao.updateFrag(frag)
                }
                catch(e) {
                    console.log(e)
                }
            }
            prevCode = frag;
        }.bind(this));
    },
    drawScene: function() {
        var ctx = this.getContext()
        ctx.setClearColor(1, 0.2, 0.2, 1);
        ctx.clear(ctx.COLOR_BIT | ctx.DEPTH_BIT);
        ctx.setDepthTest(true);

        var levelHeight = 0.04;
        ctx.bindProgram(this.solidColorProgram);
        ctx.setLineWidth(2)
        this.rects.forEach(function(rect) {
            ctx.pushModelMatrix()
            ctx.translate([rect[0] + rect[2]/2, levelHeight*(1+rect[4])/2, rect[1] + rect[3]/2])
            ctx.scale([rect[2], levelHeight*(1+rect[4]), rect[3]])
            this.solidColorProgram.setUniform('uColor', rect[5] || [1,1,1,1])
            ctx.bindMesh(this.cubeMesh);
            ctx.drawMesh();
            this.solidColorProgram.setUniform('uColor', [0.5,0.5,0.5,1])
            ctx.bindMesh(this.cubeWireMesh);
            ctx.drawMesh();
            ctx.popModelMatrix()
        }.bind(this))
    },
    drawSceneNormals: function() {
        var ctx = this.getContext()
        ctx.setClearColor(1, 0.2, 0.2, 1);
        ctx.clear(ctx.COLOR_BIT | ctx.DEPTH_BIT);
        ctx.setDepthTest(true);

        var levelHeight = 0.04;
        ctx.bindProgram(this.showNormalsProgram);
        ctx.setLineWidth(2)
        this.rects.forEach(function(rect) {
            ctx.pushModelMatrix()
            ctx.translate([rect[0] + rect[2]/2, levelHeight*(1+rect[4])/2, rect[1] + rect[3]/2])
            ctx.scale([rect[2], levelHeight*(1+rect[4]), rect[3]])
            this.solidColorProgram.setUniform('uColor', [1,1,1,1])
            ctx.bindMesh(this.cubeMesh);
            ctx.drawMesh();
            ctx.popModelMatrix()
        }.bind(this))
    },
    draw: function() {
        var ctx = this.getContext();

        this.arcball.apply();
        ctx.setViewMatrix(this.camera.getViewMatrix());

        var W = this.getWidth();
        var H = this.getHeight()

        ctx.setViewport(0, 0, W, H);

        var root = this.fx.reset();
        var color = root.render({ drawFunc: this.drawScene.bind(this), depth: this.depthMap });
        var normals = root.render({ drawFunc: this.drawSceneNormals.bind(this), depth: this.depthMap });
        var ssao = root.ssao({ depthMap: this.depthMap, normalMap: normals, kernelMap: this.ssaoKernelMap, noiseMap: this.ssaoNoiseMap, camera: this.camera, strength: 1, offset: 0.2, width: W, height: H }).blur3().blur3();
        var fin = color.mult(ssao);

        color.blit({x: 0, y: H/2, width: W/2, height: H/2});
        root.asFXStage(this.depthMap, 'bla').blit({x: W/2, y: H/2, width: W/2, height: H/2});
        normals.blit({x: W/2, y: H/2, width: W/2, height: H/2});
        ssao.blit({x: 0, y: 0, width:W/2, height: H/2});
        fin.blit({x: W/2, y: 0, width: W/2, height: H/2});

        //this.gui.draw();
    }
})
<|endoftext|>


<commit_before>#!/usr/bin/env node
var request = require('request');
var async = require('async');
var MCrypt = require('mcrypt').MCrypt;
var buffertrim = require('buffertrim');
var mqtt = require('mqtt');
require('require-yaml');

// Load config from path in km200_config
console.log('Starting km200 to mqtt');
if (process.env.km200config == null) {
  console.error('Missing enviroment variable km200config');
  process.exit(-1);
}
console.log('Config file: ' + process.env.km200config);
var config = require(process.env.km200config);
console.log(config);
var key = Buffer.from(config.km200.key, 'hex');
var desEcb = new MCrypt('rijndael-128', 'ecb');
desEcb.open(key);
var km200host = config.km200.host;

var writables = {};

function mnemonizeWritable (result) {
  if (result.writeable === 1) {
    if (writables[result.id] == null) {
      if (result.allowedValues) {
        console.log('Writable: ' + result.id + ' (' + result.type + '): ' + JSON.stringify(result.allowedValues));
      } else {
        console.log('Writable: ' + result.id + ' (' + result.type + '): ' + result.minValue + ' - ' + result.maxValue);
      }
      writables[result.id] = {
        valueType: result.type,
        minValue: result.minValue,
        maxValue: result.maxValue,
        allowedValues: result.allowedValues
      };
    }
  }
}

console.log('Connect mqtt: ' + config.mqtt.server);
var mqttCon = mqtt.connect(config.mqtt.server);

mqttCon.on('connect', () => {
  mqttCon.subscribe('km200/set/#');
});

mqttCon.on('message', (topic, message) => {
  if (topic.startsWith('km200/set/')) {
    let url = topic.substring(9);
    let value = message.toString();
    let writable = writables[url];
    if (writable != null) {
      if ((writable.valueType === 'stringValue' && writable.allowedValues.indexOf(value) !== -1) ||
          (writable.valueType === 'floatValue' && parseFloat(value) >= writable.minValue && parseFloat(value) <= writable.maxValue)) {
        console.log('WRITE: ' + value);
        const postValue = desEcb.encrypt(JSON.stringify({
          value: parseFloat(value)
        })).toString('base64');
        var options = {
          url: 'http://' + km200host + url,
          body: postValue,
          headers: {
            'Content-type': 'application/json',
            'User-Agent': 'TeleHeater/2.2.3'
          }
        };
        request.post(options, function (error, response, body) {
          if (!error) {
            getKM200(url, function done () {});
          }
          console.log(error, response.statusCode);
        });
      } else {
        console.log('Invalid valiue: ' + value);
      }
    } else {
      console.log(url + ' not writavle!');
    }
  }
});

function getKM200 (url, done) {
  var options = {
    url: 'http://' + km200host + url,
    headers: {
      'Content-type': 'application/json',
      'User-Agent': 'TeleHeater/2.2.3'
    }
  };
  request.get(options, function (error, response, body) {
    if (!error && response.statusCode === 200) {
      var result = JSON.parse(buffertrim.trimEnd(desEcb.decrypt(Buffer.from(body, 'base64'), 'base64')).toString());
      mnemonizeWritable(result);
      var topic = 'km200/status' + result.id;
      var state = {
        ts: Math.floor(new Date() / 1000),
        val: result.value,
        km200_unitOfMeasure: result.unitOfMeasure
      };
      mqttCon.publish(topic, JSON.stringify(state), { retain: true }, function () {
        // console.log(topic, value);
      });
      done(null);
    } else {
      done(null);
    }
  });
}

function checkKM200 () {
  async.eachSeries(config.measurements,
    function (measurement, cb) {
      getKM200(measurement.url, function (done) {
        cb(done);
      });
    },
    function (err, result) {
      if (err) {
        console.error(err);
      }
    }
  );
}

checkKM200();
setInterval(checkKM200, 60000);
<commit_msg>fix value set<commit_after>#!/usr/bin/env node
var request = require('request');
var async = require('async');
var MCrypt = require('mcrypt').MCrypt;
var buffertrim = require('buffertrim');
var mqtt = require('mqtt');
require('require-yaml');

// Load config from path in km200_config
console.log('Starting km200 to mqtt');
if (process.env.km200config == null) {
  console.error('Missing enviroment variable km200config');
  process.exit(-1);
}
console.log('Config file: ' + process.env.km200config);
var config = require(process.env.km200config);
console.log(config);
var key = Buffer.from(config.km200.key, 'hex');
var desEcb = new MCrypt('rijndael-128', 'ecb');
desEcb.open(key);
var km200host = config.km200.host;

var writables = {};

function mnemonizeWritable (result) {
  if (result.writeable === 1) {
    if (writables[result.id] == null) {
      if (result.allowedValues) {
        console.log('Writable: ' + result.id + ' (' + result.type + '): ' + JSON.stringify(result.allowedValues));
      } else {
        console.log('Writable: ' + result.id + ' (' + result.type + '): ' + result.minValue + ' - ' + result.maxValue);
      }
      writables[result.id] = {
        valueType: result.type,
        minValue: result.minValue,
        maxValue: result.maxValue,
        allowedValues: result.allowedValues
      };
    }
  }
}

console.log('Connect mqtt: ' + config.mqtt.server);
var mqttCon = mqtt.connect(config.mqtt.server);

mqttCon.on('connect', () => {
  mqttCon.subscribe('km200/set/#');
});

mqttCon.on('message', (topic, message) => {
  if (topic.startsWith('km200/set/')) {
    let url = topic.substring(9);
    let value = message.toString();
    let writable = writables[url];
    if (writable != null) {
      if ((writable.valueType === 'stringValue' && writable.allowedValues.indexOf(value) !== -1) ||
          (writable.valueType === 'floatValue' && parseFloat(value) >= writable.minValue && parseFloat(value) <= writable.maxValue)) {
        console.log('WRITE: ' + value);
        const postValue = desEcb.encrypt(JSON.stringify({
          value: writable.valueType === 'stringValue' ? value : parseFloat(value)
        })).toString('base64');
        var options = {
          url: 'http://' + km200host + url,
          body: postValue,
          headers: {
            'Content-type': 'application/json',
            'User-Agent': 'TeleHeater/2.2.3'
          }
        };
        request.post(options, function (error, response, body) {
          if (!error) {
            getKM200(url, function done () {});
          }
          console.log(error, response.statusCode);
        });
      } else {
        console.log('Invalid valiue: ' + value);
      }
    } else {
      console.log(url + ' not writavle!');
    }
  }
});

function getKM200 (url, done) {
  var options = {
    url: 'http://' + km200host + url,
    headers: {
      'Content-type': 'application/json',
      'User-Agent': 'TeleHeater/2.2.3'
    }
  };
  request.get(options, function (error, response, body) {
    if (!error && response.statusCode === 200) {
      var result = JSON.parse(buffertrim.trimEnd(desEcb.decrypt(Buffer.from(body, 'base64'), 'base64')).toString());
      mnemonizeWritable(result);
      var topic = 'km200/status' + result.id;
      var state = {
        ts: Math.floor(new Date() / 1000),
        val: result.value,
        km200_unitOfMeasure: result.unitOfMeasure
      };
      mqttCon.publish(topic, JSON.stringify(state), { retain: true }, function () {
        // console.log(topic, value);
      });
      done(null);
    } else {
      done(null);
    }
  });
}

function checkKM200 () {
  async.eachSeries(config.measurements,
    function (measurement, cb) {
      getKM200(measurement.url, function (done) {
        cb(done);
      });
    },
    function (err, result) {
      if (err) {
        console.error(err);
      }
    }
  );
}

checkKM200();
setInterval(checkKM200, 60000);
<|endoftext|>


<commit_before>/**
 * EZ-TC Plugin
 * Gold Server - http://gold.psim.us/
 *
 * Allows for adding custom trainer cards to be easier.
 * Credits: jd
 *
 * @license MIT license
 */
'use strict';

const fs = require('fs');
const serialize = require('node-serialize');
let trainerCards = {};

function loadTrainerCards() {
	try {
		trainerCards = serialize.unserialize(fs.readFileSync('config/trainercards.json', 'utf8'));
		Object.assign(Chat.commands, trainerCards);
	} catch (e) {}
}

setTimeout(function load() {
	loadTrainerCards();
}, 1000);

function saveTrainerCards() {
	fs.writeFileSync('config/trainercards.json', serialize.serialize(trainerCards));
	Object.assign(Chat.commands, trainerCards);
}

exports.commands = {
	eztc: 'trainercard',
	trainercards: 'trainercard',
	tc: 'trainercard',
	trainercard: function (target, room, user) {
		if (!target) target = 'help';
		let parts = target.split(',');
		for (let u in parts) parts[u] = parts[u].trim();

		switch (parts[0]) {
		case 'add':
			if (!this.can('trainercard')) return false;
			if (!parts[2]) return this.sendReply("Usage: /trainercard add, [command name], [html]");
			let commandName = toId(parts[1]);
			if (Chat.commands[commandName]) return this.sendReply("/trainercards - The command \"" + commandName + "\" already exists.");
			let html = parts.splice(2, parts.length).join(',');
			trainerCards[commandName] = new Function('target', 'room', 'user', "if (!room.disableTrainerCards) if (!this.runBroadcast()) return; this.sendReplyBox('" + html.replace(/'/g, "\\'") + "');"); // eslint-disable-line no-new-func
			saveTrainerCards();
			this.sendReply("The trainer card \"" + commandName + "\" has been added.");
			this.logModCommand(user.name + " added the trainer card " + commandName);
			break;

		case 'rem':
		case 'del':
		case 'delete':
		case 'remove':
			if (!this.can('trainercard')) return false;
			if (!parts[1]) return this.sendReply("Usage: /trainercard remove, [command name]");
			let command = toId(parts[1]);
			if (!trainerCards[command]) return this.sendReply("/trainercards - The command \"" + command + "\" does not exist, or was added manually.");
			delete Chat.commands[command];
			delete trainerCards[command];
			saveTrainerCards();
			this.sendReply("The trainer card \"" + command + "\" has been removed.");
			this.logModCommand(user.name + " removed the trainer card " + command);
			break;

		case 'list':
			if (!this.can('trainercard')) return false;
			let output = "There's a total of " + Object.keys(trainerCards).length + " trainer cards added with this command:
";
			for (let tc in trainerCards) {
				output += tc + "
";
			}
			this.sendReplyBox(output);
			break;

		case 'off':
			if (!this.can('roommod', null, room)) return false;
			if (room.disableTrainerCards) return this.sendReply("Broadcasting trainer cards is already disabled in this room.");
			room.disableTrainerCards = true;
			room.chatRoomData.disableTrainerCards = true;
			Rooms.global.writeChatRoomData();
			this.privateModCommand("(" + user.name + " has disabled broadcasting trainer cards in this room.)");
			break;

		case 'on':
			if (!this.can('roommod', null, room)) return false;
			if (!room.disableTrainerCards) return this.sendReply("Broadcasing trainer cards is already enabled in this room.");
			delete room.disableTrainerCards;
			delete room.chatRoomData.disableTrainerCards;
			Rooms.global.writeChatRoomData();
			this.privateModCommand("(" + user.name + " has enabled broadcasting trainer cards in this room.)");
			break;

		case 'reload':
			if (!this.can('pban')) return false;
			loadTrainerCards();
			this.sendReply("Trainer cards have been reloaded.");
			break;

		case 'source':
			if (!this.can('pban')) return false;
			if (!parts[1]) return this.errorReply("Usage: /tc source, [commands] - displays the source code of a trainer card.");
			if (!Chat.commands[parts[1]]) return this.errorReply("Command not found.  Check spelling?");
			this.sendReply(Chat.commands[parts[1]]);
			break;

		default:
		case 'info':
		case 'help':
			if (!this.runBroadcast()) return;
			this.sendReplyBox(
				"EZ-TC Commands:
" +
				"/trainercard add, [command name], [html] - Adds a trainer card.
" +
				"/trainercard remove, [command name] - Removes a trainer card.
" +
				"/trainercard list - Shows a list of all trainer cards added with this command.
" +
				"/trainercard off - Disables broadcasting trainer cards in the current room.
" +
				"/trainercard on - Enables broadcasting trainer cards in the current room.
" +
				"/trainercard help - Shows this help command.
" +
				"EZ-TC Plugin by jd"
			);
		}
	},
};
<commit_msg>Fix a crash in ez tc command<commit_after>/**
 * EZ-TC Plugin
 * Gold Server - http://gold.psim.us/
 *
 * Allows for adding custom trainer cards to be easier.
 * Credits: jd
 *
 * @license MIT license
 */
'use strict';

const fs = require('fs');
const serialize = require('node-serialize');
let trainerCards = {};

function loadTrainerCards() {
	try {
		trainerCards = serialize.unserialize(fs.readFileSync('config/trainercards.json', 'utf8'));
		Object.assign(Chat.commands, trainerCards);
	} catch (e) {}
}

setTimeout(function load() {
	loadTrainerCards();
}, 1000);

function saveTrainerCards() {
	fs.writeFileSync('config/trainercards.json', serialize.serialize(trainerCards));
	Object.assign(Chat.commands, trainerCards);
}

exports.commands = {
	eztc: 'trainercard',
	trainercards: 'trainercard',
	tc: 'trainercard',
	trainercard: function (target, room, user) {
		if (!target) target = 'help';
		let parts = target.split(',');
		for (let u in parts) parts[u] = parts[u].trim();

		switch (parts[0]) {
		case 'add':
			if (!this.can('trainercard')) return false;
			if (!parts[2]) return this.sendReply("Usage: /trainercard add, [command name], [html]");
			let commandName = toId(parts[1]);
			if (Chat.commands[commandName]) return this.sendReply("/trainercards - The command \"" + commandName + "\" already exists.");
			let html = parts.splice(2, parts.length).join(',');
			if (html.includes('\\')) html = html.replace('\\', '');
			trainerCards[commandName] = new Function('target', 'room', 'user', "if (!room.disableTrainerCards) if (!this.runBroadcast()) return; this.sendReplyBox('" + html.replace(/'/g, "\\'") + "');"); // eslint-disable-line no-new-func
			saveTrainerCards();
			this.sendReply("The trainer card \"" + commandName + "\" has been added.");
			this.logModCommand(user.name + " added the trainer card " + commandName);
			break;

		case 'rem':
		case 'del':
		case 'delete':
		case 'remove':
			if (!this.can('trainercard')) return false;
			if (!parts[1]) return this.sendReply("Usage: /trainercard remove, [command name]");
			let command = toId(parts[1]);
			if (!trainerCards[command]) return this.sendReply("/trainercards - The command \"" + command + "\" does not exist, or was added manually.");
			delete Chat.commands[command];
			delete trainerCards[command];
			saveTrainerCards();
			this.sendReply("The trainer card \"" + command + "\" has been removed.");
			this.logModCommand(user.name + " removed the trainer card " + command);
			break;

		case 'list':
			if (!this.can('trainercard')) return false;
			let output = "There's a total of " + Object.keys(trainerCards).length + " trainer cards added with this command:
";
			for (let tc in trainerCards) {
				output += tc + "
";
			}
			this.sendReplyBox(output);
			break;

		case 'off':
			if (!this.can('roommod', null, room)) return false;
			if (room.disableTrainerCards) return this.sendReply("Broadcasting trainer cards is already disabled in this room.");
			room.disableTrainerCards = true;
			room.chatRoomData.disableTrainerCards = true;
			Rooms.global.writeChatRoomData();
			this.privateModCommand("(" + user.name + " has disabled broadcasting trainer cards in this room.)");
			break;

		case 'on':
			if (!this.can('roommod', null, room)) return false;
			if (!room.disableTrainerCards) return this.sendReply("Broadcasing trainer cards is already enabled in this room.");
			delete room.disableTrainerCards;
			delete room.chatRoomData.disableTrainerCards;
			Rooms.global.writeChatRoomData();
			this.privateModCommand("(" + user.name + " has enabled broadcasting trainer cards in this room.)");
			break;

		case 'reload':
			if (!this.can('pban')) return false;
			loadTrainerCards();
			this.sendReply("Trainer cards have been reloaded.");
			break;

		case 'source':
			if (!this.can('pban')) return false;
			if (!parts[1]) return this.errorReply("Usage: /tc source, [commands] - displays the source code of a trainer card.");
			if (!Chat.commands[parts[1]]) return this.errorReply("Command not found.  Check spelling?");
			this.sendReply(Chat.commands[parts[1]]);
			break;

		default:
		case 'info':
		case 'help':
			if (!this.runBroadcast()) return;
			this.sendReplyBox(
				"EZ-TC Commands:
" +
				"/trainercard add, [command name], [html] - Adds a trainer card.
" +
				"/trainercard remove, [command name] - Removes a trainer card.
" +
				"/trainercard list - Shows a list of all trainer cards added with this command.
" +
				"/trainercard off - Disables broadcasting trainer cards in the current room.
" +
				"/trainercard on - Enables broadcasting trainer cards in the current room.
" +
				"/trainercard help - Shows this help command.
" +
				"EZ-TC Plugin by jd"
			);
		}
	},
};
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { writeText } from '../../lib/util';

export class Settings extends Component {
  constructor(props){
    super(props);
    this.state = {
      title: "",
      description: "",
      comments: false,
      disabled: false
    }
    this.deleteNotebook = this.deleteNotebook.bind(this);
    this.changeTitle = this.changeTitle.bind(this);
    this.changeDescription = this.changeDescription.bind(this);
    this.changeComments = this.changeComments.bind(this);
  }

  componentDidMount() {
    const { props } = this;
    if (props.notebook) {
      this.setState({
        title: props.notebook.title,
        description: props.notebook.about,
        comments: props.notebook.comments
      })
    }
  }

  componentDidUpdate(prevProps) {
    const { props } = this;
    if (prevProps !== this.props) {
      if (props.notebook) {
        this.setState({
          title: props.notebook.title,
          description: props.notebook.about,
          comments: props.notebook.comments
        })
      }
    }
  }

  changeTitle(event) {
    this.setState({title: event.target.value});
  }

  changeDescription(event) {
    this.setState({description: event.target.value});
  }

  changeComments() {
    this.setState({comments: !this.state.comments}, (() => {
      window.api.setSpinner(true);
      window.api.action("publish", "publish-action", {
        "edit-book": {
          book: this.props.book,
          title: this.props.notebook.title,
          about: this.props.notebook.about,
          coms: this.state.comments,
          group: null
        }
      }).then(() => {
        window.api.setSpinner(false);
      })
    }));
  }

  deleteNotebook(){
    let action = {
      "del-book": {
        book: this.props.book
      }
    }
    window.api.setSpinner(true);
    window.api.action("publish", "publish-action", action).then(() => {
      window.api.setSpinner(false);
      this.props.history.push("/~publish");
    });
  }

  render() {
    let commentsSwitchClasses = (this.state.comments)
      ? "relative checked bg-green2 br3 h1 toggle v-mid z-0"
      : "relative bg-gray4 bg-gray1-d br3 h1 toggle v-mid z-0";

    let copyShortcode =

Share

Share a shortcode to join this notebook

        <input
          className={"f8 mono ba b--gray3 b--gray2-d bg-gray0-d white-d " +
            "pa3 db w-100 flex-auto mr3"}
          disabled={true}
          value={`${this.props.host}/${this.props.book}` || ""}
        />
         {
            writeText(`${this.props.host}/${this.props.book}`);
            this.refs.copy.innerText = "Copied"
          }}>
            Copy

    if (this.props.host.slice(1) === window.ship) {
      return (

          {copyShortcode}

Delete Notebook

            Permanently delete this notebook. (All current members will no
            longer see this notebook)

          <button
            className="bg-transparent b--red2 red2 pointer dib f9 ba pa2"
            onClick={this.deleteNotebook}>
            Delete this notebook
          </button>

Rename

Change the name of this notebook

            <input
              className={
                "f8 ba b--gray3 b--gray2-d bg-gray0-d white-d " +
                "focus-b--black focus-b--white-d pa3 db w-100 flex-auto mr3"
              }
              value={this.state.title}
              onChange={this.changeTitle}
              disabled={this.state.disabled}
              onBlur={() => {
                this.setState({ disabled: true });
                window.api.setSpinner(true);
                window.api
                  .action("publish", "publish-action", {
                    "edit-book": {
                      book: this.props.book,
                      title: this.state.title,
                      about: this.props.notebook.about,
                      coms: this.props.notebook.comments,
                      group: null
                    }
                  })
                  .then(() => {
                    this.setState({ disabled: false })
                    window.api.setSpinner(false);
                  });
              }}
            />

Change description

Change the description of this notebook

            <input
              className={
                "f8 ba b--gray3 b--gray2-d bg-gray0-d white-d " +
                "focus-b--black focus-b--white-d pa3 db w-100 flex-auto mr3"
              }
              value={this.state.description}
              onChange={this.changeDescription}
              onBlur={() => {
                this.setState({ disabled: true });
                window.api.setSpinner(true);
                window.api
                  .action("publish", "publish-action", {
                    "edit-book": {
                      book: this.props.book,
                      title: this.props.notebook.title,
                      about: this.state.description,
                      coms: this.props.notebook.comments,
                      group: null
                    }
                  })
                  .then(() => {
                    this.setState({ disabled: false });
                    window.api.setSpinner(false);
                  });
              }}
            />

            <input
              type="checkbox"
              style={{ WebkitAppearance: "none", width: 28 }}
              className={commentsSwitchClasses}
              onChange={this.changeComments}
            />
            Comments

            Subscribers may comment when enabled

      );
    } else {
      return copyShortcode;
    }
  }
}

export default Settings
<commit_msg>publish: atomically refresh our settings inputs<commit_after>import React, { Component } from 'react';
import { writeText } from '../../lib/util';

export class Settings extends Component {
  constructor(props){
    super(props);
    this.state = {
      title: "",
      description: "",
      comments: false,
      disabled: false
    }
    this.deleteNotebook = this.deleteNotebook.bind(this);
    this.changeTitle = this.changeTitle.bind(this);
    this.changeDescription = this.changeDescription.bind(this);
    this.changeComments = this.changeComments.bind(this);
  }

  componentDidMount() {
    const { props } = this;
    if (props.notebook) {
      this.setState({
        title: props.notebook.title,
        description: props.notebook.about,
        comments: props.notebook.comments
      })
    }
  }

  componentDidUpdate(prevProps) {
    const { props } = this;
    if (prevProps !== props) {
      if (props.notebook) {
        if (prevProps.notebook && prevProps.notebook !== props.notebook) {
          if (prevProps.notebook.title !== props.notebook.title) {
            this.setState({title: props.notebook.title});
          }
          if (prevProps.notebook.about !== props.notebook.about) {
            this.setState({description: props.notebook.about});
          }
          if (prevProps.notebook.comments !== props.notebook.comments) {
            this.setState({comments: props.notebook.comments})
          }
        }
      }
    }
  }

  changeTitle(event) {
    this.setState({title: event.target.value});
  }

  changeDescription(event) {
    this.setState({description: event.target.value});
  }

  changeComments() {
    this.setState({comments: !this.state.comments}, (() => {
      window.api.setSpinner(true);
      window.api.action("publish", "publish-action", {
        "edit-book": {
          book: this.props.book,
          title: this.props.notebook.title,
          about: this.props.notebook.about,
          coms: this.state.comments,
          group: null
        }
      }).then(() => {
        window.api.setSpinner(false);
      })
    }));
  }

  deleteNotebook(){
    let action = {
      "del-book": {
        book: this.props.book
      }
    }
    window.api.setSpinner(true);
    window.api.action("publish", "publish-action", action).then(() => {
      window.api.setSpinner(false);
      this.props.history.push("/~publish");
    });
  }

  render() {
    let commentsSwitchClasses = (this.state.comments)
      ? "relative checked bg-green2 br3 h1 toggle v-mid z-0"
      : "relative bg-gray4 bg-gray1-d br3 h1 toggle v-mid z-0";

    let copyShortcode =

Share

Share a shortcode to join this notebook

        <input
          className={"f8 mono ba b--gray3 b--gray2-d bg-gray0-d white-d " +
            "pa3 db w-100 flex-auto mr3"}
          disabled={true}
          value={`${this.props.host}/${this.props.book}` || ""}
        />
         {
            writeText(`${this.props.host}/${this.props.book}`);
            this.refs.copy.innerText = "Copied"
          }}>
            Copy

    if (this.props.host.slice(1) === window.ship) {
      return (

          {copyShortcode}

Delete Notebook

            Permanently delete this notebook. (All current members will no
            longer see this notebook)

          <button
            className="bg-transparent b--red2 red2 pointer dib f9 ba pa2"
            onClick={this.deleteNotebook}>
            Delete this notebook
          </button>

Rename

Change the name of this notebook

            <input
              className={
                "f8 ba b--gray3 b--gray2-d bg-gray0-d white-d " +
                "focus-b--black focus-b--white-d pa3 db w-100 flex-auto mr3"
              }
              value={this.state.title}
              onChange={this.changeTitle}
              disabled={this.state.disabled}
              onBlur={() => {
                this.setState({ disabled: true });
                window.api.setSpinner(true);
                window.api
                  .action("publish", "publish-action", {
                    "edit-book": {
                      book: this.props.book,
                      title: this.state.title,
                      about: this.props.notebook.about,
                      coms: this.props.notebook.comments,
                      group: null
                    }
                  })
                  .then(() => {
                    this.setState({ disabled: false })
                    window.api.setSpinner(false);
                  });
              }}
            />

Change description

Change the description of this notebook

            <input
              className={
                "f8 ba b--gray3 b--gray2-d bg-gray0-d white-d " +
                "focus-b--black focus-b--white-d pa3 db w-100 flex-auto mr3"
              }
              value={this.state.description}
              onChange={this.changeDescription}
              onBlur={() => {
                this.setState({ disabled: true });
                window.api.setSpinner(true);
                window.api
                  .action("publish", "publish-action", {
                    "edit-book": {
                      book: this.props.book,
                      title: this.props.notebook.title,
                      about: this.state.description,
                      coms: this.props.notebook.comments,
                      group: null
                    }
                  })
                  .then(() => {
                    this.setState({ disabled: false });
                    window.api.setSpinner(false);
                  });
              }}
            />

            <input
              type="checkbox"
              style={{ WebkitAppearance: "none", width: 28 }}
              className={commentsSwitchClasses}
              onChange={this.changeComments}
            />
            Comments

            Subscribers may comment when enabled

      );
    } else {
      return copyShortcode;
    }
  }
}

export default Settings
<|endoftext|>


<commit_before>var DataGrid = Class.extend({
    rows: null,
    columns: null,
    allowEdit: false,
    editRoute: null,
    allowCreate: false,
    createRoute: null,
    allowDelete: false,
    deleteRoute: null,
    sortable: true,
    sorting: null,
    pageSize: null,
    page: null,
    filter: null,
    enableFilter: null,
    enablePageSizeSelector: null,
    currentPage: null,

    init: function(columns, data, options) {
        this.sorting = ko.observable({ column: null, inverse: false });

        this.inline = options.inline;
        this.allowEdit = options.allowEdit;
        this.editRoute = options.editRoute;
        this.allowCreate = options.allowCreate;
        this.createRoute = options.createRoute;
        this.allowDelete = options.allowDelete;
        this.deleteRoute = options.deleteRoute;
        this.sortable = options.sortable;
        this.pageSize = ko.observable(options.pageSize);
        this.filter = ko.observable();
        this.enableFilter = options.enableFilter;
        this.enablePageSizeSelector = options.enablePageSizeSelector;

        this.columns = ko.observableArray();
        for (i in columns) {
            columns[i].setGrid(this);
            this.columns.push(columns[i]);
        }
        this.page = ko.observable(options.page);

        this.rows = ko.observableArray();
        for (i in data) {
            var row = data[i];
            row.setGrid(this);
            this.rows.push(row);
        }

        this.existingRows = ko.computed(function() {
            return this.rows().filter(function(element) {
                return !element.isNew();
            }, this)
        }, this);
        this.newRows = ko.computed(function() {
            return this.rows().filter(function(element) {
                return element.isNew();
            }, this)
        }, this);
    },

    loadCreateForm: function() {
        var data = {};
        for (i in this.columns()) {
            data[this.columns()[i].name] = "";
        }
        var row = new Row(data);
        row.setGrid(this);
        row.isNew(true);
        $.get(row.getSaveUrl(), function(data) {
            row.createForm(data);
        });

        this.rows.push(row);
    },

    pageHref: function() {
        return '#';
    },

    createCreateUrl: function() {
        return Routing.generate(this.createRoute, { _format: this.inline ? 'json' : 'html' } );
    },

    sort: function(column) {
        if (!this.sortable || !column.sortable) {
            return;
        }

        this.sorting({ column: column, inverse: this.sorting().column === column ? !this.sorting().inverse : false });

        var self = this;
        this.rows.sort(function(a, b) {
            var ret = 0;

            if (a.textData()[self.sorting().column.name] > b.textData()[self.sorting().column.name]) {
                ret = 1;
            } else if (a.textData()[self.sorting().column.name] < b.textData()[self.sorting().column.name]) {
                ret = -1;
            }

            if (self.sorting().inverse) {
                ret *= -1;
            }

            return ret;
        });
    }
});

DataGrid.Column = Class.extend({
    name: null,
    title: null,
    grid: null,
    classes: null,
    sortable: null,
    headCellTemplate: 'head-cell-template',
    bodyCellTemplate: 'body-cell-template',

    init: function(name, options) {
        this.name = name;

        var options = $.extend({
            title: name,
            sortable: true
        }, options, true);

        this.title = options.title;
        this.sortable = options.sortable;
    },

    setGrid: function(grid) {
        this.grid = grid;

        this.classes = ko.computed(function() {
            return {
                sortable: this.grid.sortable && this.sortable,
                sorting_asc: this.grid.sorting().column === this && this.grid.sorting().inverse === false,
                sorting_desc: this.grid.sorting().column === this && this.grid.sorting().inverse === true
            };
        }, this)
    },

    sort: function() {
        this.grid.sort(this);
    }
});

goog.require('goog.i18n.DateTimeFormat');
goog.require('goog.date');
DataGrid.DateColumn = DataGrid.Column.extend({
    bodyCellTemplate: 'date-body-cell-template',
    init: function() {
        this.formatter = new goog.i18n.DateTimeFormat(goog.i18n.DateTimeFormat.Format.MEDIUM_DATETIME);
        this['_super'].apply(this, arguments);
    },
    format: function(date) {
        return this.formatter.format(goog.date.DateTime.fromRfc822String(date));
    }
});

DataGrid.SingleIconColumn = DataGrid.Column.extend({
    icon: null,
    headCellTemplate: 'single-icon-head-cell-template',
    bodyCellTemplate: 'single-icon-body-cell-template',
    init: function(name, icon, options) {
        this._super(name, options);
        this.icon = icon;
        this.sortable = false;
    }
});

DataGrid.Row = Class.extend({
    grid: null,
    textData: null,
    formData: null,
    data: null,
    savingForm: null,
    errors: null,
    isNew: null,

    init: function(data) {
        this.textData = ko.observable(data);
        this.formData = ko.observable(null);
        this.data = ko.computed(function() {
            if (this.formData() === null) {
                return this.textData();
            }
            return this.formData();
        }, this);
        this.editing = ko.computed(function() {
            return this.formData() !== null;
        }, this);
        this.savingForm = ko.observable(false);
        this.errors = ko.observable(false);
        this.hiddenFields = ko.computed(function() {
            if (null === this.formData()) {
                return "";
            }
            var hiddenFields = "";
            for (i in this.formData()) {
                if (!(i in this.textData())) {
                    hiddenFields += this.formData()[i];
                }
            }
            return hiddenFields;
        }, this);
        this.isNew = ko.observable(false);
    },

    setGrid: function(grid) {
        this.grid = grid;
    },

    isColumnForm: function(name) {
        return this.formData() && name in this.formData();
    },

    getColumnData: function(name) {
        if (name in this.data()) {
            return this.data()[name];
        }
        if (name in this.textData()) {
            return this.textData()[name];
        }
        //throw "The row data doesn't have a "+name+" field!";
        return '';
    },

    getSaveUrl: function() {
        return this.isNew() ? this.grid.createCreateUrl() : this.createEditUrl();
    },

    createEditUrl: function() {
        return Routing.generate(this.grid.editRoute, { id: this.getColumnData('id'), _format: this.grid.inline ? 'json' : 'html' } );
    },

    createDeleteUrl: function() {
        return Routing.generate(this.grid.deleteRoute, { id: this.getColumnData('id'), _format: 'json' } );
    },

    deleteRow: function() {
        var self = this;

        if (this.isNew()) {
            self.grid.rows.remove(self);
        } else {
            $.post(this.createDeleteUrl(), {}, function(data) {
                self.grid.rows.remove(self);
            });
        }
    },

    loadForm: function() {
        var self = this;
        $.get(this.getSaveUrl(), function(data) {
            self.createForm(data);
        });
    },

    createForm: function(data) {
        var formData = {};
        for (i in data.widgets) {
            formData[i] = $("<form>").html(data.widgets[i].error + data.widgets[i].widget).text();
        }
        this.formData(formData);
        if (data.errors) {
            this.errors($("<div>").html(data.errors).text());
        }
    },

    saveForm: function(row, event) {
        this.savingForm(true);
        this.errors(null);

        var $row = $(event.target).closest('tr');
        var self = this;
        var $form = $("<form>");
        $row.find(':input').each(function() {
            $form.append($(this).clone());
        });

        $.post(this.getSaveUrl(), $form.serialize(), function(data) {
            self.formData(null);
            self.savingForm(false);
            self.textData(data);

            self.isNew(false);
        }).fail(function(data) {
            if (data.status === 400) {
                self.savingForm(false);
                self.createForm($.parseJSON(data.responseText));
            }
        });
    }
});
<commit_msg>bugfix<commit_after>var DataGrid = Class.extend({
    rows: null,
    columns: null,
    allowEdit: false,
    editRoute: null,
    allowCreate: false,
    createRoute: null,
    allowDelete: false,
    deleteRoute: null,
    sortable: true,
    sorting: null,
    pageSize: null,
    page: null,
    filter: null,
    enableFilter: null,
    enablePageSizeSelector: null,
    currentPage: null,

    init: function(columns, data, options) {
        this.sorting = ko.observable({ column: null, inverse: false });

        this.inline = options.inline;
        this.allowEdit = options.allowEdit;
        this.editRoute = options.editRoute;
        this.allowCreate = options.allowCreate;
        this.createRoute = options.createRoute;
        this.allowDelete = options.allowDelete;
        this.deleteRoute = options.deleteRoute;
        this.sortable = options.sortable;
        this.pageSize = ko.observable(options.pageSize);
        this.filter = ko.observable();
        this.enableFilter = options.enableFilter;
        this.enablePageSizeSelector = options.enablePageSizeSelector;

        this.columns = ko.observableArray();
        for (i in columns) {
            columns[i].setGrid(this);
            this.columns.push(columns[i]);
        }
        this.page = ko.observable(options.page);

        this.rows = ko.observableArray();
        for (i in data) {
            var row = data[i];
            row.setGrid(this);
            this.rows.push(row);
        }

        this.existingRows = ko.computed(function() {
            return this.rows().filter(function(element) {
                return !element.isNew();
            }, this)
        }, this);
        this.newRows = ko.computed(function() {
            return this.rows().filter(function(element) {
                return element.isNew();
            }, this)
        }, this);
    },

    loadCreateForm: function() {
        var data = {};
        for (i in this.columns()) {
            data[this.columns()[i].name] = "";
        }
        var row = new Row(data);
        row.setGrid(this);
        row.isNew(true);
        $.get(row.getSaveUrl(), function(data) {
            row.createForm(data);
        });

        this.rows.push(row);
    },

    pageHref: function() {
        return '#';
    },

    createCreateUrl: function() {
        return Routing.generate(this.createRoute, { _format: this.inline ? 'json' : 'html' } );
    },

    sort: function(column) {
        if (!this.sortable || !column.sortable) {
            return;
        }

        this.sorting({ column: column, inverse: this.sorting().column === column ? !this.sorting().inverse : false });

        var self = this;
        this.rows.sort(function(a, b) {
            var ret = 0;

            if (a.textData()[self.sorting().column.name] > b.textData()[self.sorting().column.name]) {
                ret = 1;
            } else if (a.textData()[self.sorting().column.name] < b.textData()[self.sorting().column.name]) {
                ret = -1;
            }

            if (self.sorting().inverse) {
                ret *= -1;
            }

            return ret;
        });
    }
});

DataGrid.Column = Class.extend({
    name: null,
    title: null,
    grid: null,
    classes: null,
    sortable: null,
    headCellTemplate: 'head-cell-template',
    bodyCellTemplate: 'body-cell-template',

    init: function(name, options) {
        this.name = name;

        var options = $.extend({
            title: name,
            sortable: true
        }, options, true);

        this.title = options.title;
        this.sortable = options.sortable;
    },

    setGrid: function(grid) {
        this.grid = grid;

        this.classes = ko.computed(function() {
            return {
                sortable: this.grid.sortable && this.sortable,
                sorting_asc: this.grid.sorting().column === this && this.grid.sorting().inverse === false,
                sorting_desc: this.grid.sorting().column === this && this.grid.sorting().inverse === true
            };
        }, this)
    },

    sort: function() {
        this.grid.sort(this);
    }
});

goog.require('goog.i18n.DateTimeFormat');
goog.require('goog.date');
DataGrid.DateColumn = DataGrid.Column.extend({
    bodyCellTemplate: 'date-body-cell-template',
    init: function() {
        this.formatter = new goog.i18n.DateTimeFormat(goog.i18n.DateTimeFormat.Format.MEDIUM_DATETIME);
        this['_super'].apply(this, arguments);
    },
    format: function(date) {
        if (!date) {
            return;
        }
        return this.formatter.format(goog.date.DateTime.fromRfc822String(date));
    }
});

DataGrid.SingleIconColumn = DataGrid.Column.extend({
    icon: null,
    headCellTemplate: 'single-icon-head-cell-template',
    bodyCellTemplate: 'single-icon-body-cell-template',
    init: function(name, icon, options) {
        this._super(name, options);
        this.icon = icon;
        this.sortable = false;
    }
});

DataGrid.Row = Class.extend({
    grid: null,
    textData: null,
    formData: null,
    data: null,
    savingForm: null,
    errors: null,
    isNew: null,

    init: function(data) {
        this.textData = ko.observable(data);
        this.formData = ko.observable(null);
        this.data = ko.computed(function() {
            if (this.formData() === null) {
                return this.textData();
            }
            return this.formData();
        }, this);
        this.editing = ko.computed(function() {
            return this.formData() !== null;
        }, this);
        this.savingForm = ko.observable(false);
        this.errors = ko.observable(false);
        this.hiddenFields = ko.computed(function() {
            if (null === this.formData()) {
                return "";
            }
            var hiddenFields = "";
            for (i in this.formData()) {
                if (!(i in this.textData())) {
                    hiddenFields += this.formData()[i];
                }
            }
            return hiddenFields;
        }, this);
        this.isNew = ko.observable(false);
    },

    setGrid: function(grid) {
        this.grid = grid;
    },

    isColumnForm: function(name) {
        return this.formData() && name in this.formData();
    },

    getColumnData: function(name) {
        if (name in this.data()) {
            return this.data()[name];
        }
        if (name in this.textData()) {
            return this.textData()[name];
        }
        //throw "The row data doesn't have a "+name+" field!";
        return '';
    },

    getSaveUrl: function() {
        return this.isNew() ? this.grid.createCreateUrl() : this.createEditUrl();
    },

    createEditUrl: function() {
        return Routing.generate(this.grid.editRoute, { id: this.getColumnData('id'), _format: this.grid.inline ? 'json' : 'html' } );
    },

    createDeleteUrl: function() {
        return Routing.generate(this.grid.deleteRoute, { id: this.getColumnData('id'), _format: 'json' } );
    },

    deleteRow: function() {
        var self = this;

        if (this.isNew()) {
            self.grid.rows.remove(self);
        } else {
            $.post(this.createDeleteUrl(), {}, function(data) {
                self.grid.rows.remove(self);
            });
        }
    },

    loadForm: function() {
        var self = this;
        $.get(this.getSaveUrl(), function(data) {
            self.createForm(data);
        });
    },

    createForm: function(data) {
        var formData = {};
        for (i in data.widgets) {
            formData[i] = $("<form>").html(data.widgets[i].error + data.widgets[i].widget).text();
        }
        this.formData(formData);
        if (data.errors) {
            this.errors($("<div>").html(data.errors).text());
        }
    },

    saveForm: function(row, event) {
        this.savingForm(true);
        this.errors(null);

        var $row = $(event.target).closest('tr');
        var self = this;
        var $form = $("<form>");
        $row.find(':input').each(function() {
            $form.append($(this).clone());
        });

        $.post(this.getSaveUrl(), $form.serialize(), function(data) {
            self.formData(null);
            self.savingForm(false);
            self.textData(data);

            self.isNew(false);
        }).fail(function(data) {
            if (data.status === 400) {
                self.savingForm(false);
                self.createForm($.parseJSON(data.responseText));
            }
        });
    }
});
<|endoftext|>


<commit_before>/*jshint -W089 */
(function($)
{
	var pluginName = 'moneyBehavior',
		defaults = {
			debug: false,
			selectOnFocus: true
		};

	var KEY_0 = 48;
	var KEY_1 = 49;
	var KEY_2 = 50;
	var KEY_3 = 51;
	var KEY_4 = 52;
	var KEY_5 = 53;
	var KEY_6 = 54;
	var KEY_7 = 55;
	var KEY_8 = 56;
	var KEY_9 = 57;

	var KEY_NUMPAD_0 = 96;
	var KEY_NUMPAD_1 = 97;
	var KEY_NUMPAD_2 = 98;
	var KEY_NUMPAD_3 = 99;
	var KEY_NUMPAD_4 = 100;
	var KEY_NUMPAD_5 = 101;
	var KEY_NUMPAD_6 = 102;
	var KEY_NUMPAD_7 = 103;
	var KEY_NUMPAD_8 = 104;
	var KEY_NUMPAD_9 = 105;

	var KEY_ENTER     = 13;
	var KEY_SHIFT     = 16;
	var KEY_CTRL      = 17;
	var KEY_ALT       = 18;
	var KEY_BACKSPACE = 8;
	var KEY_TAB       = 9;

	var KEY_ALPHA_NUMERIC_A = 65;
	var KEY_ALPHA_NUMERIC_C = 67;
	var KEY_ALPHA_NUMERIC_V = 86;
	var KEY_ALPHA_NUMERIC_X = 88;

	var numberKeys           = [KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9];
	var numericPadKeys       = [KEY_NUMPAD_0 , KEY_NUMPAD_1, KEY_NUMPAD_2, KEY_NUMPAD_3, KEY_NUMPAD_4, KEY_NUMPAD_5, KEY_NUMPAD_6, KEY_NUMPAD_7, KEY_NUMPAD_8, KEY_NUMPAD_9];
	var non_digit_valid_keys = [KEY_TAB, KEY_ENTER, KEY_SHIFT, KEY_CTRL, KEY_ALT];

	var numbers = numberKeys.concat(numericPadKeys);

	var control_keys = [KEY_ALPHA_NUMERIC_A, KEY_ALPHA_NUMERIC_C, KEY_ALPHA_NUMERIC_V, KEY_ALPHA_NUMERIC_X];

	function Plugin( element, options )
	{
		this.$element   = $(element);
		this.element    = element;
		this.options    = $.extend({}, defaults, options, this.$element.data()) ;
		this._defaults  = defaults;
		this._name      = pluginName;
		this.can_render = false;

		this.value = [];

		if (this.$element.attr("value") && this.$element.attr("value").length > 0) {
			this.value = this.$element.val().replace(/[^0-9]/g, "").split("");
		} else {
			this.$element.val("0,00");
		}

		init.call(this);
	}

	function init()
	{
		var self = this;

		if (this.options.selectOnFocus)
		{
			this.$element.on("click", function()
			{
				self.$element.select();
			});
		}

		this.$element.on("paste", function(event)
		{
			var pastedData = event.originalEvent.clipboardData.getData('text');
			var cleaned = pastedData.replace(/[^0-9]/g, "");

			if (cleaned.length > 0)
			{
				self.value = cleaned.split("");
				self.can_render = true;
				render.call(self);
			}

			event.preventDefault();
			return true;
		});

		this.$element.on("keydown", function(event)
		{
			var value = event.which;
			var meta = event.metaKey || event.ctrlKey;

			if (window.getSelection().toString() === self.$element.val()) {
				self.value = [];
			}

			if (is_digit(value))
			{
				_push.call(self, value);
			}
			else if (value === KEY_BACKSPACE)
			{
				_pop.call(self);
			}

			if (self.value === undefined)
			{
				self.value = [];
			}

			render.call(self);

			// prevent default action... but allow paste
			if(meta && control_keys.indexOf(value) !== -1) {
				return true;
			}
			else if (non_digit_valid_keys.indexOf( value ) !== -1) {
				return true;
			}
			else
			{
				event.preventDefault();
				return false;
			}
		});

		this.$element.on("blur", function()
		{
			self.can_render = true;
			render.call(self);
		});
	}

	function is_digit( value )
	{
		return numbers.indexOf( value ) !== -1;
	}

	function _pop()
	{
		this.can_render = true;

		return this.value.pop();
	}

	function _push( digit )
	{
		this.can_render = true;

		var ascii_math = 0;

		if (numberKeys.indexOf(digit) !== -1) {
			ascii_math = 48;
		}
		else if (numericPadKeys.indexOf(digit) !== -1) {
			ascii_math = 96;
		}

		this.value.push( digit - ascii_math );
	}

	function render()
	{
		if (!this.can_render) {
			return;
		}

		var v = 0.0;

		if (this.value.length > 0)
		{
			v = parseFloat(this.value.join(""));

			v = v / 100;

			v = _format( v );

			this.$element.val( v );
		}
		else
		{
			this.$element.val( "0,00" );
		}

		// after render, reset.
		this.can_render = false;
	}

	// http://blog.tompawlak.org/number-currency-formatting-javascript
	function _format(num)
	{
		return num
		   .toFixed(2)
		   .replace(".", ",")
		   .replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1.");
	}

	$.fn[pluginName] = function( options )
	{
		return this.each(function()
		{
			if (!$.data(this, 'plugin_' + pluginName)) {
				$.data(this, 'plugin_' + pluginName, new Plugin( this, options ));
			}
		});
	};

})(jQuery);<commit_msg>new line at the end<commit_after>/*jshint -W089 */
(function($)
{
	var pluginName = 'moneyBehavior',
		defaults = {
			debug: false,
			selectOnFocus: true
		};

	var KEY_0 = 48;
	var KEY_1 = 49;
	var KEY_2 = 50;
	var KEY_3 = 51;
	var KEY_4 = 52;
	var KEY_5 = 53;
	var KEY_6 = 54;
	var KEY_7 = 55;
	var KEY_8 = 56;
	var KEY_9 = 57;

	var KEY_NUMPAD_0 = 96;
	var KEY_NUMPAD_1 = 97;
	var KEY_NUMPAD_2 = 98;
	var KEY_NUMPAD_3 = 99;
	var KEY_NUMPAD_4 = 100;
	var KEY_NUMPAD_5 = 101;
	var KEY_NUMPAD_6 = 102;
	var KEY_NUMPAD_7 = 103;
	var KEY_NUMPAD_8 = 104;
	var KEY_NUMPAD_9 = 105;

	var KEY_ENTER     = 13;
	var KEY_SHIFT     = 16;
	var KEY_CTRL      = 17;
	var KEY_ALT       = 18;
	var KEY_BACKSPACE = 8;
	var KEY_TAB       = 9;

	var KEY_ALPHA_NUMERIC_A = 65;
	var KEY_ALPHA_NUMERIC_C = 67;
	var KEY_ALPHA_NUMERIC_V = 86;
	var KEY_ALPHA_NUMERIC_X = 88;

	var numberKeys           = [KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9];
	var numericPadKeys       = [KEY_NUMPAD_0 , KEY_NUMPAD_1, KEY_NUMPAD_2, KEY_NUMPAD_3, KEY_NUMPAD_4, KEY_NUMPAD_5, KEY_NUMPAD_6, KEY_NUMPAD_7, KEY_NUMPAD_8, KEY_NUMPAD_9];
	var non_digit_valid_keys = [KEY_TAB, KEY_ENTER, KEY_SHIFT, KEY_CTRL, KEY_ALT];

	var numbers = numberKeys.concat(numericPadKeys);

	var control_keys = [KEY_ALPHA_NUMERIC_A, KEY_ALPHA_NUMERIC_C, KEY_ALPHA_NUMERIC_V, KEY_ALPHA_NUMERIC_X];

	function Plugin( element, options )
	{
		this.$element   = $(element);
		this.element    = element;
		this.options    = $.extend({}, defaults, options, this.$element.data()) ;
		this._defaults  = defaults;
		this._name      = pluginName;
		this.can_render = false;

		this.value = [];

		if (this.$element.attr("value") && this.$element.attr("value").length > 0) {
			this.value = this.$element.val().replace(/[^0-9]/g, "").split("");
		} else {
			this.$element.val("0,00");
		}

		init.call(this);
	}

	function init()
	{
		var self = this;

		if (this.options.selectOnFocus)
		{
			this.$element.on("click", function()
			{
				self.$element.select();
			});
		}

		this.$element.on("paste", function(event)
		{
			var pastedData = event.originalEvent.clipboardData.getData('text');
			var cleaned = pastedData.replace(/[^0-9]/g, "");

			if (cleaned.length > 0)
			{
				self.value = cleaned.split("");
				self.can_render = true;
				render.call(self);
			}

			event.preventDefault();
			return true;
		});

		this.$element.on("keydown", function(event)
		{
			var value = event.which;
			var meta = event.metaKey || event.ctrlKey;

			if (window.getSelection().toString() === self.$element.val()) {
				self.value = [];
			}

			if (is_digit(value))
			{
				_push.call(self, value);
			}
			else if (value === KEY_BACKSPACE)
			{
				_pop.call(self);
			}

			if (self.value === undefined)
			{
				self.value = [];
			}

			render.call(self);

			// prevent default action... but allow paste
			if(meta && control_keys.indexOf(value) !== -1) {
				return true;
			}
			else if (non_digit_valid_keys.indexOf( value ) !== -1) {
				return true;
			}
			else
			{
				event.preventDefault();
				return false;
			}
		});

		this.$element.on("blur", function()
		{
			self.can_render = true;
			render.call(self);
		});
	}

	function is_digit( value )
	{
		return numbers.indexOf( value ) !== -1;
	}

	function _pop()
	{
		this.can_render = true;

		return this.value.pop();
	}

	function _push( digit )
	{
		this.can_render = true;

		var ascii_math = 0;

		if (numberKeys.indexOf(digit) !== -1) {
			ascii_math = 48;
		}
		else if (numericPadKeys.indexOf(digit) !== -1) {
			ascii_math = 96;
		}

		this.value.push( digit - ascii_math );
	}

	function render()
	{
		if (!this.can_render) {
			return;
		}

		var v = 0.0;

		if (this.value.length > 0)
		{
			v = parseFloat(this.value.join(""));

			v = v / 100;

			v = _format( v );

			this.$element.val( v );
		}
		else
		{
			this.$element.val( "0,00" );
		}

		// after render, reset.
		this.can_render = false;
	}

	// http://blog.tompawlak.org/number-currency-formatting-javascript
	function _format(num)
	{
		return num
		   .toFixed(2)
		   .replace(".", ",")
		   .replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1.");
	}

	$.fn[pluginName] = function( options )
	{
		return this.each(function()
		{
			if (!$.data(this, 'plugin_' + pluginName)) {
				$.data(this, 'plugin_' + pluginName, new Plugin( this, options ));
			}
		});
	};

})(jQuery);
<|endoftext|>


<commit_before>import { iconNode } from 'discourse/helpers/fa-icon';
import { createWidget } from 'discourse/widgets/widget';
import { h } from 'virtual-dom';

const MAX_GUTTER_LINKS = 5;

export default createWidget('post-gutter', {
  tagName: 'div.gutter',

  defaultState() {
    return { collapsed: true };
  },

  html(attrs, state) {
    const links = this.attrs.links || [];

    const result = [];
    let toShow = links.length;
    if (state.collapsed && toShow > MAX_GUTTER_LINKS) { toShow = MAX_GUTTER_LINKS; }

    const seenTitles = {};

    let i = 0;
    while (i < links.length && result.length < toShow) {
      const l = links[i++];

      let title = l.title;
      if (title && !seenTitles[title]) {
        seenTitles[title] = true;
        const linkBody = [Discourse.Emoji.unescape(title)];
        if (l.clicks) {
          linkBody.push(h('span.badge.badge-notification.clicks', l.clicks.toString()));
        }

        const link = h('a.track-link', { attributes: { href: l.url } }, linkBody);
        result.push(h('li', link));
      }
    }

    if (state.collapsed) {
      const remaining = links.length - MAX_GUTTER_LINKS;
      if (remaining > 0) {
        result.push(h('li', h('a.toggle-more', I18n.t('post.more_links', {count: remaining}))));
      }
    }

    if (attrs.canReplyAsNewTopic) {
      result.push(h('a.reply-new', [iconNode('plus'), I18n.t('post.reply_as_new_topic')]));
    }

    return h('ul.post-links', result);
  },

  click(e) {
    const $target = $(e.target);
    if ($target.hasClass('toggle-more')) {
      this.sendWidgetAction('showAll');
    } else if ($target.closest('.reply-new').length) {
      this.sendWidgetAction('newTopicAction');
    }
    return true;
  },

  showAll() {
    this.state.collapsed = false;
  }
});
<commit_msg>FIX: Emoji in the post gutter weren't being rendered properly<commit_after>import { iconNode } from 'discourse/helpers/fa-icon';
import { createWidget } from 'discourse/widgets/widget';
import { h } from 'virtual-dom';
import RawHtml from 'discourse/widgets/raw-html';

const MAX_GUTTER_LINKS = 5;

export default createWidget('post-gutter', {
  tagName: 'div.gutter',

  defaultState() {
    return { collapsed: true };
  },

  html(attrs, state) {
    const links = this.attrs.links || [];

    const result = [];
    let toShow = links.length;
    if (state.collapsed && toShow > MAX_GUTTER_LINKS) { toShow = MAX_GUTTER_LINKS; }

    const seenTitles = {};

    let i = 0;
    while (i < links.length && result.length < toShow) {
      const l = links[i++];

      let title = l.title;
      if (title && !seenTitles[title]) {
        seenTitles[title] = true;
        const linkBody = [new RawHtml({ html: `<span>${Discourse.Emoji.unescape(title)}</span>` })];
        if (l.clicks) {
          linkBody.push(h('span.badge.badge-notification.clicks', l.clicks.toString()));
        }

        const link = h('a.track-link', { attributes: { href: l.url } }, linkBody);
        result.push(h('li', link));
      }
    }

    if (state.collapsed) {
      const remaining = links.length - MAX_GUTTER_LINKS;
      if (remaining > 0) {
        result.push(h('li', h('a.toggle-more', I18n.t('post.more_links', {count: remaining}))));
      }
    }

    if (attrs.canReplyAsNewTopic) {
      result.push(h('a.reply-new', [iconNode('plus'), I18n.t('post.reply_as_new_topic')]));
    }

    return h('ul.post-links', result);
  },

  click(e) {
    const $target = $(e.target);
    if ($target.hasClass('toggle-more')) {
      this.sendWidgetAction('showAll');
    } else if ($target.closest('.reply-new').length) {
      this.sendWidgetAction('newTopicAction');
    }
    return true;
  },

  showAll() {
    this.state.collapsed = false;
  }
});
<|endoftext|>