<commit_before>define(function () {
    'use strict';
    /**
     * Save game
     * @param {LifeMap} lifeMap
     * @returns {Boolean}
     */
    var saveGame = function (lifeMap) {
        try {
            window.localStorage['save'] = lifeMap.serialize();
            console.info('Game successfully saved');
            return true;
        } catch (e) {
            console.warn('Save error');
            return false;
        }
    };

    /**
     * Is save data exists
     * @returns {Boolean}
     */
    var saveExists = function () {
        console.info('Save data exists');
        return Boolean(typeof window.localStorage === 'object' && window.localStorage !== null && window.localStorage['save']);
    };

    /**
     * Load state from save data
     * @param {LifeMap} lifeMap
     */
    var loadGame = function (lifeMap) {
        var dump;
        if (saveExists()) {
            dump = window.localStorage['save'];
            lifeMap.loadSerializedState(dump);
            console.info('Loading initialized');
        }
    };

    return {
        saveGame: saveGame,
        saveExists: saveExists,
        loadGame: loadGame
    }
});
<commit_msg>remove console calls<commit_after>define(function () {
    'use strict';
    /**
     * Save game
     * @param {LifeMap} lifeMap
     * @returns {Boolean}
     */
    var saveGame = function (lifeMap) {
        window.localStorage['save'] = lifeMap.serialize();
    };

    /**
     * Is save data exists
     * @returns {Boolean}
     */
    var saveExists = function () {
        return Boolean(typeof window.localStorage === 'object' && window.localStorage !== null && window.localStorage['save']);
    };

    /**
     * Load state from save data
     * @param {LifeMap} lifeMap
     */
    var loadGame = function (lifeMap) {
        var dump;
        if (saveExists()) {
            dump = window.localStorage['save'];
            lifeMap.loadSerializedState(dump);
        }
    };

    return {
        saveGame: saveGame,
        saveExists: saveExists,
        loadGame: loadGame
    }
});
<|endoftext|>


<commit_before>var uglifyjs = require('uglify-js');

exports.getBaseController = function(code) {
	var baseController = '"baseController"';
	var ast = uglifyjs.parse(code);
	ast.walk(new uglifyjs.TreeWalker(function(node) {
		if (node instanceof uglifyjs.AST_Assign) {
			var left = node.left.print_to_string();
			if (left === 'exports.baseController' ||
				left === 'exports["baseController"]' ||
				left === "exports['baseController']") {
				baseController = node.right.print_to_string();
			}
		}
	}));

	return baseController;
};<commit_msg>fixed casing of BaseController for default controller inheritance<commit_after>var uglifyjs = require('uglify-js');

exports.getBaseController = function(code) {
	var baseController = '"BaseController"';
	var ast = uglifyjs.parse(code);
	ast.walk(new uglifyjs.TreeWalker(function(node) {
		if (node instanceof uglifyjs.AST_Assign) {
			var left = node.left.print_to_string();
			if (left === 'exports.baseController' ||
				left === 'exports["baseController"]' ||
				left === "exports['baseController']") {
				baseController = node.right.print_to_string();
			}
		}
	}));

	return baseController;
};<|endoftext|>


<commit_before>( function( $ ) {

	$.widget( "vui.vui_moreless", {

		options: {
			height: '4em',
			lineHeight: 'normal',
			title: {
				more: "more",
				less: "less"
			}
		},

		_create: function() {
			var me = this;
			var $moreblur = null;
			var $moreless = $( this.element );
			var $morelink = $( '
' );

			$moreless.after( $morelink );

			me.options.title.more = $moreless.attr( 'data-moreless-moretitle' ) !== undefined ? $moreless.attr( 'data-moreless-moretitle' ) : this.options.title.more;
			me.options.title.less = $moreless.attr( 'data-moreless-lesstitle' ) !== undefined ? $moreless.attr( 'data-moreless-lesstitle' ) : this.options.title.less;
			me.options.accessible = $moreless.attr( 'data-moreless-accessible' ) !== undefined ? true : false;

			var lineHeight = $moreless.attr( 'data-moreless-lineHeight' ) !== undefined ? $moreless.attr( 'data-moreless-lineHeight' ) : this.options.lineHeight;
			$moreless.css( 'line-height', lineHeight );

			$breakafter = $moreless.find( '.vui-moreless-breakafter' );
			var height = 0;

			if( $breakafter.length ) {
				$breakafter = $( $breakafter[0] );
				//determine hieght based on bottom on node with breakafter class
				height = ( $breakafter.position().top - $moreless.position().top ) + $breakafter.get(0).scrollHeight;

			} else {
				height = $moreless.attr( 'data-moreless-height' ) !== undefined ? $moreless.attr( 'data-moreless-height' ) : this.options.height;

				if( height.indexOf( '%' ) > -1 ) { //convert percent to px to prevent loss of transition
					height = parseInt( ( $moreless.get( 0 ).scrollHeight * ( parseInt( height, 10 ) / 100 ) ) + 0.5, 10 );
				}

				$moreblur = $( '
');
				$moreless.after( $moreblur );
			}

			$moreless.height( height );

			me._accessibileButton( me.options.title.more, $morelink, 'vui-icon-cheverondownblue' );

			$morelink.on( 'click', function( e ) {
				me._switchMoreLess( $moreless, height, $morelink, $moreblur );
			} );

			if( $moreless.height() >= $moreless.get( 0 ).scrollHeight ) {
				$morelink.css( 'display', 'none' );
				if( $moreblur ) {
					$moreblur.css( 'display', 'none' );
				}
			}

		},

		_switchMoreLess: function( $moreless, inHeight, $morelink, $moreblur ) {
			if( $moreless.hasClass( 'vui-moreless-more' ) ) {
				$moreless.removeClass( 'vui-moreless-more' );
				if( $moreblur ) {
					$moreblur.css( 'display', 'block' );
				}
				$moreless.css( 'height', inHeight );
				this._accessibileButton( this.options.title.more, $morelink, 'vui-icon-cheverondownblue' );

			} else {
				$moreless.addClass( 'vui-moreless-more' );
				if( $moreblur ) {
					$moreblur.css( 'display', 'none' );
				}
				$moreless.css( 'height', $moreless.get( 0 ).scrollHeight );
				this._accessibileButton( this.options.title.less, $morelink, 'vui-icon-cheveronupblue' );
			}
		},

		_accessibileButton: function( title, $morelink, iconClass ) {

			$morelink.empty();
			$morelink.append( '' );
			if( !this.options.accessible ) {
				$morelink.attr( 'aria-hidden', 'true' );
				$morelink.append( title );
			} else {
				$morelink.attr( 'aria-role', 'button' );
				title = $( '' + title + '' );
				$morelink.append( title );
			}
		}
	} );

	vui.addClassInitializer(
		'vui-moreless',
		function( node ) {
			$( node ).vui_moreless();
		}
	);

} )( window.jQuery );<commit_msg>MoreLess fixed breakafter from hiding last line<commit_after>( function( $ ) {

	$.widget( "vui.vui_moreless", {

		options: {
			height: '4em',
			lineHeight: 'normal',
			title: {
				more: "more",
				less: "less"
			}
		},

		_create: function() {
			var me = this;
			var $moreblur = null;
			var $moreless = $( this.element );
			var $morelink = $( '
' );

			$moreless.after( $morelink );

			me.options.title.more = $moreless.attr( 'data-moreless-moretitle' ) !== undefined ? $moreless.attr( 'data-moreless-moretitle' ) : this.options.title.more;
			me.options.title.less = $moreless.attr( 'data-moreless-lesstitle' ) !== undefined ? $moreless.attr( 'data-moreless-lesstitle' ) : this.options.title.less;
			me.options.accessible = $moreless.attr( 'data-moreless-accessible' ) !== undefined ? true : false;

			var lineHeight = $moreless.attr( 'data-moreless-lineHeight' ) !== undefined ? $moreless.attr( 'data-moreless-lineHeight' ) : this.options.lineHeight;
			$moreless.css( 'line-height', lineHeight );

			$breakafter = $moreless.find( '.vui-moreless-breakafter' );
			var height = 0;

			if( $breakafter.length ) {
				$breakafter = $( $breakafter[0] );
				//determine hieght based on bottom on node with breakafter class
				height =  $breakafter.get(0).scrollHeight;

			} else {
				height = $moreless.attr( 'data-moreless-height' ) !== undefined ? $moreless.attr( 'data-moreless-height' ) : this.options.height;

				if( height.indexOf( '%' ) > -1 ) { //convert percent to px to prevent loss of transition
					height = parseInt( ( $moreless.get( 0 ).scrollHeight * ( parseInt( height, 10 ) / 100 ) ) + 0.5, 10 );
				}

				$moreblur = $( '
');
				$moreless.after( $moreblur );
			}

			$moreless.height( height );

			me._accessibileButton( me.options.title.more, $morelink, 'vui-icon-cheverondownblue' );

			$morelink.on( 'click', function( e ) {
				me._switchMoreLess( $moreless, height, $morelink, $moreblur );
			} );

			if( $moreless.height() >= $moreless.get( 0 ).scrollHeight ) {
				$morelink.css( 'display', 'none' );
				if( $moreblur ) {
					$moreblur.css( 'display', 'none' );
				}
			}

		},

		_switchMoreLess: function( $moreless, inHeight, $morelink, $moreblur ) {
			if( $moreless.hasClass( 'vui-moreless-more' ) ) {
				$moreless.removeClass( 'vui-moreless-more' );
				if( $moreblur ) {
					$moreblur.css( 'display', 'block' );
				}
				$moreless.css( 'height', inHeight );
				this._accessibileButton( this.options.title.more, $morelink, 'vui-icon-cheverondownblue' );

			} else {
				$moreless.addClass( 'vui-moreless-more' );
				if( $moreblur ) {
					$moreblur.css( 'display', 'none' );
				}
				$moreless.css( 'height', $moreless.get( 0 ).scrollHeight );
				this._accessibileButton( this.options.title.less, $morelink, 'vui-icon-cheveronupblue' );
			}
		},

		_accessibileButton: function( title, $morelink, iconClass ) {

			$morelink.empty();
			$morelink.append( '' );
			if( !this.options.accessible ) {
				$morelink.attr( 'aria-hidden', 'true' );
				$morelink.append( title );
			} else {
				$morelink.attr( 'aria-role', 'button' );
				title = $( '' + title + '' );
				$morelink.append( title );
			}
		}
	} );

	vui.addClassInitializer(
		'vui-moreless',
		function( node ) {
			$( node ).vui_moreless();
		}
	);

} )( window.jQuery );<|endoftext|>


<commit_before>'use strict';

var path = require('path')
  , soap = require('soap')
  , path = require('path')
  , fs = require('fs')
  , WebBot = require('../lib/webbot')
  , specUtil = require('./spec-util')
  , logger = require('nice-logger').logger
  , baseDir;

baseDir = path.join(__dirname, '..');

describe('Automate web visit', function () {
  var baseDir;
  baseDir = path.join(__dirname, '..');
  before(function (done) {
    var myService, wsdl;
    myService = {
      StockQuoteService: {
        StockQuotePort: {
          GetLastTradePrice: function (args) {
            if (args.tickerSymbol === 'trigger error') {
              throw new Error('triggered server error');
            } else {
              return { price: 19.56 };
            }
          }
        }
      }
    };
    wsdl = fs.readFileSync(path.join(__dirname, 'stockquote.wsdl'), 'utf8');
    specUtil.startWebServer(function () {
      soap.listen(specUtil.httpServer, '/stockquote', myService, wsdl);
      done();
    });
  });
  after(function (done) {
    specUtil.stopWebServer(done);
  });
  it.only('should play a json soap test scenario', function (done) {
    var webBot, scenario = require('./soapScenario.json');

    function completed(err) {
      var elapsedTime = webBot.elapsedTime();
      logger.info('WebBotjs test took %s seconds', elapsedTime);
      done(err);
    }

    this.timeout(10000);
    webBot = new WebBot(baseDir);
    webBot.runStepsFromJsonScenario({ jsonScenario: scenario }, completed);
  });
});<commit_msg>all tests<commit_after>'use strict';

var path = require('path')
  , soap = require('soap')
  , path = require('path')
  , fs = require('fs')
  , WebBot = require('../lib/webbot')
  , specUtil = require('./spec-util')
  , logger = require('nice-logger').logger
  , baseDir;

baseDir = path.join(__dirname, '..');

describe('Automate web visit', function () {
  var baseDir;
  baseDir = path.join(__dirname, '..');
  before(function (done) {
    var myService, wsdl;
    myService = {
      StockQuoteService: {
        StockQuotePort: {
          GetLastTradePrice: function (args) {
            if (args.tickerSymbol === 'trigger error') {
              throw new Error('triggered server error');
            } else {
              return { price: 19.56 };
            }
          }
        }
      }
    };
    wsdl = fs.readFileSync(path.join(__dirname, 'stockquote.wsdl'), 'utf8');
    specUtil.startWebServer(function () {
      soap.listen(specUtil.httpServer, '/stockquote', myService, wsdl);
      done();
    });
  });
  after(function (done) {
    specUtil.stopWebServer(done);
  });
  it('should play a json soap test scenario', function (done) {
    var webBot, scenario = require('./soapScenario.json');

    function completed(err) {
      var elapsedTime = webBot.elapsedTime();
      logger.info('WebBotjs test took %s seconds', elapsedTime);
      done(err);
    }

    this.timeout(10000);
    webBot = new WebBot(baseDir);
    webBot.runStepsFromJsonScenario({ jsonScenario: scenario }, completed);
  });
});<|endoftext|>


<commit_before>var utils = require('./test-utils');
var assert = require('chai').assert;
var http = require('http');
var fs = require('fs');

suite('dashboard', function() {
  var server;

  setup(function() {
    server = utils.setupServer();
  });

  teardown(function() {
    server.close();
  });

  test('GET /', function(done) {
    var options = {
      host: utils.testHost,
      port: utils.testPort,
      path: '/'
    };
    http.get(options, function(response) {
      assert.equal(response.statusCode, 200);
      var body = '';
      response.on('data', function(data) {
        body += data;
      });
      response.on('end', function() {
        assert.match(body, /<h1>Groonga CloudSearch<\/h1>/);
        done();
      });
    });
  });
});
<commit_msg>Follow the test to view modification<commit_after>var utils = require('./test-utils');
var assert = require('chai').assert;
var http = require('http');
var fs = require('fs');

suite('dashboard', function() {
  var server;

  setup(function() {
    server = utils.setupServer();
  });

  teardown(function() {
    server.close();
  });

  test('GET /', function(done) {
    var options = {
      host: utils.testHost,
      port: utils.testPort,
      path: '/'
    };
    http.get(options, function(response) {
      assert.equal(response.statusCode, 200);
      var body = '';
      response.on('data', function(data) {
        body += data;
      });
      response.on('end', function() {
        assert.match(body, /Groonga CloudSearch/);
        done();
      });
    });
  });
});
<|endoftext|>


<commit_before>
describe('tbl', function () {
    before(function (done) {
        async.eachSeries(['tbl','mto','tbl_has_mtm','mto_has_mtm'], function (table, done) {
            client.query(sql.truncate(table, 'x'), done);
        }, function () {
            $('a[href="/tbl"]')[0].click();
            page.load(done);
        });
    });

    it('should be empty', function (done) {
        $('.x-table tbody tr').length.should.equal(0);
        $('a[href="/tbl/add"]')[0].click();
        page.load(done);
    });

    it('empty fields', function (done) {
        $('[name="action[save]"')[0].click();
        page.load(function () {
            win.location.pathname.should.equal('/tbl/add');
            $('.alert-danger strong').text().should.equal('Error:');
            $('.has-error').length.should.equal(13);
            done();
        });
    });

    it('persist state', function (done) {
        // otm
        $('[name="view[tbl][records][0][columns][otm_id]"] option:eq(1)').attr('selected',true);
        $('[name="view[tbl][records][0][columns][otm_id]"]').trigger('chosen:updated');
        // mtm
        $('[name="view[tbl][records][0][columns][mtm]"] option:eq(1)').attr('selected',true);
        $('[name="view[tbl][records][0][columns][mtm]"]').trigger('chosen:updated');
        // static
        $('[name="view[tbl][records][0][columns][static]"] option:eq(1)').attr('selected',true);
        $('[name="view[tbl][records][0][columns][static]"]').trigger('chosen:updated');

        $('[name="view[tbl][records][0][columns][text]"]').val('one');
        $('[name="view[tbl][records][0][columns][boolean]"]:eq(0)').attr('checked', true);
        $('[name="view[tbl][records][0][columns][int]"]').val('1');
        $('[name="view[tbl][records][0][columns][decimal]"]').val('1.1');

        $('[name="view[tbl][records][0][columns][date]"]').val('2014-06-01').datetimepicker('update');
        $('[name="view[tbl][records][0][columns][time]"]').val('10:00').datetimepicker('update');
        $('[name="view[tbl][records][0][columns][datetime]"]').val('2014-06-01 10:00').datetimepicker('update');
        $('[name="view[tbl][records][0][columns][year]"]').val('2014');

        win.CKEDITOR.instances['view[tbl][records][0][columns][textarea]'].setData('one', function () {
            $('[name="action[save]"')[0].click();
            page.load(function () {
                win.location.pathname.should.equal('/tbl/add');
                $('.alert-danger strong').text().should.equal('Error:');
                $('.has-error').length.should.equal(1);

                // otm
                $('[name="view[tbl][records][0][columns][otm_id]"] option:eq(1)').attr('selected').should.equal('selected');
                // mtm
                $('[name="view[tbl][records][0][columns][mtm]"] option:eq(1)').attr('selected').should.equal('selected');
                // static
                $('[name="view[tbl][records][0][columns][static]"] option:eq(1)').attr('selected').should.equal('selected');

                $('[name="view[tbl][records][0][columns][text]"]').val().should.equal('one');
                $('[name="view[tbl][records][0][columns][boolean]"]:eq(0)').attr('checked').should.equal('checked');
                $('[name="view[tbl][records][0][columns][int]"]').val().should.equal('1');
                $('[name="view[tbl][records][0][columns][decimal]"]').val().should.equal('1.1');

                $('[name="view[tbl][records][0][columns][date]"]').val().should.equal('2014-06-01');
                $('[name="view[tbl][records][0][columns][time]"]').val().should.equal('10:00');
                $('[name="view[tbl][records][0][columns][datetime]"]').val().should.equal('2014-06-01 10:00:00');

                win.CKEDITOR.instances['view[tbl][records][0][columns][textarea]'].getData().trim().should.equal('
one
');

                done();
            });
        });
    });

    it('jq plugin init', function (done) {
        $('.add-another:eq(0)')[0].click();

        // chosen
        $('[name="manyToOne[mto][records][0][columns][otm_id]"]').trigger('chosen:open');
        $('[name="manyToOne[mto][records][0][columns][otm_id]"]').next().hasClass('chosen-container-active').should.equal(true);
        $('[name="manyToOne[mto][records][0][columns][otm_id]"]').next().find('.chosen-drop').is(':visible').should.equal(true);

        $('[name="manyToOne[mto][records][0][columns][mtm]"]').trigger('chosen:open');
        $('[name="manyToOne[mto][records][0][columns][mtm]"]').next().hasClass('chosen-container-active').should.equal(true);
        $('[name="manyToOne[mto][records][0][columns][mtm]"]').next().find('.chosen-drop').is(':visible').should.equal(true);

        $('[name="manyToOne[mto][records][0][columns][static]"]').trigger('chosen:open');
        $('[name="manyToOne[mto][records][0][columns][static]"]').next().hasClass('chosen-container-active').should.equal(true);
        $('[name="manyToOne[mto][records][0][columns][static]"]').next().find('.chosen-drop').is(':visible').should.equal(true);

        // datetime picker
        $('[name="manyToOne[mto][records][0][columns][date]"]').datetimepicker('show');
        $('.datetimepicker-dropdown-bottom-right:visible').length.should.equal(1);
        $('[name="manyToOne[mto][records][0][columns][date]"]').datetimepicker('hide');

        $('[name="manyToOne[mto][records][0][columns][time]"]').datetimepicker('show');
        $('.datetimepicker-dropdown-bottom-right:visible').length.should.equal(1);
        $('[name="manyToOne[mto][records][0][columns][time]"]').datetimepicker('hide');

        $('[name="manyToOne[mto][records][0][columns][datetime]"]').datetimepicker('show');
        $('.datetimepicker-dropdown-bottom-right:visible').length.should.equal(1);
        $('[name="manyToOne[mto][records][0][columns][datetime]"]').datetimepicker('hide');

        // editor
        Object.keys(win.CKEDITOR.instances).length.should.equal(2);
        done();
    });

    it('store inline record with all fields empty', function (done) {
        $('[type=text][name="view[tbl][records][0][columns][upload]"]').val('one');
        $('[name="action[another]"')[0].click();
        page.load(function () {
            win.location.pathname.should.equal('/tbl/add');
            $('.alert-success strong').text().should.equal('Success:');
            done();
        });
    });

    it('add 3 more records', function (done) {
        $('a[href="/tbl"]')[0].click();
        page.load(done);
    });

    after(function (done) {
        $('a[href="/"]')[0].click();
        page.load(done);
    });
});
<commit_msg>Add 3 more records to data-types tbl test<commit_after>
describe('tbl', function () {
    before(function (done) {
        async.eachSeries(['tbl','mto','tbl_has_mtm','mto_has_mtm'], function (table, done) {
            client.query(sql.truncate(table, 'x'), done);
        }, function () {
            $('a[href="/tbl"]')[0].click();
            page.load(done);
        });
    });

    it('should be empty', function (done) {
        $('.x-table tbody tr').length.should.equal(0);
        $('a[href="/tbl/add"]')[0].click();
        page.load(done);
    });

    it('empty fields', function (done) {
        $('[name="action[save]"')[0].click();
        page.load(function () {
            win.location.pathname.should.equal('/tbl/add');
            $('.alert-danger strong').text().should.equal('Error:');
            $('.has-error').length.should.equal(13);
            done();
        });
    });

    it('persist state', function (done) {
        // otm
        $('[name="view[tbl][records][0][columns][otm_id]"] option:eq(1)').attr('selected',true);
        $('[name="view[tbl][records][0][columns][otm_id]"]').trigger('chosen:updated');
        // mtm
        $('[name="view[tbl][records][0][columns][mtm]"] option:eq(1)').attr('selected',true);
        $('[name="view[tbl][records][0][columns][mtm]"]').trigger('chosen:updated');
        // static
        $('[name="view[tbl][records][0][columns][static]"] option:eq(1)').attr('selected',true);
        $('[name="view[tbl][records][0][columns][static]"]').trigger('chosen:updated');

        $('[name="view[tbl][records][0][columns][text]"]').val('one');
        $('[name="view[tbl][records][0][columns][boolean]"]:eq(0)').attr('checked', true);
        $('[name="view[tbl][records][0][columns][int]"]').val('1');
        $('[name="view[tbl][records][0][columns][decimal]"]').val('1.1');

        $('[name="view[tbl][records][0][columns][date]"]').val('2014-06-01').datetimepicker('update');
        $('[name="view[tbl][records][0][columns][time]"]').val('10:00').datetimepicker('update');
        $('[name="view[tbl][records][0][columns][datetime]"]').val('2014-06-01 10:00').datetimepicker('update');
        $('[name="view[tbl][records][0][columns][year]"]').val('2014');

        win.CKEDITOR.instances['view[tbl][records][0][columns][textarea]'].setData('one', function () {
            $('[name="action[save]"')[0].click();
            page.load(function () {
                win.location.pathname.should.equal('/tbl/add');
                $('.alert-danger strong').text().should.equal('Error:');
                $('.has-error').length.should.equal(1);

                // otm
                $('[name="view[tbl][records][0][columns][otm_id]"] option:eq(1)').attr('selected').should.equal('selected');
                // mtm
                $('[name="view[tbl][records][0][columns][mtm]"] option:eq(1)').attr('selected').should.equal('selected');
                // static
                $('[name="view[tbl][records][0][columns][static]"] option:eq(1)').attr('selected').should.equal('selected');

                $('[name="view[tbl][records][0][columns][text]"]').val().should.equal('one');
                $('[name="view[tbl][records][0][columns][boolean]"]:eq(0)').attr('checked').should.equal('checked');
                $('[name="view[tbl][records][0][columns][int]"]').val().should.equal('1');
                $('[name="view[tbl][records][0][columns][decimal]"]').val().should.equal('1.1');

                $('[name="view[tbl][records][0][columns][date]"]').val().should.equal('2014-06-01');
                $('[name="view[tbl][records][0][columns][time]"]').val().should.equal('10:00');
                $('[name="view[tbl][records][0][columns][datetime]"]').val().should.equal('2014-06-01 10:00:00');

                win.CKEDITOR.instances['view[tbl][records][0][columns][textarea]'].getData().trim().should.equal('
one
');

                done();
            });
        });
    });

    it('jq plugin init', function (done) {
        $('.add-another:eq(0)')[0].click();

        // chosen
        $('[name="manyToOne[mto][records][0][columns][otm_id]"]').trigger('chosen:open');
        $('[name="manyToOne[mto][records][0][columns][otm_id]"]').next().hasClass('chosen-container-active').should.equal(true);
        $('[name="manyToOne[mto][records][0][columns][otm_id]"]').next().find('.chosen-drop').is(':visible').should.equal(true);

        $('[name="manyToOne[mto][records][0][columns][mtm]"]').trigger('chosen:open');
        $('[name="manyToOne[mto][records][0][columns][mtm]"]').next().hasClass('chosen-container-active').should.equal(true);
        $('[name="manyToOne[mto][records][0][columns][mtm]"]').next().find('.chosen-drop').is(':visible').should.equal(true);

        $('[name="manyToOne[mto][records][0][columns][static]"]').trigger('chosen:open');
        $('[name="manyToOne[mto][records][0][columns][static]"]').next().hasClass('chosen-container-active').should.equal(true);
        $('[name="manyToOne[mto][records][0][columns][static]"]').next().find('.chosen-drop').is(':visible').should.equal(true);

        // datetime picker
        $('[name="manyToOne[mto][records][0][columns][date]"]').datetimepicker('show');
        $('.datetimepicker-dropdown-bottom-right:visible').length.should.equal(1);
        $('[name="manyToOne[mto][records][0][columns][date]"]').datetimepicker('hide');

        $('[name="manyToOne[mto][records][0][columns][time]"]').datetimepicker('show');
        $('.datetimepicker-dropdown-bottom-right:visible').length.should.equal(1);
        $('[name="manyToOne[mto][records][0][columns][time]"]').datetimepicker('hide');

        $('[name="manyToOne[mto][records][0][columns][datetime]"]').datetimepicker('show');
        $('.datetimepicker-dropdown-bottom-right:visible').length.should.equal(1);
        $('[name="manyToOne[mto][records][0][columns][datetime]"]').datetimepicker('hide');

        // editor
        Object.keys(win.CKEDITOR.instances).length.should.equal(2);
        done();
    });

    it('store inline record with all fields empty', function (done) {
        $('[type=text][name="view[tbl][records][0][columns][upload]"]').val('one');
        $('[name="action[another]"')[0].click();
        page.load(function () {
            win.location.pathname.should.equal('/tbl/add');
            $('.alert-success strong').text().should.equal('Success:');
            done();
        });
    });

    it('add 3 more records', function (done) {
        var queries = [
            "INSERT INTO `tbl` (`id`,`otm_id`,`static`,`text`,`boolean`,`int`,`decimal`,`upload`,`date`,`time`,`datetime`,`year`,`textarea`) \
            VALUES ('e1AmGnGcel',2,'two','two',0,2,2.2,'two','2014-06-02','10:10','2014-06-02 10:10','2014','
two
') ; \
            INSERT INTO `tbl_has_mtm` (`tbl_id`,`mtm_id`) VALUES ('e1AmGnGcel','2') ;",

            "INSERT INTO `tbl` (`id`,`otm_id`,`static`,`text`,`boolean`,`int`,`decimal`,`upload`,`date`,`time`,`datetime`,`year`,`textarea`) \
            VALUES ('ekIHuTfqel',3,'three','three',1,3,3.3,'three','2014-06-03','10:30','2014-06-03 10:30','2014','
three
') ; \
            INSERT INTO `tbl_has_mtm` (`tbl_id`,`mtm_id`) VALUES ('ekIHuTfqel','3') ;",

            "INSERT INTO `tbl` (`id`,`otm_id`,`static`,`text`,`boolean`,`int`,`decimal`,`upload`,`date`,`time`,`datetime`,`year`,`textarea`) \
            VALUES ('g1i_D0f5le',1,'one','one',1,1,1.1,'one','2014-06-04','10:40','2014-06-04 10:40','2014','
four
') ; \
            INSERT INTO `tbl_has_mtm` (`tbl_id`,`mtm_id`) VALUES ('g1i_D0f5le','1'),('g1i_D0f5le','3') ;"
        ];
        async.eachSeries(queries, function (query, done) {
            client.query(query, done);
        }, function (err) {
            if (err) return done(err);
            $('a[href="/tbl"]')[0].click();
            page.load(function () {
                $('.x-table tbody tr').length.should.equal(2);
                $('.pagination li').length.should.equal(4);
                $('.pagination li:eq(0)').hasClass('active').should.equal(true);
                $('.pagination li:eq(1) a').text().should.equal('2');
                done();
            });
        });
    });

    after(function (done) {
        $('a[href="/"]')[0].click();
        page.load(done);
    });
});
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012-2013 André Bargull
 * Alle Rechte vorbehalten / All Rights Reserved.  Use is subject to license terms.
 *
 * <https://github.com/anba/es6draft>
 */

(function OldProxyAPI(global) {
"use strict";

const Object = global.Object,
      Function = global.Function,
      Array = global.Array,
      Proxy = global.Proxy,
      TypeError = global.TypeError;

const Object_create = Object.create,
      Object_assign = Object.assign;

const Function_call = Function.prototype.call.bind(Function.prototype.call);

const iteratorSym = Symbol.iterator;

// pseudo-symbol in SpiderMonkey
const mozIteratorSym = "@@iterator";

function toProxyHandler(handler) {
  var TypeErrorThrower = () => { throw TypeError() };
  /* fundamental traps mapping:
   * getOwnPropertyDescriptor -> getOwnPropertyDescriptor
   * getPropertyDescriptor -> -
   * getOwnPropertyNames -> ownKeys
   * getPropertyNames -> -
   * defineProperty -> defineProperty
   * delete -> deleteProperty
   * fix -> -
   */
  var proxyHandler = {
    getOwnPropertyDescriptor: TypeErrorThrower,
    defineProperty: TypeErrorThrower,
    deleteProperty: TypeErrorThrower,
    ownKeys: TypeErrorThrower,
  };

  // fundamental traps
  if ('getOwnPropertyDescriptor' in handler) {
    proxyHandler['getOwnPropertyDescriptor'] = (_, pk) => handler['getOwnPropertyDescriptor'](pk);
  } else if ('getPropertyDescriptor' in handler) {
    proxyHandler['getOwnPropertyDescriptor'] = (_, pk) => handler['getPropertyDescriptor'](pk);
  }
  if ('getOwnPropertyNames' in handler) {
    proxyHandler['ownKeys'] = () => [...handler['getOwnPropertyNames']()][iteratorSym]();
  }
  if ('defineProperty' in handler) {
    proxyHandler['defineProperty'] = (_, pk, desc) => (handler['defineProperty'](pk, desc), true);
  }
  if ('delete' in handler) {
    proxyHandler['deleteProperty'] = (_, pk) => handler['delete'](pk);
  }

  // derived traps
  if ('has' in handler) {
    proxyHandler['has'] = (_, pk) => handler['has'](pk);
  } else {
    proxyHandler['has'] = (_, pk) => !!handler['getPropertyDescriptor'](pk);
  }
  if ('hasOwn' in handler) {
    proxyHandler['hasOwn'] = (_, pk) => handler['hasOwn'](pk);
  } else {
    proxyHandler['hasOwn'] = (_, pk) => !!handler['getOwnPropertyDescriptor'](pk);
  }
  if ('get' in handler && typeof handler['get'] == 'function') {
    proxyHandler['get'] = (_, pk, receiver) => handler['get'](receiver, pk);
  } else {
    proxyHandler['get'] = (_, pk, receiver) => {
      // XXX: special case for iteration tests
      if (pk === iteratorSym) {
        var desc = handler['getPropertyDescriptor'](mozIteratorSym);
        if (desc !== undefined && 'value' in desc) {
          // call @@iterator() so we don't end up with a StopIteration based Iterator
          return function() { return desc.value.call(this)[iteratorSym]() };
        }
      }
      var desc = handler['getPropertyDescriptor'](pk);
      if (desc !== undefined && 'value' in desc) {
        return desc.value;
      }
      if (desc !== undefined && desc.get !== undefined) {
        return desc.get.call(receiver);
      }
    };
  }
  if ('set' in handler) {
    proxyHandler['set'] = (_, pk, value, receiver) => handler['set'](receiver, pk, value);
  } else {
    proxyHandler['set'] = (_, pk, value, receiver) => {
      var desc = handler['getOwnPropertyDescriptor'](pk);
      if (!desc) {
        desc = handler['getPropertyDescriptor'](pk);
        if (!desc) {
          desc = {
            writable: true, enumerable: true, configurable: true
          };
        }
      }
      if (('writable' in desc) && desc.writable) {
        handler['defineProperty'](pk, (desc.value = value, desc));
        return true;
      }
      if (!('writable' in desc) && desc.set) {
        desc.set.call(receiver, value);
        return true;
      }
      return false;
    };
  }
  if ('enumerate' in handler) {
    proxyHandler['enumerate'] = () => [...handler['enumerate']()][iteratorSym]();
  } else if ('iterate' in handler) {
    proxyHandler['enumerate'] = () => handler['iterate']()[iteratorSym]();
  } else {
    proxyHandler['enumerate'] = () => handler['getPropertyNames']().filter(
      pk => handler['getPropertyDescriptor'](pk).enumerable
    )[iteratorSym]();
  }
  if ('keys' in handler) {
    proxyHandler['ownKeys'] = () => [...handler['keys']()][iteratorSym]();
  }
  proxyHandler['invoke'] = (_, pk, args, receiver) => Function_call(proxyHandler['get'](_, pk, receiver), receiver, ...args);
  return proxyHandler;
}

Object.defineProperties(Object_assign(Proxy, {
  create(handler, proto = null) {
    if (Object(handler) !== handler) throw TypeError();
    var proxyTarget = Object_create(proto);
    var proxyHandler = Object_assign({
      setPrototypeOf() { throw TypeError() }
    }, toProxyHandler(handler));
    return new Proxy(proxyTarget, proxyHandler);
  },
  createFunction(handler, callTrap, constructTrap = callTrap) {
    if (Object(handler) !== handler) throw TypeError();
    if (typeof callTrap != 'function') throw TypeError();
    if (typeof constructTrap != 'function') throw TypeError();
    var proxyTarget = function(){};
    var proxyHandler = Object_assign({
      setPrototypeOf() { throw TypeError() },
      apply(_, thisValue, args) { return callTrap.apply(thisValue, args) },
      construct(_, args) { return new constructTrap(...args) }
    }, toProxyHandler(handler));
    return new Proxy(proxyTarget, proxyHandler);
  }
}), {
  create: {enumerable: false},
  createFunction: {enumerable: false},
});

})(this);
<commit_msg>Handle missing 'invoke' trap support in proxy.js compatibility script<commit_after>/*
 * Copyright (c) 2012-2013 André Bargull
 * Alle Rechte vorbehalten / All Rights Reserved.  Use is subject to license terms.
 *
 * <https://github.com/anba/es6draft>
 */

(function OldProxyAPI(global) {
"use strict";

const Object = global.Object,
      Function = global.Function,
      Array = global.Array,
      Proxy = global.Proxy,
      Reflect = global.Reflect,
      TypeError = global.TypeError;

const Object_create = Object.create,
      Object_assign = Object.assign;

const $CallFunction = Function.prototype.call.bind(Function.prototype.call);

const iteratorSym = Symbol.iterator;

// pseudo-symbol in SpiderMonkey
const mozIteratorSym = "@@iterator";

function toProxyHandler(handler) {
  var TypeErrorThrower = () => { throw TypeError() };
  /* fundamental traps mapping:
   * getOwnPropertyDescriptor -> getOwnPropertyDescriptor
   * getPropertyDescriptor -> -
   * getOwnPropertyNames -> ownKeys
   * getPropertyNames -> -
   * defineProperty -> defineProperty
   * delete -> deleteProperty
   * fix -> -
   */
  var proxyHandler = {
    getOwnPropertyDescriptor: TypeErrorThrower,
    defineProperty: TypeErrorThrower,
    deleteProperty: TypeErrorThrower,
    ownKeys: TypeErrorThrower,
  };

  // fundamental traps
  if ('getOwnPropertyDescriptor' in handler) {
    proxyHandler['getOwnPropertyDescriptor'] = (_, pk) => handler['getOwnPropertyDescriptor'](pk);
  } else if ('getPropertyDescriptor' in handler) {
    proxyHandler['getOwnPropertyDescriptor'] = (_, pk) => handler['getPropertyDescriptor'](pk);
  }
  if ('getOwnPropertyNames' in handler) {
    proxyHandler['ownKeys'] = () => [...handler['getOwnPropertyNames']()][iteratorSym]();
  }
  if ('defineProperty' in handler) {
    proxyHandler['defineProperty'] = (_, pk, desc) => (handler['defineProperty'](pk, desc), true);
  }
  if ('delete' in handler) {
    proxyHandler['deleteProperty'] = (_, pk) => handler['delete'](pk);
  }

  // derived traps
  if ('has' in handler) {
    proxyHandler['has'] = (_, pk) => handler['has'](pk);
  } else {
    proxyHandler['has'] = (_, pk) => !!handler['getPropertyDescriptor'](pk);
  }
  if ('hasOwn' in handler) {
    proxyHandler['hasOwn'] = (_, pk) => handler['hasOwn'](pk);
  } else {
    proxyHandler['hasOwn'] = (_, pk) => !!handler['getOwnPropertyDescriptor'](pk);
  }
  if ('get' in handler && typeof handler['get'] == 'function') {
    proxyHandler['get'] = (_, pk, receiver) => handler['get'](receiver, pk);
  } else {
    proxyHandler['get'] = (_, pk, receiver) => {
      // XXX: special case for iteration tests
      if (pk === iteratorSym) {
        var desc = handler['getPropertyDescriptor'](mozIteratorSym);
        if (desc !== undefined && 'value' in desc) {
          // call @@iterator() so we don't end up with a StopIteration based Iterator
          return function() { return desc.value.call(this)[iteratorSym]() };
        }
      }
      var desc = handler['getPropertyDescriptor'](pk);
      if (desc !== undefined && 'value' in desc) {
        return desc.value;
      }
      if (desc !== undefined && desc.get !== undefined) {
        return desc.get.call(receiver);
      }
    };
  }
  if ('set' in handler) {
    proxyHandler['set'] = (_, pk, value, receiver) => handler['set'](receiver, pk, value);
  } else {
    proxyHandler['set'] = (_, pk, value, receiver) => {
      var desc = handler['getOwnPropertyDescriptor'](pk);
      if (!desc) {
        desc = handler['getPropertyDescriptor'](pk);
        if (!desc) {
          desc = {
            writable: true, enumerable: true, configurable: true
          };
        }
      }
      if (('writable' in desc) && desc.writable) {
        handler['defineProperty'](pk, (desc.value = value, desc));
        return true;
      }
      if (!('writable' in desc) && desc.set) {
        desc.set.call(receiver, value);
        return true;
      }
      return false;
    };
  }
  if ('enumerate' in handler) {
    proxyHandler['enumerate'] = () => [...handler['enumerate']()][iteratorSym]();
  } else if ('iterate' in handler) {
    proxyHandler['enumerate'] = () => handler['iterate']()[iteratorSym]();
  } else {
    proxyHandler['enumerate'] = () => handler['getPropertyNames']().filter(
      pk => handler['getPropertyDescriptor'](pk).enumerable
    )[iteratorSym]();
  }
  if ('keys' in handler) {
    proxyHandler['ownKeys'] = () => [...handler['keys']()][iteratorSym]();
  }
  proxyHandler['invoke'] = (_, pk, args, receiver) => $CallFunction(proxyHandler['get'](_, pk, receiver), receiver, ...args);
  return proxyHandler;
}

// Create a Proxy for the Proxy function in order to add an "invoke" trap while still passing Proxy surface tests
var BuiltinProxy = Proxy;
var NewProxy = new BuiltinProxy(BuiltinProxy, {
  addInvokeTrap(p, handler) {
    // Directly assign "invoke" trap to handler; this change is visible to other scripts, but tests don't complain
    Object.mixin(handler, {
      invoke(_, pk, args, receiver) {
        var fn = Reflect.get(p, pk, receiver);
        return $CallFunction(fn, p, ...args);
      }
    });
    return p;
  },
  apply(target, thisValue, args) {
    var p = $CallFunction(target, thisValue, ...args);
    return this.addInvokeTrap(p, args[1]);
  },
  construct(target, args) {
    var p = new target(...args);
    return this.addInvokeTrap(p, args[1]);
  }
});

Object.defineProperties(Object_assign(NewProxy, {
  create(handler, proto = null) {
    if (Object(handler) !== handler) throw TypeError();
    var proxyTarget = Object_create(proto);
    var proxyHandler = Object_assign({
      setPrototypeOf() { throw TypeError() }
    }, toProxyHandler(handler));
    return new BuiltinProxy(proxyTarget, proxyHandler);
  },
  createFunction(handler, callTrap, constructTrap = callTrap) {
    if (Object(handler) !== handler) throw TypeError();
    if (typeof callTrap != 'function') throw TypeError();
    if (typeof constructTrap != 'function') throw TypeError();
    var proxyTarget = function(){};
    var proxyHandler = Object_assign({
      setPrototypeOf() { throw TypeError() },
      apply(_, thisValue, args) { return callTrap.apply(thisValue, args) },
      construct(_, args) { return new constructTrap(...args) }
    }, toProxyHandler(handler));
    return new BuiltinProxy(proxyTarget, proxyHandler);
  }
}), {
  create: {enumerable: false},
  createFunction: {enumerable: false},
});

Object.defineProperty(global, "Proxy", {
  value: NewProxy,
  writable: true, enumerable: false, configurable: true
});

})(this);
<|endoftext|>


<commit_before>$(function() {
  QUnit.module('Thorax Core');

  Backbone.history = new Backbone.History();
  Backbone.history.start();

  Thorax.templates['parent'] = '
{{view child}}
';
  Thorax.templates['child'] = '
{{value}}
';

  test("registry", function() {
    var viewClass = Thorax.View.extend({name: 'a-name'}, {});
    equal(viewClass.prototype.name, 'a-name', 'sets a name attribute on view class');
    equal((new viewClass).name, 'a-name', 'sets a name attribute on view instance');
    var viewClassB = viewClass.extend({
      name: 'b-name',
      key: 'value'
    });
    equal(viewClassB.prototype.key, 'value', 'can accept a class as a value');
    viewClass.extend({
      name: 'b-name',
      key: 'value2'
    });
    equal(Thorax.Views['b-name'].prototype.key, 'value2', 'will overwrite a previous class when passed a new one');
    Thorax.View.extend({
      name: 'a-name',
      key: 'value'
    });
    equal(Thorax.Views['a-name'].prototype.key, 'value', 'registry will extend an existing class prototype');

    //test nested
    Thorax.Views.Outer = {
      Inner: Thorax.View.extend({
        template: 'inner'
      }),
      More: {
        Nested: Thorax.View.extend({
          template: 'nested'
        })
      }
    };

    var view = new Thorax.View({
      template: '
{{view "Outer.Inner" tag="span"}}

{{view "Outer.More.Nested" tag="span"}}
'
    });
    view.render();
    equal(view.$('p > span').html(), 'inner', 'test nested registryGet');
    equal(view.$('div > span').html(), 'nested', 'test nested registryGet');
  });

  test("context may be an object", function() {
    var view = new (Thorax.View.extend({
      context: {
        a: 'a',
        b: 'b',
        c: function() {
          return 'c'
        }
      },
      template: '{{a}}{{b}}{{c}}'
    }));
    view.render();
    equal(view.html(), 'abc');
  });

  test("child views", function() {
    var childRenderedCount = 0,
        parentRenderedCount = 0;
    var Child = Thorax.View.extend({
      name: 'child',
      initialize: function() {
        this.on('rendered', function() {
          ++childRenderedCount;
        });
      }
    });
    var Parent = Thorax.View.extend({
      name: 'parent',
      initialize: function() {
        this.on('rendered', function() {
          ++parentRenderedCount;
        });
        this.childModel = new Backbone.Model({
          value: 'a'
        });
        this.child = new Thorax.Views['child']({
          model: this.childModel
        });
      }
    });
    var parent = new Parent();
    parent.render();
    equal(parent.$('[data-view-name="child"] > div').html(), 'a', 'view embedded');
    equal(parentRenderedCount, 1);
    equal(childRenderedCount, 1);

    parent.render();
    equal(parent.$('[data-view-name="child"] > div').html(), 'a', 'view embedded');
    equal(parentRenderedCount, 2, 're-render of parent does not render child');
    equal(childRenderedCount, 1, 're-render of parent does not render child');

    parent.childModel.set({value: 'b'});
    equal(parent.$('[data-view-name="child"] > div').html(), 'b', 'view embedded');
    equal(parentRenderedCount, 2, 're-render of child does not parent child');
    equal(childRenderedCount, 2, 're-render of child does not render parent');

    //ensure recursion does not happen when child view has the same model
    //as parent
    parent.setModel(parent.childModel);
    parent.model.set({value: 'c'});
    equal(parentRenderedCount, 4);
    equal(childRenderedCount, 3);
  });

  test("child view re-render will keep dom events intact", function() {
    this.clock.restore();
    var callCount = 0;
    var parent = new Thorax.View({
      child: new Thorax.View({
        events: {
          'click .test': function() {
            ++callCount;
          }
        },
        template: "

"
      }),
      template: "{{view child}}"
    });
    parent.render();
    document.body.appendChild(parent.el);
    expect(3);
    stop();
    setTimeout(function() {
      parent.child.$('.test').trigger('click');
      equal(callCount, 1);
      parent.render();
      equal(callCount, 1);
      parent.child.$('.test').trigger('click');
      equal(callCount, 2);
      $(parent.el).remove();
      start();
    }, 2);
  });

  test("can set view el", function() {
    $('body').append('

');
    var view = new Thorax.View({
      template: 'testing123',
      el: $('#test-target')[0]
    });
    view.render();
    equal($('#test-target-container > #test-target')[0].innerHTML, 'testing123');
    equal(view.el.parentNode, $('#test-target-container')[0]);
    $('#test-target-container').remove();
  });

  test("template function can be specified", function() {
    var childReturningString = new Thorax.View({
      template: function(data) {
        ok(data.cid && data.cid.match(/^t/));
        return 'template';
      }
    });
    childReturningString.render();
    equal(childReturningString.html(), 'template');
    var childReturningElement = new Thorax.View({
      template: function(data) {
        return $('
template
')[0];
      }
    });
    childReturningElement.render();
    equal(childReturningElement.$('p').html(), 'template');
    var childReturning$ = new Thorax.View({
      template: function(data) {
        return $('
template
');
      }
    });
    childReturning$.render();
    equal(childReturning$.$('p').html(), 'template');
  });

  test("super helper", function() {
    var parent, child;
    var t = Thorax.templates['super-named-test'] = '

';
    parent = Thorax.View.extend({
      name: 'super-named-test'
    });
    child = new (parent.extend({
      template: '

{{super}}'
    }));
    child.render();
    equal(child.$('.parent').length, 1);
    equal(child.$('.child').length, 1);

    parent = Thorax.View.extend({
      name: 'super-test',
      template: '

'
    });
    child = new (parent.extend({
      template: '

{{super}}'
    }));
    child.render();
    equal(child.$('.parent').length, 1);
    equal(child.$('.child').length, 1);
  });

  test("element helper", function() {
    var a = document.createElement('li');
    a.innerHTML = 'one';
    var view = new Thorax.View({
      template: '
{{element a tag="li"}}{{element b tag="li"}}{{element c}}{{element d}}
',
      a: a,
      b: function() {
        var li = document.createElement('li');
        li.innerHTML = 'two';
        return li;
      },
      c: function() {
        return $('
three

four
')
      },
      d: $('
five
')
    });
    view.render();
    equal(view.$('li')[0].innerHTML, 'one');
    equal(view.$('li')[1].innerHTML, 'two');
    equal(view.$('li')[2].innerHTML, 'three');
    equal(view.$('li')[3].innerHTML, 'four');
    equal(view.$('li')[4].innerHTML, 'five');
    view.html('');
    equal(view.$('li').length, 0);
    view.render();
    equal(view.$('li')[0].innerHTML, 'one');
    equal(view.$('li')[1].innerHTML, 'two');
    equal(view.$('li')[2].innerHTML, 'three');
    equal(view.$('li')[3].innerHTML, 'four');
    equal(view.$('li')[4].innerHTML, 'five');
  });

  test("local view functions are called in template scope", function() {
    var child = new Thorax.View({
      template: '{{key "value"}}',
      key: function(value) {
        return value;
      }
    });
    child.render();
    equal('value', child.html());
  });

  test("template not found handling", function() {
    var view = new Thorax.View();
    raises(function() {
      view.render();
    });
  });

  test("render() subclassing", function() {
    var a = new Thorax.View({
      render: function() {
        Thorax.View.prototype.render.call(this, '
a
');
      }
    });
    a.render();

    var b = new Thorax.View({
      render: function() {
        Thorax.View.prototype.render.call(this, $('
b
'));
      }
    });
    b.render();

    var c = new Thorax.View({
      render: function() {
        var el = document.createElement('p');
        el.innerHTML = 'c';
        Thorax.View.prototype.render.call(this, el);
      }
    });
    c.render();

    var d = new Thorax.View({
      render: function() {
        var view = new Thorax.View({
          render: function() {
            Thorax.View.prototype.render.call(this, '
d
');
          }
        });
        view.render();
        Thorax.View.prototype.render.call(this, view);
      }
    });
    d.render();

    equal(a._renderCount, 1, '_renderCount incrimented');
    equal(b._renderCount, 1, '_renderCount incrimented');
    equal(c._renderCount, 1, '_renderCount incrimented');
    equal(d._renderCount, 1, '_renderCount incrimented');
    equal(a.$('p').html(), 'a', 'parent render accepts string');
    equal(b.$('p').html(), 'b', 'parent render accepts dom array');
    equal(c.$('p').html(), 'c', 'parent render accepts dom element');
    equal(d.$('p').html(), 'd', 'parent render accepts view');
  });

  test("template passed to constructor and view block", function() {
    var view = new Thorax.View({
      template: '
{{key}}
',
      key: 'value'
    });
    view.render();
    equal(view.$('p').html(), 'value');

    var view = new (Thorax.View.extend({
      template: '
{{key}}
',
      key: 'value'
    }));
    view.render();
    equal(view.$('p').html(), 'value');

    var Child = Thorax.View.extend({
      template: '
{{key}}
',
      key: 'value'
    });

    var a = new Child;
    var b = new Child;

    var parent = new Thorax.View({
      template: '
{{#view b}}
{{key}}
{{/view}}{{view a}}
',
      a: a,
      b: b
    });
    parent.render();
    equal(parent.$('.child-a').html(), 'value');
    equal(parent.$('.child-b').html(), 'value');

    //ensure that override does not persist to view itself
    b.render();
    equal(b.$('.child-a').html(), 'value');

    //test nesting
    var outer = new Thorax.View({
      template: '
{{#view inner}}
{{#view child}}
value
{{/view}}
{{/view}}
',
      inner: new Thorax.View({
        child: new Thorax.View
      })
    });
    outer.render();
    equal(outer.$('.c').html(), 'value');
  });

  test("nestable scope of view helper", function() {
    Handlebars.registerViewHelper('test', function(viewHelper) {
      equal(view.cid, viewHelper.parent.cid);
    });
    var view = new Thorax.View({
      name: 'outer',
      template: '{{#test}}{{#test}}{{#test}}{{key}}{{/test}}{{/test}}{{/test}}',
      key: 'value'
    });
    view.render();
    equal(view.$('[data-view-helper]')[2].innerHTML, 'value');
    delete Handlebars.helpers.test;
  });

  test("$.fn.view", function() {
    var child = new Thorax.View({
      template: '

'
    });
    child.render();
    equal(child.$('div.child').view(), child);
    var parent = new Thorax.View({
      template: '
{{view child}}
',
      child: child
    });
    parent.render();
    equal(parent.$('div.parent').view(), parent);
    equal(parent.$('div.child').view(), child);
  });

});
<commit_msg>add names to failing test cases for better debugging<commit_after>$(function() {
  QUnit.module('Thorax Core');

  Backbone.history = new Backbone.History();
  Backbone.history.start();

  Thorax.templates['parent'] = '
{{view child}}
';
  Thorax.templates['child'] = '
{{value}}
';

  test("registry", function() {
    var viewClass = Thorax.View.extend({name: 'a-name'}, {});
    equal(viewClass.prototype.name, 'a-name', 'sets a name attribute on view class');
    equal((new viewClass).name, 'a-name', 'sets a name attribute on view instance');
    var viewClassB = viewClass.extend({
      name: 'b-name',
      key: 'value'
    });
    equal(viewClassB.prototype.key, 'value', 'can accept a class as a value');
    viewClass.extend({
      name: 'b-name',
      key: 'value2'
    });
    equal(Thorax.Views['b-name'].prototype.key, 'value2', 'will overwrite a previous class when passed a new one');
    Thorax.View.extend({
      name: 'a-name',
      key: 'value'
    });
    equal(Thorax.Views['a-name'].prototype.key, 'value', 'registry will extend an existing class prototype');

    //test nested
    Thorax.Views.Outer = {
      Inner: Thorax.View.extend({
        template: 'inner'
      }),
      More: {
        Nested: Thorax.View.extend({
          template: 'nested'
        })
      }
    };

    var view = new Thorax.View({
      template: '
{{view "Outer.Inner" tag="span"}}

{{view "Outer.More.Nested" tag="span"}}
'
    });
    view.render();
    equal(view.$('p > span').html(), 'inner', 'test nested registryGet');
    equal(view.$('div > span').html(), 'nested', 'test nested registryGet');
  });

  test("context may be an object", function() {
    var view = new (Thorax.View.extend({
      context: {
        a: 'a',
        b: 'b',
        c: function() {
          return 'c'
        }
      },
      template: '{{a}}{{b}}{{c}}'
    }));
    view.render();
    equal(view.html(), 'abc');
  });

  test("child views", function() {
    var childRenderedCount = 0,
        parentRenderedCount = 0;
    var Child = Thorax.View.extend({
      name: 'child',
      initialize: function() {
        this.on('rendered', function() {
          ++childRenderedCount;
        });
      }
    });
    var Parent = Thorax.View.extend({
      name: 'parent',
      initialize: function() {
        this.on('rendered', function() {
          ++parentRenderedCount;
        });
        this.childModel = new Backbone.Model({
          value: 'a'
        });
        this.child = new Thorax.Views['child']({
          model: this.childModel
        });
      }
    });
    var parent = new Parent();
    parent.render();
    equal(parent.$('[data-view-name="child"] > div').html(), 'a', 'view embedded');
    equal(parentRenderedCount, 1);
    equal(childRenderedCount, 1);

    parent.render();
    equal(parent.$('[data-view-name="child"] > div').html(), 'a', 'view embedded');
    equal(parentRenderedCount, 2, 're-render of parent does not render child');
    equal(childRenderedCount, 1, 're-render of parent does not render child');

    parent.childModel.set({value: 'b'});
    equal(parent.$('[data-view-name="child"] > div').html(), 'b', 'view embedded');
    equal(parentRenderedCount, 2, 're-render of child does not parent child');
    equal(childRenderedCount, 2, 're-render of child does not render parent');

    //ensure recursion does not happen when child view has the same model
    //as parent
    parent.setModel(parent.childModel);
    parent.model.set({value: 'c'});
    equal(parentRenderedCount, 4);
    equal(childRenderedCount, 3);
  });

  test("child view re-render will keep dom events intact", function() {
    this.clock.restore();
    var callCount = 0;
    var parent = new Thorax.View({
      name: 'parent-event-dom-test',
      child: new Thorax.View({
        name: 'child-event-dom-test',
        events: {
          'click .test': function() {
            ++callCount;
          }
        },
        template: "

"
      }),
      template: "{{view child}}"
    });
    parent.render();
    document.body.appendChild(parent.el);
    expect(3);
    stop();
    setTimeout(function() {
      parent.child.$('.test').trigger('click');
      equal(callCount, 1);
      parent.render();
      equal(callCount, 1);
      parent.child.$('.test').trigger('click');
      equal(callCount, 2);
      $(parent.el).remove();
      start();
    }, 2);
  });

  test("can set view el", function() {
    $('body').append('

');
    var view = new Thorax.View({
      template: 'testing123',
      el: $('#test-target')[0]
    });
    view.render();
    equal($('#test-target-container > #test-target')[0].innerHTML, 'testing123');
    equal(view.el.parentNode, $('#test-target-container')[0]);
    $('#test-target-container').remove();
  });

  test("template function can be specified", function() {
    var childReturningString = new Thorax.View({
      template: function(data) {
        ok(data.cid && data.cid.match(/^t/));
        return 'template';
      }
    });
    childReturningString.render();
    equal(childReturningString.html(), 'template');
    var childReturningElement = new Thorax.View({
      template: function(data) {
        return $('
template
')[0];
      }
    });
    childReturningElement.render();
    equal(childReturningElement.$('p').html(), 'template');
    var childReturning$ = new Thorax.View({
      template: function(data) {
        return $('
template
');
      }
    });
    childReturning$.render();
    equal(childReturning$.$('p').html(), 'template');
  });

  test("super helper", function() {
    var parent, child;
    var t = Thorax.templates['super-named-test'] = '

';
    parent = Thorax.View.extend({
      name: 'super-named-test'
    });
    child = new (parent.extend({
      template: '

{{super}}'
    }));
    child.render();
    equal(child.$('.parent').length, 1);
    equal(child.$('.child').length, 1);

    parent = Thorax.View.extend({
      name: 'super-test',
      template: '

'
    });
    child = new (parent.extend({
      template: '

{{super}}'
    }));
    child.render();
    equal(child.$('.parent').length, 1);
    equal(child.$('.child').length, 1);
  });

  test("element helper", function() {
    var a = document.createElement('li');
    a.innerHTML = 'one';
    var view = new Thorax.View({
      template: '
{{element a tag="li"}}{{element b tag="li"}}{{element c}}{{element d}}
',
      a: a,
      b: function() {
        var li = document.createElement('li');
        li.innerHTML = 'two';
        return li;
      },
      c: function() {
        return $('
three

four
')
      },
      d: $('
five
')
    });
    view.render();
    equal(view.$('li')[0].innerHTML, 'one');
    equal(view.$('li')[1].innerHTML, 'two');
    equal(view.$('li')[2].innerHTML, 'three');
    equal(view.$('li')[3].innerHTML, 'four');
    equal(view.$('li')[4].innerHTML, 'five');
    view.html('');
    equal(view.$('li').length, 0);
    view.render();
    equal(view.$('li')[0].innerHTML, 'one');
    equal(view.$('li')[1].innerHTML, 'two');
    equal(view.$('li')[2].innerHTML, 'three');
    equal(view.$('li')[3].innerHTML, 'four');
    equal(view.$('li')[4].innerHTML, 'five');
  });

  test("local view functions are called in template scope", function() {
    var child = new Thorax.View({
      template: '{{key "value"}}',
      key: function(value) {
        return value;
      }
    });
    child.render();
    equal('value', child.html());
  });

  test("template not found handling", function() {
    var view = new Thorax.View();
    raises(function() {
      view.render();
    });
  });

  test("render() subclassing", function() {
    var a = new Thorax.View({
      render: function() {
        Thorax.View.prototype.render.call(this, '
a
');
      }
    });
    a.render();

    var b = new Thorax.View({
      render: function() {
        Thorax.View.prototype.render.call(this, $('
b
'));
      }
    });
    b.render();

    var c = new Thorax.View({
      render: function() {
        var el = document.createElement('p');
        el.innerHTML = 'c';
        Thorax.View.prototype.render.call(this, el);
      }
    });
    c.render();

    var d = new Thorax.View({
      render: function() {
        var view = new Thorax.View({
          render: function() {
            Thorax.View.prototype.render.call(this, '
d
');
          }
        });
        view.render();
        Thorax.View.prototype.render.call(this, view);
      }
    });
    d.render();

    equal(a._renderCount, 1, '_renderCount incrimented');
    equal(b._renderCount, 1, '_renderCount incrimented');
    equal(c._renderCount, 1, '_renderCount incrimented');
    equal(d._renderCount, 1, '_renderCount incrimented');
    equal(a.$('p').html(), 'a', 'parent render accepts string');
    equal(b.$('p').html(), 'b', 'parent render accepts dom array');
    equal(c.$('p').html(), 'c', 'parent render accepts dom element');
    equal(d.$('p').html(), 'd', 'parent render accepts view');
  });

  test("template passed to constructor and view block", function() {
    var view = new Thorax.View({
      template: '
{{key}}
',
      key: 'value'
    });
    view.render();
    equal(view.$('p').html(), 'value');

    var view = new (Thorax.View.extend({
      template: '
{{key}}
',
      key: 'value'
    }));
    view.render();
    equal(view.$('p').html(), 'value');

    var Child = Thorax.View.extend({
      template: '
{{key}}
',
      key: 'value'
    });

    var a = new Child;
    var b = new Child;

    var parent = new Thorax.View({
      template: '
{{#view b}}
{{key}}
{{/view}}{{view a}}
',
      a: a,
      b: b
    });
    parent.render();
    equal(parent.$('.child-a').html(), 'value');
    equal(parent.$('.child-b').html(), 'value');

    //ensure that override does not persist to view itself
    b.render();
    equal(b.$('.child-a').html(), 'value');

    //test nesting
    var outer = new Thorax.View({
      template: '
{{#view inner}}
{{#view child}}
value
{{/view}}
{{/view}}
',
      inner: new Thorax.View({
        child: new Thorax.View
      })
    });
    outer.render();
    equal(outer.$('.c').html(), 'value');
  });

  test("nestable scope of view helper", function() {
    Handlebars.registerViewHelper('test', function(viewHelper) {
      equal(view.cid, viewHelper.parent.cid);
    });
    var view = new Thorax.View({
      name: 'outer',
      template: '{{#test}}{{#test}}{{#test}}{{key}}{{/test}}{{/test}}{{/test}}',
      key: 'value'
    });
    view.render();
    equal(view.$('[data-view-helper]')[2].innerHTML, 'value');
    delete Handlebars.helpers.test;
  });

  test("$.fn.view", function() {
    var child = new Thorax.View({
      template: '

'
    });
    child.render();
    equal(child.$('div.child').view(), child);
    var parent = new Thorax.View({
      template: '
{{view child}}
',
      child: child
    });
    parent.render();
    equal(parent.$('div.parent').view(), parent);
    equal(parent.$('div.child').view(), child);
  });

});
<|endoftext|>


<commit_before>import { Meteor }                           from 'meteor/meteor';
import { moment }                           from 'meteor/momentjs:moment';
import numeral                              from 'numeral';
import { Picker }                           from 'meteor/meteorhacks:picker';
import { CapacityPlans }                    from '../../capacity_plans/capacity_plans';
import { CapacityPlanOptions }              from '../../capacity_plans/capacity_plan_options';
import { CapacityPlanReleases }             from '../../capacity_plans/capacity_plan_releases';
import { CapacityPlanSprintBlocks }         from '../../capacity_plans/capacity_plan_sprint_blocks';
import { CapacityPlanSprintLinks }          from '../../capacity_plans/capacity_plan_sprint_links';
import { CapacityPlanSprints }              from '../../capacity_plans/capacity_plan_sprints';
import { CapacityPlanStrategicEffortItems } from '../../capacity_plans/capacity_plan_strategic_effort_items';
import { CapacityPlanStrategicEfforts }     from '../../capacity_plans/capacity_plan_strategic_efforts';
import { Contributors }                     from '../../contributors/contributors';
import { ContributorProjectAssignments }    from '../../contributors/contributor_project_assignments';
import { ContributorRoleDefinitions }       from '../../contributors/contributor_role_definitions';
import { ContributorTeamRoles }             from '../../contributors/contributor_team_roles';
import { DisplayTemplates }                 from '../../display_templates/display_templates';
import { DisplayTemplateGroups }            from '../../display_templates/display_template_groups';
import { Efforts }                          from '../../efforts/efforts';
import { PublishedDisplayTemplates }        from '../../display_templates/published_display_templates';
import { IntegrationCalculatedFields }      from '../../integrations/integration_calculated_fields';
import { IntegrationImportFunctions }       from '../../integrations/integration_import_functions';
import { IntegrationServerAuthProviders }   from '../../integrations/integration_server_auth_providers';
import { IntegrationServerCaches }          from '../../integrations/integration_server_caches';
import { IntegrationServers }               from '../../integrations/integration_servers';
import { Integrations }                     from '../../integrations/integrations';
import { Priorities }                       from '../../priorities/priorities';
import { Projects }                         from '../../projects/projects';
import { StatusReportSettings }             from '../../status_reports/status_report_settings';
import { StatusReports }                    from '../../status_reports/status_reports';
import { Subtasks }                         from '../../subtasks/subtasks';
import { Tasks }                            from '../../tasks/tasks';
import { Teams }                            from '../../teams/teams';
import { Users }                            from '../../users/users';

let AdmZip        = require('adm-zip'),
    fs            = require('fs'),
    path          = require('path'),
    os            = require('os'),
    collectionMap = {
      CapacityPlans                   : CapacityPlans,
      CapacityPlanReleases            : CapacityPlanReleases,
      CapacityPlanSprintBlocks        : CapacityPlanSprintBlocks,
      CapacityPlanSprintLinks         : CapacityPlanSprintLinks,
      CapacityPlanSprints             : CapacityPlanSprints,
      CapacityPlanStrategicEffortItems: CapacityPlanStrategicEffortItems,
      CapacityPlanStrategicEfforts    : CapacityPlanStrategicEfforts,
      // This needs to be after all of the blocks and sprints so that the after.insert handler doesn't create duplicate sprints
      CapacityPlanOptions             : CapacityPlanOptions,
      Contributors                    : Contributors,
      ContributorProjectAssignments   : ContributorProjectAssignments,
      ContributorRoleDefinitions      : ContributorRoleDefinitions,
      ContributorTeamRoles            : ContributorTeamRoles,
      DisplayTemplates                : DisplayTemplates,
      DisplayTemplateGroups           : DisplayTemplateGroups,
      Efforts                         : Efforts,
      PublishedDisplayTemplates       : PublishedDisplayTemplates,
      IntegrationCalculatedFields     : IntegrationCalculatedFields,
      IntegrationImportFunctions      : IntegrationImportFunctions,
      IntegrationServerAuthProviders  : IntegrationServerAuthProviders,
      IntegrationServerCaches         : IntegrationServerCaches,
      IntegrationServers              : IntegrationServers,
      Integrations                    : Integrations,
      Projects                        : Projects,
      Priorities                      : Priorities,
      StatusReportSettings            : StatusReportSettings,
      StatusReports                   : StatusReports,
      Subtasks                        : Subtasks,
      Tasks                           : Tasks,
      Teams                           : Teams,
      Users                           : Users
    },
    importKeys    = {
      DisplayTemplates         : 'templateName',
      PublishedDisplayTemplates: 'templateName'
    };

/**
 * Setup the server side route for downloading the files
 */
Picker.route('/export/:fileName', function (params, req, res, next) {
  if (params && params.fileName && !(params.fileName.includes('..') && params.fileName.includes('/'))) {
    if (fs.existsSync(ImportExportTool.dataPath, params.fileName)) {
      let filePath = path.join(ImportExportTool.dataPath, params.fileName),
          name     = 'journalist_export_' + moment().format('YYYYMMDD_HHmmss') + '.zip';

      res.writeHead(200, {
        'Content-Type'       : 'application/zip',
        'Content-Disposition': 'attachment; filename=' + name
      });

      fs.createReadStream(filePath).pipe(res);
    } else {
      return 'Not Found';
    }
  }
});

export const ImportExportTool = {
  dataPath : os.tmpdir(),
  fileRegex: /journalist_export/i,
  encoding : 'utf8',

  /**
   * Import a paritcular collection
   * @param fileName      The name of the file being uploaded
   */
  importData (fileName) {
    let handler  = this,
        filePath = path.join(handler.dataPath, fileName),
        data, collectionName;

    console.info('ImportExportTool.importData importing file:', fileName);

    // unzip it if needed
    if (filePath.match(/\.zip$/)) {
      let zipFile     = new AdmZip(filePath),
          zipEntries  = zipFile.getEntries(),
          sortedNames = zipEntries.map((zipEntry) => {
            return zipEntry.entryName
          }).sort();

      //console.info('ImportExportTool.importData file list', sortedNames);

      if (sortedNames && sortedNames.length) {
        sortedNames.forEach((entryName) => {
          let zipEntry = zipFile.getEntry(entryName);
          if (zipEntry.entryName && zipEntry.entryName.match(/^[0-9]+.+\.json$/)) {
            //console.info('ImportExportTool.importData reading file ' + zipEntry.entryName);

            let input = zipFile.readAsText(zipEntry, handler.encoding);

            try {
              data           = JSON.parse(input);
              collectionName = zipEntry.entryName.replace(/\.json$/i, '').replace(/^[0-9]+_/i, '');

              handler.insertDataIntoCollection(data, collectionName);
            } catch (e) {
              console.error('ImportExportTool.importData JSON parse failed: ' + e.toString());
            }
          } else {
            console.error('Zip file did not contain any zip entries: ' + filePath);
          }
        });
      }
    } else {
      // read a raw json file
      try {
        let input      = fs.readFileSync(filePath, handler.encoding);
        data           = JSON.parse(input);
        collectionName = path.basename(filePath).replace(/\.json$/i, '').replace(/^[0-9]+_/i, '');

        handler.insertDataIntoCollection(data, collectionName);
      } catch (e) {
        console.error('JSON file did not contain any entries: ' + filePath);
      }
    }

    fs.unlinkSync(filePath);
  },
  /**
   * Inserts the Data into your Collections.
   * @param data The contents of the file
   * @param collectionName The name of the collection we will insert
   */
  insertDataIntoCollection (data, collectionName) {
    if (data && collectionName && collectionMap[ collectionName ]) {
      // if you have a partial duplicate, you should still be able to import the rest of the file.
      try {
        let key         = importKeys[ collectionName ] || '_id',
            query       = {},
            importCount = 0;
        data.forEach((record) => {
          query[ key ] = record[ key ];
          importCount += 1;
          //console.info('ImportExportTool.insertDataIntoCollection inserting', collectionName, key, record[ key ]);

          // Updating the _id field will fail, so remove it
          if (key !== '_id') {
            delete record._id
          }

          // Upsert triggers insert handler default values, so do an update or insert
          let check = collectionMap[ collectionName ].findOne(query);
          if (check) {
            collectionMap[ collectionName ].update(query, { $set: record }, { validate: false });
          } else {
            collectionMap[ collectionName ].insert(record, { validate: false });
          }
        });
        console.info('ImportExportTool inserted', importCount, 'records into', collectionName);
      } catch (e) {
        console.error(e);
      }
    } else {
      console.error('ImportExportTool.insertDataIntoCollection failed: collection [', collectionName, '] not found');
    }
  },
  /**
   * Export a set of the Collections
   * @param collectionNames (optional) A list of collection names
   */
  exportData (collectionNames) {
    console.info('ImportExportTool.exportData:', collectionNames);
    let handler = this,
        dataDir = path.join(handler.dataPath, moment().format('YYYYMMDD_HHmmss'));

    collectionNames = _.isArray(collectionNames) ? collectionNames : _.keys(collectionMap);

    if (fs.existsSync(dataDir)) {
      console.info('ImportExportTool.exportData removing existing files');
      fs.readdirSync(dataDir).filter((filepath) => {
        return path.basename(filepath).match(handler.fileRegex) != null
      }).forEach((filepath) => {
        console.info('Removing file: ' + filepath);

        fs.unlinkSync(path.join(dataDir, filepath));
      });
    }

    let zipFile = new AdmZip();

    console.info('ImportExportTool.exportData exporting collections:', collectionNames);
    collectionNames.forEach((collectionName, i) => {
      let cursor   = collectionMap[ collectionName ].find({}),
          fileName = numeral(i).format('000') + '_' + collectionName + '.json';

      zipFile.addFile(fileName, new Buffer(handler.getPayload(cursor, collectionName), handler.encoding), collectionName, 644);
    });

    dataDir = dataDir + '.zip';

    console.info('ImportExportTool.exportData creating data directory: ' + dataDir);

    zipFile.writeZip(dataDir);

    // delete the file after a bit, the download should start automatically
    Meteor.setTimeout(function () {
      fs.unlinkSync(dataDir);
      console.info('ImportExportTool.exportData deleted ' + dataDir);
    }, 25000);

    console.info('ImportExportTool.exportData complete');

    return path.basename(dataDir);
  },
  /**
   * Export a paritcular collection
   * @param cursor        Collection cursor poiting to records to export
   * @param collectionName The name of the collection to tag the data with
   */
  getPayload (cursor, collectionName) {
    let handler     = this,
        output      = '',
        recordCount = cursor.count();

    console.info('ImportExportTool.getPayload exporting ' + collectionName + ' (' + recordCount + ' records)');

    output += "[\n";
    cursor.forEach((record, i) => {
      output += JSON.stringify(record) + (i < recordCount - 1 ? ",\n" : "\n");
    });
    output += "]\n";

    console.info('ImportExportTool.getPayload done Exporting ' + collectionName);

    return output;
  }
};<commit_msg>Fixed the import/export tool to bypass Collection 2 on import which was adding a lot of complexity<commit_after>import { Meteor }                           from 'meteor/meteor';
import { moment }                           from 'meteor/momentjs:moment';
import numeral                              from 'numeral';
import { Picker }                           from 'meteor/meteorhacks:picker';
import { CapacityPlans }                    from '../../capacity_plans/capacity_plans';
import { CapacityPlanOptions }              from '../../capacity_plans/capacity_plan_options';
import { CapacityPlanReleases }             from '../../capacity_plans/capacity_plan_releases';
import { CapacityPlanSprintBlocks }         from '../../capacity_plans/capacity_plan_sprint_blocks';
import { CapacityPlanSprintLinks }          from '../../capacity_plans/capacity_plan_sprint_links';
import { CapacityPlanSprints }              from '../../capacity_plans/capacity_plan_sprints';
import { CapacityPlanStrategicEffortItems } from '../../capacity_plans/capacity_plan_strategic_effort_items';
import { CapacityPlanStrategicEfforts }     from '../../capacity_plans/capacity_plan_strategic_efforts';
import { Contributors }                     from '../../contributors/contributors';
import { ContributorProjectAssignments }    from '../../contributors/contributor_project_assignments';
import { ContributorRoleDefinitions }       from '../../contributors/contributor_role_definitions';
import { ContributorTeamRoles }             from '../../contributors/contributor_team_roles';
import { DisplayTemplates }                 from '../../display_templates/display_templates';
import { DisplayTemplateGroups }            from '../../display_templates/display_template_groups';
import { Efforts }                          from '../../efforts/efforts';
import { PublishedDisplayTemplates }        from '../../display_templates/published_display_templates';
import { IntegrationCalculatedFields }      from '../../integrations/integration_calculated_fields';
import { IntegrationImportFunctions }       from '../../integrations/integration_import_functions';
import { IntegrationServerAuthProviders }   from '../../integrations/integration_server_auth_providers';
import { IntegrationServerCaches }          from '../../integrations/integration_server_caches';
import { IntegrationServers }               from '../../integrations/integration_servers';
import { Integrations }                     from '../../integrations/integrations';
import { Priorities }                       from '../../priorities/priorities';
import { Projects }                         from '../../projects/projects';
import { StatusReportSettings }             from '../../status_reports/status_report_settings';
import { StatusReports }                    from '../../status_reports/status_reports';
import { Subtasks }                         from '../../subtasks/subtasks';
import { Tasks }                            from '../../tasks/tasks';
import { Teams }                            from '../../teams/teams';
import { Users }                            from '../../users/users';

let AdmZip        = require('adm-zip'),
    fs            = require('fs'),
    path          = require('path'),
    os            = require('os'),
    collectionMap = {
      CapacityPlans                   : CapacityPlans,
      CapacityPlanReleases            : CapacityPlanReleases,
      CapacityPlanSprintBlocks        : CapacityPlanSprintBlocks,
      CapacityPlanSprintLinks         : CapacityPlanSprintLinks,
      CapacityPlanSprints             : CapacityPlanSprints,
      CapacityPlanStrategicEffortItems: CapacityPlanStrategicEffortItems,
      CapacityPlanStrategicEfforts    : CapacityPlanStrategicEfforts,
      // This needs to be after all of the blocks and sprints so that the after.insert handler doesn't create duplicate sprints
      CapacityPlanOptions             : CapacityPlanOptions,
      Contributors                    : Contributors,
      ContributorProjectAssignments   : ContributorProjectAssignments,
      ContributorRoleDefinitions      : ContributorRoleDefinitions,
      ContributorTeamRoles            : ContributorTeamRoles,
      DisplayTemplates                : DisplayTemplates,
      DisplayTemplateGroups           : DisplayTemplateGroups,
      Efforts                         : Efforts,
      PublishedDisplayTemplates       : PublishedDisplayTemplates,
      IntegrationCalculatedFields     : IntegrationCalculatedFields,
      IntegrationImportFunctions      : IntegrationImportFunctions,
      IntegrationServerAuthProviders  : IntegrationServerAuthProviders,
      IntegrationServerCaches         : IntegrationServerCaches,
      IntegrationServers              : IntegrationServers,
      Integrations                    : Integrations,
      Projects                        : Projects,
      Priorities                      : Priorities,
      StatusReportSettings            : StatusReportSettings,
      StatusReports                   : StatusReports,
      Subtasks                        : Subtasks,
      Tasks                           : Tasks,
      Teams                           : Teams,
      Users                           : Users
    },
    importKeys    = {
      DisplayTemplates         : 'templateName',
      PublishedDisplayTemplates: 'templateName'
    };

/**
 * Setup the server side route for downloading the files
 */
Picker.route('/export/:fileName', function (params, req, res, next) {
  if (params && params.fileName && !(params.fileName.includes('..') && params.fileName.includes('/'))) {
    if (fs.existsSync(ImportExportTool.dataPath, params.fileName)) {
      let filePath = path.join(ImportExportTool.dataPath, params.fileName),
          name     = 'journalist_export_' + moment().format('YYYYMMDD_HHmmss') + '.zip';

      res.writeHead(200, {
        'Content-Type'       : 'application/zip',
        'Content-Disposition': 'attachment; filename=' + name
      });

      fs.createReadStream(filePath).pipe(res);
    } else {
      return 'Not Found';
    }
  }
});

export const ImportExportTool = {
  dataPath : os.tmpdir(),
  fileRegex: /journalist_export/i,
  encoding : 'utf8',

  /**
   * Import a paritcular collection
   * @param fileName      The name of the file being uploaded
   */
  importData (fileName) {
    let handler  = this,
        filePath = path.join(handler.dataPath, fileName),
        data, collectionName;

    console.info('ImportExportTool.importData importing file:', fileName);

    // unzip it if needed
    if (filePath.match(/\.zip$/)) {
      let zipFile     = new AdmZip(filePath),
          zipEntries  = zipFile.getEntries(),
          sortedNames = zipEntries.map((zipEntry) => {
            return zipEntry.entryName
          }).sort();

      //console.info('ImportExportTool.importData file list', sortedNames);

      if (sortedNames && sortedNames.length) {
        sortedNames.forEach((entryName) => {
          let zipEntry = zipFile.getEntry(entryName);
          if (zipEntry.entryName && zipEntry.entryName.match(/^[0-9]+.+\.json$/)) {
            //console.info('ImportExportTool.importData reading file ' + zipEntry.entryName);

            let input = zipFile.readAsText(zipEntry, handler.encoding);

            try {
              data           = JSON.parse(input);
              collectionName = zipEntry.entryName.replace(/\.json$/i, '').replace(/^[0-9]+_/i, '');

              handler.insertDataIntoCollection(data, collectionName);
            } catch (e) {
              console.error('ImportExportTool.importData JSON parse failed: ' + e.toString());
            }
          } else {
            console.error('Zip file did not contain any zip entries: ' + filePath);
          }
        });
      }
    } else {
      // read a raw json file
      try {
        let input      = fs.readFileSync(filePath, handler.encoding);
        data           = JSON.parse(input);
        collectionName = path.basename(filePath).replace(/\.json$/i, '').replace(/^[0-9]+_/i, '');

        handler.insertDataIntoCollection(data, collectionName);
      } catch (e) {
        console.error('JSON file did not contain any entries: ' + filePath);
      }
    }

    fs.unlinkSync(filePath);
  },
  /**
   * Inserts the Data into your Collections.
   * @param data The contents of the file
   * @param collectionName The name of the collection we will insert
   */
  insertDataIntoCollection (data, collectionName) {
    if (data && collectionName && collectionMap[ collectionName ]) {
      // if you have a partial duplicate, you should still be able to import the rest of the file.
      try {
        let key         = importKeys[ collectionName ] || '_id',
            query       = {},
            importCount = 0;
        data.forEach((record) => {
          query[ key ] = record[ key ];
          importCount += 1;
          //console.info('ImportExportTool.insertDataIntoCollection inserting', collectionName, key, record[ key ]);

          // Updating the _id field will fail, so remove it
          if (key !== '_id') {
            delete record._id
          }

          // Upsert triggers insert handler default values, so do an update or insert
          let check = collectionMap[ collectionName ].findOne(query);
          if (check) {
            collectionMap[ collectionName ].remove(query);
          }
          collectionMap[ collectionName ].insert(record, { bypassCollection2: true });
        });
        console.info('ImportExportTool inserted', importCount, 'records into', collectionName);
      } catch (e) {
        console.error(e);
      }
    } else {
      console.error('ImportExportTool.insertDataIntoCollection failed: collection [', collectionName, '] not found');
    }
  },
  /**
   * Export a set of the Collections
   * @param collectionNames (optional) A list of collection names
   */
  exportData (collectionNames) {
    console.info('ImportExportTool.exportData:', collectionNames);
    let handler = this,
        dataDir = path.join(handler.dataPath, moment().format('YYYYMMDD_HHmmss'));

    collectionNames = _.isArray(collectionNames) ? collectionNames : _.keys(collectionMap);

    if (fs.existsSync(dataDir)) {
      console.info('ImportExportTool.exportData removing existing files');
      fs.readdirSync(dataDir).filter((filepath) => {
        return path.basename(filepath).match(handler.fileRegex) != null
      }).forEach((filepath) => {
        console.info('Removing file: ' + filepath);

        fs.unlinkSync(path.join(dataDir, filepath));
      });
    }

    let zipFile = new AdmZip();

    console.info('ImportExportTool.exportData exporting collections:', collectionNames);
    collectionNames.forEach((collectionName, i) => {
      let cursor   = collectionMap[ collectionName ].find({}),
          fileName = numeral(i).format('000') + '_' + collectionName + '.json';

      zipFile.addFile(fileName, new Buffer(handler.getPayload(cursor, collectionName), handler.encoding), collectionName, 644);
    });

    dataDir = dataDir + '.zip';

    console.info('ImportExportTool.exportData creating data directory: ' + dataDir);

    zipFile.writeZip(dataDir);

    // delete the file after a bit, the download should start automatically
    Meteor.setTimeout(function () {
      fs.unlinkSync(dataDir);
      console.info('ImportExportTool.exportData deleted ' + dataDir);
    }, 25000);

    console.info('ImportExportTool.exportData complete');

    return path.basename(dataDir);
  },
  /**
   * Export a paritcular collection
   * @param cursor        Collection cursor poiting to records to export
   * @param collectionName The name of the collection to tag the data with
   */
  getPayload (cursor, collectionName) {
    let handler     = this,
        output      = '',
        recordCount = cursor.count();

    console.info('ImportExportTool.getPayload exporting ' + collectionName + ' (' + recordCount + ' records)');

    output += "[\n";
    cursor.forEach((record, i) => {
      output += JSON.stringify(record) + (i < recordCount - 1 ? ",\n" : "\n");
    });
    output += "]\n";

    console.info('ImportExportTool.getPayload done Exporting ' + collectionName);

    return output;
  }
};<|endoftext|>