<commit_before>var houndApi = require('houndjs');
var MHSDK = houndApi.models.MHSDK;
var MHObject = houndApi.models.MHObject;
var MHMedia = houndApi.models.MHMedia;
var MHSearch = houndApi.quickSearch;
var MHSourceModel = houndApi.models.MHSourceModel;

var buttonsForSources = function(sources) {
  var buttons = [];
  sources.forEach(function(source) {
    //we skip sources like rotten tomatoes that don't have mediums here
    if (source.mediums) {
      source.mediums.forEach(function(medium) {
        medium.methods.forEach(function(method) {
          var format = method.formats[0];
          var formattedPrice = '$' + format.price;
          var pitch;

          switch (medium.type){
            case 'download':
              if (method.type === 'purchase') {
                pitch = 'Buy from ' + formattedPrice;
              }
              else {
                pitch = 'Rent from ' + formattedPrice;
              }

              break;
            case 'stream':
              if (method.type === 'adSupported') {
                pitch = 'Watch with ads';
              }
              else if (method.type === 'subscription') {
                if (source.name === 'Hulu') {
                  pitch = 'Watch with Plus';
                }
                else {
                  pitch = 'Watch Now';
                }
              }
              else if (method.type === 'purchase') {
                pitch = 'Buy from ' + formattedPrice;
              }
              else if (method.type === 'rental') {
                pitch = 'Rent from ' + formattedPrice;
              }

              break;

            case 'deliver':
              pitch = 'Watch on ' + format.type;
              break;

            default:
              pitch = 'Watch It';
              break;
          }
          buttons.push({
            pitch: pitch,
            url: format.launchInfo.view.http,
            source: source
          });
        });
      });
    }
  });

  return buttons;
};

var sourceButtonsForMHObject = function(obj) {
  return obj
    .fetchSources()
    .then(buttonsForSources);
};

var relatedForMHObjects = function(firstObj, secondObj) {
  return MHMedia
    .fetchRelatedTo([firstObj, secondObj])
    .currentPromise
    .then(function(res) {
      return res.content.map(function(pair) {
        return pair.object;
      });
    });
};

var mediaForContributor = function(contributor) {
  return contributor
    .fetchMedia()
    .currentPromise
    .then(function(res) {
      return res.content.map(function(pair) {
        return pair.object;
      });
    });
};

var search = function(query, mediaType) {
  return MHSearch.type(query, 10, mediaType)
    .then(function(results) {
      if (results.length < 1) {
        throw 'not found';
      }
      else {
        return results;
      }
    });
};

var luckySearch = function(query, mediaType) {
  return search(query, mediaType)
    .then(function(results) {
      return results[0];
    });
};

var configure = function() {
  return MHSDK.configure(
    'mhclt_fMediaCLI',
    'IzyHwiU9t1n0hJtst10AKd6mW5QDFJAByBGdoTEXn9JXjI9m'
  );
};

module.exports = {
  configure: configure,
  search: search,
  luckySearch: luckySearch,
  sourceButtonsForMHObject: sourceButtonsForMHObject,
  buttonsForSources: buttonsForSources,
  relatedForMHObjects: relatedForMHObjects,
  mediaForContributor: mediaForContributor
};
<commit_msg>Update source parser for new cases<commit_after>var houndApi = require('houndjs');
var MHSDK = houndApi.models.MHSDK;
var MHObject = houndApi.models.MHObject;
var MHMedia = houndApi.models.MHMedia;
var MHSearch = houndApi.quickSearch;
var MHSourceModel = houndApi.models.MHSourceModel;

var buttonsForSources = function(sources) {
  var buttons = [];
  sources.forEach(function(source) {
    //we skip sources like rotten tomatoes that don't have mediums here
    if (source.mediums) {
      source.mediums.forEach(function(medium) {
        medium.methods.forEach(function(method) {
          var format = method.formats[0];
          var formattedPrice = '$' + format.price;
          var pitch;

          var isSeries = false;
          var countInfo;
          if (format.contentCount) {
            if (format.contentCount === 1) {
              countInfo = format.contentCount + " season";
            }
            else {
              countInfo = format.contentCount + " seasons";
            }
          }
          else {
            countInfo = "";
          }

          switch(medium.type){
            case "download":
              if(method.type === "purchase"){
                if (isSeries) {
                  pitch = "Buy " + countInfo;
                }
                else {
                  pitch = "Buy from " + formattedPrice;
                }
              } else {
                if (isSeries) {
                  pitch = "Rent " + countInfo;
                }
                else {
                  pitch = "Rent from " + formattedPrice;
                }
              }
              break;
            case "stream":
              if(method.type === "adSupported"){
                if (isSeries) {
                  pitch = "Watch " + countInfo;
                }
                else {
                  pitch = "Watch with ads";
                }
              } else if (method.type === "subscription") {
                if (source.name === "Hulu") {
                  if (isSeries) {
                    pitch = "Watch " + countInfo;
                  }
                  else {
                    pitch = "Watch with Plus";
                  }
                }
                else {
                  if (isSeries) {
                    pitch = "Watch " + countInfo;
                  }
                  else {
                    pitch = "Watch Now";
                  }
                }
              } else if (method.type === "purchase"){
                if (isSeries) {
                  pitch = "Buy " + countInfo;
                }
                else {
                  pitch = "Buy from " + formattedPrice;
                }
              } else if (method.type === "rental" || method.type === "rent"){
                if (isSeries) {
                  pitch = "Rent " + countInfo;
                }
                else {
                  pitch = "Rent from " + formattedPrice;
                }
              } else if (method.type === "broker"){
                if (isSeries) {
                  pitch = "Watch " + countInfo;
                }
                else {
                  pitch = "Watch Now";
                }
              }

              break;

            case "deliver":
              if (isSeries) {
                pitch = "Watch " + countInfo;
              }
              else {
                pitch = "Watch on " + format.type;
              }
              break;

            case "pickup":
              if(method.type === "rental" || method.type === "rent"){
                if (isSeries) {
                  pitch = "Rent " + countInfo;
                }
                else {
                  pitch = "Rent for " + formattedPrice;
                }
              } else if(method.type === "purchase"){
                pitch = "Buy tickets";
              }
              break;

            default:
              if (isSeries) {
                pitch = "Watch " + countInfo;
              }
              else {
                pitch = "Watch It";
              }
              break;

          }
          buttons.push({
            pitch: pitch,
            url: format.launchInfo.view.http,
            source: source
          });
        });
      });
    }
  });

  return buttons;
};

var sourceButtonsForMHObject = function(obj) {
  return obj
    .fetchSources()
    .then(buttonsForSources);
};

var relatedForMHObjects = function(firstObj, secondObj) {
  return MHMedia
    .fetchRelatedTo([firstObj, secondObj])
    .currentPromise
    .then(function(res) {
      return res.content.map(function(pair) {
        return pair.object;
      });
    });
};

var mediaForContributor = function(contributor) {
  return contributor
    .fetchMedia()
    .currentPromise
    .then(function(res) {
      return res.content.map(function(pair) {
        return pair.object;
      });
    });
};

var search = function(query, mediaType) {
  return MHSearch.type(query, 10, mediaType)
    .then(function(results) {
      if (results.length < 1) {
        throw 'not found';
      }
      else {
        return results;
      }
    });
};

var luckySearch = function(query, mediaType) {
  return search(query, mediaType)
    .then(function(results) {
      return results[0];
    });
};

var configure = function() {
  return MHSDK.configure(
    'mhclt_fMediaCLI',
    'IzyHwiU9t1n0hJtst10AKd6mW5QDFJAByBGdoTEXn9JXjI9m'
  );
};

module.exports = {
  configure: configure,
  search: search,
  luckySearch: luckySearch,
  sourceButtonsForMHObject: sourceButtonsForMHObject,
  buttonsForSources: buttonsForSources,
  relatedForMHObjects: relatedForMHObjects,
  mediaForContributor: mediaForContributor
};
<|endoftext|>


<commit_before>need([
    "lib.LSystem",
    "lib.Task",
    "lib.ProcGeom"
], function(){

function CustomAttribute( buffer, itemSize ){
    THREE.BufferAttribute.call( this, buffer, itemSize );
}

CustomAttribute.prototype = THREE.Float32BufferAttribute.prototype;

CLAZZ("cmp.ThreeTreeGen", {

    INJECT:['entity', 'asset', 'seed', 'iterations', 'source', 'tiles', 'amount', 'ground', 'spread'],

    '@hidePlaceholder':{type:'bool'},
    hidePlaceholder:true,

    '@amount':{type:'int', min:1},
    amount:1,

    '@ground':{type:'node', test:{gt:{amount:1}} },
    ground:null,

    '@spread':{type:'float', test:{gt:{amount:1}} },
    spread:10,

    "@seed":{type:"int"},
    seed:0xDEADBEEF,

    '@iterations':{ type:'int', min:1, max:20 },
    iterations:5,

    '@source':{ type:'longstring' },
    source: '',

    '@tiles':{ type:'vec2i', min:1 },
    tiles:{x:1, y:1},

    asset:null,

    create: function(){
        if( this.hidePlaceholder )
            this.asset.visible = false;

        cmp.ThreeTreeGen.Service.generate( this, true );
    },

    editorAsset:null,
    onPreviewComplete:null,
    preview: function( editorAsset, callback ){
        this.editorAsset = editorAsset;
        this.onPreviewComplete = callback;
        cmp.ThreeTreeGen.Service.generate( this, false );
    },

    '@_onGenerate':{__hidden:true},
    _onGenerate: function( mesh )
    {
        if( !mesh || !mesh.position )
            return;

        var geometry = clone.apply( this.asset.geometry ); // new THREE.BoxBufferGeometry();

        var node = this.asset;

        geometry.setIndex(null);
        geometry.addAttribute('position', new CustomAttribute( mesh.position, 3 ) );
        geometry.addAttribute('uv', new CustomAttribute( mesh.uv, 2 ) );
        geometry.addAttribute('normal', new CustomAttribute( mesh.normal, 3 ) );
        geometry.addAttribute('color', new CustomAttribute( mesh.color, 3 ) );

        var oldGeometry = this.asset.geometry;
        if( oldGeometry && oldGeometry.dispose )
            oldGeometry.dispose();

        if( this.hidePlaceholder )
            this.asset.visible = true;

        node.geometry = geometry;
        if( this.entity ){
            this.entity.setPosition(0,0,0);
            this.entity.setRotation(0,0,0);
            this.entity.setScale(1,1,1);

            if( this.entity.onGenerate )
                this.entity.onGenerate();
        } else {
            this.onPreviewComplete();
        }

        function clone(){
            var parameters = this.parameters;

            if ( parameters !== undefined ) {

                var values = [];

                for ( var key in parameters ) {

                    values.push( parameters[ key ] );

                }

                var geometry = Object.create( this.constructor.prototype );
                this.constructor.apply( geometry, values );
                return geometry;

            }

            return new this.constructor().copy( this );
        }
    }
});

CLAZZ("cmp.ThreeTreeGen.Service", {

    taskman:null,

    CONSTRUCTOR:function(){
        var root = window.ROOT_PATH || "";
        this.taskman = new lib.Task([
            root + "lib/CLAZZ.js",
            root + "lib/doc.js",
            root + "lib/three.js",
            root + "lib/LSystem.js",
            root + "lib/ProcGeom.js",
            root + "lib/mersenne-twister.js"
        ]);
    },

    generate:function( tree, applyTransform ){
        var node = tree.asset || tree.entity.getNode();
        var transfer = [], list = [], params = {
            iterations: tree.iterations,
            source: tree.source,
            seed: tree.seed,
            lod:0,
            tiles: tree.tiles
        };

        var a = 0;
        for( var i=0; i<tree.amount; ++i ){
            var transform = new THREE.Matrix4();

            var r = Math.pow(1+i/tree.amount, 2) * tree.spread;
            var cosa = Math.cos(a * Math.PI * 0.2) * r;
            var sina = Math.sin(a * Math.PI * 0.2) * r;
            a += 1.618033;

            transform.makeTranslation(sina, 0, cosa);

            if( applyTransform )
                transform.multiplyMatrices( node.matrixWorld, transform );

            list[i] = transform.elements;
            transfer[i] = transform.elements.buffer;
        }

        this.taskman.call( this._generate, [params, list], transfer, tree._onGenerate.bind( tree ) );
    },

    _generate:function( params, list ){
        // debugger;

        var treeCtx = {
            treeCfg:{
                scale:3,
                tree:null
            },

            PUSH:function(){
                this.treeCfg.tree.push("rotX", "rotY", "rotZ");
            },

            POP:function(){
                this.treeCfg.tree.pop();
            },

            RX:function( min, max ){
                var tmp = this.procgeom.rnd(min,max);
                this.treeCfg.tree.rotateX( tmp )
                this.treeCfg.tree.data.rotX += tmp;
            },

            RY:function( min, max ){
                var tmp = this.procgeom.rnd(min,max);
                this.treeCfg.tree.rotateY( tmp )
                this.treeCfg.tree.data.rotY += tmp;
            },

            RZ:function( min, max ){
                var tmp = this.procgeom.rnd(min,max);
                this.treeCfg.tree.rotateZ( tmp )
                this.treeCfg.tree.data.rotZ += tmp;
            },

            RGB:function(r,g,b){
	            this.treeCfg.tree.color(r,g,b);
            },

            BEGINTREE:function( cfg ){
                var def = {
                    scale:1,
                    trunkSegments:3,
                    trunkLength:20,
                    trunkWidth:2,
                    trunkRound:1,
                    trunkTwist:this.procgeom.rnd(-20,20),
                    trunkTile:1,

                    leafLength:7,
                    leafWidth:7,
                    leafShape:0,
                    leafColorR:1,
                    leafColorG:1,
                    leafColorB:1,
                    leafDetailMin:3,
                    leafDetailMax:10,
                    leafRoundMin:0.0005,
                    leafRoundMax:0.004,
                    leafTile:2
                };

                cfg = Object.assign(this.treeCfg, def, cfg);

                this.treeCfg.tree = this.procgeom.pointSet({ width:this.treeCfg.scale*(this.treeCfg.trunkWidth), ring:1, data:{
                    length:this.treeCfg.scale*this.treeCfg.trunkLength,
                    seg:this.treeCfg.trunkSegments,
                    rotX:0,
                    rotY:0,
                    rotZ:0,
                    twist:this.treeCfg.trunkTwist
                } })
                .setId( this.treeCfg.trunkTile )
                .rotateY(this.procgeom.rnd(0, 360))
            },

            LEAF:function( leafCfg ){
                var cfg = Object.assign({}, this.treeCfg, leafCfg);
                var length = cfg.leafLength * cfg.scale;

                var tree = this.treeCfg.tree;
                var detail = (1-this.procgeom.lod) * (cfg.leafDetailMax - cfg.leafDetailMin) + cfg.leafDetailMin;

                tree
                    .rotateZ(-tree.data.rotZ)
                    .rotateY( (tree.data.rotX+tree.data.rotZ>0?180-tree.data.rotX:-90))
                    .rotateX(-30)
                    .setDetail( cfg.leafDetailMin, cfg.leafDetailMax)
                    .setWidth(0);

                var ring = this.procgeom.rnd( cfg.leafRoundMin, cfg.leafRoundMax );

                for( var i=0; i<detail; ++i ){
                    var it = (i+1) / (detail);

                    switch( cfg.leafShape ){
                    case 0: // round
                        tree = tree.pointSet()
                            .color( cfg.leafColorR, cfg.leafColorG, cfg.leafColorB )
                            .setWidth( Math.sin(it*Math.PI) * cfg.leafWidth * cfg.scale )
                            .setRing( ring )
                            .setId( cfg.leafTile + 0.999 - it*0.998 )
                            .translate( 0, length / (detail - 1), 0 );

                    break;

                    default:

                    }

                }

            },

            TRUNK: function(){
                var tree = this.treeCfg.tree;
                var width = this.procgeom.rnd(0.7, 0.9);
                if( !tree.data.seg )
                    tree.data.seg = 1;
                var base = tree, seg = tree.data.seg;
                var len = base.data.length * this.procgeom.rnd(0.4, 0.6);
                var uv = Math.floor(base.uvId), z=this.procgeom.rnd(-10,10), x=this.procgeom.rnd(-10,10);
                for( var i=0; i<seg; ++i ){
                    tree = tree
                        .pointSet()
                        .mulWidth( width )
                        .setId( uv + i/seg )
                        .translate(0, len / seg, 0)
                        .rotateX(x)
                        .rotateY(base.data.twist)
                        .rotateZ(z)
                        .setDetail( undefined, 3+tree.width*5 )
                        .set("length", len)
                        .set("base", base);
                    tree.data.rotY += base.data.twist;
                    tree.data.rotZ += z;
                    tree.data.rotX += x;
                }
                tree.setId(uv);
                this.treeCfg.tree = tree;
            },

            ENDTRUNK: function(){
                this.treeCfg.tree = this.treeCfg.tree.data.base;
            }
        };

        var lsys = new lib.LSystem();

        lsys.source("tree",`
        [ -> PUSH();

        ] -> POP();

        { -> TRUNK();

        } -> ENDTRUNK();
        `);

        var MT = new MersenneTwister( params.seed );
        lsys.random = MT.random.bind(MT);

        var proc = new lib.ProcGeom( params.lod, params.tiles, lsys.random );
        var keys = Object.keys( lib.ProcGeom.methods );
        var values = keys.map( k => proc[k].bind(proc) );
        var ctxkeys = Object.keys( treeCtx );
        values = values.concat( ctxkeys.map(
            k =>
            typeof treeCtx[k] == "function" ?
                treeCtx[k].bind(treeCtx) :
                treeCtx[k]
        ));
        treeCtx.procgeom = proc;

        keys = keys.concat(ctxkeys);

        keys.unshift( null );

        try {
            lsys.source( params.source );

            for( var i=0; i<list.length; i++ ){
                var code = lsys.generate( params.iterations );
                keys.push( code );

                proc.transform.elements.set( list[i] );
                func = new (Function.bind.apply( Function, keys ));
                var ret = func.apply( null, values );
                if( ret !== undefined )
                    console.log( ret );

                keys.pop();
            }
            var mesh = proc._build();
        } catch( ex ) {
            if( lsys.debug || lsys.log )
                console.warn( ex.stack );

            if( lsys.debug )
                (function(){debugger;})();

            return null;
        }

        return new REPLY( new NESTED(mesh) );
    },

    STATIC:{
        instance:null,
        generate:function( tree, applyTransform ){
            if( !this.instance )
                this.instance = new cmp.ThreeTreeGen.Service();
            this.instance.generate( tree, applyTransform );
        }
    }

});

});<commit_msg>put trees on ground<commit_after>need([
    "lib.LSystem",
    "lib.Task",
    "lib.ProcGeom"
], function(){

function CustomAttribute( buffer, itemSize ){
    THREE.BufferAttribute.call( this, buffer, itemSize );
}

CustomAttribute.prototype = THREE.Float32BufferAttribute.prototype;

CLAZZ("cmp.ThreeTreeGen", {

    INJECT:['entity', 'game', 'asset', 'seed', 'iterations', 'source', 'tiles', 'amount', 'ground', 'spread'],

    '@hidePlaceholder':{type:'bool'},
    hidePlaceholder:true,

    '@amount':{type:'int', min:1},
    amount:1,

    '@ground':{type:'node', test:{gt:{amount:1}} },
    ground:null,

    '@spread':{type:'float', test:{gt:{amount:1}} },
    spread:10,

    "@seed":{type:"int"},
    seed:0xDEADBEEF,

    '@iterations':{ type:'int', min:1, max:20 },
    iterations:5,

    '@source':{ type:'longstring' },
    source: '',

    '@tiles':{ type:'vec2i', min:1 },
    tiles:{x:1, y:1},

    asset:null,

    create: function(){
        if( this.hidePlaceholder )
            this.asset.visible = false;

        cmp.ThreeTreeGen.Service.generate( this, true );
    },

    editorAsset:null,
    onPreviewComplete:null,
    preview: function( editorAsset, callback ){
        this.editorAsset = editorAsset;
        this.onPreviewComplete = callback;
        cmp.ThreeTreeGen.Service.generate( this, false );
    },

    '@_onGenerate':{__hidden:true},
    _onGenerate: function( mesh )
    {
        if( !mesh || !mesh.position )
            return;

        var geometry = clone.apply( this.asset.geometry ); // new THREE.BoxBufferGeometry();

        var node = this.asset;

        geometry.setIndex(null);
        geometry.addAttribute('position', new CustomAttribute( mesh.position, 3 ) );
        geometry.addAttribute('uv', new CustomAttribute( mesh.uv, 2 ) );
        geometry.addAttribute('normal', new CustomAttribute( mesh.normal, 3 ) );
        geometry.addAttribute('color', new CustomAttribute( mesh.color, 3 ) );

        var oldGeometry = this.asset.geometry;
        if( oldGeometry && oldGeometry.dispose )
            oldGeometry.dispose();

        if( this.hidePlaceholder )
            this.asset.visible = true;

        node.geometry = geometry;
        if( this.entity ){
            this.entity.setPosition(0,0,0);
            this.entity.setRotation(0,0,0);
            this.entity.setScale(1,1,1);

            if( this.entity.onGenerate )
                this.entity.onGenerate();
        } else {
            this.onPreviewComplete();
        }

        function clone(){
            var parameters = this.parameters;

            if ( parameters !== undefined ) {

                var values = [];

                for ( var key in parameters ) {

                    values.push( parameters[ key ] );

                }

                var geometry = Object.create( this.constructor.prototype );
                this.constructor.apply( geometry, values );
                return geometry;

            }

            return new this.constructor().copy( this );
        }
    }
});

CLAZZ("cmp.ThreeTreeGen.Service", {

    taskman:null,

    CONSTRUCTOR:function(){
        var root = window.ROOT_PATH || "";
        this.taskman = new lib.Task([
            root + "lib/CLAZZ.js",
            root + "lib/doc.js",
            root + "lib/three.js",
            root + "lib/LSystem.js",
            root + "lib/ProcGeom.js",
            root + "lib/mersenne-twister.js"
        ]);
    },

    generate:function( tree, applyTransform ){
        var node = tree.asset || tree.entity.getNode();
        var ground, groundGeometry, raycaster, groundBox, raycasterNormal, groundSide, scene;

        if( tree.game ){
            scene = tree.game.scene;
        }else if( self.editor ){
            scene = self.editor.scene;
        }

        if( tree.ground ){
            ground = scene.getObjectByProperty( 'uuid', tree.ground );
            if( ground ){
                if( ground.getHeightAtXZ ){
                    // nop
                }else if( ground.geometry ){
                    groundGeometry = ground.geometry;
                    if( !groundGeometry.boundingBox )
                        groundGeometry.computeBoundingBox();
                    groundBox = groundGeometry.boundingBox;
                    raycaster = new THREE.Raycaster();
                    raycasterNormal = new THREE.Vector3(0,1,0);
                    groundSide = ground.material.side;
                    ground.material.side = THREE.DoubleSide;
                }else
                    ground = null;
            }
        }

        var transfer = [], list = [], params = {
            iterations: tree.iterations,
            source: tree.source,
            seed: tree.seed,
            lod:0,
            tiles: tree.tiles
        };

        var a = 0, worldTransform = new THREE.Matrix4();
        for( var i=0; i<tree.amount; ++i ){
            var transform = new THREE.Matrix4();

            var r = Math.pow(1+i/tree.amount, 2) * tree.spread;
            var cosa = Math.cos(a * Math.PI * 0.2) * r;
            var sina = Math.sin(a * Math.PI * 0.2) * r;
            a += 1.618033;

            transform.makeTranslation(sina, 0, cosa);

            if( applyTransform ){
                transform.multiplyMatrices( node.matrixWorld, transform );
                worldTransform.copy( transform );
            }else{
                worldTransform.multiplyMatrices( node.matrixWorld, transform );
            }

            if( ground ){
                var y;
                if( groundGeometry ){
                    raycaster.set( new THREE.Vector3(
                        worldTransform.elements[12],
                        groundBox.min.y * ground.scale.y + ground.position.y,
                        worldTransform.elements[14]
                    ), raycasterNormal );

                    var intersects = raycaster.intersectObject( ground );
                    if( intersects && intersects.length ){
                        y = intersects[0].point.y;
                    }else{
                        y = worldTransform.elements[13];
                    }

                }else if( ground ){
                    y = ground.getHeightAtXZ( worldTransform[12], worldTransform[14] );
                }
                transform.elements[13] = y;
            }

            list[i] = transform.elements;
            transfer[i] = transform.elements.buffer;
        }

        if( groundGeometry ){
            ground.material.side = groundSide;
        }

        this.taskman.call( this._generate, [params, list], transfer, tree._onGenerate.bind( tree ) );
    },

    _generate:function( params, list ){
        // debugger;

        var treeCtx = {
            treeCfg:{
                scale:3,
                tree:null
            },

            PUSH:function(){
                this.treeCfg.tree.push("rotX", "rotY", "rotZ");
            },

            POP:function(){
                this.treeCfg.tree.pop();
            },

            RX:function( min, max ){
                var tmp = this.procgeom.rnd(min,max);
                this.treeCfg.tree.rotateX( tmp )
                this.treeCfg.tree.data.rotX += tmp;
            },

            RY:function( min, max ){
                var tmp = this.procgeom.rnd(min,max);
                this.treeCfg.tree.rotateY( tmp )
                this.treeCfg.tree.data.rotY += tmp;
            },

            RZ:function( min, max ){
                var tmp = this.procgeom.rnd(min,max);
                this.treeCfg.tree.rotateZ( tmp )
                this.treeCfg.tree.data.rotZ += tmp;
            },

            RGB:function(r,g,b){
	            this.treeCfg.tree.color(r,g,b);
            },

            BEGINTREE:function( cfg ){
                var def = {
                    scale:1,
                    trunkSegments:3,
                    trunkLength:20,
                    trunkWidth:2,
                    trunkRound:1,
                    trunkTwist:this.procgeom.rnd(-20,20),
                    trunkTile:1,

                    leafLength:7,
                    leafWidth:7,
                    leafShape:0,
                    leafColorR:1,
                    leafColorG:1,
                    leafColorB:1,
                    leafDetailMin:3,
                    leafDetailMax:10,
                    leafRoundMin:0.0005,
                    leafRoundMax:0.004,
                    leafTile:2
                };

                cfg = Object.assign(this.treeCfg, def, cfg);

                this.treeCfg.tree = this.procgeom.pointSet({ width:this.treeCfg.scale*(this.treeCfg.trunkWidth), ring:1, data:{
                    length:this.treeCfg.scale*this.treeCfg.trunkLength,
                    seg:this.treeCfg.trunkSegments,
                    rotX:0,
                    rotY:0,
                    rotZ:0,
                    twist:this.treeCfg.trunkTwist
                } })
                .setId( this.treeCfg.trunkTile )
                .rotateY(this.procgeom.rnd(0, 360))
            },

            LEAF:function( leafCfg ){
                var cfg = Object.assign({}, this.treeCfg, leafCfg);
                var length = cfg.leafLength * cfg.scale;

                var tree = this.treeCfg.tree;
                var detail = (1-this.procgeom.lod) * (cfg.leafDetailMax - cfg.leafDetailMin) + cfg.leafDetailMin;

                tree
                    .rotateZ(-tree.data.rotZ)
                    .rotateY( (tree.data.rotX+tree.data.rotZ>0?180-tree.data.rotX:-90))
                    .rotateX(-30)
                    .setDetail( cfg.leafDetailMin, cfg.leafDetailMax)
                    .setWidth(0);

                var ring = this.procgeom.rnd( cfg.leafRoundMin, cfg.leafRoundMax );

                for( var i=0; i<detail; ++i ){
                    var it = (i+1) / (detail);

                    switch( cfg.leafShape ){
                    case 0: // round
                        tree = tree.pointSet()
                            .color( cfg.leafColorR, cfg.leafColorG, cfg.leafColorB )
                            .setWidth( Math.sin(it*Math.PI) * cfg.leafWidth * cfg.scale )
                            .setRing( ring )
                            .setId( cfg.leafTile + 0.999 - it*0.998 )
                            .translate( 0, length / (detail - 1), 0 );

                    break;

                    default:

                    }

                }

            },

            TRUNK: function(){
                var tree = this.treeCfg.tree;
                var width = this.procgeom.rnd(0.7, 0.9);
                if( !tree.data.seg )
                    tree.data.seg = 1;
                var base = tree, seg = tree.data.seg;
                var len = base.data.length * this.procgeom.rnd(0.4, 0.6);
                var uv = Math.floor(base.uvId), z=this.procgeom.rnd(-10,10), x=this.procgeom.rnd(-10,10);
                for( var i=0; i<seg; ++i ){
                    tree = tree
                        .pointSet()
                        .mulWidth( width )
                        .setId( uv + i/seg )
                        .translate(0, len / seg, 0)
                        .rotateX(x)
                        .rotateY(base.data.twist)
                        .rotateZ(z)
                        .setDetail( undefined, 3+tree.width*5 )
                        .set("length", len)
                        .set("base", base);
                    tree.data.rotY += base.data.twist;
                    tree.data.rotZ += z;
                    tree.data.rotX += x;
                }
                tree.setId(uv);
                this.treeCfg.tree = tree;
            },

            ENDTRUNK: function(){
                this.treeCfg.tree = this.treeCfg.tree.data.base;
            }
        };

        var lsys = new lib.LSystem();

        lsys.source("tree",`
        [ -> PUSH();

        ] -> POP();

        { -> TRUNK();

        } -> ENDTRUNK();
        `);

        var MT = new MersenneTwister( params.seed );
        lsys.random = MT.random.bind(MT);

        var proc = new lib.ProcGeom( params.lod, params.tiles, lsys.random );
        var keys = Object.keys( lib.ProcGeom.methods );
        var values = keys.map( k => proc[k].bind(proc) );
        var ctxkeys = Object.keys( treeCtx );
        values = values.concat( ctxkeys.map(
            k =>
            typeof treeCtx[k] == "function" ?
                treeCtx[k].bind(treeCtx) :
                treeCtx[k]
        ));
        treeCtx.procgeom = proc;

        keys = keys.concat(ctxkeys);

        keys.unshift( null );

        try {
            lsys.source( params.source );

            for( var i=0; i<list.length; i++ ){
                var code = lsys.generate( params.iterations );
                keys.push( code );

                proc.transform.elements.set( list[i] );
                func = new (Function.bind.apply( Function, keys ));
                var ret = func.apply( null, values );
                if( ret !== undefined )
                    console.log( ret );

                keys.pop();
            }
            var mesh = proc._build();
        } catch( ex ) {
            if( lsys.debug || lsys.log )
                console.warn( ex.stack );

            if( lsys.debug )
                (function(){debugger;})();

            return null;
        }

        return new REPLY( new NESTED(mesh) );
    },

    STATIC:{
        instance:null,
        generate:function( tree, applyTransform ){
            if( !this.instance )
                this.instance = new cmp.ThreeTreeGen.Service();
            this.instance.generate( tree, applyTransform );
        }
    }

});

});<|endoftext|>


<commit_before>"use strict";

module.exports = Model;

/**
 * Constructor.
 *
 * @param model - The mongoose model that is being restified.
 */
function Model(restifier, model) {
  this.restifier = restifier;
  this.model = model;

  // Restricted fields
  var restricted = this.restricted = [];
  model.schema.eachPath(function(pathName, path) {
    if (path.options.restricted) {
      restricted.push(pathName);
    }
  });

  this.constraints = [];
  this.filters = {};
}

/**
 * Constrains a query parameter.
 *
 * @param field - The field to constrain
 * @paran fn(req, num) - The constraint function.
 */
Model.prototype.constrain = function(field, fn) {
  this.constraints.push({
    field: field,
    fn: fn
  });
  return this;
};

/**
 * Adds a filter to this model.
 *
 * @param name - The name of the filter.
 * @param filter(req, query, params...) - The filter function that will be run on queries.
 */
Model.prototype.filter = function(name, filter) {
  this.filters[name] = filter;
  return this;
};

/**
 * Serves this model on a RESTful API.
 *
 * @param app - The Express app to serve the model on.
 */
Model.prototype.serve = function(app) {
  var thiz = this;
  var model = this.model;

  var uriBase = '/' + model.collection.name;

  app.get(this.restifier.prefix + uriBase, function(req, res) {
    var query = model.find();
    var reqQuery = req.query;

    // Fields
    try {
      model.schema.eachPath(function(name) {
        if (!reqQuery.hasOwnProperty(name)) {
          return;
        }
        var val = reqQuery[name];

        if (thiz.restricted.indexOf(name) !== -1) {
          var err = new Error('Field is restricted.');
          err.status = 401;
          throw err;
        }

        query.where(name).equals(val);
      });
    } catch (err) {
      return res.error(err.status, err.message);
    }

    query.exec(function(err, docs) {
      res.json(docs);
    });
  });

  return this;
};
<commit_msg>Use nicer looking error syntax<commit_after>"use strict";

module.exports = Model;

/**
 * Constructor.
 *
 * @param model - The mongoose model that is being restified.
 */
function Model(restifier, model) {
  this.restifier = restifier;
  this.model = model;

  // Restricted fields
  var restricted = this.restricted = [];
  model.schema.eachPath(function(pathName, path) {
    if (path.options.restricted) {
      restricted.push(pathName);
    }
  });

  this.constraints = [];
  this.filters = {};
}

/**
 * Constrains a query parameter.
 *
 * @param field - The field to constrain
 * @paran fn(req, num) - The constraint function.
 */
Model.prototype.constrain = function(field, fn) {
  this.constraints.push({
    field: field,
    fn: fn
  });
  return this;
};

/**
 * Adds a filter to this model.
 *
 * @param name - The name of the filter.
 * @param filter(req, query, params...) - The filter function that will be run on queries.
 */
Model.prototype.filter = function(name, filter) {
  this.filters[name] = filter;
  return this;
};

/**
 * Serves this model on a RESTful API.
 *
 * @param app - The Express app to serve the model on.
 */
Model.prototype.serve = function(app) {
  var thiz = this;
  var model = this.model;

  var uriBase = '/' + model.collection.name;

  app.get(this.restifier.prefix + uriBase, function(req, res) {
    var query = model.find();
    var reqQuery = req.query;

    // Fields
    try {
      model.schema.eachPath(function(name) {
        if (!reqQuery.hasOwnProperty(name)) {
          return;
        }
        var val = reqQuery[name];

        if (thiz.restricted.indexOf(name) !== -1) {
          throw res.error(401, 'Cannot access restrictd field "' + name + '".');
        }

        query.where(name).equals(val);
      });
    } catch (err) {
      return;
    }

    query.exec(function(err, docs) {
      res.json(docs);
    });
  });

  return this;
};
<|endoftext|>


<commit_before>/**
 * model
 *
 * Defines a model that represents a discrete blob of data.
 *
 * The model should expose a few methods for finding and manipulating unique
 * model instances but should also be able to create and return an instance of
 * a specific model to work with.
 */

var util = require( 'util' ),
    EventEmitter = require( 'EventEmitter3' ),
    Promise = require( 'es6-promise' ).Promise,
    _ = require( 'lodash-node' );

/**
 * Model Constructor function
 *
 * @param opts {object}
 *    schema {Schema} the schema for the model
 */
var Model = module.exports = function( opts ) {

    if ( !opts ) {
        throw new Error( 'Models should be defined by a schema' );
    }

    this.opts = opts;
    this.id = opts.id;
    this.schema = opts.schema;
};

_.extend( Model.prototype, {

    /**
     * The database level instance associated with model.
     *
     * @private
     * @type {Sublevel}
     */
    _db: null,

    /**
     * Checks to see if a db has been registered with this model
     *
     * @return {Boolean}
     */
    ready: function() {
        return !!this._db && !!this.schema;
    },

    /**
     * Creates a unique instance of the model
     */
    create: function() {
        if ( !this.ready() ) {
            console.log( 'Model not ready to be created' );
        }

        var model = new ModelInstance( this.schema );
        model.setDB( this._db );

        // should also be a synchronous return
        // return Promise.resolve( new ModelInstance( this._db ) );
        return Promise.resolve( model );
    }

});

/**
 * Model Instance Constructor
 *
 * @inherits https://github.com/3rd-Eden/EventEmitter3
 * @param schema {Schema} the schema from which to create the model
 */
var ModelInstance = function( schema ) {

    EventEmitter.call( this );

    this._schema = schema;

    // Create the change record object for this instance
    this._changes = (function() {

        /**
         * Private changes array
         * @type {Array}
         */
        var changes = [];

        /**
         * Expose public API
         */
        return {
            /**
             * Adds a record to the change array if it needs updating
             *
             * @param record {ChangeRecord} the new change record
             * @returns {Boolean}
             */
            push: function( record ) {
                // Bail if the record exists and is not being changed
                var rec = _.find( changes, { key: record.key } );
                if ( rec && rec.newValue === record.newValue ) {
                    return false;
                }

                // If it exists then just update it
                if ( rec ) {
                    // @todo - is there a quicker way to update an existing record?
                    changes[ _.findKey( changes, { key: record.key } ) ] = record;
                    return true;
                }

                // Otherwise add it
                changes.push( record );
                return true;
            },

            /**
             * Pops the latest change record off the stack
             *
             * @returns {ChangeRecord}
             */
            pop: function() {
                return changes.pop();
            },

            /**
             * Returns the length of the list of current changes
             *
             * @returns {Integer}
             */
            length: function() {
                return changes.length;
            },

            /**
             * Returns the raw changes array.
             * Use with caution outside of testing.
             *
             * @returns {Array}
             */
            show: function() {
                return changes;
            },

            /**
             * Clears the list of changes
             */
            clear: function() {
                while( changes.length ) {
                    changes.pop();
                }
            }
        }
    })();

    // Add each prop from the schema on to the instance
    _.each( schema, function( val, key ) {
        this.addProp( key, val )
    }, this );

}

util.inherits( ModelInstance, EventEmitter );

/**
 * Model Instance Prototype
 */
_.extend( ModelInstance.prototype, {

    /**
     * Pointer to the instance of the db this model is connected to
     */
    _db: null,

    /**
     * The array of changes that should be persisted on save
     */
    _changes: null,

    /**
     * Accessor function to set the db
     * @param db {LevelDB}
     */
    setDB: function( db ) {
        this._db = db;

        // Set up indices
        this._index = this._db.sublevel( 'INDEX', {
            sync: true
        });
    },

    // @todo Setting shouldnt happen like this, each model should be updated but require
    // a discrete call to save to put the model to the db.
    /**
     * Sets a value on the model.
     * Only properties defined by the schema can be manipulated.
     *
     * @param key {string}
     * @param value
     * @param opts {Object}
     *   #sync - synchronous put
     *   #silent - dont update the db but update the model
     * @return {Boolean} was the operation successful - @todo currently returns a promise
     */
    set: function( key, value, opts ) {

        // @todo - only writing to db as a test
        return this._db.put( key, value );
    },

    /**
     * Adds a property to the model
     *
     * @param key {string} object key to set
     * @param def {Object} property definition - should be a schema prop def
     */
    addProp: function( key, def ) {
        var value = def.default || null;

        this.emit( 'add:' + key, value );

        this._changes.push({
            key: key,
            oldValue: null,
            newValue: value,
            index: def.index || false
        });

        Object.defineProperty( this, key, {
            get: function() {
                return value;
            },

            set: function( val ) {
                // Set a change record
                this._changes.push({
                    key: key,
                    oldValue: value,
                    newValue: val,
                    index: def.index || false
                });

                value = val;

                // Fire change events
                this.emit( 'change:' + key, value );
                var ret = {};
                ret[ key ] = value;
                this.emit( 'change', ret );
            }
        });
    },

    /**
     * Saves the model to the db
     * @todo - currently this is a pretty raw implementation of what it should look like in the db
     */
    save: function() {

        var batchOps = [],
            ops = [],    // List of promises waiting to be fulfilled
            saveItem = {};

        // Remove each change record to build the batch request object
        while( this._changes.length() ) {
            var record = this._changes.pop();

            // Insert indexable records
            if ( record.index ) {
                // Remove old record
                ops.push( this._index.del([
                    record.key,
                    '\xff',
                    record.oldValue
                ].join('')));

                ops.push( this._index.put([
                    record.key,
                    '\xff',
                    record.newValue
                ].join(''), 'uniqueID') );
            }

            // batchOps.push({
            //     type: 'put',
            //     key: record.key,
            //     value: record.newValue
            // });
        }

        // Now put the saveable props as a json block
        _.each( this._schema, function( value, key ) {
            // Dont save silent schema props
            if ( value.silent ) {
                return;
            }

            saveItem[ key ] = this[ key ];
        }, this );

        ops.push( this._db.put( 'uniqueID', saveItem ) );

        // Change record array will now be empty so go ahead and batch the changes
        // return this._db.batch( batchOps );
        return Promise.all( ops );

        // @todo this is batching stuff in which means each individual prop goes in,
        // however, the actual implementation should add a json rep of the object saveable props
        // and add indexable props to their separate indexed sublevels

    }

});
<commit_msg>#add - sync model creation<commit_after>/**
 * model
 *
 * Defines a model that represents a discrete blob of data.
 *
 * The model should expose a few methods for finding and manipulating unique
 * model instances but should also be able to create and return an instance of
 * a specific model to work with.
 */

var util = require( 'util' ),
    EventEmitter = require( 'EventEmitter3' ),
    Promise = require( 'es6-promise' ).Promise,
    _ = require( 'lodash-node' );

/**
 * Model Constructor function
 *
 * @param opts {object}
 *    schema {Schema} the schema for the model
 */
var Model = module.exports = function( opts ) {

    if ( !opts ) {
        throw new Error( 'Models should be defined by a schema' );
    }

    this.opts = opts;
    this.id = opts.id;
    this.schema = opts.schema;
};

_.extend( Model.prototype, {

    /**
     * The database level instance associated with model.
     *
     * @private
     * @type {Sublevel}
     */
    _db: null,

    /**
     * The schema associated with this model type
     *
     * @private
     * @type {Schema}
     */
    _schema: null,

    /**
     * Checks to see if a db has been registered with this model
     *
     * @return {Boolean}
     */
    ready: function() {
        return !!this._db && !!this.schema;
    },

    /**
     * Creates a unique instance of the model
     */
    create: function( opts ) {
        var opts = opts || {};

        if ( !this.ready() ) {
            console.log( 'Model not ready to be created - does it have an associated db and schema?' );
        }

        var model = new ModelInstance( this.schema, this._db );

        return opts.sync ? model : Promise.resolve( model );
    }

});

/**
 * Model Instance Constructor
 *
 * @inherits https://github.com/3rd-Eden/EventEmitter3
 * @param schema {Schema} the schema from which to create the model
 */
var ModelInstance = function( schema, db ) {

    EventEmitter.call( this );

    this._schema = schema;
    this.setDB( db );

    // Create the change record object for this instance
    this._changes = (function() {

        /**
         * Private changes array
         * @type {Array}
         */
        var changes = [];

        /**
         * Expose public API
         */
        return {
            /**
             * Adds a record to the change array if it needs updating
             *
             * @param record {ChangeRecord} the new change record
             * @returns {Boolean}
             */
            push: function( record ) {
                // Bail if the record exists and is not being changed
                var rec = _.find( changes, { key: record.key } );
                if ( rec && rec.newValue === record.newValue ) {
                    return false;
                }

                // If it exists then just update it
                if ( rec ) {
                    // @todo - is there a quicker way to update an existing record?
                    changes[ _.findKey( changes, { key: record.key } ) ] = record;
                    return true;
                }

                // Otherwise add it
                changes.push( record );
                return true;
            },

            /**
             * Pops the latest change record off the stack
             *
             * @returns {ChangeRecord}
             */
            pop: function() {
                return changes.pop();
            },

            /**
             * Returns the length of the list of current changes
             *
             * @returns {Integer}
             */
            length: function() {
                return changes.length;
            },

            /**
             * Returns the raw changes array.
             * Use with caution outside of testing.
             *
             * @returns {Array}
             */
            show: function() {
                return changes;
            },

            /**
             * Clears the list of changes
             */
            clear: function() {
                while( changes.length ) {
                    changes.pop();
                }
            }
        }
    })();

    // Add each prop from the schema on to the instance
    _.each( schema, function( val, key ) {
        this.addProp( key, val )
    }, this );

}

util.inherits( ModelInstance, EventEmitter );

/**
 * Model Instance Prototype
 */
_.extend( ModelInstance.prototype, {

    /**
     * Pointer to the instance of the db this model is connected to
     */
    _db: null,

    /**
     * The array of changes that should be persisted on save
     */
    _changes: null,

    /**
     * Accessor function to set the db
     * @param db {LevelDB}
     */
    setDB: function( db ) {
        this._db = db;

        // Set up indices
        this._index = this._db.sublevel( 'INDEX', {
            sync: true
        });
    },

    /**
     * Adds a property to the model
     *
     * @param key {string} object key to set
     * @param def {Object} property definition - should be a schema prop def
     */
    addProp: function( key, def ) {
        var value = def.default || null;

        this.emit( 'add:' + key, value );

        this._changes.push({
            key: key,
            oldValue: null,
            newValue: value,
            index: def.index || false
        });

        Object.defineProperty( this, key, {
            get: function() {
                return value;
            },

            set: function( val ) {
                // Set a change record
                this._changes.push({
                    key: key,
                    oldValue: value,
                    newValue: val,
                    index: def.index || false
                });

                value = val;

                // Fire change events
                this.emit( 'change:' + key, value );
                var ret = {};
                ret[ key ] = value;
                this.emit( 'change', ret );
            }
        });
    },

    /**
     * Saves the model to the db.
     * Searches the change records and updates index links to the actual model object,
     * then saves the model based upon its schema.
     *
     * @returns {Promise}
     */
    save: function() {

        var ops = [],    // List of promises waiting to be fulfilled
            saveItem = {};

        // Remove each change record to build the batch request object
        while( this._changes.length() ) {
            var record = this._changes.pop();

            // Insert indexable records
            if ( record.index ) {
                // Remove old record
                ops.push( this._index.del([
                    record.key,
                    '\xff',
                    record.oldValue
                ].join('')));

                ops.push( this._index.put([
                    record.key,
                    '\xff',
                    record.newValue
                ].join(''), 'uniqueID') );
            }
        }

        // Now put the saveable props as a json block
        _.each( this._schema, function( value, key ) {
            // Dont save silent schema props
            if ( value.silent ) {
                return;
            }

            saveItem[ key ] = this[ key ];
        }, this );

        ops.push( this._db.put( 'uniqueID', saveItem ) );

        return Promise.all( ops );
    }

});
<|endoftext|>


<commit_before>// Generated by CoffeeScript 1.9.2
(function() {
  var CloudFormationTemplateContext, CoffeeScript, fs, path, vm,
    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
    slice = [].slice;

  fs = require('fs');

  vm = require('vm');

  path = require('path');

  CoffeeScript = require('coffee-script');

  CloudFormationTemplateContext = (function() {
    function CloudFormationTemplateContext() {
      this.Description = bind(this.Description, this);
      this.Output = bind(this.Output, this);
      this.Mapping = bind(this.Mapping, this);
      this._resourceByType = bind(this._resourceByType, this);
      this.DeclareResource = bind(this.DeclareResource, this);
      this._buildCall = bind(this._buildCall, this);
      this._paramByType = bind(this._paramByType, this);
      this._resources = {};
      this._parameters = {};
      this._mappings = null;
      this._outputs = {};
      this._description = null;
      this.Params = {};
      this.Resources = {};
      this.Mappings = {};
      this.AWS = {
        AutoScaling: {
          AutoScalingGroup: null,
          LaunchConfiguration: null,
          ScalingPolicy: null,
          LifecycleHook: null,
          ScheduledAction: null,
          Trigger: null
        },
        CloudFormation: {
          Authentication: null,
          CustomResource: null,
          Stack: null,
          WaitCondition: null,
          WaitConditionHandle: null
        },
        CloudFront: {
          Distribution: null
        },
        CloudWatch: {
          Alarm: null
        },
        DynamoDB: {
          Table: null
        },
        EC2: {
          CustomerGateway: null,
          DHCPOptions: null,
          EIP: null,
          EIPAssociation: null,
          Instance: null,
          InternetGateway: null,
          NetworkAcl: null,
          NetworkAclEntry: null,
          NetworkInterface: null,
          Route: null,
          RouteTable: null,
          SecurityGroup: null,
          SecurityGroupIngress: null,
          SecurityGroupEgress: null,
          Subnet: null,
          SubnetNetworkAclAssociation: null,
          SubnetRouteTableAssociation: null,
          Volume: null,
          VolumeAttachment: null,
          VPC: null,
          VPCDHCPOptionsAssociation: null,
          VPCGatewayAttachment: null,
          VPNConnection: null,
          VPNGateway: null
        },
        ElastiCache: {
          CacheCluster: null,
          ParameterGroup: null,
          SecurityGroup: null,
          SecurityGroupIngress: null
        },
        ElasticBeanstalk: {
          Application: null,
          Environment: null
        },
        ElasticLoadBalancing: {
          LoadBalancer: null
        },
        IAM: {
          AccessKey: null,
          Group: null,
          InstanceProfile: null,
          Policy: null,
          Role: null,
          User: null,
          UserToGroupAddition: null
        },
        Kinesis: {
          Stream: null
        },
        Logs: {
          LogGroup: null,
          MetricFilter: null
        },
        RDS: {
          DBInstance: null,
          DBParameterGroup: null,
          DBSubnetGroup: null,
          DBSecurityGroup: null,
          DBSecurityGroupIngress: null
        },
        Route53: {
          RecordSet: null,
          RecordSetGroup: null
        },
        SDB: {
          Domain: null
        },
        S3: {
          Bucket: null,
          BucketPolicy: null
        },
        SNS: {
          Topic: null,
          TopicPolicy: null
        },
        SQS: {
          Queue: null,
          QueuePolicy: null
        }
      };
      this.Param = {
        String: (function(_this) {
          return function(name, arg1, arg2) {
            return _this._paramByType('String', name, arg1, arg2);
          };
        })(this),
        Number: (function(_this) {
          return function(name, arg1, arg2) {
            return _this._paramByType('Number', name, arg1, arg2);
          };
        })(this),
        CommaDelimitedList: (function(_this) {
          return function(name, arg1, arg2) {
            return _this._paramByType('CommaDelimitedList', name, arg1, arg2);
          };
        })(this)
      };
      this._buildCall(null, null, 'AWS', this.AWS);
    }

    CloudFormationTemplateContext.prototype._paramByType = function(type, name, arg1, arg2) {
      var result;
      result = {};
      if (arg1 == null) {
        result[name] = {};
      } else if (arg2 == null) {
        result[name] = typeof arg1 === 'string' ? {
          Description: arg1
        } : arg1;
      } else {
        result[name] = arg2;
        result[name].Description = arg1;
      }
      result[name].Type = type;
      this._set(result, this._parameters);
      return this.Params[name] = {
        Ref: name
      };
    };

    CloudFormationTemplateContext.prototype._buildCall = function(parent, lastKey, awsType, leaf) {
      var key, val;
      if (leaf != null) {
        for (key in leaf) {
          val = leaf[key];
          this._buildCall(leaf, key, awsType + "::" + key, val);
        }
        return;
      }
      return parent[lastKey] = (function(_this) {
        return function(name, props) {
          return _this._resourceByType(awsType, name, props);
        };
      })(this);
    };

    CloudFormationTemplateContext.prototype.DeclareResource = function(name) {
      var base;
      return (base = this.Resources)[name] != null ? base[name] : base[name] = {
        Ref: name
      };
    };

    CloudFormationTemplateContext.prototype._resourceByType = function(type, name, props) {
      var result;
      result = {};
      if (((props != null ? props.Metadata : void 0) != null) || ((props != null ? props.Properties : void 0) != null) || ((props != null ? props.DependsOn : void 0) != null) || ((props != null ? props.UpdatePolicy : void 0) != null) || ((props != null ? props.CreationPolicy : void 0) != null)) {
        result[name] = props;
        result[name].Type = type;
      } else {
        result[name] = {
          Type: type,
          Properties: props
        };
      }
      this._set(result, this._resources);
      return this.DeclareResource(name);
    };

    CloudFormationTemplateContext.prototype._set = function(source, target) {
      var key, results, val;
      results = [];
      for (key in source) {
        val = source[key];
        results.push(target[key] = val);
      }
      return results;
    };

    CloudFormationTemplateContext.prototype.Mapping = function(name, map) {
      var result;
      if (this._mappings == null) {
        this._mappings = {};
      }
      result = {};
      result[name] = map;
      return this._set(result, this._mappings);
    };

    CloudFormationTemplateContext.prototype.Output = function() {
      var args, name, result;
      name = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
      result = {};
      if (args.length === 1) {
        result[name] = {
          Value: args[0]
        };
      }
      if (args.length === 2) {
        result[name] = {
          Description: args[0],
          Value: args[1]
        };
      }
      return this._set(result, this._outputs);
    };

    CloudFormationTemplateContext.prototype.Description = function(d) {
      return this._description = d;
    };

    CloudFormationTemplateContext.prototype.Tag = function(key, val) {
      return {
        Key: key,
        Value: val
      };
    };

    CloudFormationTemplateContext.prototype.Join = function() {
      var args, delimiter;
      delimiter = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
      if (args.length === 1 && (args[0] instanceof Array)) {
        return {
          'Fn::Join': [delimiter, args[0]]
        };
      } else {
        return {
          'Fn::Join': [delimiter, args]
        };
      }
    };

    CloudFormationTemplateContext.prototype.FindInMap = function() {
      var args;
      args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
      return {
        'Fn::FindInMap': args
      };
    };

    CloudFormationTemplateContext.prototype.GetAtt = function() {
      var args;
      args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
      return {
        'Fn::GetAtt': args
      };
    };

    CloudFormationTemplateContext.prototype.Base64 = function(arg) {
      return {
        'Fn::Base64': arg
      };
    };

    CloudFormationTemplateContext.prototype.GetAZs = function(arg) {
      return {
        'Fn::GetAZs': arg
      };
    };

    CloudFormationTemplateContext.prototype.Select = function() {
      var args, index;
      index = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
      if (args.length === 1 && (args[0] instanceof Array)) {
        return {
          'Fn::Select': [index, args[0]]
        };
      } else {
        return {
          'Fn::Select': [index, args]
        };
      }
    };

    CloudFormationTemplateContext.prototype.AccountId = {
      Ref: 'AWS::AccountId'
    };

    CloudFormationTemplateContext.prototype.NotificationARNs = {
      Ref: 'AWS::NotificationARNs'
    };

    CloudFormationTemplateContext.prototype.NoValue = {
      Ref: 'AWS::NoValue'
    };

    CloudFormationTemplateContext.prototype.Region = {
      Ref: 'AWS::Region'
    };

    CloudFormationTemplateContext.prototype.StackId = {
      Ref: 'AWS::StackId'
    };

    CloudFormationTemplateContext.prototype.StackName = {
      Ref: 'AWS::StackName'
    };

    CloudFormationTemplateContext.prototype.InitScript = function(arg) {
      var chunks, compiled, existsSyncFunc, match, pattern, text;
      existsSyncFunc = fs.existsSync != null ? fs.existsSync : path.existsSync;
      if (!existsSyncFunc(arg)) {
        text = arg;
      } else {
        text = fs.readFileSync(arg).toString();
      }
      chunks = [];
      pattern = /((.|\n)*?)%{([^}?]+)}?((.|\n)*)/;
      match = text.match(pattern);
      while (match) {
        chunks.push(match[1]);
        compiled = CoffeeScript.compile(match[3], {
          bare: true
        });
        chunks.push(eval(compiled));
        text = match[4];
        match = text.match(pattern);
      }
      if (text && text.length > 0) {
        chunks.push(text);
      }
      return this.Base64(this.Join('', chunks));
    };

    return CloudFormationTemplateContext;

  })();

  module.exports.CloudFormationTemplateContext = CloudFormationTemplateContext;

  module.exports = function(func) {
    var context, template;
    context = new CloudFormationTemplateContext;
    func.apply(context, [context]);
    template = {
      AWSTemplateFormatVersion: '2010-09-09'
    };
    if (context._description != null) {
      template.Description = context._description;
    }
    template.Parameters = context._parameters;
    if (context._mappings != null) {
      template.Mappings = context._mappings;
    }
    template.Resources = context._resources;
    template.Outputs = context._outputs;
    return template;
  };

  require('pkginfo')(module, 'version');

}).call(this);
<commit_msg>update lib<commit_after>// Generated by CoffeeScript 1.9.2
(function() {
  var CloudFormationTemplateContext, CoffeeScript, fs, path, vm,
    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
    slice = [].slice;

  fs = require('fs');

  vm = require('vm');

  path = require('path');

  CoffeeScript = require('coffee-script');

  CloudFormationTemplateContext = (function() {
    function CloudFormationTemplateContext() {
      this.Description = bind(this.Description, this);
      this.Output = bind(this.Output, this);
      this.Mapping = bind(this.Mapping, this);
      this._resourceByType = bind(this._resourceByType, this);
      this.DeclareResource = bind(this.DeclareResource, this);
      this._buildCall = bind(this._buildCall, this);
      this._paramByType = bind(this._paramByType, this);
      this._resources = {};
      this._parameters = {};
      this._mappings = null;
      this._outputs = {};
      this._description = null;
      this.Params = {};
      this.Resources = {};
      this.Mappings = {};
      this.AWS = {
        AutoScaling: {
          AutoScalingGroup: null,
          LaunchConfiguration: null,
          ScalingPolicy: null,
          LifecycleHook: null,
          ScheduledAction: null,
          Trigger: null
        },
        CloudFormation: {
          Authentication: null,
          CustomResource: null,
          Stack: null,
          WaitCondition: null,
          WaitConditionHandle: null
        },
        CloudFront: {
          Distribution: null
        },
        CloudWatch: {
          Alarm: null
        },
        DynamoDB: {
          Table: null
        },
        EC2: {
          CustomerGateway: null,
          DHCPOptions: null,
          EIP: null,
          EIPAssociation: null,
          Instance: null,
          InternetGateway: null,
          NetworkAcl: null,
          NetworkAclEntry: null,
          NetworkInterface: null,
          Route: null,
          RouteTable: null,
          SecurityGroup: null,
          SecurityGroupIngress: null,
          SecurityGroupEgress: null,
          Subnet: null,
          SubnetNetworkAclAssociation: null,
          SubnetRouteTableAssociation: null,
          Volume: null,
          VolumeAttachment: null,
          VPC: null,
          VPCDHCPOptionsAssociation: null,
          VPCGatewayAttachment: null,
          VPNConnection: null,
          VPNGateway: null
        },
        ElastiCache: {
          CacheCluster: null,
          ParameterGroup: null,
          SecurityGroup: null,
          SecurityGroupIngress: null
        },
        ElasticBeanstalk: {
          Application: null,
          Environment: null
        },
        ElasticLoadBalancing: {
          LoadBalancer: null
        },
        IAM: {
          AccessKey: null,
          Group: null,
          InstanceProfile: null,
          Policy: null,
          Role: null,
          User: null,
          UserToGroupAddition: null
        },
        Kinesis: {
          Stream: null
        },
        Logs: {
          LogGroup: null,
          MetricFilter: null
        },
        Lambda: {
          Function: null
        },
        RDS: {
          DBInstance: null,
          DBParameterGroup: null,
          DBSubnetGroup: null,
          DBSecurityGroup: null,
          DBSecurityGroupIngress: null
        },
        Route53: {
          RecordSet: null,
          RecordSetGroup: null
        },
        SDB: {
          Domain: null
        },
        S3: {
          Bucket: null,
          BucketPolicy: null
        },
        SNS: {
          Topic: null,
          TopicPolicy: null
        },
        SQS: {
          Queue: null,
          QueuePolicy: null
        }
      };
      this.Param = {
        String: (function(_this) {
          return function(name, arg1, arg2) {
            return _this._paramByType('String', name, arg1, arg2);
          };
        })(this),
        Number: (function(_this) {
          return function(name, arg1, arg2) {
            return _this._paramByType('Number', name, arg1, arg2);
          };
        })(this),
        CommaDelimitedList: (function(_this) {
          return function(name, arg1, arg2) {
            return _this._paramByType('CommaDelimitedList', name, arg1, arg2);
          };
        })(this)
      };
      this._buildCall(null, null, 'AWS', this.AWS);
    }

    CloudFormationTemplateContext.prototype._paramByType = function(type, name, arg1, arg2) {
      var result;
      result = {};
      if (arg1 == null) {
        result[name] = {};
      } else if (arg2 == null) {
        result[name] = typeof arg1 === 'string' ? {
          Description: arg1
        } : arg1;
      } else {
        result[name] = arg2;
        result[name].Description = arg1;
      }
      result[name].Type = type;
      this._set(result, this._parameters);
      return this.Params[name] = {
        Ref: name
      };
    };

    CloudFormationTemplateContext.prototype._buildCall = function(parent, lastKey, awsType, leaf) {
      var key, val;
      if (leaf != null) {
        for (key in leaf) {
          val = leaf[key];
          this._buildCall(leaf, key, awsType + "::" + key, val);
        }
        return;
      }
      return parent[lastKey] = (function(_this) {
        return function(name, props) {
          return _this._resourceByType(awsType, name, props);
        };
      })(this);
    };

    CloudFormationTemplateContext.prototype.DeclareResource = function(name) {
      var base;
      return (base = this.Resources)[name] != null ? base[name] : base[name] = {
        Ref: name
      };
    };

    CloudFormationTemplateContext.prototype._resourceByType = function(type, name, props) {
      var result;
      result = {};
      if (((props != null ? props.Metadata : void 0) != null) || ((props != null ? props.Properties : void 0) != null) || ((props != null ? props.DependsOn : void 0) != null) || ((props != null ? props.UpdatePolicy : void 0) != null) || ((props != null ? props.CreationPolicy : void 0) != null)) {
        result[name] = props;
        result[name].Type = type;
      } else {
        result[name] = {
          Type: type,
          Properties: props
        };
      }
      this._set(result, this._resources);
      return this.DeclareResource(name);
    };

    CloudFormationTemplateContext.prototype._set = function(source, target) {
      var key, results, val;
      results = [];
      for (key in source) {
        val = source[key];
        results.push(target[key] = val);
      }
      return results;
    };

    CloudFormationTemplateContext.prototype.Mapping = function(name, map) {
      var result;
      if (this._mappings == null) {
        this._mappings = {};
      }
      result = {};
      result[name] = map;
      return this._set(result, this._mappings);
    };

    CloudFormationTemplateContext.prototype.Output = function() {
      var args, name, result;
      name = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
      result = {};
      if (args.length === 1) {
        result[name] = {
          Value: args[0]
        };
      }
      if (args.length === 2) {
        result[name] = {
          Description: args[0],
          Value: args[1]
        };
      }
      return this._set(result, this._outputs);
    };

    CloudFormationTemplateContext.prototype.Description = function(d) {
      return this._description = d;
    };

    CloudFormationTemplateContext.prototype.Tag = function(key, val) {
      return {
        Key: key,
        Value: val
      };
    };

    CloudFormationTemplateContext.prototype.Join = function() {
      var args, delimiter;
      delimiter = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
      if (args.length === 1 && (args[0] instanceof Array)) {
        return {
          'Fn::Join': [delimiter, args[0]]
        };
      } else {
        return {
          'Fn::Join': [delimiter, args]
        };
      }
    };

    CloudFormationTemplateContext.prototype.FindInMap = function() {
      var args;
      args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
      return {
        'Fn::FindInMap': args
      };
    };

    CloudFormationTemplateContext.prototype.GetAtt = function() {
      var args;
      args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
      return {
        'Fn::GetAtt': args
      };
    };

    CloudFormationTemplateContext.prototype.Base64 = function(arg) {
      return {
        'Fn::Base64': arg
      };
    };

    CloudFormationTemplateContext.prototype.GetAZs = function(arg) {
      return {
        'Fn::GetAZs': arg
      };
    };

    CloudFormationTemplateContext.prototype.Select = function() {
      var args, index;
      index = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
      if (args.length === 1 && (args[0] instanceof Array)) {
        return {
          'Fn::Select': [index, args[0]]
        };
      } else {
        return {
          'Fn::Select': [index, args]
        };
      }
    };

    CloudFormationTemplateContext.prototype.AccountId = {
      Ref: 'AWS::AccountId'
    };

    CloudFormationTemplateContext.prototype.NotificationARNs = {
      Ref: 'AWS::NotificationARNs'
    };

    CloudFormationTemplateContext.prototype.NoValue = {
      Ref: 'AWS::NoValue'
    };

    CloudFormationTemplateContext.prototype.Region = {
      Ref: 'AWS::Region'
    };

    CloudFormationTemplateContext.prototype.StackId = {
      Ref: 'AWS::StackId'
    };

    CloudFormationTemplateContext.prototype.StackName = {
      Ref: 'AWS::StackName'
    };

    CloudFormationTemplateContext.prototype.InitScript = function(arg) {
      var chunks, compiled, existsSyncFunc, match, pattern, text;
      existsSyncFunc = fs.existsSync != null ? fs.existsSync : path.existsSync;
      if (!existsSyncFunc(arg)) {
        text = arg;
      } else {
        text = fs.readFileSync(arg).toString();
      }
      chunks = [];
      pattern = /((.|\n)*?)%{([^}?]+)}?((.|\n)*)/;
      match = text.match(pattern);
      while (match) {
        chunks.push(match[1]);
        compiled = CoffeeScript.compile(match[3], {
          bare: true
        });
        chunks.push(eval(compiled));
        text = match[4];
        match = text.match(pattern);
      }
      if (text && text.length > 0) {
        chunks.push(text);
      }
      return this.Base64(this.Join('', chunks));
    };

    return CloudFormationTemplateContext;

  })();

  module.exports.CloudFormationTemplateContext = CloudFormationTemplateContext;

  module.exports = function(func) {
    var context, template;
    context = new CloudFormationTemplateContext;
    func.apply(context, [context]);
    template = {
      AWSTemplateFormatVersion: '2010-09-09'
    };
    if (context._description != null) {
      template.Description = context._description;
    }
    template.Parameters = context._parameters;
    if (context._mappings != null) {
      template.Mappings = context._mappings;
    }
    template.Resources = context._resources;
    template.Outputs = context._outputs;
    return template;
  };

  require('pkginfo')(module, 'version');

}).call(this);
<|endoftext|>


<commit_before>const window = require('xwindow')
const { Node } = window
const { indexOf } = Array.prototype

function morph(nodeA, nodeB, options = {}) {
  options.getKey ??= getNodeId
  if(options.childrenOnly) {
    morphChildren(nodeA, nodeB, options)
    return nodeA
  }
  morphNode(nodeA, nodeB, options)
  return nodeA
}

function morphNode(nodeA, nodeB, options) {
  const {
    nodeWillUpdate,
    nodeDidUpdate,
    childrenWillUpdate,
    childrenDidUpdate,
  } = options
  nodeWillUpdate?.(nodeA, nodeB)
  morphAttrs(nodeA, nodeB, options)
  nodeDidUpdate?.(nodeA)
  childrenWillUpdate?.(nodeA, nodeB)
  morphChildren(nodeA, nodeB, options)
  childrenDidUpdate?.(nodeA)
}

function morphAttrs(nodeA, nodeB) {
  const names = new Set
  let attr, value
  for(attr of nodeA.attributes) {
    names.add(attr.name)
    value = nodeB.getAttribute(attr.name)
    if(value === null) {
      nodeA.removeAttribute(attr.name)
      continue
    }
    if(attr.value !== value) {
      attr.value = value
    }
  }
  for(attr of nodeB.attributes) {
    if(!names.has(attr.name)) {
      nodeA.setAttribute(attr.name, attr.value)
    }
  }
  switch(nodeA.tagName) {
    case 'OPTION':
      nodeA.selected = nodeB.selected
      break
    case 'INPUT':
      nodeA.checked = nodeB.checked
      nodeA.indeterminate = nodeB.indeterminate
    case 'TEXTAREA':
      nodeA.value = nodeB.value
  }
}

function morphChildren(nodeA, nodeB, options) {
  const indexA = getKeyIndex(nodeA, options.getKey)
  const indexB = getKeyIndex(nodeB, options.getKey)
  if(!indexA || !indexB) {
    morphChildNodes(nodeA, nodeB, options)
    return
  }
  const {
    getKey,
    nodeWillAppend,
    nodeDidAppend,
    nodeWillRemove,
    nodeDidRemove,
  } = options
  const childrenB = Array.from(nodeB.children)
  let childA = nodeA.firstElementChild
  let childB, nextA, i, j
  while(childA) {
    nextA = childA.nextElementSibling
    if(!indexB[getKey(childA)]) {
      nodeWillRemove?.(childA)
      childA.remove()
      deep(childA, nodeDidRemove)
    }
    childA = nextA
  }
  for(i = 0; i < childrenB.length; i++) {
    childB = childrenB[i]
    childA = indexA[getKey(childB)]
    if(childA) {
      morphNode(childA, childB, options)
      continue
    }
    nextA = nodeA.children[i]
    nodeWillAppend?.(childB)
    if(nextA) {
      nextA.before(childB)
    }
    else nodeA.append(childB)
    deep(childB, nodeDidAppend)
  }
  for(i = 0; i < childrenB.length; i++) {
    childB = childrenB[i]
    childA = indexA[getKey(childB)]
    if(!childA) {
      continue
    }
    j = indexOf.call(nodeA.children, childA)
    if(i === j) {
      continue
    }
    nextA = nodeA.children[i].nextElementSibling
    if(nextA) {
      if(childA !== nextA && childA.nextElementSibling !== nextA) {
        nextA.before(childA)
      }
    }
    else nodeA.append(childA)
    morphNode(childA, childB, options)
  }
}

function morphChildNodes(nodeA, nodeB, options) {
  const {
    getKey,
    nodeWillAppend,
    nodeDidAppend,
    nodeWillUpdate,
    nodeDidUpdate,
    nodeWillRemove,
    nodeDidRemove,
  } = options
  const childNodesA = Array.from(nodeA.childNodes)
  const childNodesB = Array.from(nodeB.childNodes)
  const length = Math.max(childNodesA.length, childNodesB.length)
  let i, childA, childB
  for(i = 0; i < length; i++) {
    childA = childNodesA[i]
    childB = childNodesB[i]
    if(!childA) {
      nodeWillAppend?.(childB)
      nodeA.append(childB)
      deep(childB, nodeDidAppend)
      continue
    }
    if(!childB) {
      nodeWillRemove?.(childA)
      childA.remove()
      deep(childA, nodeDidRemove)
      continue
    }
    if(childA.nodeType === Node.TEXT_NODE && childB.nodeType === Node.TEXT_NODE) {
      nodeWillUpdate?.(childA, childB)
      childA.data = childB.data
      nodeDidUpdate?.(childA)
      continue
    }
    if(childA.nodeType === childB.nodeType && childA.tagName === childB.tagName) {
      if(getKey(childA) === getKey(childB)) {
        morphNode(childA, childB, options)
        continue
      }
    }
    nodeWillRemove?.(childA)
    nodeWillAppend?.(childB)
    childA.replaceWith(childB)
    deep(childA, nodeDidRemove)
    deep(childB, nodeDidAppend)
  }
}

function getNodeId(node) {
  return node.id
}

function getKeyIndex(node, getKey) {
  const index = {}
  let child, key
  for(child of node.childNodes) {
    key = getKey(child)
    if(!key) {
      return null
    }
    index[key] = child
  }
  return index
}

function deep(node, handler) {
  if(!handler) {
    return
  }
  handler(node)
  if(!node.children) {
    return
  }
  for(const child of node.children) {
    deep(child, handler)
  }
}

module.exports = morph
<commit_msg>Preventing unnecessary text node updates<commit_after>const window = require('xwindow')
const { Node } = window
const { indexOf } = Array.prototype

function morph(nodeA, nodeB, options = {}) {
  options.getKey ??= getNodeId
  if(options.childrenOnly) {
    morphChildren(nodeA, nodeB, options)
    return nodeA
  }
  morphNode(nodeA, nodeB, options)
  return nodeA
}

function morphNode(nodeA, nodeB, options) {
  const {
    nodeWillUpdate,
    nodeDidUpdate,
    childrenWillUpdate,
    childrenDidUpdate,
  } = options
  nodeWillUpdate?.(nodeA, nodeB)
  morphAttrs(nodeA, nodeB, options)
  nodeDidUpdate?.(nodeA)
  childrenWillUpdate?.(nodeA, nodeB)
  morphChildren(nodeA, nodeB, options)
  childrenDidUpdate?.(nodeA)
}

function morphAttrs(nodeA, nodeB) {
  const names = new Set
  let attr, value
  for(attr of nodeA.attributes) {
    names.add(attr.name)
    value = nodeB.getAttribute(attr.name)
    if(value === null) {
      nodeA.removeAttribute(attr.name)
      continue
    }
    if(attr.value !== value) {
      attr.value = value
    }
  }
  for(attr of nodeB.attributes) {
    if(!names.has(attr.name)) {
      nodeA.setAttribute(attr.name, attr.value)
    }
  }
  switch(nodeA.tagName) {
    case 'OPTION':
      nodeA.selected = nodeB.selected
      break
    case 'INPUT':
      nodeA.checked = nodeB.checked
      nodeA.indeterminate = nodeB.indeterminate
    case 'TEXTAREA':
      nodeA.value = nodeB.value
  }
}

function morphChildren(nodeA, nodeB, options) {
  const indexA = getKeyIndex(nodeA, options.getKey)
  const indexB = getKeyIndex(nodeB, options.getKey)
  if(!indexA || !indexB) {
    morphChildNodes(nodeA, nodeB, options)
    return
  }
  const {
    getKey,
    nodeWillAppend,
    nodeDidAppend,
    nodeWillRemove,
    nodeDidRemove,
  } = options
  const childrenB = Array.from(nodeB.children)
  let childA = nodeA.firstElementChild
  let childB, nextA, i, j
  while(childA) {
    nextA = childA.nextElementSibling
    if(!indexB[getKey(childA)]) {
      nodeWillRemove?.(childA)
      childA.remove()
      deep(childA, nodeDidRemove)
    }
    childA = nextA
  }
  for(i = 0; i < childrenB.length; i++) {
    childB = childrenB[i]
    childA = indexA[getKey(childB)]
    if(childA) {
      morphNode(childA, childB, options)
      continue
    }
    nextA = nodeA.children[i]
    nodeWillAppend?.(childB)
    if(nextA) {
      nextA.before(childB)
    }
    else nodeA.append(childB)
    deep(childB, nodeDidAppend)
  }
  for(i = 0; i < childrenB.length; i++) {
    childB = childrenB[i]
    childA = indexA[getKey(childB)]
    if(!childA) {
      continue
    }
    j = indexOf.call(nodeA.children, childA)
    if(i === j) {
      continue
    }
    nextA = nodeA.children[i].nextElementSibling
    if(nextA) {
      if(childA !== nextA && childA.nextElementSibling !== nextA) {
        nextA.before(childA)
      }
    }
    else nodeA.append(childA)
    morphNode(childA, childB, options)
  }
}

function morphChildNodes(nodeA, nodeB, options) {
  const {
    getKey,
    nodeWillAppend,
    nodeDidAppend,
    nodeWillUpdate,
    nodeDidUpdate,
    nodeWillRemove,
    nodeDidRemove,
  } = options
  const childNodesA = Array.from(nodeA.childNodes)
  const childNodesB = Array.from(nodeB.childNodes)
  const length = Math.max(childNodesA.length, childNodesB.length)
  let i, childA, childB
  for(i = 0; i < length; i++) {
    childA = childNodesA[i]
    childB = childNodesB[i]
    if(!childA) {
      nodeWillAppend?.(childB)
      nodeA.append(childB)
      deep(childB, nodeDidAppend)
      continue
    }
    if(!childB) {
      nodeWillRemove?.(childA)
      childA.remove()
      deep(childA, nodeDidRemove)
      continue
    }
    if(childA.nodeType === Node.TEXT_NODE && childB.nodeType === Node.TEXT_NODE) {
      if(childA.data !== childB.data) {
        nodeWillUpdate?.(childA, childB)
        childA.data = childB.data
        nodeDidUpdate?.(childA)
      }
      continue
    }
    if(childA.nodeType === childB.nodeType && childA.tagName === childB.tagName) {
      if(getKey(childA) === getKey(childB)) {
        morphNode(childA, childB, options)
        continue
      }
    }
    nodeWillRemove?.(childA)
    nodeWillAppend?.(childB)
    childA.replaceWith(childB)
    deep(childA, nodeDidRemove)
    deep(childB, nodeDidAppend)
  }
}

function getNodeId(node) {
  return node.id
}

function getKeyIndex(node, getKey) {
  const index = {}
  let child, key
  for(child of node.childNodes) {
    key = getKey(child)
    if(!key) {
      return null
    }
    index[key] = child
  }
  return index
}

function deep(node, handler) {
  if(!handler) {
    return
  }
  handler(node)
  if(!node.children) {
    return
  }
  for(const child of node.children) {
    deep(child, handler)
  }
}

module.exports = morph
<|endoftext|>


<commit_before>var ddg_spice_amazon_carousel_add_items;
var ddg_spice_amazon_single_item;
var ddg_spice_amazon_query;

function ddg_spice_amazon(api_response) {
    if (!api_response || api_response.length == 0) return;

    ddg_spice_amazon_query =
        DDG.get_query().replace(/\s+amazon\s*$|^\s*amazon\s+/i, '');

    if (api_response.length == 1) {
        ddg_spice_amazon_single_item = api_response[0];
        nrj('/m.js?r='+ escape(ddg_spice_amazon_single_item.rating
                        .replace('http://www.amazon.com/reviews/iframe?', ''))
            + '&cb=ddg_spice_amazon_render_single');
        return;
    }

    var spotlight_resize = function(index, item, obj, is_cached) {
        $('#amazon .spotlight').css(
            {'max-height' : ($('#ddgc_detail').height() > 150 ?
                                $('#ddgc_detail').height() : 150) + 'px'}
        );
        $('#amazon .description').width(
            // width - parent margins (both sides)
            // - parent padding - spotlight image width - left-margin
            $('#ddgc_nav').width() - 10 - (12*2) - 150 - 20
        );
        if (item && !is_cached)
            nrj('/m.js?r='
                + escape(item.rating.replace('http://www.amazon.com/reviews/iframe?', ''))
                + '&cb=ddg_spice_amazon_detail');
    };

    ddg_spice_amazon_carousel_add_items =
        Spice.render({
            header1                  : ddg_spice_amazon_query + ' (Amazon)',
            source_url               : 'http://www.amazon.com/s/?'
                                        + 'tag=duc0c-20&field-keywords='
                                        + encodeURIComponent(ddg_spice_amazon_query),
            source_name              : 'Amazon',
            force_big_header         : true,
            force_favicon_domain     : 'www.amazon.com',
            template_frame           : 'carousel',
            template_normal          : 'amazon',
            carousel_css_id          : 'amazon',
            carousel_template_detail : 'amazon_detail',
            carousel_items           : api_response,
            force_no_fold            : true,
            item_callback            : spotlight_resize,
						template_options         : { li_height : 145 }
        });

    nrj('/m.js?pg=2'
	+ '&cb=ddg_spice_amazon_wait_for_render'
	+ '&q=' + escape(DDG.get_query()));

    $(window).resize(spotlight_resize);
}

function ddg_spice_amazon_wait_for_render(items) {
    window.setTimeout(function() {
            ddg_spice_amazon_carousel_add_items(items)
    }, 500);
}

function ddg_spice_amazon_detail(api_response) {
    if (api_response.stars == 'unrated') {
        $('unrated')
            .insertAfter('#ddgc_detail .stars');
        $('#ddgc_detail .stars').hide();
    } else {
        $('#ddgc_detail .stars')
            .attr('src', '/iu/?u=' + api_response.stars);
    }
    $('#ddgc_detail .review-count')
        .text(api_response.reviews);
}

function ddg_spice_amazon_deep_image(api_result) {
    var link_container = $('#links').css({ 'float' : 'left' });
    var links = $('#links div[id|="r1"]');

    var image_column = $('

')
                        .css({ 'float' : 'left', 'width' : '100px' })
                        .insertAfter(link_container);

    links.each(function(i, el) {
        image_column.append(
            $('

')
            .css({ 'height' : $(el).outerHeight(true),
                   'border' : '1px solid black' })
        );
    });

}

function ddg_spice_amazon_render_single(api_result) {
    ddg_spice_amazon_single_item.stars = api_result.stars;
    ddg_spice_amazon_single_item.reviews = api_result.reviews;
    Spice.render({
        source_url               : 'http://www.amazon.com/s/?'
                                    + 'tag=duc0c-20&field-keywords='
                                    + encodeURIComponent(ddg_spice_amazon_query),
        source_name              : 'Amazon',
        data                     : ddg_spice_amazon_single_item,
        image_url                : ddg_spice_amazon_single_item.img,
        force_favicon_domain     : 'www.amazon.com',
        template_normal          : 'amazon_single',
        force_no_fold            : true,
    });
}
<commit_msg>Amazon: add api_response.length defined check.<commit_after>var ddg_spice_amazon_carousel_add_items;
var ddg_spice_amazon_single_item;
var ddg_spice_amazon_query;

function ddg_spice_amazon(api_response) {
    if (!api_response || !api_response.length || api_response.length == 0) return;

    ddg_spice_amazon_query =
        DDG.get_query().replace(/\s+amazon\s*$|^\s*amazon\s+/i, '');

    if (api_response.length == 1) {
        ddg_spice_amazon_single_item = api_response[0];
        nrj('/m.js?r='+ escape(ddg_spice_amazon_single_item.rating
                        .replace('http://www.amazon.com/reviews/iframe?', ''))
            + '&cb=ddg_spice_amazon_render_single');
        return;
    }

    var spotlight_resize = function(index, item, obj, is_cached) {
        $('#amazon .spotlight').css(
            {'max-height' : ($('#ddgc_detail').height() > 150 ?
                                $('#ddgc_detail').height() : 150) + 'px'}
        );
        $('#amazon .description').width(
            // width - parent margins (both sides)
            // - parent padding - spotlight image width - left-margin
            $('#ddgc_nav').width() - 10 - (12*2) - 150 - 20
        );
        if (item && !is_cached)
            nrj('/m.js?r='
                + escape(item.rating.replace('http://www.amazon.com/reviews/iframe?', ''))
                + '&cb=ddg_spice_amazon_detail');
    };

    ddg_spice_amazon_carousel_add_items =
        Spice.render({
            header1                  : ddg_spice_amazon_query + ' (Amazon)',
            source_url               : 'http://www.amazon.com/s/?'
                                        + 'tag=duc0c-20&field-keywords='
                                        + encodeURIComponent(ddg_spice_amazon_query),
            source_name              : 'Amazon',
            force_big_header         : true,
            force_favicon_domain     : 'www.amazon.com',
            template_frame           : 'carousel',
            template_normal          : 'amazon',
            carousel_css_id          : 'amazon',
            carousel_template_detail : 'amazon_detail',
            carousel_items           : api_response,
            force_no_fold            : true,
            item_callback            : spotlight_resize,
						template_options         : { li_height : 145 }
        });

    nrj('/m.js?pg=2'
	+ '&cb=ddg_spice_amazon_wait_for_render'
	+ '&q=' + escape(DDG.get_query()));

    $(window).resize(spotlight_resize);
}

function ddg_spice_amazon_wait_for_render(items) {
    window.setTimeout(function() {
            ddg_spice_amazon_carousel_add_items(items)
    }, 500);
}

function ddg_spice_amazon_detail(api_response) {
    if (api_response.stars == 'unrated') {
        $('unrated')
            .insertAfter('#ddgc_detail .stars');
        $('#ddgc_detail .stars').hide();
    } else {
        $('#ddgc_detail .stars')
            .attr('src', '/iu/?u=' + api_response.stars);
    }
    $('#ddgc_detail .review-count')
        .text(api_response.reviews);
}

function ddg_spice_amazon_deep_image(api_result) {
    var link_container = $('#links').css({ 'float' : 'left' });
    var links = $('#links div[id|="r1"]');

    var image_column = $('

')
                        .css({ 'float' : 'left', 'width' : '100px' })
                        .insertAfter(link_container);

    links.each(function(i, el) {
        image_column.append(
            $('

')
            .css({ 'height' : $(el).outerHeight(true),
                   'border' : '1px solid black' })
        );
    });

}

function ddg_spice_amazon_render_single(api_result) {
    ddg_spice_amazon_single_item.stars = api_result.stars;
    ddg_spice_amazon_single_item.reviews = api_result.reviews;
    Spice.render({
        source_url               : 'http://www.amazon.com/s/?'
                                    + 'tag=duc0c-20&field-keywords='
                                    + encodeURIComponent(ddg_spice_amazon_query),
        source_name              : 'Amazon',
        data                     : ddg_spice_amazon_single_item,
        image_url                : ddg_spice_amazon_single_item.img,
        force_favicon_domain     : 'www.amazon.com',
        template_normal          : 'amazon_single',
        force_no_fold            : true,
    });
}
<|endoftext|>