<commit_before>dojo.provide("dojox.grid._ViewManager");

dojo.declare('dojox.grid._ViewManager', null, {
	// summary:
	//		A collection of grid views. Owned by grid and used internally for managing grid views.
	// description:
	//		Grid creates views automatically based on grid's layout structure.
	//		Users should typically not need to access individual views or the views collection directly.
	constructor: function(inGrid){
		this.grid = inGrid;
	},

	defaultWidth: 200,

	views: [],

	// operations
	resize: function(){
		this.onEach("resize");
	},

	render: function(){
		this.onEach("render");
	},

	// views
	addView: function(inView){
		inView.idx = this.views.length;
		this.views.push(inView);
	},

	destroyViews: function(){
		for(var i=0, v; v=this.views[i]; i++){
			v.destroy();
		}
		this.views = [];
	},

	getContentNodes: function(){
		var nodes = [];
		for(var i=0, v; v=this.views[i]; i++){
			nodes.push(v.contentNode);
		}
		return nodes;
	},

	forEach: function(inCallback){
		for(var i=0, v; v=this.views[i]; i++){
			inCallback(v, i);
		}
	},

	onEach: function(inMethod, inArgs){
		inArgs = inArgs || [];
		for(var i=0, v; v=this.views[i]; i++){
			if(inMethod in v){
				v[inMethod].apply(v, inArgs);
			}
		}
	},

	// layout
	normalizeHeaderNodeHeight: function(){
		var rowNodes = [];
		for(var i=0, v; (v=this.views[i]); i++){
			if(v.headerContentNode.firstChild){
				rowNodes.push(v.headerContentNode);
			}
		}
		this.normalizeRowNodeHeights(rowNodes);
	},

	normalizeRowNodeHeights: function(inRowNodes){
		var h = 0;
		for(var i=0, n, o; (n=inRowNodes[i]); i++){
			h = Math.max(h, dojo.marginBox(n.firstChild).h);
		}
		h = (h >= 0 ? h : 0);
		//
		//
		for(var i=0, n; (n=inRowNodes[i]); i++){
			dojo.marginBox(n.firstChild, {h:h});
		}
		//
		//console.log('normalizeRowNodeHeights ', h);
		//
		// querying the height here seems to help scroller measure the page on IE
		if(inRowNodes&&inRowNodes[0]&&inRowNodes[0].parentNode){
			inRowNodes[0].parentNode.offsetHeight;
		}
	},

	resetHeaderNodeHeight: function(){
		for(var i=0, v, n; (v=this.views[i]); i++){
			n = v.headerContentNode.firstChild;
			if(n){
				n.style.height = "";
			}
		}
	},

	renormalizeRow: function(inRowIndex){
		var rowNodes = [];
		for(var i=0, v, n; (v=this.views[i])&&(n=v.getRowNode(inRowIndex)); i++){
			n.firstChild.style.height = '';
			rowNodes.push(n);
		}
		this.normalizeRowNodeHeights(rowNodes);
	},

	getViewWidth: function(inIndex){
		return this.views[inIndex].getWidth() || this.defaultWidth;
	},

	// must be called after view widths are properly set or height can be miscalculated
	// if there are flex columns
	measureHeader: function(){
		// need to reset view header heights so they are properly measured.
		this.resetHeaderNodeHeight();
		this.forEach(function(inView){
			inView.headerContentNode.style.height = '';
		});
		var h = 0;
		// calculate maximum view header height
		this.forEach(function(inView){
			h = Math.max(inView.headerNode.offsetHeight, h);
		});
		return h;
	},

	measureContent: function(){
		var h = 0;
		this.forEach(function(inView){
			h = Math.max(inView.domNode.offsetHeight, h);
		});
		return h;
	},

	findClient: function(inAutoWidth){
		// try to use user defined client
		var c = this.grid.elasticView || -1;
		// attempt to find implicit client
		if(c < 0){
			for(var i=1, v; (v=this.views[i]); i++){
				if(v.viewWidth){
					for(i=1; (v=this.views[i]); i++){
						if(!v.viewWidth){
							c = i;
							break;
						}
					}
					break;
				}
			}
		}
		// client is in the middle by default
		if(c < 0){
			c = Math.floor(this.views.length / 2);
		}
		return c;
	},

	arrange: function(l, w){
		var i, v, vw, len = this.views.length;
		// find the client
		var c = (w <= 0 ? len : this.findClient());
		// layout views
		var setPosition = function(v, l){
			var ds = v.domNode.style;
			var hs = v.headerNode.style;

			if(!dojo._isBodyLtr()){
				ds.right = l + 'px';
				hs.right = l + 'px';
			}else{
				ds.left = l + 'px';
				hs.left = l + 'px';
			}
			ds.top = 0 + 'px';
			hs.top = 0;
		}
		// for views left of the client
		//BiDi TODO: The left and right should not appear in BIDI environment. Should be replaced with
		//leading and tailing concept.
		for(i=0; (v=this.views[i])&&(i<c); i++){
			// get width
			vw = this.getViewWidth(i);
			// process boxes
			v.setSize(vw, 0);
			setPosition(v, l);
			if(v.headerContentNode && v.headerContentNode.firstChild){
				vw = v.getColumnsWidth()+v.getScrollbarWidth();
			}else{
				vw = v.domNode.offsetWidth;
			}
			// update position
			l += vw;
		}
		// next view (is the client, i++ == c)
		i++;
		// start from the right edge
		var r = w;
		// for views right of the client (iterated from the right)
		for(var j=len-1; (v=this.views[j])&&(i<=j); j--){
			// get width
			vw = this.getViewWidth(j);
			// set size
			v.setSize(vw, 0);
			// measure in pixels
			vw = v.domNode.offsetWidth;
			// update position
			r -= vw;
			// set position
			setPosition(v, r);
		}
		if(c<len){
			v = this.views[c];
			// position the client box between left and right boxes
			vw = Math.max(1, r-l);
			// set size
			v.setSize(vw + 'px', 0);
			setPosition(v, l);
		}
		return l;
	},

	// rendering
	renderRow: function(inRowIndex, inNodes){
		var rowNodes = [];
		for(var i=0, v, n, rowNode; (v=this.views[i])&&(n=inNodes[i]); i++){
			rowNode = v.renderRow(inRowIndex);
			n.appendChild(rowNode);
			rowNodes.push(rowNode);
		}
		this.normalizeRowNodeHeights(rowNodes);
	},

	rowRemoved: function(inRowIndex){
		this.onEach("rowRemoved", [ inRowIndex ]);
	},

	// updating
	updateRow: function(inRowIndex){
		for(var i=0, v; v=this.views[i]; i++){
			v.updateRow(inRowIndex);
		}
		this.renormalizeRow(inRowIndex);
	},

	updateRowStyles: function(inRowIndex){
		this.onEach("updateRowStyles", [ inRowIndex ]);
	},

	// scrolling
	setScrollTop: function(inTop){
		var top = inTop;
		for(var i=0, v; v=this.views[i]; i++){
			top = v.setScrollTop(inTop);
		}
		return top;
		//this.onEach("setScrollTop", [ inTop ]);
	},

	getFirstScrollingView: function(){
		// summary: Returns the first grid view with a scroll bar
		for(var i=0, v; (v=this.views[i]); i++){
			if(v.hasHScrollbar() || v.hasVScrollbar()){
				return v;
			}
		}
	}

});
<commit_msg>  * Applying patch from Jared (fixes #7273 !strict).<commit_after>dojo.provide("dojox.grid._ViewManager");

dojo.declare('dojox.grid._ViewManager', null, {
	// summary:
	//		A collection of grid views. Owned by grid and used internally for managing grid views.
	// description:
	//		Grid creates views automatically based on grid's layout structure.
	//		Users should typically not need to access individual views or the views collection directly.
	constructor: function(inGrid){
		this.grid = inGrid;
	},

	defaultWidth: 200,

	views: [],

	// operations
	resize: function(){
		this.onEach("resize");
	},

	render: function(){
		this.onEach("render");
	},

	// views
	addView: function(inView){
		inView.idx = this.views.length;
		this.views.push(inView);
	},

	destroyViews: function(){
		for(var i=0, v; v=this.views[i]; i++){
			v.destroy();
		}
		this.views = [];
	},

	getContentNodes: function(){
		var nodes = [];
		for(var i=0, v; v=this.views[i]; i++){
			nodes.push(v.contentNode);
		}
		return nodes;
	},

	forEach: function(inCallback){
		for(var i=0, v; v=this.views[i]; i++){
			inCallback(v, i);
		}
	},

	onEach: function(inMethod, inArgs){
		inArgs = inArgs || [];
		for(var i=0, v; v=this.views[i]; i++){
			if(inMethod in v){
				v[inMethod].apply(v, inArgs);
			}
		}
	},

	// layout
	normalizeHeaderNodeHeight: function(){
		var rowNodes = [];
		for(var i=0, v; (v=this.views[i]); i++){
			if(v.headerContentNode.firstChild){
				rowNodes.push(v.headerContentNode);
			}
		}
		this.normalizeRowNodeHeights(rowNodes);
	},

	normalizeRowNodeHeights: function(inRowNodes){
		var h = 0;
		for(var i=0, n, o; (n=inRowNodes[i]); i++){
			h = Math.max(h, dojo.marginBox(n.firstChild).h);
		}
		h = (h >= 0 ? h : 0);
		//
		//
		for(var i=0, n; (n=inRowNodes[i]); i++){
			dojo.marginBox(n.firstChild, {h:h});
		}
		//
		//console.log('normalizeRowNodeHeights ', h);
		//
		// querying the height here seems to help scroller measure the page on IE
		if(inRowNodes&&inRowNodes[0]&&inRowNodes[0].parentNode){
			inRowNodes[0].parentNode.offsetHeight;
		}
	},

	resetHeaderNodeHeight: function(){
		for(var i=0, v, n; (v=this.views[i]); i++){
			n = v.headerContentNode.firstChild;
			if(n){
				n.style.height = "";
			}
		}
	},

	renormalizeRow: function(inRowIndex){
		var rowNodes = [];
		for(var i=0, v, n; (v=this.views[i])&&(n=v.getRowNode(inRowIndex)); i++){
			n.firstChild.style.height = '';
			rowNodes.push(n);
		}
		this.normalizeRowNodeHeights(rowNodes);
	},

	getViewWidth: function(inIndex){
		return this.views[inIndex].getWidth() || this.defaultWidth;
	},

	// must be called after view widths are properly set or height can be miscalculated
	// if there are flex columns
	measureHeader: function(){
		// need to reset view header heights so they are properly measured.
		this.resetHeaderNodeHeight();
		this.forEach(function(inView){
			inView.headerContentNode.style.height = '';
		});
		var h = 0;
		// calculate maximum view header height
		this.forEach(function(inView){
			h = Math.max(inView.headerNode.offsetHeight, h);
		});
		return h;
	},

	measureContent: function(){
		var h = 0;
		this.forEach(function(inView){
			h = Math.max(inView.domNode.offsetHeight, h);
		});
		return h;
	},

	findClient: function(inAutoWidth){
		// try to use user defined client
		var c = this.grid.elasticView || -1;
		// attempt to find implicit client
		if(c < 0){
			for(var i=1, v; (v=this.views[i]); i++){
				if(v.viewWidth){
					for(i=1; (v=this.views[i]); i++){
						if(!v.viewWidth){
							c = i;
							break;
						}
					}
					break;
				}
			}
		}
		// client is in the middle by default
		if(c < 0){
			c = Math.floor(this.views.length / 2);
		}
		return c;
	},

	arrange: function(l, w){
		var i, v, vw, len = this.views.length;
		// find the client
		var c = (w <= 0 ? len : this.findClient());
		// layout views
		var setPosition = function(v, l){
			var ds = v.domNode.style;
			var hs = v.headerNode.style;

			if(!dojo._isBodyLtr()){
				ds.right = l + 'px';
				hs.right = l + 'px';
			}else{
				ds.left = l + 'px';
				hs.left = l + 'px';
			}
			ds.top = 0 + 'px';
			hs.top = 0;
		}
		// for views left of the client
		//BiDi TODO: The left and right should not appear in BIDI environment. Should be replaced with
		//leading and tailing concept.
		for(i=0; (v=this.views[i])&&(i<c); i++){
			// get width
			vw = this.getViewWidth(i);
			// process boxes
			v.setSize(vw, 0);
			setPosition(v, l);
			if(v.headerContentNode && v.headerContentNode.firstChild){
				vw = v.getColumnsWidth()+v.getScrollbarWidth();
			}else{
				vw = v.domNode.offsetWidth;
			}
			// update position
			l += vw;
		}
		// next view (is the client, i++ == c)
		i++;
		// start from the right edge
		var r = w;
		// for views right of the client (iterated from the right)
		for(var j=len-1; (v=this.views[j])&&(i<=j); j--){
			// get width
			vw = this.getViewWidth(j);
			// set size
			v.setSize(vw, 0);
			// measure in pixels
			vw = v.domNode.offsetWidth;
			// update position
			r -= vw;
			// set position
			setPosition(v, r);
		}
		if(c<len){
			v = this.views[c];
			// position the client box between left and right boxes
			vw = Math.max(1, r-l);
			// set size
			v.setSize(vw + 'px', 0);
			setPosition(v, l);
		}
		return l;
	},

	// rendering
	renderRow: function(inRowIndex, inNodes){
		var rowNodes = [];
		for(var i=0, v, n, rowNode; (v=this.views[i])&&(n=inNodes[i]); i++){
			rowNode = v.renderRow(inRowIndex);
			n.appendChild(rowNode);
			rowNodes.push(rowNode);
		}
		this.normalizeRowNodeHeights(rowNodes);
	},

	rowRemoved: function(inRowIndex){
		this.onEach("rowRemoved", [ inRowIndex ]);
	},

	// updating
	updateRow: function(inRowIndex){
		for(var i=0, v; v=this.views[i]; i++){
			v.updateRow(inRowIndex);
		}
		this.renormalizeRow(inRowIndex);
	},

	updateRowStyles: function(inRowIndex){
		this.onEach("updateRowStyles", [ inRowIndex ]);
	},

	// scrolling
	setScrollTop: function(inTop){
		var top = inTop;
		for(var i=0, v; v=this.views[i]; i++){
			top = v.setScrollTop(inTop);
			// Work around IE not firing scroll events that cause header offset
			// issues to occur.
			if(dojo.isIE && v.headerNode && v.scrollboxNode){
				v.headerNode.scrollLeft = v.scrollboxNode.scrollLeft;
			}
		}
		return top;
		//this.onEach("setScrollTop", [ inTop ]);
	},

	getFirstScrollingView: function(){
		// summary: Returns the first grid view with a scroll bar
		for(var i=0, v; (v=this.views[i]); i++){
			if(v.hasHScrollbar() || v.hasVScrollbar()){
				return v;
			}
		}
	}

});
<|endoftext|>


<commit_before>module.exports = function(grunt) {

	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),
		src_dir: 'src',
		template_dir: 'grunt/templates',
		build_dir: 'build',
		docs_dir: 'build_docs',
		dist_dir: 'dist',

		modules: grunt.file.expand({ cwd: 'src' }, 'mm-*'),
		shared: grunt.file.expand({ cwd: 'src' }, 'shared/**/*.html'),

		clean: {
			build: '<%= build_dir %>',
			docs: '<%= docs_dir %>',
			dist: '<%= dist_dir %>',
			src: ['src/mm-*/*.+(css|min.js)', 'src/mm-*/template.html']
		},

		copy: {
			build: {
				expand: true,
				cwd: '<%= src_dir %>',
				src: ['**/*.+(html|js|woff)', '!**/example.html'],
				dest: '<%= build_dir %>'
			},
			dist: {
				expand: true,
				cwd: '<%= build_dir %>',
				src: ['**/*.html', '!mm-*/index.html'],
				dest: '<%= dist_dir %>'
			}
		},

		sass: {
			options: {
				includePaths: ['bower_components/bourbon/app/assets/stylesheets/', '<%= src_dir %>/shared/sass/']
			},

			dev: {
				files: [{
					expand: true,
					cwd: '<%= src_dir %>',
					src: ['**/*.scss'],
					dest: '<%= build_dir %>',
					ext: '.css'
				}]
			},

			docs: {
				files: {
					'<%= docs_dir %>/docs.css':'docs/docs.scss'
				}
			},

			dist: {
				options: {
					outputStyle: 'compressed'
				},
				files: [{
					expand: true,
					cwd: '<%= src_dir %>',
					src: ['**/*.scss'],
					dest: '<%= build_dir %>',
					ext: '.css'
				}]
			}
		},

		uglify: {
			dist: {
				files: [{
					expand: true,
					cwd: '<%= build_dir %>',
					src: ['mm-*/*.js', 'shared/js/*.js'],
					dest: '<%= build_dir %>'
				}]
			}
		},

		cssUrlEmbed: {
			fonts: {
				files: {
					'<%= build_dir %>/shared/fonts/fonts.css' : '<%= build_dir %>/shared/fonts/fonts.css'
				}
			}
		},

		hogan_static: {
			index: {
				options: {
					data: {
						modules: '<%= modules %>'
					}
				},
				files: [{
					src: '<%= template_dir %>/index_template.html',
					dest: '<%= build_dir %>/index.html'
				}]
			},
			colors: {
				options: {
					// delimiters:'<< >>',
					data: {
						colors: {}
					}
				},
				files: {
					'src/lib/Colors.js': '<%= template_dir %>/lib_color.js'
				}
			},
			lib: {
				options:{
					data: {
						modules: '<%= modules %>',
						shared: '<%= shared %>',
					}
				},
				files: [{
					src: '<%= template_dir %>/lib_template.html',
					dest: '<%= build_dir %>/<%= pkg.name %>.html'
				}]
			}
		},

		vulcanize: {
			options: {
				inline:true,
				strip:true,
				'strip-excludes':false,
			},
			shared: {
				files: [{
					expand: true,
					cwd: '<%= build_dir %>',
					src: ['shared/**/*.html'],
					dest: '<%= build_dir %>'
				}]
			},
			modules: {
				options:{
					excludes: {
						imports: ['.*\.html']
					}
				},
				files: [{
					expand: true,
					cwd: '<%= build_dir %>',
					src: ['mm-*/mm-*.html'],
					dest: '<%= build_dir %>'
				}]
			},
			dist:{
				options:{
					excludes: {
						imports: ['polymer.html']
					}
				},
				files: {
					'<%= build_dir %>/<%= pkg.name %>.html' : '<%= build_dir %>/<%= pkg.name %>.html'
				}
			}
		},

		replace: {
            bower: {
                src: ['<%= build_dir %>/**/*.html'],
                overwrite: true,
                replacements: [{
                    from: 'bower_components',
                    to: '..'
                }]
            }
        },

		banner: grunt.file.read("BANNER.txt"),

		usebanner: {
			jscss: {
				options: {
					banner: "/**\n<%= banner %>\n*/"
				},
				files: {
					src: [ 'src/**/*.js', 'src/**/*.scss' ]
				}
			},
			html: {
				options: {
					banner: "<!--\n<%= banner %>\n-->"
				},
				files: {
					src: [ 'src/mm-*/mm-*.html' ]
				}
			},
			dist: {
				options: {
					banner: "<!--\n<%= banner %>\n-->"
				},
				files: {
					src: [ '<%= build_dir %>/strand.html' ]
				}
			},
		},

		shell: {
			stageRelease: {
				command: [
							'git checkout -b release-<%= pkg.version %>',
							'git add <%= dist_dir %>/*'
						].join('&&')
			},
			publishRelease: {
				command: [
							'git checkout master',
							'git merge --no-ff release-<%= pkg.version %>',
							'git tag -a v<%= pkg.version %> -m "Version <%= pkg.version %>"',
							'git checkout develop',
							'git merge --no-ff release-<%= pkg.version %>',
							'git branch -d release-<%= pkg.version %>'
						].join('&&')
			}
		},

		githubChanges: {
			release: {
				options: {
					owner: 'MediaMath',
					repository: 'strand',
					branch: 'develop',
					noMerges: true,
					tagName: 'v<%= pkg.version %>'
				}
			}
		},

		bump: {
			options: {
				files: [ 'package.json', 'bower.json' ],
				commitFiles: [ 'package.json', 'bower.json', 'CHANGELOG.md', 'dist/*' ],
				updateConfigs: ['pkg'],
				push: false,
				pushTo: 'upstream',
				createTag: false
			}
		}

	});

	grunt.registerTask('sassShadowFix', function() {
		var files = grunt.file.expand(grunt.config('build_dir') + '/mm-*/mm-*.css'),
			css;
		function stripQuotes(str, arg) {
			return str.replace(/'/g,'');
		}

		files.forEach(function(file) {
			css = grunt.file.read(file);
			//strip '' from :host('.select') workaround
			css = css.replace(/:host\('.*'\)/ig, stripQuotes);
			//strip '' from :host-context('.select') workaround
			css = css.replace(/:host-context\('.*'\)/ig, stripQuotes);
			//strip '' from '/shadow/' workaround
			css = css.replace(/'\/(shadow|shadow-child|shadow-deep)\/'/ig, stripQuotes);
			grunt.log.writeln('Wrote ' + file);
			grunt.file.write(file, css);
		}) ;

		grunt.log.ok();

	});

	//Create HTML imports for JS library
	grunt.registerTask('jslib:imports', function() {
		var files = grunt.file.expand({cwd: grunt.config('src_dir') + '/shared/js/' }, '*.js');

		files.forEach(function(file) {
			var fileName = file.substr(0, file.lastIndexOf('.')) || file;
			grunt.file.write(grunt.config('build_dir') + '/shared/js/' + fileName.toLowerCase() + '.html', '<script src=\"' + file + '\"></script>');
		});

		grunt.log.ok();
	});

	grunt.registerTask('color', function() {
		var path = grunt.config('src_dir') + '/shared/sass/_color.scss';
		var color = grunt.file.read(path);
		color = color.replace(/\$color-(\w\d\d*): (#[\w\d]*);/g, function(a, key, value) {
			return "\t\"" + key + "\":\"" + value + "\",";
		});
		grunt.config.set('hogan_static.colors.options.data.colors', color);
		grunt.task.run(['hogan_static:colors']);
	});

	grunt.registerTask('build:dist', function(arg){
		grunt.task.run([
			'clean:build',
			'copy:build',
			'sass:dist',
			'sassShadowFix',
			'cssUrlEmbed',
			'hogan_static:lib',
			'vulcanize:shared',
			'vulcanize:modules',
			'vulcanize:dist',
			'usebanner:dist'
		]);
	});

	grunt.registerTask('build:dev', function() {
		grunt.task.run([
			'clean:build',
			'copy:build',
			'sass:dev',
			'sassShadowFix',
			'hogan_static:index',
			'hogan_static:lib'
		]);
	});

	grunt.registerTask('default', ['build:dev']);

	grunt.registerTask('release', function(version){
		version = version || "patch";
		grunt.task.run( "bump-only:" + version, "clean:dist", "build:dist", "replace:bower", "copy:dist", "shell:stageRelease", "githubChanges", "bump-commit");
	});
};
<commit_msg>simplify release build script<commit_after>module.exports = function(grunt) {

	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),
		src_dir: 'src',
		template_dir: 'grunt/templates',
		build_dir: 'build',
		docs_dir: 'build_docs',
		dist_dir: 'dist',

		modules: grunt.file.expand({ cwd: 'src' }, 'mm-*'),
		shared: grunt.file.expand({ cwd: 'src' }, 'shared/**/*.html'),

		clean: {
			build: '<%= build_dir %>',
			docs: '<%= docs_dir %>',
			dist: '<%= dist_dir %>',
			src: ['src/mm-*/*.+(css|min.js)', 'src/mm-*/template.html']
		},

		copy: {
			build: {
				expand: true,
				cwd: '<%= src_dir %>',
				src: ['**/*.+(html|js|woff)', '!**/example.html'],
				dest: '<%= build_dir %>'
			},
			dist: {
				expand: true,
				cwd: '<%= build_dir %>',
				src: ['**/*.html', '!mm-*/index.html'],
				dest: '<%= dist_dir %>'
			}
		},

		sass: {
			options: {
				includePaths: ['bower_components/bourbon/app/assets/stylesheets/', '<%= src_dir %>/shared/sass/']
			},

			dev: {
				files: [{
					expand: true,
					cwd: '<%= src_dir %>',
					src: ['**/*.scss'],
					dest: '<%= build_dir %>',
					ext: '.css'
				}]
			},

			docs: {
				files: {
					'<%= docs_dir %>/docs.css':'docs/docs.scss'
				}
			},

			dist: {
				options: {
					outputStyle: 'compressed'
				},
				files: [{
					expand: true,
					cwd: '<%= src_dir %>',
					src: ['**/*.scss'],
					dest: '<%= build_dir %>',
					ext: '.css'
				}]
			}
		},

		uglify: {
			dist: {
				files: [{
					expand: true,
					cwd: '<%= build_dir %>',
					src: ['mm-*/*.js', 'shared/js/*.js'],
					dest: '<%= build_dir %>'
				}]
			}
		},

		cssUrlEmbed: {
			fonts: {
				files: {
					'<%= build_dir %>/shared/fonts/fonts.css' : '<%= build_dir %>/shared/fonts/fonts.css'
				}
			}
		},

		hogan_static: {
			index: {
				options: {
					data: {
						modules: '<%= modules %>'
					}
				},
				files: [{
					src: '<%= template_dir %>/index_template.html',
					dest: '<%= build_dir %>/index.html'
				}]
			},
			colors: {
				options: {
					// delimiters:'<< >>',
					data: {
						colors: {}
					}
				},
				files: {
					'src/lib/Colors.js': '<%= template_dir %>/lib_color.js'
				}
			},
			lib: {
				options:{
					data: {
						modules: '<%= modules %>',
						shared: '<%= shared %>',
					}
				},
				files: [{
					src: '<%= template_dir %>/lib_template.html',
					dest: '<%= build_dir %>/<%= pkg.name %>.html'
				}]
			}
		},

		vulcanize: {
			options: {
				inline:true,
				strip:true,
				'strip-excludes':false,
			},
			shared: {
				files: [{
					expand: true,
					cwd: '<%= build_dir %>',
					src: ['shared/**/*.html'],
					dest: '<%= build_dir %>'
				}]
			},
			modules: {
				options:{
					excludes: {
						imports: ['.*\.html']
					}
				},
				files: [{
					expand: true,
					cwd: '<%= build_dir %>',
					src: ['mm-*/mm-*.html'],
					dest: '<%= build_dir %>'
				}]
			},
			dist:{
				options:{
					excludes: {
						imports: ['polymer.html']
					}
				},
				files: {
					'<%= build_dir %>/<%= pkg.name %>.html' : '<%= build_dir %>/<%= pkg.name %>.html'
				}
			}
		},

		replace: {
            bower: {
                src: ['<%= build_dir %>/**/*.html'],
                overwrite: true,
                replacements: [{
                    from: 'bower_components',
                    to: '..'
                }]
            }
        },

		banner: grunt.file.read("BANNER.txt"),

		usebanner: {
			jscss: {
				options: {
					banner: "/**\n<%= banner %>\n*/"
				},
				files: {
					src: [ 'src/**/*.js', 'src/**/*.scss' ]
				}
			},
			html: {
				options: {
					banner: "<!--\n<%= banner %>\n-->"
				},
				files: {
					src: [ 'src/mm-*/mm-*.html' ]
				}
			},
			dist: {
				options: {
					banner: "<!--\n<%= banner %>\n-->"
				},
				files: {
					src: [ '<%= build_dir %>/strand.html' ]
				}
			},
		},

		shell: {
			stageRelease: {
				command: 'git add <%= dist_dir %>/*'
			}
		},

		githubChanges: {
			release: {
				options: {
					owner: 'MediaMath',
					repository: 'strand',
					branch: 'develop',
					noMerges: true,
					tagName: 'v<%= pkg.version %>'
				}
			}
		},

		bump: {
			options: {
				files: [ 'package.json', 'bower.json' ],
				commitFiles: [ 'package.json', 'bower.json', 'CHANGELOG.md', 'dist/*' ],
				updateConfigs: ['pkg'],
				push: false,
				pushTo: 'upstream',
				createTag: true
			}
		}

	});

	grunt.registerTask('sassShadowFix', function() {
		var files = grunt.file.expand(grunt.config('build_dir') + '/mm-*/mm-*.css'),
			css;
		function stripQuotes(str, arg) {
			return str.replace(/'/g,'');
		}

		files.forEach(function(file) {
			css = grunt.file.read(file);
			//strip '' from :host('.select') workaround
			css = css.replace(/:host\('.*'\)/ig, stripQuotes);
			//strip '' from :host-context('.select') workaround
			css = css.replace(/:host-context\('.*'\)/ig, stripQuotes);
			//strip '' from '/shadow/' workaround
			css = css.replace(/'\/(shadow|shadow-child|shadow-deep)\/'/ig, stripQuotes);
			grunt.log.writeln('Wrote ' + file);
			grunt.file.write(file, css);
		}) ;

		grunt.log.ok();

	});

	//Create HTML imports for JS library
	grunt.registerTask('jslib:imports', function() {
		var files = grunt.file.expand({cwd: grunt.config('src_dir') + '/shared/js/' }, '*.js');

		files.forEach(function(file) {
			var fileName = file.substr(0, file.lastIndexOf('.')) || file;
			grunt.file.write(grunt.config('build_dir') + '/shared/js/' + fileName.toLowerCase() + '.html', '<script src=\"' + file + '\"></script>');
		});

		grunt.log.ok();
	});

	grunt.registerTask('color', function() {
		var path = grunt.config('src_dir') + '/shared/sass/_color.scss';
		var color = grunt.file.read(path);
		color = color.replace(/\$color-(\w\d\d*): (#[\w\d]*);/g, function(a, key, value) {
			return "\t\"" + key + "\":\"" + value + "\",";
		});
		grunt.config.set('hogan_static.colors.options.data.colors', color);
		grunt.task.run(['hogan_static:colors']);
	});

	grunt.registerTask('build:dist', function(arg){
		grunt.task.run([
			'clean:build',
			'copy:build',
			'sass:dist',
			'sassShadowFix',
			'cssUrlEmbed',
			'hogan_static:lib',
			'vulcanize:shared',
			'vulcanize:modules',
			'vulcanize:dist',
			'usebanner:dist'
		]);
	});

	grunt.registerTask('build:dev', function() {
		grunt.task.run([
			'clean:build',
			'copy:build',
			'sass:dev',
			'sassShadowFix',
			'hogan_static:index',
			'hogan_static:lib'
		]);
	});

	grunt.registerTask('default', ['build:dev']);

	grunt.registerTask('release', function(version){
		version = version || "patch";
		grunt.task.run( "bump-only:" + version, "clean:dist", "build:dist", "replace:bower", "copy:dist", "shell:stageRelease", "githubChanges", "bump-commit");
	});
};
<|endoftext|>


<commit_before>"use strict";

var tape = require("tape")
var owns = Object.prototype.hasOwnProperty
var keys = Object.keys || function(object) {
  var result = []
  for (var key in object) {
    if (owns.call(object, object[key])) {
      result.push(key)
    }
  }
}

function adaptAsync(unit) {
  return function(assert) {
    unit(assert, function() {
      assert.end()
    })
  }
}

function adaptSync(unit) {
  return function(assert) {
    unit(assert)
    assert.end()
  }
}

function adapt(suite, test) {
  test = test || tape
  Object.keys(suite).forEach(function(name) {
    // CommonJS cares only about names that start with test.
    if (name.indexOf("test") !== 0) return
    var unit = suite[name]

    // If unit is a function then adapt it.
    if (typeof(unit) === "function") {
      var adapted = unit.length > 1 ? adaptAsync(unit) :
                    adaptSync(unit)

      test(name, adapted)
    } else if (unit && typeof(unit) === "object"){
      test(name, function(test) {
        adapt(unit, function(name, unit) {
          test.test(name, unit)
          test.end()
        })
      })
    }
  })
}

module.exports = adapt
<commit_msg>End unit only after all tests have being adapted.<commit_after>"use strict";

var tape = require("tape")
var owns = Object.prototype.hasOwnProperty
var keys = Object.keys || function(object) {
  var result = []
  for (var key in object) {
    if (owns.call(object, object[key])) {
      result.push(key)
    }
  }
}

function adaptAsync(unit) {
  return function(assert) {
    unit(assert, function() {
      assert.end()
    })
  }
}

function adaptSync(unit) {
  return function(assert) {
    unit(assert)
    assert.end()
  }
}

function adapt(suite, test) {
  test = test || tape
  Object.keys(suite).forEach(function(name) {
    // CommonJS cares only about names that start with test.
    if (name.indexOf("test") !== 0) return
    var unit = suite[name]

    // If unit is a function then adapt it.
    if (typeof(unit) === "function") {
      var adapted = unit.length > 1 ? adaptAsync(unit) :
                    adaptSync(unit)

      test(name, adapted)
    } else if (unit && typeof(unit) === "object"){
      test(name, function(test) {
        adapt(unit, function(name, unit) {
          test.test(name, unit)
        })
        test.end()
      })
    }
  })
}

module.exports = adapt
<|endoftext|>


<commit_before>import React, {PropTypes} from 'react';
import {connect} from 'react-redux';
import format from 'date-fns/format';
import startCase from 'lodash/startCase';
import Icon from '../../../../shared/Icon';
import isEmpty from 'lodash/isEmpty'

import './SubmitConfirmation.css'

class SubmitConfirmation extends React.Component {

    render() {
        const {
            opportunityUrl,
            domain,
            closingDate,
            briefTitle,
            briefLot,
            previewUrl,
            profileUrl,
            initial,        // arrives via brief response check that found no casestudies
            inReview,       // supplier arrives after initiating a casestudy assessment
            isRecruiter,
            created         // if an assessment has been created
        } = this.props;

        return (

                {((inReview || !initial) &&

                            You have been prioritised for assessment

Completed

Demonstrate your experience in your seller profile

To do

We're assessing your expertise

Up next

You're ready to respond to briefs

We are reviewing your profile and expertise,
                            and will let you know soon if you're approved to offer {domain} on the Digital Marketplace.

If we can confirm your expertise before the opportunity closes on
                             {format(new Date(closingDate), 'Do MMMM YYYY')}, you’ll be invited to apply.

While you wait you can prepare your response
                            using our Google sheets template.

                                Download brief response template

                )}
                {(initial &&

                            Have you got experience in {domain}?

To do

Demonstrate your experience in your seller profile

Up next

We're assessing your expertise

Up next

You're ready to respond to briefs

                        {(
                            created &&
We are reviewing your profile and expertise,
                            and will let you know soon if you're approved to offer {domain} on the Digital Marketplace.

                        )}

                            If we can confirm your expertise before the opportunity closes on
                             {format(new Date(closingDate), 'Do MMMM YYYY')}, you’ll be invited to apply.

                        {(
                            !isRecruiter &&
While you wait you can prepare your response
                             using our Google sheets template.

                        )}
                        {(
                            isRecruiter &&
If you're successful but the opportunity has
                            closed, you won't need to be reassessed for {domain} opportunties in future.

                        )}

                                Edit your profile

                )}

        )
    }
}

SubmitConfirmation.propTypes = {
    opportunityUrl: PropTypes.string.isRequired,
    domain: PropTypes.string,
    closingDate: PropTypes.string.isRequired,
    briefTitle: PropTypes.string,
    briefLot: PropTypes.string,
    previewUrl: PropTypes.string,
    profileUrl: PropTypes.string,
    unassessedDomains: PropTypes.object,
    assessedDomains: PropTypes.object,
    created: PropTypes.bool
}

const mapStateToProps = (state, ownProps) => {
    return {
        ...state.form_options,
        ...ownProps
    }
}

export default connect(mapStateToProps)(SubmitConfirmation)
<commit_msg>Remove progress indicator fro brief response flow (#425)<commit_after>import React, {PropTypes} from 'react';
import {connect} from 'react-redux';
import format from 'date-fns/format';
import startCase from 'lodash/startCase';
import Icon from '../../../../shared/Icon';
import isEmpty from 'lodash/isEmpty'

import './SubmitConfirmation.css'

class SubmitConfirmation extends React.Component {

    render() {
        const {
            opportunityUrl,
            domain,
            closingDate,
            briefTitle,
            briefLot,
            previewUrl,
            profileUrl,
            initial,        // arrives via brief response check that found no casestudies
            inReview,       // supplier arrives after initiating a casestudy assessment
            isRecruiter,
            created         // if an assessment has been created
        } = this.props;

        return (

                {((inReview || !initial) &&

                            You have been prioritised for assessment

                            Before you can respond to this brief we need to assess your expertise in {domain}.

                            You’ll be able to apply if we can confirm your expertise before the opportunity closes on
                             {format(new Date(closingDate), 'Do MMMM YYYY')}.

                            While you wait you can prepare your response
                            using our Google sheets template.

                                Download brief response template

                )}
                {(initial &&

                            Have you got experience in {domain}?

                        {(
                            created &&
We are reviewing your profile and expertise,
                            and will let you know soon if you're approved to offer {domain} on the Digital Marketplace.

                        )}

                            If we can confirm your expertise before the opportunity closes on
                             {format(new Date(closingDate), 'Do MMMM YYYY')}, you’ll be invited to apply.

                        {(
                            !isRecruiter &&
While you wait you can prepare your response
                             using our Google sheets template.

                        )}
                        {(
                            isRecruiter &&
If you're successful but the opportunity has
                            closed, you won't need to be reassessed for {domain} opportunties in future.

                        )}

                                Edit your profile

                )}

        )
    }
}

SubmitConfirmation.propTypes = {
    opportunityUrl: PropTypes.string.isRequired,
    domain: PropTypes.string,
    closingDate: PropTypes.string.isRequired,
    briefTitle: PropTypes.string,
    briefLot: PropTypes.string,
    previewUrl: PropTypes.string,
    profileUrl: PropTypes.string,
    unassessedDomains: PropTypes.object,
    assessedDomains: PropTypes.object,
    created: PropTypes.bool
}

const mapStateToProps = (state, ownProps) => {
    return {
        ...state.form_options,
        ...ownProps
    }
}

export default connect(mapStateToProps)(SubmitConfirmation)
<|endoftext|>


<commit_before>import React from 'react';
import { expect } from 'chai';
import { shallow, mount } from 'enzyme';
import Immutable from 'immutable';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';

import '../../../../config/chai/chai.config';
import UserPermissionsTable from './UserPermissionsTable';

const middlewares = []
const mockStore = configureStore(middlewares);

describe('UserPermissionsTable', function() {
  const headers = ['Users', 'Roles', 'Permissions'];
  const userPermissions = Immutable.fromJS([{
    email: 'test@gmail.com',
    id: '123',
    individualPermissions: ['Read'],
    permissions: ['Read', 'Discover'],
    roles: ['admin', 'user']
  }]);
  const rolePermissions = Immutable.fromJS({
    AuthenticatedUser: ['Read', 'Link', 'Discover'],
    Admin: ['Read'],
    User: []
  });
  const defaultProps = {
    headers,
    userPermissions,
    rolePermissions
  };

  it('Should render', function() {
    const initialState = {};
    const store = mockStore(initialState);
    const wrapper = shallow(
      <Provider store={store}>
        <UserPermissionsTable {...defaultProps} />
      </Provider>
    );

    expect(wrapper.length).to.eql(1);
  });

  it('Should render headers', function() {
    const initialState = {};
    const store = mockStore(initialState);
    const wrapper = mount(
      <Provider store={store}>
        <UserPermissionsTable {...defaultProps} />
      </Provider>
    );

    expect(wrapper.find('th')).to.have.length(3);
  });
});
<commit_msg>use beforeEach<commit_after>import React from 'react';
import { expect } from 'chai';
import { shallow, mount } from 'enzyme';
import Immutable from 'immutable';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';

import '../../../../config/chai/chai.config';
import UserPermissionsTable from './UserPermissionsTable';

const middlewares = []
const mockStore = configureStore(middlewares);

describe('UserPermissionsTable', function() {
  const headers = ['Users', 'Roles', 'Permissions'];
  const userPermissions = Immutable.fromJS([{
    email: 'test@gmail.com',
    id: '123',
    individualPermissions: ['Read'],
    permissions: ['Read', 'Discover'],
    roles: ['admin', 'user']
  }]);
  const rolePermissions = Immutable.fromJS({
    AuthenticatedUser: ['Read', 'Link', 'Discover'],
    Admin: ['Read'],
    User: []
  });
  const defaultProps = {
    headers,
    userPermissions,
    rolePermissions
  };

  let initialState;
  let store;
  beforeEach(() => {
    initialState = {};
    store = mockStore(initialState);
  })

  it('Should render', function() {
    const wrapper = shallow(
      <Provider store={store}>
        <UserPermissionsTable {...defaultProps} />
      </Provider>
    );

    expect(wrapper.length).to.eql(1);
  });

  it('Should render headers', function() {
    const wrapper = mount(
      <Provider store={store}>
        <UserPermissionsTable {...defaultProps} />
      </Provider>
    );

    expect(wrapper.find('th')).to.have.length(3);
  });
});
<|endoftext|>


<commit_before>/*! ***********************************************************************

    License: MIT Licence

    Description:
        State type to manage custom system type.

************************************************************************ */

/**
 * State type to manage custom system type.
 *
 * @constructor
*/
a.state.type = {
    /**
     * The store.
     *
     * @property _store
     * @private
    */
    _store: a.mem.getInstance('app.state.type'),

    /**
     * Add a new type to state system.
     * Type allow you to control how the html will be loaded to system.
     *
     * @param {String} name                 The name to use inside state
     * @param {Function} input              The function to call when name is
     *                                      found on a loading state.
     *                                      The first param given to this
     *                                      function will be entry point
     *                                      (a.dom), then the html, and finally
     *                                      if async the chain object.
     *                                      This is the function to call on
     *                                      input
     * @param {Function} output             The function to call on output
     * @param {Boolean} async               Indicate if the type should be run
     *                                      as an async or not. If the async
     *                                      is set to true, the last parameter
     *                                      will be the chain objet to continue
     *                                      like in default state.
    */
    add: function(name, input, output, async) {
        this._store.set(name, {
            input:  input,
            output: output,
            async:  async
        });
    },

    /**
     * Remove a type from existing type elements.
     *
     * @param {String} name                 The type name to remove
    */
    remove: function(name) {
        this._store.remove(name);
    },

    /**
     * Get a type from existing type list.
     *
     * @param {String} name                 The name to get
     * @return {Object | Function | Null}   The founded elements
    */
    get: function(name) {
        return this._store.get(name);
    },

    /**
     * Print the full list of type currently available.
     *
     * @return {Object}                     The list of types found
    */
    list: function() {
        return this._store.list();
    }

    /*!
     * @private
    */
};
<commit_msg>Update doc.<commit_after>/*! ***********************************************************************

    License: MIT Licence

    Description:
        State type to manage custom system type.

************************************************************************ */

/**
 * State type to manage custom system type.
 * A type can be for example 'replace', 'append', it's used between transition
 * during html loading or unloading of a given state.
 *
 * @constructor
*/
a.state.type = {
    /**
     * The store.
     *
     * @property _store
     * @private
    */
    _store: a.mem.getInstance('app.state.type'),

    /**
     * Add a new type to state system.
     * Type allow you to control how the html will be loaded to system.
     *
     * @param {String} name                 The name to use inside state
     * @param {Function} input              The function to call when name is
     *                                      found on a loading state.
     *                                      The first param given to this
     *                                      function will be entry point
     *                                      (a.dom), then the html, and finally
     *                                      if async the chain object.
     *                                      This is the function to call on
     *                                      input
     * @param {Function} output             The function to call on output
     * @param {Boolean} async               Indicate if the type should be run
     *                                      as an async or not. If the async
     *                                      is set to true, the last parameter
     *                                      will be the chain objet to continue
     *                                      like in default state.
    */
    add: function(name, input, output, async) {
        this._store.set(name, {
            input:  input,
            output: output,
            async:  async
        });
    },

    /**
     * Remove a type from existing type elements.
     *
     * @param {String} name                 The type name to remove
    */
    remove: function(name) {
        this._store.remove(name);
    },

    /**
     * Get a type from existing type list.
     *
     * @param {String} name                 The name to get
     * @return {Object | Function | Null}   The founded elements
    */
    get: function(name) {
        return this._store.get(name);
    },

    /**
     * Print the full list of type currently available.
     *
     * @return {Object}                     The list of types found
    */
    list: function() {
        return this._store.list();
    }

    /*!
     * @private
    */
};
<|endoftext|>


<commit_before>/**
 * admin.babel.js
 *
 * Description:
 *   Admin page to controll questions
 *
 * Author:
 *  sota1235
 */
'use strict';

import React      from 'react';
import { render } from 'react-dom';
import $          from 'jquery';
// react bootstrap
import {
  ListGroup, ListGroupItem, Panel,
  ButtonToolbar, Button, Input
} from 'react-bootstrap';
// own files
import {
  getQuestions,
  addQuestion,
  deleteQuestion
} from './ajax.babel.js';

var socket    = io();
var Component = React.Component;

/* commponents */
// form to add question
class QuestionForm extends Component {
  constructor(props) {
    super(props);
    this.state = {
      value: ''
    };
    this.handleQuestionClick = this.handleQuestionClick.bind(this);
    this.handleChange        = this.handleChange.bind(this);
  }

  handleQuestionClick() {
    var choice = ['A1', 'A2', 'A3', 'A4'];
    var text   = this.refs.input.getValue();
    this.props.onQuestionClick(text, choice);
    this.refs.input.setValue('');
    return;
  }

  handleChange() {
    this.setState({
      value: this.refs.input.getValue()
    });
  }

  render() {
    return (

        <Input
          type='text'
          placeholder='クイズ本文を入力してください'
          hasFeedback
          value={this.state.value}
          ref='input'
          groupClassName='group-class'
          labelClassName='label-class'
          onChange={this.handleChange} />
        <ButtonToolbar>
          <Button
            bsStyle='primary'
            onClick={this.handleQuestionClick}>
            クイズを作成
          </Button>
        </ButtonToolbar>

    );
  }
}

// question
class Question extends Component {
  constructor(props) {
    super(props);
    this.state = {
      buttonStyle: {
        float: 'right'
      }
    }
    this.handleDeleteClick = this.handleDeleteClick.bind(this);
  }

  handleDeleteClick() {
    var id = this.props.id;
    this.props.onDeleteClick(id);
    return;
  }

  render() {
    return (

        <ListGroupItem>

            {this.props.children}
            <Button
              bsStyle='danger'
              pullRight
              style={this.state.buttonStyle}
              onClick={this.handleDeleteClick}
            >
              削除
            </Button>

        </ListGroupItem>

    );
  }
}

// question list
class QuestionList extends Component {
  render() {
    var onDeleteClick = this.props.onDeleteClick;
    var questionNodes = this.props.questions.map(function(question) {
      return (
        <Question
          id={question._id}
          key={question._id}
          onDeleteClick={onDeleteClick}
        >
          {question.text}
        </Question>
      )
    });
    return (

        <ListGroup>
          {questionNodes}
        </ListGroup>

    );
  }
}

// admin page
class QuestionAdmin extends Component {
  constructor(props) {
    super(props);
    this.state = {
      questions: []
    };
    this.handleQuestionClick = this.handleQuestionClick.bind(this);
    this.handleDeleteClick   = this.handleDeleteClick.bind(this);
  }

  handleQuestionClick(text, choice) {
    var that = this;
    addQuestion(text, choice)
      .then(function(result) {
        console.log(`Submit comment success : ${JSON.stringify(result)}`);
        that.loadQuestions();
      });
  }

  handleDeleteClick(id) {
    var that = this;
    deleteQuestion(id)
      .then(function(result) {
        console.log(`Delete question success id : ${id}`);
        that.loadQuestions();
      });
  }

  loadQuestions() {
    var that = this;
    getQuestions()
      .then(function(questions) {
        that.setState({questions: questions});
      });
  }

  componentDidMount() {
    this.loadQuestions();
  }

  render() {
    return (

Hello, question admin

        <QuestionForm onQuestionClick={this.handleQuestionClick} />
        <QuestionList
          questions={this.state.questions}
          onDeleteClick={this.handleDeleteClick}
        />

    );
  }
}

/* rendering */
render(
  <QuestionAdmin />,
  document.getElementById('question-admin')
);
<commit_msg>modify: change way to listen event - using event emitter 2<commit_after>/**
 * admin.babel.js
 *
 * Description:
 *   Admin page to controll questions
 *
 * Author:
 *  sota1235
 */
'use strict';

import React             from 'react';
import { render }        from 'react-dom';
import $                 from 'jquery';
import { EventEmitter2 } from 'eventemitter2';
// react bootstrap
import {
  ListGroup, ListGroupItem, Panel,
  ButtonToolbar, Button, Input
} from 'react-bootstrap';
// own files
import {
  getQuestions,
  addQuestion,
  deleteQuestion
} from './ajax.babel.js';

var socket    = io();
var emitter   = new EventEmitter2();
var Component = React.Component;

/* commponents */
// form to add question
class QuestionForm extends Component {
  constructor(props) {
    super(props);
    this.state = {
      value: ''
    };
    this.handleQuestionClick = this.handleQuestionClick.bind(this);
    this.handleChange        = this.handleChange.bind(this);
  }

  handleQuestionClick() {
    var choice = ['A1', 'A2', 'A3', 'A4'];
    var text   = this.refs.input.getValue();
    emitter.emit('onQuestionClick', text, choice);
    this.setState({value: ''});
    return;
  }

  handleChange() {
    this.setState({
      value: this.refs.input.getValue()
    });
  }

  render() {
    return (

        <Input
          type='text'
          placeholder='クイズ本文を入力してください'
          hasFeedback
          value={this.state.value}
          ref='input'
          groupClassName='group-class'
          labelClassName='label-class'
          onChange={this.handleChange} />
        <ButtonToolbar>
          <Button
            bsStyle='primary'
            onClick={this.handleQuestionClick}>
            クイズを作成
          </Button>
        </ButtonToolbar>

    );
  }
}

// question
class Question extends Component {
  constructor(props) {
    super(props);
    this.state = {
      buttonStyle: {
        float: 'right'
      }
    }
    this.handleDeleteClick = this.handleDeleteClick.bind(this);
  }

  handleDeleteClick() {
    var id = this.props.id;
    emitter.emit('onDeleteClick', id);
    return;
  }

  render() {
    return (

        <ListGroupItem>

            {this.props.children}
            <Button
              bsStyle='danger'
              pullRight
              style={this.state.buttonStyle}
              onClick={this.handleDeleteClick}
            >
              削除
            </Button>

        </ListGroupItem>

    );
  }
}

// question list
class QuestionList extends Component {
  render() {
    var questionNodes = this.props.questions.map(function(question) {
      return (
        <Question
          id={question._id}
          key={question._id}
        >
          {question.text}
        </Question>
      )
    });
    return (

        <ListGroup>
          {questionNodes}
        </ListGroup>

    );
  }
}

// admin page
class QuestionAdmin extends Component {
  constructor(props) {
    super(props);
    this.state = {
      questions: []
    };
    this.handleQuestionClick = this.handleQuestionClick.bind(this);
    this.handleDeleteClick   = this.handleDeleteClick.bind(this);
  }

  handleQuestionClick(text, choice) {
    var that = this;
    addQuestion(text, choice)
      .then(function(result) {
        console.log(`Submit comment success : ${JSON.stringify(result)}`);
        that.loadQuestions();
      });
  }

  handleDeleteClick(id) {
    var that = this;
    deleteQuestion(id)
      .then(function(result) {
        console.log(`Delete question success id : ${id}`);
        that.loadQuestions();
      });
  }

  loadQuestions() {
    var that = this;
    getQuestions()
      .then(function(questions) {
        that.setState({questions: questions});
      });
  }

  componentDidMount() {
    let that = this;
    this.loadQuestions();
    // event listener
    emitter.on('onDeleteClick', function(id) {
      that.handleDeleteClick(id);
    });
    emitter.on('onQuestionClick', function(text, choice) {
      that.handleQuestionClick(text, choice);
    });
  }

  render() {
    return (

Hello, question admin

        <QuestionForm />
        <QuestionList questions={this.state.questions}/>

    );
  }
}

/* rendering */
render(
  <QuestionAdmin />,
  document.getElementById('question-admin')
);
<|endoftext|>


<commit_before>s(function(module) {

  var teacherView = {};
  var ref = new Firebase('https://queue-you.firebaseio.com/');
  var ref2 = new Firebase('https://queue-you.firebaseio.com/users/');
  var profileList = [];
  var keys = [];

  teacherView.showList = function() {
    $('#teacher-button').on('click', function(e) {
      e.preventDefault();
      $('#teacher').show().siblings().hide();
      $('#chosen-one').hide();
      $('#current').hide();
      teacherView.post();
    });
  };

  teacherView.storagePull = function(){
    ref2.on('child_added', function(snapshot, prevChildKey){
      var newPost = snapshot.val();
      profileList.push(snapshot.val());
      keys.push(snapshot.key());
      console.log(snapshot.key());
      // storeLocal(userid);
    });
  };
  teacherView.post = function(){
    for (var i = 0; i < profileList.length; i++) {
      $('ol').append('
Name: ' + profileList[i].name + '
' +
      profileList[i].issue + '
' +
      profileList[i].desc +'
' + '
' + '
');
      teacherView.selected();
    }
  };

  teacherView.removeFromdb = function() {
    $('#chosen').on('click', function(e) { //on button click remove from db
      e.preventDefault();
      var $key = $(this).index();
      console.log($key);
      var deleteRef = new Firebase('https://queue-you.firebaseio.com/users/' + keys[$key]);
      deleteRef.remove();
      $(this).remove();
    });
  };

  teacherView.selected = function() {
    $('li').on('click', function(e) {
      e.preventDefault();
      var $chosen = $(this).clone();
      $(this).hide();
      $('#current').show();
      $('#chosen-one').append($chosen).show();
      $('#chosen-one li').attr('id', 'chosen');
      teacherView.removeFromdb();
    });
  };

  teacherView.showList();
  teacherView.selected();
  teacherView.storagePull();
  module.teacherView = teacherView;
})(window);
<commit_msg>imported old working functionality to new design and styling<commit_after>(function(module) {

  var teacherView = {};
  var ref = new Firebase('https://queue-you.firebaseio.com/');
  var ref2 = new Firebase('https://queue-you.firebaseio.com/users/');
  var profileList = [];
  var keys = [];

  teacherView.showList = function() {
    $('#teacher-button').on('click', function() {
      $('#teacher').show().siblings().hide();
      $('#current').hide();
      teacherView.post();
    });
  };

  teacherView.storagePull = function(){
    ref2.on('child_added', function(snapshot, prevChildKey){
      var newPost = snapshot.val();
      profileList.push(snapshot.val());
      keys.push(snapshot.key());

    });
  };

  teacherView.post = function(){
    for (var i = 0; i < profileList.length; i++) {
      $('ol').append('
Name: ' + profileList[i].name + '
' +
      profileList[i].issue + '
' +
      profileList[i].desc +'
' + '
' + '
');
      teacherView.remove();
    }
  };

  teacherView.remove = function() {
    $('li').click(function() {
      var $key = $(this).index();
      console.log($key);
      console.log(keys[$key]);
      var deleteRef = new Firebase('https://queue-you.firebaseio.com/users/' + keys[$key]);
      console.log(deleteRef);
      console.log($(this).index());
      deleteRef.remove();
      $(this).remove();
    });
  };

  teacherView.showList();
  teacherView.storagePull();
  module.teacherView = teacherView;
})(window);
<|endoftext|>


<commit_before>let electrumServers = {
  /*zcash: {
    address: '173.212.225.176',
    port: 50032,
    proto: 'tcp',
  },*/
  revs: { // !estimatefee
    address: '173.212.225.176',
    port: 50050,
    proto: 'tcp',
    txfee: 10000,
    abbr: 'REVS',
    serverList: [
      '173.212.225.176:50050',
      '136.243.45.140:50050'
    ],
  },
  mnz: { // !estimatefee
    address: '173.212.225.176',
    port: 50053,
    proto: 'tcp',
    txfee: 10000,
    abbr: 'MNZ',
    serverList: [
      '173.212.225.176:50053',
      '136.243.45.140:50053'
    ],
  },
  wlc: { // !estimatefee
    address: '173.212.225.176',
    port: 50052,
    proto: 'tcp',
    txfee: 10000,
    abbr: 'WLC',
    serverList: [
      '173.212.225.176:50052',
      '136.243.45.140:50052'
    ],
  },
  jumblr: { // !estimatefee
    address: '173.212.225.176',
    port: 50051,
    proto: 'tcp',
    txfee: 10000,
    abbr: 'JUMBLR',
    serverList: [
      '173.212.225.176:50051',
      '136.243.45.140:50051'
    ],
  },
  komodo: { // !estimatefee
    address: '173.212.225.176',
    port: 50011,
    proto: 'tcp',
    txfee: 10000,
    abbr: 'KMD',
    serverList: [
      '173.212.225.176:50011',
      '136.243.45.140:50011'
    ],
  },
  dogecoin: { // !estimatefee
    address: '173.212.225.176',
    port: 50015,
    proto: 'tcp',
    txfee: 100000000,
    abbr: 'DOGE',
  },
  viacoin: { // !estimatefee
    address: 'vialectrum.bitops.me',
    port: 50002,
    proto: 'ssl',
    txfee: 100000,
    abbr: 'VIA',
  },
  vertcoin: {
    address: '173.212.225.176',
    port: 50088,
    proto: 'tcp',
    txfee: 100000,
    abbr: 'VTC',
  },
  namecoin: {
    address: '173.212.225.176',
    port: 50036,
    proto: 'tcp',
    txfee: 100000,
    abbr: 'NMC',
  },
  monacoin: { // !estimatefee
    address: '173.212.225.176',
    port: 50002,
    proto: 'tcp',
    txfee: 100000,
    abbr: 'MONA',
  },
  litecoin: {
    address: '173.212.225.176',
    port: 50012,
    proto: 'tcp',
    txfee: 10000,
    abbr: 'LTC',
  },
  faircoin: {
    address: '173.212.225.176',
    port: 50005,
    proto: 'tcp',
    txfee: 1000000,
    abbr: 'FAIR',
  },
  digibyte: {
    address: '173.212.225.176',
    port: 50022,
    proto: 'tcp',
    txfee: 100000,
    abbr: 'DGB',
  },
  dash: {
    address: '173.212.225.176',
    port: 50098,
    proto: 'tcp',
    txfee: 10000,
    abbr: 'DASH',
  },
  crown: {
    address: '173.212.225.176',
    port: 50041,
    proto: 'tcp',
    txfee: 10000,
    abbr: 'CRW',
  },
  bitcoin: {
    address: '173.212.225.176',
    port: 50001,
    proto: 'tcp',
    abbr: 'BTC',
  },
  argentum: { // !estimatefee
    address: '173.212.225.176',
    port: 50081,
    proto: 'tcp',
    txfee: 50000,
    abbr: 'ARG',
  },
  chips: { // !estimatefee
    address: '173.212.225.176',
    port: 50076,
    proto: 'tcp',
    txfee: 10000,
    abbr: 'CHIPS',
    serverList: [
      '173.212.225.176:50076',
      '136.243.45.140:50076'
    ],
  },
};

module.exports = electrumServers;<commit_msg>update spv kmd and mnz server list<commit_after>let electrumServers = {
  /*zcash: {
    address: '173.212.225.176',
    port: 50032,
    proto: 'tcp',
  },*/
  revs: { // !estimatefee
    address: '173.212.225.176',
    port: 50050,
    proto: 'tcp',
    txfee: 10000,
    abbr: 'REVS',
    serverList: [
      '173.212.225.176:50050',
      '136.243.45.140:50050'
    ],
  },
  mnz: { // !estimatefee
    address: '173.212.225.176',
    port: 10002,
    proto: 'tcp',
    txfee: 10000,
    abbr: 'MNZ',
    serverList: [
      '173.212.225.176:10002',
      '136.243.45.140:10002',
      '18.216.195.109:10002',
      '52.41.58.116:10002',
      '52.67.48.29 :10002',
      '13.124.87.194:10002',
      '52.63.107.102:10002'
    ],
  },
  wlc: { // !estimatefee
    address: '173.212.225.176',
    port: 50052,
    proto: 'tcp',
    txfee: 10000,
    abbr: 'WLC',
    serverList: [
      '173.212.225.176:50052',
      '136.243.45.140:50052'
    ],
  },
  jumblr: { // !estimatefee
    address: '173.212.225.176',
    port: 50051,
    proto: 'tcp',
    txfee: 10000,
    abbr: 'JUMBLR',
    serverList: [
      '173.212.225.176:50051',
      '136.243.45.140:50051'
    ],
  },
  komodo: { // !estimatefee
    address: '173.212.225.176',
    port: 10001,
    proto: 'tcp',
    txfee: 10000,
    abbr: 'KMD',
    serverList: [
      '173.212.225.176:10001',
      '136.243.45.140:10001',
      '18.216.195.109:10001',
      '52.41.58.116:10001',
      '52.67.48.29 :10001',
      '13.124.87.194:10001',
      '52.63.107.102:10001'
    ],
  },
  dogecoin: { // !estimatefee
    address: '173.212.225.176',
    port: 50015,
    proto: 'tcp',
    txfee: 100000000,
    abbr: 'DOGE',
  },
  viacoin: { // !estimatefee
    address: 'vialectrum.bitops.me',
    port: 50002,
    proto: 'ssl',
    txfee: 100000,
    abbr: 'VIA',
  },
  vertcoin: {
    address: '173.212.225.176',
    port: 50088,
    proto: 'tcp',
    txfee: 100000,
    abbr: 'VTC',
  },
  namecoin: {
    address: '173.212.225.176',
    port: 50036,
    proto: 'tcp',
    txfee: 100000,
    abbr: 'NMC',
  },
  monacoin: { // !estimatefee
    address: '173.212.225.176',
    port: 50002,
    proto: 'tcp',
    txfee: 100000,
    abbr: 'MONA',
  },
  litecoin: {
    address: '173.212.225.176',
    port: 50012,
    proto: 'tcp',
    txfee: 10000,
    abbr: 'LTC',
  },
  faircoin: {
    address: '173.212.225.176',
    port: 50005,
    proto: 'tcp',
    txfee: 1000000,
    abbr: 'FAIR',
  },
  digibyte: {
    address: '173.212.225.176',
    port: 50022,
    proto: 'tcp',
    txfee: 100000,
    abbr: 'DGB',
  },
  dash: {
    address: '173.212.225.176',
    port: 50098,
    proto: 'tcp',
    txfee: 10000,
    abbr: 'DASH',
  },
  crown: {
    address: '173.212.225.176',
    port: 50041,
    proto: 'tcp',
    txfee: 10000,
    abbr: 'CRW',
  },
  bitcoin: {
    address: '173.212.225.176',
    port: 50001,
    proto: 'tcp',
    abbr: 'BTC',
  },
  argentum: { // !estimatefee
    address: '173.212.225.176',
    port: 50081,
    proto: 'tcp',
    txfee: 50000,
    abbr: 'ARG',
  },
  chips: { // !estimatefee
    address: '173.212.225.176',
    port: 50076,
    proto: 'tcp',
    txfee: 10000,
    abbr: 'CHIPS',
    serverList: [
      '173.212.225.176:50076',
      '136.243.45.140:50076'
    ],
  },
};

module.exports = electrumServers;<|endoftext|>


<commit_before>"use strict";

import {LyricRenderer, NoteRenderer, ProgressRenderer, ScoreRenderer, TitleRenderer} from "./ultrastar/render";
import EventEmitter from "events";

export class GameDisplay extends EventEmitter {
    constructor(container, width, height, song, players, audio) {
        super();
        this.song = song;
        this.container = container;
        this.width = width;
        this.height = height;
        this.players = players;
        this.audio = audio;
        this.div = null;
        this.videoElement = null;
        this.canvasElement = null;
        this.canvasContext = null;
        this._currentCanvasScale = null;
        this.progressRenderer = null;
        this.lyricRenderers = [];
        this.noteRenderers = [];
        this.scoreRenderers = [];
        this.running = false;
        this._ready = false;
        this._scaleInterval = null;
        this._gapTimer = null;
        this._canKillVideo = false; // Chrome throws a fit if we unload a video it's trying to start playing.
        this.container.style.overflow = 'hidden';
        this.container.style.width = `${width}px`;
        this.container.style.height = `${height}px`;
    }

    setSize(width, height) {
        this.width = width;
        this.height = height;
        this.container.style.width = `${width}px`;
        this.container.style.height = `${height}px`;
        this._scaleCanvas(true);
        this._updateVideoSize();
        this.updateLayout();
    }

    createGameLayout() {
        console.log("players", this.players);
        this.canvasElement = document.createElement('canvas');
        this.canvasElement.style.position = 'absolute';
        this.canvasElement.style.top = 0;
        this.canvasElement.style.left = 0;
        this.canvasContext = this.canvasElement.getContext('2d');
        this._scaleCanvas();

        this.div = document.createElement('div');
        this.div.style.position = 'relative';

        this.div.appendChild(this.videoElement);
        this.div.appendChild(this.canvasElement);
        this.container.appendChild(this.div);

        this.progressRenderer = new ProgressRenderer(this.canvasElement, this.song, this.audio);

        let lyricColours = ['#4287f4', '#d70000'];

        for (let i = 0; i < this.song.parts.length; ++i) {
            let renderer = new LyricRenderer(this.canvasElement);
            renderer.activeColour = lyricColours[i];
            renderer.setSong(this.song, i);
            this.lyricRenderers.push(renderer);
        }

        for (let i = 0; i < this.players.length; ++i) {
            let notes = new NoteRenderer(this.canvasElement);
            notes.setSong(this.song, this.players[i].part);
            notes.setPlayer(this.players[i]);
            this.noteRenderers.push(notes);

            let score = new ScoreRenderer(this.canvasElement);
            score.setPlayer(this.players[i]);
            this.scoreRenderers.push(score);
        }

        this.updateLayout();
    }

    updateLayout() {
        const SCALE_W = 1280;
        const SCALE_H = 720;
        let cw = this.canvasElement.clientWidth;
        let ch = this.canvasElement.clientHeight;

        let d = (x, y, w, h) => ({x: x / SCALE_W * cw, y: y / SCALE_H * ch, w: w / SCALE_W * cw, h: h / SCALE_H * ch});

        const LYRICS = [d(0, 610, 1280, 90.999), d(0, 0, 1280, 90)];
        const LAYOUTS = {
            1: [{notes: d(20, 340, 1240, 250), score: d(0, 280, 1260, 60)}],
            2: [{notes: d(20, 390, 1240, 220), score: d(0, 350, 1260, 40)},
                {notes: d(20, 130, 1240, 220), score: d(0, 90, 1260, 40)}],
            3: [{notes: d(20, 476, 1240, 133), score: d(0, 436, 1260, 40)},
                {notes: d(20, 303, 1240, 133), score: d(0, 263, 1260, 40)},
                {notes: d(20, 130, 1240, 133), score: d(0, 90, 1260, 40)}],
            4: [{notes: d(20, 130, 610, 220), score: d(0, 90, 610, 40)},
                {notes: d(650, 130, 610, 220), score: d(650, 90, 610, 40)},
                {notes: d(20, 390, 610, 220), score: d(0, 350, 610, 40)},
                {notes: d(650, 390, 610, 220), score: d(650, 350, 610, 40)}],
            5: [{notes: d(20, 476, 610, 133), score: d(0, 436, 610, 40)},
                {notes: d(20, 303, 610, 133), score: d(0, 263, 610, 40)},
                {notes: d(650, 303, 610, 133), score: d(650, 263, 610, 40)},
                {notes: d(20, 130, 610, 133), score: d(0, 90, 610, 40)},
                {notes: d(650, 130, 610, 133), score: d(650, 90, 610, 40)}],
            6: [{notes: d(20, 476, 610, 133), score: d(0, 436, 610, 40)},
                {notes: d(650, 476, 610, 133), score: d(650, 436, 610, 40)},
                {notes: d(20, 303, 610, 133), score: d(0, 263, 610, 40)},
                {notes: d(650, 303, 610, 133), score: d(650, 263, 610, 40)},
                {notes: d(20, 130, 610, 133), score: d(0, 90, 610, 40)},
                {notes: d(650, 130, 610, 133), score: d(650, 90, 610, 40)}],
        };

        this.canvasContext.clearRect(0, 0, cw, ch);

        let progressRect = d(0, 700, 1280, 20);
        this.progressRenderer.setRect(progressRect.x, progressRect.y, progressRect.w, progressRect.h);

        for (let [i, renderer] of this.lyricRenderers.entries()) {
            let {x, y, w, h} = LYRICS[i];
            renderer.setRect(x, y, w, h);
        }

        let layout = LAYOUTS[this.players.length];
        for (let [i, renderer] of this.noteRenderers.entries()) {
            let {x, y, w, h} = layout[i].notes;
            renderer.setRect(x, y, w, h);
        }

        for (let [i, renderer] of this.scoreRenderers.entries()) {
            let {x, y, w, h} = layout[i].score;
            renderer.setRect(x, y, w, h);
        }

        this._renderFrame(true);
    }

    prepareVideo() {
        this.videoElement = document.createElement('video');
        this.videoElement.style.position = 'absolute';
        this.videoElement.style.top = 0;
        this.videoElement.style.left = 0;
        this._updateVideoSize();
        this.videoElement.preload = "auto";
        this.videoElement.poster = this.song.background;
        this.videoElement.addEventListener("canplaythrough", () => {
            if (!this._ready) {
                this._ready = true;
                this.emit("ready");
            }
        });
        this.videoElement.addEventListener('error', () => {
            this.videoElement.removeAttribute('src');
            if (!this._ready) {
                this._ready = true;
                this.emit("ready");
            }
        });

        if (this.song.video) {
            let start = this.getVideoStartTime();
            // if (start > 0) {
            //     this.videoElement.addEventListener('loadedmetadata', () => {
            //         console.log('setting start time');
            //         this.videoElement.currentTime = start;
            //     });
            // }
            this.videoElement.src = `${this.song.video}#t=${start}`;
            console.log(this.videoElement.src);
        } else {
            setTimeout(() => {
                this._ready = true;
                this.emit("ready");
            }, 0);
        }
    }

    _updateVideoSize() {
        if (this.height > (this.width / 16*9)) {
            this.videoElement.height = this.height;
            this.videoElement.width = this.height / 9 * 16;
        } else {
            this.videoElement.height = this.width / 16 * 9;
            this.videoElement.width = this.width;
        }
        this.videoElement.style.top = `${(this.height - this.videoElement.height) / 2}px`;
        this.videoElement.style.left = `${(this.width - this.videoElement.width) / 2}px`;
    }

    get ready() {
        return this._ready;
    }

    title() {
        let titleRenderer = new TitleRenderer(this.canvasElement, this.song);
        this.videoElement.currentTime = 0;
        return new Promise((resolve) => {
            titleRenderer.render();
            setTimeout(() => {
                let start = Date.now();
                let fade = () => {
                    let opacity = 1 - ((Date.now() - start) / 33) * 0.05;
                    if (opacity > 0) {
                        titleRenderer.render(opacity);
                        requestAnimationFrame(fade);
                    }
                };
                setTimeout(() =>{
                    this.canvasContext.clearRect(0, 0, this.canvasElement.clientWidth, this.canvasElement.clientHeight);
                    resolve();
                }, 667);
                requestAnimationFrame(fade);
            }, 3000);
        });
    }

    getVideoStartTime() {
        return this.song.videogap + (this.song.start || 0);
    }

    async start() {
        this.running = true;
        this.videoElement.currentTime = 0;
        let start = this.getVideoStartTime();
        if (this.videoElement.src) {
            if (start >= 0) {
                console.log("Video start time: ",start, this.videoElement.currentTime);
                // this.videoElement.currentTime = start;
                this._canKillVideo = false;
                await this.videoElement.play();
                this._canKillVideo = true;
            } else {
                console.log(`Delaying video playback by ${-start} seconds...`);
                this.videoElement.currentTime = 0;
                this._gapTimer = setTimeout(() => {console.log("video start."); this.videoElement.play()}, -start * 1000);
            }
        }
        this._scaleInterval = setInterval(() => this._scaleCanvas(), 1000);
        requestAnimationFrame(() => this._renderFrame());
    }

    stop() {
        clearInterval(this._scaleInterval);
        clearTimeout(this._gapTimer);
        this.videoElement.pause();
        this.running = false;
        this.container.style.overflow = 'unset';
        this.container.style.width = `unset`;
        this.container.style.height = `unset`;
    }

    _renderFrame(manual) {
        if (!this.running) {
            return;
        }
        if (this._canKillVideo && this.videoElement.src) {
            if (this.audio.currentTime >= Math.max(0, -this.song.videogap)) {
                if (Math.abs(this.videoElement.currentTime - this.audio.currentTime - this.song.videogap) > 0.2) {
                    console.log('A/V desync; disabling.', this.videoElement.currentTime, this.audio.currentTime, this.song.videogap);
                    this.videoElement.removeAttribute('src');
                    this.videoElement.load();
                    // If this line is omitted, Chrome just shows white. I don't know why.
                    this.videoElement.poster = this.videoElement.poster;
                }
            }
        }

        let time = (this.audio.currentTime * 1000) | 0;

        // hide all our OOB rendering errors by clearing everything each frame.
        this.canvasContext.clearRect(0, 0, this.canvasElement.clientWidth,  this.canvasElement.clientHeight);

        this.progressRenderer.render(time);

        for (let lyricRenderer of this.lyricRenderers) {
            lyricRenderer.render(time);
        }
        for (let noteRenderer of this.noteRenderers) {
            noteRenderer.render(time);
        }
        for (let scoreRenderer of this.scoreRenderers) {
            scoreRenderer.render();
        }
        if (!manual) {
            requestAnimationFrame(() => this._renderFrame());
        }
    }

    _scaleCanvas(force) {
        // assume the device pixel ratio is 1 if the browser doesn't specify it
        const devicePixelRatio = window.devicePixelRatio || 1;
        if (!force && this._currentCanvasScale === devicePixelRatio) {
            return;
        }
        this._currentCanvasScale = devicePixelRatio;

        // determine the 'backing store ratio' of the canvas context
        let context = this.canvasContext;
        const backingStoreRatio = (
            context.webkitBackingStorePixelRatio ||
            context.mozBackingStorePixelRatio ||
            context.msBackingStorePixelRatio ||
            context.oBackingStorePixelRatio ||
            context.backingStorePixelRatio || 1
        );

        // determine the actual ratio we want to draw at
        const ratio = devicePixelRatio / backingStoreRatio;

        let canvas = this.canvasElement;

        if (devicePixelRatio !== backingStoreRatio) {
            // set the 'real' canvas size to the higher width/height
            canvas.width = this.width * ratio;
            canvas.height = this.height * ratio;

            // ...then scale it back down with CSS
            canvas.style.width = this.width + 'px';
            canvas.style.height = this.height + 'px';
        }
        else {
            // this is a normal 1:1 device; just scale it simply
            canvas.width = this.width;
            canvas.height = this.height;
            canvas.style.width = '';
            canvas.style.height = '';
        }

        // scale the drawing context so everything will work at the higher ratio
        context.scale(ratio, ratio);
    }
}
<commit_msg>Restore video jumping and handle exceptions in playing videos.<commit_after>"use strict";

import {LyricRenderer, NoteRenderer, ProgressRenderer, ScoreRenderer, TitleRenderer} from "./ultrastar/render";
import EventEmitter from "events";

export class GameDisplay extends EventEmitter {
    constructor(container, width, height, song, players, audio) {
        super();
        this.song = song;
        this.container = container;
        this.width = width;
        this.height = height;
        this.players = players;
        this.audio = audio;
        this.div = null;
        this.videoElement = null;
        this.canvasElement = null;
        this.canvasContext = null;
        this._currentCanvasScale = null;
        this.progressRenderer = null;
        this.lyricRenderers = [];
        this.noteRenderers = [];
        this.scoreRenderers = [];
        this.running = false;
        this._ready = false;
        this._scaleInterval = null;
        this._gapTimer = null;
        this._canKillVideo = false; // Chrome throws a fit if we unload a video it's trying to start playing.
        this.container.style.overflow = 'hidden';
        this.container.style.width = `${width}px`;
        this.container.style.height = `${height}px`;
    }

    setSize(width, height) {
        this.width = width;
        this.height = height;
        this.container.style.width = `${width}px`;
        this.container.style.height = `${height}px`;
        this._scaleCanvas(true);
        this._updateVideoSize();
        this.updateLayout();
    }

    createGameLayout() {
        console.log("players", this.players);
        this.canvasElement = document.createElement('canvas');
        this.canvasElement.style.position = 'absolute';
        this.canvasElement.style.top = 0;
        this.canvasElement.style.left = 0;
        this.canvasContext = this.canvasElement.getContext('2d');
        this._scaleCanvas();

        this.div = document.createElement('div');
        this.div.style.position = 'relative';

        this.div.appendChild(this.videoElement);
        this.div.appendChild(this.canvasElement);
        this.container.appendChild(this.div);

        this.progressRenderer = new ProgressRenderer(this.canvasElement, this.song, this.audio);

        let lyricColours = ['#4287f4', '#d70000'];

        for (let i = 0; i < this.song.parts.length; ++i) {
            let renderer = new LyricRenderer(this.canvasElement);
            renderer.activeColour = lyricColours[i];
            renderer.setSong(this.song, i);
            this.lyricRenderers.push(renderer);
        }

        for (let i = 0; i < this.players.length; ++i) {
            let notes = new NoteRenderer(this.canvasElement);
            notes.setSong(this.song, this.players[i].part);
            notes.setPlayer(this.players[i]);
            this.noteRenderers.push(notes);

            let score = new ScoreRenderer(this.canvasElement);
            score.setPlayer(this.players[i]);
            this.scoreRenderers.push(score);
        }

        this.updateLayout();
    }

    updateLayout() {
        const SCALE_W = 1280;
        const SCALE_H = 720;
        let cw = this.canvasElement.clientWidth;
        let ch = this.canvasElement.clientHeight;

        let d = (x, y, w, h) => ({x: x / SCALE_W * cw, y: y / SCALE_H * ch, w: w / SCALE_W * cw, h: h / SCALE_H * ch});

        const LYRICS = [d(0, 610, 1280, 90.999), d(0, 0, 1280, 90)];
        const LAYOUTS = {
            1: [{notes: d(20, 340, 1240, 250), score: d(0, 280, 1260, 60)}],
            2: [{notes: d(20, 390, 1240, 220), score: d(0, 350, 1260, 40)},
                {notes: d(20, 130, 1240, 220), score: d(0, 90, 1260, 40)}],
            3: [{notes: d(20, 476, 1240, 133), score: d(0, 436, 1260, 40)},
                {notes: d(20, 303, 1240, 133), score: d(0, 263, 1260, 40)},
                {notes: d(20, 130, 1240, 133), score: d(0, 90, 1260, 40)}],
            4: [{notes: d(20, 130, 610, 220), score: d(0, 90, 610, 40)},
                {notes: d(650, 130, 610, 220), score: d(650, 90, 610, 40)},
                {notes: d(20, 390, 610, 220), score: d(0, 350, 610, 40)},
                {notes: d(650, 390, 610, 220), score: d(650, 350, 610, 40)}],
            5: [{notes: d(20, 476, 610, 133), score: d(0, 436, 610, 40)},
                {notes: d(20, 303, 610, 133), score: d(0, 263, 610, 40)},
                {notes: d(650, 303, 610, 133), score: d(650, 263, 610, 40)},
                {notes: d(20, 130, 610, 133), score: d(0, 90, 610, 40)},
                {notes: d(650, 130, 610, 133), score: d(650, 90, 610, 40)}],
            6: [{notes: d(20, 476, 610, 133), score: d(0, 436, 610, 40)},
                {notes: d(650, 476, 610, 133), score: d(650, 436, 610, 40)},
                {notes: d(20, 303, 610, 133), score: d(0, 263, 610, 40)},
                {notes: d(650, 303, 610, 133), score: d(650, 263, 610, 40)},
                {notes: d(20, 130, 610, 133), score: d(0, 90, 610, 40)},
                {notes: d(650, 130, 610, 133), score: d(650, 90, 610, 40)}],
        };

        this.canvasContext.clearRect(0, 0, cw, ch);

        let progressRect = d(0, 700, 1280, 20);
        this.progressRenderer.setRect(progressRect.x, progressRect.y, progressRect.w, progressRect.h);

        for (let [i, renderer] of this.lyricRenderers.entries()) {
            let {x, y, w, h} = LYRICS[i];
            renderer.setRect(x, y, w, h);
        }

        let layout = LAYOUTS[this.players.length];
        for (let [i, renderer] of this.noteRenderers.entries()) {
            let {x, y, w, h} = layout[i].notes;
            renderer.setRect(x, y, w, h);
        }

        for (let [i, renderer] of this.scoreRenderers.entries()) {
            let {x, y, w, h} = layout[i].score;
            renderer.setRect(x, y, w, h);
        }

        this._renderFrame(true);
    }

    prepareVideo() {
        this.videoElement = document.createElement('video');
        this.videoElement.style.position = 'absolute';
        this.videoElement.style.top = 0;
        this.videoElement.style.left = 0;
        this._updateVideoSize();
        this.videoElement.preload = "auto";
        this.videoElement.poster = this.song.background;
        this.videoElement.addEventListener("canplaythrough", () => {
            if (!this._ready) {
                this._ready = true;
                this.emit("ready");
            }
        });
        this.videoElement.addEventListener('error', () => {
            this.videoElement.removeAttribute('src');
            if (!this._ready) {
                this._ready = true;
                this.emit("ready");
            }
        });

        if (this.song.video) {
            let start = this.getVideoStartTime();
            // if (start > 0) {
            //     this.videoElement.addEventListener('loadedmetadata', () => {
            //         console.log('setting start time');
            //         this.videoElement.currentTime = start;
            //     });
            // }
            this.videoElement.src = `${this.song.video}#t=${start}`;
            console.log(this.videoElement.src);
        } else {
            setTimeout(() => {
                this._ready = true;
                this.emit("ready");
            }, 0);
        }
    }

    _updateVideoSize() {
        if (this.height > (this.width / 16*9)) {
            this.videoElement.height = this.height;
            this.videoElement.width = this.height / 9 * 16;
        } else {
            this.videoElement.height = this.width / 16 * 9;
            this.videoElement.width = this.width;
        }
        this.videoElement.style.top = `${(this.height - this.videoElement.height) / 2}px`;
        this.videoElement.style.left = `${(this.width - this.videoElement.width) / 2}px`;
    }

    get ready() {
        return this._ready;
    }

    title() {
        let titleRenderer = new TitleRenderer(this.canvasElement, this.song);
        this.videoElement.currentTime = 0;
        return new Promise((resolve) => {
            titleRenderer.render();
            setTimeout(() => {
                let start = Date.now();
                let fade = () => {
                    let opacity = 1 - ((Date.now() - start) / 33) * 0.05;
                    if (opacity > 0) {
                        titleRenderer.render(opacity);
                        requestAnimationFrame(fade);
                    }
                };
                setTimeout(() =>{
                    this.canvasContext.clearRect(0, 0, this.canvasElement.clientWidth, this.canvasElement.clientHeight);
                    resolve();
                }, 667);
                requestAnimationFrame(fade);
            }, 3000);
        });
    }

    getVideoStartTime() {
        return this.song.videogap + (this.song.start || 0);
    }

    async start() {
        this.running = true;
        this.videoElement.currentTime = 0;
        let start = this.getVideoStartTime();
        if (this.videoElement.src) {
            if (start >= 0) {
                console.log("Video start time: ",start, this.videoElement.currentTime);
                this.videoElement.currentTime = start;
                this._canKillVideo = false;
                try {
                    await this.videoElement.play();
                } catch(e) {
                    console.warn("Failed to start playing the video.");
                    console.warn(e);
                }
                this._canKillVideo = true;
            } else {
                console.log(`Delaying video playback by ${-start} seconds...`);
                this.videoElement.currentTime = 0;
                this._gapTimer = setTimeout(() => {console.log("video start."); this.videoElement.play()}, -start * 1000);
            }
        }
        this._scaleInterval = setInterval(() => this._scaleCanvas(), 1000);
        requestAnimationFrame(() => this._renderFrame());
    }

    stop() {
        clearInterval(this._scaleInterval);
        clearTimeout(this._gapTimer);
        this.videoElement.pause();
        this.running = false;
        this.container.style.overflow = 'unset';
        this.container.style.width = `unset`;
        this.container.style.height = `unset`;
    }

    _renderFrame(manual) {
        if (!this.running) {
            return;
        }
        if (this._canKillVideo && this.videoElement.src) {
            if (this.audio.currentTime >= Math.max(0, -this.song.videogap)) {
                if (Math.abs(this.videoElement.currentTime - this.audio.currentTime - this.song.videogap) > 0.2) {
                    console.log('A/V desync; disabling.', this.videoElement.currentTime, this.audio.currentTime, this.song.videogap);
                    this.videoElement.removeAttribute('src');
                    this.videoElement.load();
                    // If this line is omitted, Chrome just shows white. I don't know why.
                    this.videoElement.poster = this.videoElement.poster;
                }
            }
        }

        let time = (this.audio.currentTime * 1000) | 0;

        // hide all our OOB rendering errors by clearing everything each frame.
        this.canvasContext.clearRect(0, 0, this.canvasElement.clientWidth,  this.canvasElement.clientHeight);

        this.progressRenderer.render(time);

        for (let lyricRenderer of this.lyricRenderers) {
            lyricRenderer.render(time);
        }
        for (let noteRenderer of this.noteRenderers) {
            noteRenderer.render(time);
        }
        for (let scoreRenderer of this.scoreRenderers) {
            scoreRenderer.render();
        }
        if (!manual) {
            requestAnimationFrame(() => this._renderFrame());
        }
    }

    _scaleCanvas(force) {
        // assume the device pixel ratio is 1 if the browser doesn't specify it
        const devicePixelRatio = window.devicePixelRatio || 1;
        if (!force && this._currentCanvasScale === devicePixelRatio) {
            return;
        }
        this._currentCanvasScale = devicePixelRatio;

        // determine the 'backing store ratio' of the canvas context
        let context = this.canvasContext;
        const backingStoreRatio = (
            context.webkitBackingStorePixelRatio ||
            context.mozBackingStorePixelRatio ||
            context.msBackingStorePixelRatio ||
            context.oBackingStorePixelRatio ||
            context.backingStorePixelRatio || 1
        );

        // determine the actual ratio we want to draw at
        const ratio = devicePixelRatio / backingStoreRatio;

        let canvas = this.canvasElement;

        if (devicePixelRatio !== backingStoreRatio) {
            // set the 'real' canvas size to the higher width/height
            canvas.width = this.width * ratio;
            canvas.height = this.height * ratio;

            // ...then scale it back down with CSS
            canvas.style.width = this.width + 'px';
            canvas.style.height = this.height + 'px';
        }
        else {
            // this is a normal 1:1 device; just scale it simply
            canvas.width = this.width;
            canvas.height = this.height;
            canvas.style.width = '';
            canvas.style.height = '';
        }

        // scale the drawing context so everything will work at the higher ratio
        context.scale(ratio, ratio);
    }
}
<|endoftext|>


<commit_before>var keystone		= require('keystone'),
	async			= require('async'),
	_				= require('underscore'),
	childService	= require('../middleware/service_child'),
	familyService	= require('../middleware/service_family'),
	listsService	= require('../middleware/service_lists'),
	pageService		= require('../middleware/service_page');

exports = module.exports = function(req, res) {
	'use strict';

	var view		= new keystone.View(req, res),
		locals		= res.locals;

	// Set local variables
	locals.userType	= req.user ? req.user.get('userType') : 'anonymous';
	// Anonymous users and site users have access only to unrestricted children, registered families and social workers have access to all children
	locals.targetChildren = locals.userType === 'anonymous' || locals.userType === 'site visitor' ? 'unrestricted' : 'all';

	async.series([
		// Based on what the user is allowed to see, populate the children models into locals.children
		function(done) {
			if(locals.targetChildren === 'all') {
				childService.getAllChildren(req, res, done);
			} else {
				childService.getUnrestrictedChildren(req, res, done);
			}
		},
		function(done) { familyService.setGalleryPermissions(req, res, done); },
		function(done) { locals.canBookmarkChildren ? familyService.getBookmarkedChildren(req, res, done) : done(); },
		function(done) {
			if(locals.bookmarkedChildren && locals.bookmarkedChildren.length > 0) {
				// Loop through each child model and set a property to show they've already been bookmarked by the user during templating
				_.each(locals.children, function(child) {
					// Get the child id to compare with the array of currently bookmarked child ids
					var childId = child.get('_id');
					// Set the property for use during templating to show if the child has already been bookmarked
					child.isBookmarked = locals.bookmarkedChildren.indexOf(childId) !== -1 ? true : false;
				});

				done();

			} else {
				done();
			}
		},
		function(done) { listsService.getAllGenders(req, res, done) },
		function(done) { listsService.getAllRaces(req, res, done) },
		function(done) { listsService.getAllLanguages(req, res, done) },
		function(done) { listsService.getAllDisabilities(req, res, done) },
		function(done) { listsService.getOtherConsiderations(req, res, done) },
		function(done) { listsService.getAllFamilyConstellations(req, res, done) },
		function(done) { pageService.populateSidebar(req, res, done); },
		function(done) { pageService.getSectionHeader(req, res, done, 'Meet the Children'); }

	], function() {
		// Set the layout to render without the right sidebar
		locals['render-with-sidebar'] = false;
		// Render the view once all the data has been retrieved
		view.render('waiting-child-profiles');

	});

};
<commit_msg>#60 | update Express view loading to reflect the client-centric approach to loading data into the gallery<commit_after>var keystone		= require('keystone'),
	async			= require('async'),
	_				= require('underscore'),
	childService	= require('../middleware/service_child'),
	familyService	= require('../middleware/service_family'),
	listsService	= require('../middleware/service_lists'),
	pageService		= require('../middleware/service_page');

exports = module.exports = function(req, res) {
	'use strict';

	var view		= new keystone.View(req, res),
		locals		= res.locals;

	// Set local variables
	locals.userType	= req.user ? req.user.get('userType') : 'anonymous';
	// Anonymous users and site users have access only to unrestricted children, registered families and social workers have access to all children
	locals.targetChildren = locals.userType === 'anonymous' || locals.userType === 'site visitor' ? 'unrestricted' : 'all';

	async.series([
		function(done) { familyService.setGalleryPermissions(req, res, done); },
		function(done) { pageService.populateSidebar(req, res, done); },
		function(done) { pageService.getSectionHeader(req, res, done, 'Meet the Children'); }

	], function() {
		// Set the layout to render without the right sidebar
		locals['render-with-sidebar'] = false;
		// Render the view once all the data has been retrieved
		view.render('waiting-child-profiles');

	});

};
<|endoftext|>