<commit_before>'use strict'
const path = require('path')
const globbies = require('globbies')
const dotenv = require('dotenv').config()
const expand = require('dotenv-expand')
const bodyParser = require('body-parser')
const methodOverride = require('method-override')
const cookieParser = require('cookie-parser')
const errorHandler = require('errorhandler')
const morgan = require('morgan')
const compression = require('compression')
const cors = require('cors')
const helmet = require('helmet')

// Load environment variables from .env file.
expand(dotenv)

// Set default node environment to development.
process.env.NODE_ENV = process.env.NODE_ENV || 'development'
process.env.PORT = process.env.PORT || 3000

function authenticated (req, res, next) {
  if (req.headers.authenticated === process.env.AUTHENTICATED) return next()
  res.status(403).json({ message: 'Not authorized!' })
}

function express (app) {
  app
    .use(cors())
    .use(helmet())
    .use(compression())
    .use(bodyParser.urlencoded({ extended: false }))
    .use(bodyParser.json())
    .use(cookieParser())
    .use(methodOverride())
    .use(morgan('dev'))
    .use(errorHandler())
    .use('*', authenticated)

  globbies(path.resolve(__dirname, '../api/**/router.js'), file => {
    app.use(`/${path.basename(path.dirname(file))}s`, require(file))
  })
}

exports.load = express
<commit_msg>Status code fix<commit_after>'use strict'
const path = require('path')
const globbies = require('globbies')
const dotenv = require('dotenv').config()
const expand = require('dotenv-expand')
const bodyParser = require('body-parser')
const methodOverride = require('method-override')
const cookieParser = require('cookie-parser')
const errorHandler = require('errorhandler')
const morgan = require('morgan')
const compression = require('compression')
const cors = require('cors')
const helmet = require('helmet')

// Load environment variables from .env file.
expand(dotenv)

// Set default node environment to development.
process.env.NODE_ENV = process.env.NODE_ENV || 'development'
process.env.PORT = process.env.PORT || 3000

function authenticated (req, res, next) {
  if (req.headers.authenticated === process.env.AUTHENTICATED) return next()
  res.status(401).json({ message: 'Not authorized!' })
}

function express (app) {
  app
    .use(cors())
    .use(helmet())
    .use(compression())
    .use(bodyParser.urlencoded({ extended: false }))
    .use(bodyParser.json())
    .use(cookieParser())
    .use(methodOverride())
    .use(morgan('dev'))
    .use(errorHandler())
    .use('*', authenticated)

  globbies(path.resolve(__dirname, '../api/**/router.js'), file => {
    app.use(`/${path.basename(path.dirname(file))}s`, require(file))
  })
}

exports.load = express
<|endoftext|>


<commit_before>/*
 * Copyright 2014 WebFilings, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// https://github.com/gruntjs/grunt-contrib-jasmine
// https://github.com/maenu/grunt-template-jasmine-istanbul
// https://github.com/cloudchen/grunt-template-jasmine-requirejs

'use strict';

module.exports = function(settings) {
    var files = settings.files;
    var directories = settings.directories;
    var globs = settings.globs;

    return {
        jasmine: {
            options: {
                keepRunner: true,
                host: settings.wwwBaseUrl,
                junit: {
                    path: directories.testOutput,
                    consolidate: true
                }
            },
            testUnit: {
                src: [globs.src],
                specs: globs.unitSpecs,
                options: {
                    specs: globs.unitSpecs,
                    outfile: files.testRunner,
                    template: require('grunt-template-jasmine-requirejs'),
                    templateOptions: {
                        requireConfig: settings.requireConfigTest
                    }
                }
            },
            coverage: {
                src: [globs.src],
                options: {
                    specs: globs.unitSpecs,
                    outfile: files.coverageRunner,
                    template: require('grunt-template-jasmine-istanbul'),
                    templateOptions: {
                        coverage: directories.coverageOutput + 'coverage.json',
                        report: [
                            { type: 'text-summary' },
                            { type: 'html', options: { dir: directories.coverageOutput }}
                        ],
                        thresholds: settings.coverageThresholds,
                        template: require('grunt-template-jasmine-requirejs'),
                        templateOptions: {
                            requireConfig: settings.requireConfigCoverage
                        }
                    }
                }
            },
            testIntegration: {
                src: [globs.src],
                options: {
                    specs: globs.integrationSpecs,
                    outfile: files.testRunner,
                    template: require('grunt-template-jasmine-requirejs'),
                    templateOptions: {
                        requireConfig: settings.requireConfigTest
                    }
                }
            },
        }
    };
};
<commit_msg>Revert names<commit_after>/*
 * Copyright 2014 WebFilings, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// https://github.com/gruntjs/grunt-contrib-jasmine
// https://github.com/maenu/grunt-template-jasmine-istanbul
// https://github.com/cloudchen/grunt-template-jasmine-requirejs

'use strict';

module.exports = function(settings) {
    var files = settings.files;
    var directories = settings.directories;
    var globs = settings.globs;

    return {
        jasmine: {
            options: {
                keepRunner: true,
                host: settings.wwwBaseUrl,
                junit: {
                    path: directories.testOutput,
                    consolidate: true
                }
            },
            test: {
                src: [globs.src],
                specs: globs.unitSpecs,
                options: {
                    specs: globs.unitSpecs,
                    outfile: files.testRunner,
                    template: require('grunt-template-jasmine-requirejs'),
                    templateOptions: {
                        requireConfig: settings.requireConfigTest
                    }
                }
            },
            coverage: {
                src: [globs.src],
                options: {
                    specs: globs.unitSpecs,
                    outfile: files.coverageRunner,
                    template: require('grunt-template-jasmine-istanbul'),
                    templateOptions: {
                        coverage: directories.coverageOutput + 'coverage.json',
                        report: [
                            { type: 'text-summary' },
                            { type: 'html', options: { dir: directories.coverageOutput }}
                        ],
                        thresholds: settings.coverageThresholds,
                        template: require('grunt-template-jasmine-requirejs'),
                        templateOptions: {
                            requireConfig: settings.requireConfigCoverage
                        }
                    }
                }
            },
            integration: {
                src: [globs.src],
                options: {
                    specs: globs.integrationSpecs,
                    outfile: files.testRunner,
                    template: require('grunt-template-jasmine-requirejs'),
                    templateOptions: {
                        requireConfig: settings.requireConfigTest
                    }
                }
            },
        }
    };
};
<|endoftext|>


<commit_before>/*
This module provides functions that generate common UI elements. These include:

### header(opts) - defaults:
	logo: "Placeholder Logo"
	right: []
	rightPadding: "30px" // non-centerLogo only!
	centerLogo: true
	img: null
	panDuration: null (falls back to CT.dom.panImg() default of 5000)

### footer(opts) - defaults:
	logo: "Placeholder Logo"
	links: []
	contact: {}
	img: null
	panDuration: null (falls back to CT.dom.panImg() default of 5000)

### grid(data, columns, rows, hardheight) - defaults:
	columns: 3
	rows: 4

### profile(opts) - defaults:
	className: "w2-3 h1 noflow abs"
	buttons: []

### listView(opts) - defaults:
	parent: "ctmain"
	listClass: "ctlist big"
	contentClass: "ctcontent"
	titleClass: "biggerest bold bottompadded"
	content: null
	listNode: null
	listContent: null
	activeClass: null
	fallback: null
	hashcheck: null

### tree(opts) - defaults:
	path: ""
	nameCb: null // generates name node content, falls back to name
	branches: {}
	cb: function() {}
	name: opts.title || "root"
	className: "m5 p5 vtop centered pointer inline-block"

### form(opts) - defaults:
	className: "ctform"
	items: []
*/

CT.layout = {
	header: function(opts) {
		opts = CT.merge(opts, {
			logo: "Placeholder Logo",
			right: [],
			rightPadding: "30px", // non-centerLogo only!
			centerLogo: true
		});
		var content = [];
		if (opts.img) // opts also may include: panDuration
			content.push(CT.dom.panImg(opts));
		if (opts.right instanceof Node || opts.right.length) {
			if (opts.centerLogo)
				content.push(CT.dom.node(opts.right, "div", "right padded"));
			else
				content.push(CT.dom.node(CT.dom.node(opts.right, "div",
					"right h1", null, null, { padding: opts.rightPadding }),
					"div", "abs top0 bottom0 right0 w1-2"));
		}
		if (opts.centerLogo)
			content.push(CT.dom.node(CT.dom.link(opts.logo, null, "/"), "center", "biggerester"));
		else
			content.push(CT.dom.link(opts.logo, null, "/",
				"w1-2 biggest bold block nodecoration abs top0 bottom0 left0"));
		if (opts.marquee) { // lol
			var m = opts.marquee;
			content.push(CT.dom.marquee(m.content,
				m.className || "abs ctl mosthigh", m.direction, m.noresize));
		}
		var bighead = CT.dom.id("ctheader");
		if (opts.hamburgize) {
			bighead.classList.add("hamburgize");
			var hambo = CT.dom.div(null, null, "hambo", {
				onclick: function() {
					hambo._slidin = !hambo._slidin;
					bighead.classList[hambo._slidin ? "add" : "remove"]("slidin");
				}
			});
			content.push(hambo);
			CT.dom.head(CT.dom.node(null, "meta", null, null, {
				name: "viewport",
				content: "width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
			}));
		}
		CT.dom.setContent(bighead, CT.dom.div(content, "h1 w1"));
		if (opts.title)
			document.title = opts.title;
	},
	footer: function(opts) {
		opts = CT.merge(opts, {
			logoClass: "left nodecoration",
			linksClass: "right p20",
			bottomClass: "small bline centered",
			logo: "Placeholder Logo",
			links: [],
			contact: {}
		});
		var rights = [], content = [];
		if (opts.img) // opts also may include: panDuration
			content.push(CT.dom.panImg(opts));
		content.push(CT.dom.link(opts.logo, null, "/", opts.logoClass));
		if (opts.links.length)
			rights.push(opts.links.map(function(link) {
				return CT.dom.link(link.content, link.cb,
					link.href, link.className || "padded");
			}));
		if (opts.contact.email || opts.contact.phone) {
			var contacts = [];
			if (opts.contact.email_prefix)
				contacts.push(CT.dom.div(opts.contact.email_prefix, "padded inline-block"));
			if (opts.contact.email)
				contacts.push(CT.dom.div(opts.contact.email, "padded inline-block"));
			if (opts.contact.phone)
				contacts.push(CT.dom.div(opts.contact.phone, "padded inline-block"));
			rights.push(contacts);
		}
		if (rights.length)
			content.push(CT.dom.node(rights, "div", opts.linksClass));
		if (opts.bottom)
			content.push(CT.dom.node(opts.bottom, "div", opts.bottomClass));
		if (opts.extra)
			content.push(opts.extra);
		document.body.classList.add("footered");
		document.body.appendChild(CT.dom.node(content, "div", null, "ctfooter"));
	},
	grid: function(data, columns, rows, hardheight) {
		var className = "w1-" + (columns || 3) + " h1-" + (rows || 4)
			+ " noflow inline-block hoverglow pointer", data2cell = function(d) {
				var nstyle = {
					backgroundImage: 'url("' + d.img + '")',
					backgroundPosition: "center",
					backgroundSize: "cover"
				};
				if (hardheight)
					nstyle.height = hardheight + "px";
				return CT.dom.div(CT.dom.div(d.label,
					"biggest centered p20p overlay", d.id, null, {
						paddingTop: "20%"
					}), className, null, {
						onclick: d.onclick || function() {
							location = d.link;
						}
					}, nstyle);
			}, gnode = CT.dom.div(data.map(data2cell), "full");
		gnode.addCell = function(d) {
			gnode.appendChild(data2cell(d));
		};
		return gnode;
	},
	profile: function(opts) {
		opts = CT.merge(opts, {
			className: "w2-3 h1 noflow abs",
			buttons: []
		});
		var img = CT.dom.panImg(opts),
			buttons = CT.dom.div(null, "probutts");
		if (!opts.name && opts.firstName && opts.lastName)
			opts.name = opts.firstName + " " + opts.lastName;
		for (var b in opts.buttons)
			buttons.appendChild(CT.dom.button(b,
				opts.buttons[b], "round padded margined",
				"ctb_" + (opts.key || opts.name) + "_" + b.replace(/ /g, "_")));
		var content = [
			CT.dom.div(opts.name, "biggest"),
			CT.dom.div(opts.description || opts.blurb),
			buttons
		];
		return opts.img ? [
			CT.dom.div(content, "right w1-3 ctprofile"),
			img
		] : content;
	},
	listView: function(opts) {
		opts = CT.merge(opts, {
			content: null,
			listNode: null,
			listContent: null,
			activeClass: null,
			fallback: null,
			hashcheck: null,
			parent: "ctmain",
			listClass: "ctlist big",
			contentClass: "ctcontent",
			titleClass: "biggerest bold bottompadded"
		});
		var content = CT.dom.div(null, opts.contentClass),
			clist = CT.dom.div(null, opts.listClass),
			builder = function(data) {
				CT.dom.setContent(content, opts.content ? opts.content(data) : [
					CT.dom.div(data.name, opts.titleClass),
					data.content
				]);
			}, tl = CT.panel.triggerList(opts.data, builder, opts.listNode, opts.activeClass, opts.listContent);
		clist.appendChild(tl);
		CT.dom.setContent(opts.parent, [content, clist]);
		var hcnode, tlitem = CT.dom.id("tl" + location.hash.slice(1));
		if (tlitem)
			tlitem.trigger();
		else {
			if (opts.hashcheck)
				hcnode = opts.hashcheck();
			if (hcnode)
				CT.dom.setContent(content, hcnode);
			else {
				var titems = CT.dom.className("tlitem");
				if (titems.length)
					titems[0].trigger();
				else if (opts.fallback)
					CT.dom.setContent(content, opts.fallback());
			}
		}
		return tl;
	},
	tree: function(opts) {
		opts = CT.merge(opts, {
			path: "",
			nameCb: null,
			branches: {},
			cb: function() {},
			name: opts.title || "root",
			className: "m5 p5 vtop centered pointer inline-block"
		});
		var onclick = function(n) {
			return function(e) {
				opts.cb(n);
				e.stopPropagation();
			};
		};
		opts.path = !opts.path ? opts.name : (opts.path + "_" + opts.name);
		opts.id = "ctl_" + opts.path;
		var node = CT.dom.div([
			opts.nameCb ? opts.nameCb(opts) : opts.name,
			Object.keys(opts.branches).map(function(branch) {
				return CT.layout.tree(CT.merge({
						name: branch,
						branches: opts.branches[branch].branches || {}
					}, opts));
			})
		], opts.className, opts.id);
		node.onclick = onclick(node);
		return node;
	},
	form: function(opts) {
		opts = CT.merge(opts, {
			className: "ctform",
			items: [],
			numbers: []
		});
		var f, fields = [], nums = [], node = CT.dom.div(opts.items.map(function(item) {
			if (opts.values && opts.values[item.name] && !item.value)
				item = CT.merge(item, { value: opts.values[item.name] });
			if (item.style == "fieldList")
				item.wrap = true;
			f = CT.dom[item.style || "smartField"](item);
			item.style && f.classList.add(item.style);
			f.getVal = f.fieldValue || f.value;
			f.opts = item;
			fields.push(f);
			if (opts.labels) {
				var lab = CT.dom.label(item.label || item.name, f.id),
					cont = CT.dom.div([lab, f]);
				if (opts.extra && opts.extra[item.name])
					cont.unshift(opts.extra[item.name](f));
				cont.opts = f.opts;
				cont.getVal = f.getVal;
				cont.updateField = f.update;
				cont.setLabel = function(lval) {
					CT.dom.setContent(lab, lval);
				};
				cont.upLabel = function() {
					item.labeller && cont.setLabel(item.labeller(vals));
				};
				cont.update = function() {
					cont.upLabel();
					cont.updateField && cont.updateField(vals);
				};
				return cont;
			}
			return f;
		}).concat(opts.numbers.map(function(nitem) { // depped .. embed among items[] instead
			f = CT.dom.numberSelector(nitem);
			nums.push(f);
			return [ CT.dom.div(nitem.name, "right"), f ];
		})), opts.className), i, val, name, vals = {}, nahs = [undefined, ""];
		node.setVal = function(f) {
			val = f.getVal();
			name = f.opts.name;
			if (nahs.includes(val))
				return alert("please provide a " + CT.parse.key2title(name));
			vals[name] = val;
			return val;
		};
		node.value = function() {
			for (i = 0; i < fields.length; i++)
				if (nahs.includes(node.setVal(fields[i])))
					return;
			for (i = 0; i < nums.length; i++)
				vals[opts.numbers[i].name] = nums[i].value();
			opts.cb && opts.cb(vals);
			return vals;
		};
		if (opts.step) {
			node.classList.add("stepper");
			node.nextStep = function(submitter, recenterer) {
				if (node.curStep) {
					if (nahs.includes(node.setVal(node.curStep))) return;
					node.curStep.classList.remove("curstep");
					node.curStep = node.curStep.nextSibling;
					while (node.curStep && node.curStep.opts.condition && !vals[node.curStep.opts.condition])
						node.curStep = node.curStep.nextSibling;
				} else
					node.curStep = node.firstChild;
				if (node.curStep) {
					node.curStep.update && node.curStep.update();
					node.curStep.classList.add("curstep");
					recenterer && setTimeout(recenterer, 500);
				} else
					submitter();
			};
			node.nextStep();
		}
		node.continue = function() {
			if (opts.step && node.curStep)
				node.nextStep(node.value);
			else
				node.value();
		};
		if (opts.button)
			node.appendChild(CT.dom.button(opts.bname || "continue", node.continue, opts.buttClass));
		return node;
	}
};<commit_msg>layout.form(), nextStep(): if curStep.value == submit, null out<commit_after>/*
This module provides functions that generate common UI elements. These include:

### header(opts) - defaults:
	logo: "Placeholder Logo"
	right: []
	rightPadding: "30px" // non-centerLogo only!
	centerLogo: true
	img: null
	panDuration: null (falls back to CT.dom.panImg() default of 5000)

### footer(opts) - defaults:
	logo: "Placeholder Logo"
	links: []
	contact: {}
	img: null
	panDuration: null (falls back to CT.dom.panImg() default of 5000)

### grid(data, columns, rows, hardheight) - defaults:
	columns: 3
	rows: 4

### profile(opts) - defaults:
	className: "w2-3 h1 noflow abs"
	buttons: []

### listView(opts) - defaults:
	parent: "ctmain"
	listClass: "ctlist big"
	contentClass: "ctcontent"
	titleClass: "biggerest bold bottompadded"
	content: null
	listNode: null
	listContent: null
	activeClass: null
	fallback: null
	hashcheck: null

### tree(opts) - defaults:
	path: ""
	nameCb: null // generates name node content, falls back to name
	branches: {}
	cb: function() {}
	name: opts.title || "root"
	className: "m5 p5 vtop centered pointer inline-block"

### form(opts) - defaults:
	className: "ctform"
	items: []
*/

CT.layout = {
	header: function(opts) {
		opts = CT.merge(opts, {
			logo: "Placeholder Logo",
			right: [],
			rightPadding: "30px", // non-centerLogo only!
			centerLogo: true
		});
		var content = [];
		if (opts.img) // opts also may include: panDuration
			content.push(CT.dom.panImg(opts));
		if (opts.right instanceof Node || opts.right.length) {
			if (opts.centerLogo)
				content.push(CT.dom.node(opts.right, "div", "right padded"));
			else
				content.push(CT.dom.node(CT.dom.node(opts.right, "div",
					"right h1", null, null, { padding: opts.rightPadding }),
					"div", "abs top0 bottom0 right0 w1-2"));
		}
		if (opts.centerLogo)
			content.push(CT.dom.node(CT.dom.link(opts.logo, null, "/"), "center", "biggerester"));
		else
			content.push(CT.dom.link(opts.logo, null, "/",
				"w1-2 biggest bold block nodecoration abs top0 bottom0 left0"));
		if (opts.marquee) { // lol
			var m = opts.marquee;
			content.push(CT.dom.marquee(m.content,
				m.className || "abs ctl mosthigh", m.direction, m.noresize));
		}
		var bighead = CT.dom.id("ctheader");
		if (opts.hamburgize) {
			bighead.classList.add("hamburgize");
			var hambo = CT.dom.div(null, null, "hambo", {
				onclick: function() {
					hambo._slidin = !hambo._slidin;
					bighead.classList[hambo._slidin ? "add" : "remove"]("slidin");
				}
			});
			content.push(hambo);
			CT.dom.head(CT.dom.node(null, "meta", null, null, {
				name: "viewport",
				content: "width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
			}));
		}
		CT.dom.setContent(bighead, CT.dom.div(content, "h1 w1"));
		if (opts.title)
			document.title = opts.title;
	},
	footer: function(opts) {
		opts = CT.merge(opts, {
			logoClass: "left nodecoration",
			linksClass: "right p20",
			bottomClass: "small bline centered",
			logo: "Placeholder Logo",
			links: [],
			contact: {}
		});
		var rights = [], content = [];
		if (opts.img) // opts also may include: panDuration
			content.push(CT.dom.panImg(opts));
		content.push(CT.dom.link(opts.logo, null, "/", opts.logoClass));
		if (opts.links.length)
			rights.push(opts.links.map(function(link) {
				return CT.dom.link(link.content, link.cb,
					link.href, link.className || "padded");
			}));
		if (opts.contact.email || opts.contact.phone) {
			var contacts = [];
			if (opts.contact.email_prefix)
				contacts.push(CT.dom.div(opts.contact.email_prefix, "padded inline-block"));
			if (opts.contact.email)
				contacts.push(CT.dom.div(opts.contact.email, "padded inline-block"));
			if (opts.contact.phone)
				contacts.push(CT.dom.div(opts.contact.phone, "padded inline-block"));
			rights.push(contacts);
		}
		if (rights.length)
			content.push(CT.dom.node(rights, "div", opts.linksClass));
		if (opts.bottom)
			content.push(CT.dom.node(opts.bottom, "div", opts.bottomClass));
		if (opts.extra)
			content.push(opts.extra);
		document.body.classList.add("footered");
		document.body.appendChild(CT.dom.node(content, "div", null, "ctfooter"));
	},
	grid: function(data, columns, rows, hardheight) {
		var className = "w1-" + (columns || 3) + " h1-" + (rows || 4)
			+ " noflow inline-block hoverglow pointer", data2cell = function(d) {
				var nstyle = {
					backgroundImage: 'url("' + d.img + '")',
					backgroundPosition: "center",
					backgroundSize: "cover"
				};
				if (hardheight)
					nstyle.height = hardheight + "px";
				return CT.dom.div(CT.dom.div(d.label,
					"biggest centered p20p overlay", d.id, null, {
						paddingTop: "20%"
					}), className, null, {
						onclick: d.onclick || function() {
							location = d.link;
						}
					}, nstyle);
			}, gnode = CT.dom.div(data.map(data2cell), "full");
		gnode.addCell = function(d) {
			gnode.appendChild(data2cell(d));
		};
		return gnode;
	},
	profile: function(opts) {
		opts = CT.merge(opts, {
			className: "w2-3 h1 noflow abs",
			buttons: []
		});
		var img = CT.dom.panImg(opts),
			buttons = CT.dom.div(null, "probutts");
		if (!opts.name && opts.firstName && opts.lastName)
			opts.name = opts.firstName + " " + opts.lastName;
		for (var b in opts.buttons)
			buttons.appendChild(CT.dom.button(b,
				opts.buttons[b], "round padded margined",
				"ctb_" + (opts.key || opts.name) + "_" + b.replace(/ /g, "_")));
		var content = [
			CT.dom.div(opts.name, "biggest"),
			CT.dom.div(opts.description || opts.blurb),
			buttons
		];
		return opts.img ? [
			CT.dom.div(content, "right w1-3 ctprofile"),
			img
		] : content;
	},
	listView: function(opts) {
		opts = CT.merge(opts, {
			content: null,
			listNode: null,
			listContent: null,
			activeClass: null,
			fallback: null,
			hashcheck: null,
			parent: "ctmain",
			listClass: "ctlist big",
			contentClass: "ctcontent",
			titleClass: "biggerest bold bottompadded"
		});
		var content = CT.dom.div(null, opts.contentClass),
			clist = CT.dom.div(null, opts.listClass),
			builder = function(data) {
				CT.dom.setContent(content, opts.content ? opts.content(data) : [
					CT.dom.div(data.name, opts.titleClass),
					data.content
				]);
			}, tl = CT.panel.triggerList(opts.data, builder, opts.listNode, opts.activeClass, opts.listContent);
		clist.appendChild(tl);
		CT.dom.setContent(opts.parent, [content, clist]);
		var hcnode, tlitem = CT.dom.id("tl" + location.hash.slice(1));
		if (tlitem)
			tlitem.trigger();
		else {
			if (opts.hashcheck)
				hcnode = opts.hashcheck();
			if (hcnode)
				CT.dom.setContent(content, hcnode);
			else {
				var titems = CT.dom.className("tlitem");
				if (titems.length)
					titems[0].trigger();
				else if (opts.fallback)
					CT.dom.setContent(content, opts.fallback());
			}
		}
		return tl;
	},
	tree: function(opts) {
		opts = CT.merge(opts, {
			path: "",
			nameCb: null,
			branches: {},
			cb: function() {},
			name: opts.title || "root",
			className: "m5 p5 vtop centered pointer inline-block"
		});
		var onclick = function(n) {
			return function(e) {
				opts.cb(n);
				e.stopPropagation();
			};
		};
		opts.path = !opts.path ? opts.name : (opts.path + "_" + opts.name);
		opts.id = "ctl_" + opts.path;
		var node = CT.dom.div([
			opts.nameCb ? opts.nameCb(opts) : opts.name,
			Object.keys(opts.branches).map(function(branch) {
				return CT.layout.tree(CT.merge({
						name: branch,
						branches: opts.branches[branch].branches || {}
					}, opts));
			})
		], opts.className, opts.id);
		node.onclick = onclick(node);
		return node;
	},
	form: function(opts) {
		opts = CT.merge(opts, {
			className: "ctform",
			items: [],
			numbers: []
		});
		var f, fields = [], nums = [], node = CT.dom.div(opts.items.map(function(item) {
			if (opts.values && opts.values[item.name] && !item.value)
				item = CT.merge(item, { value: opts.values[item.name] });
			if (item.style == "fieldList")
				item.wrap = true;
			f = CT.dom[item.style || "smartField"](item);
			item.style && f.classList.add(item.style);
			f.getVal = f.fieldValue || f.value;
			f.opts = item;
			fields.push(f);
			if (opts.labels) {
				var lab = CT.dom.label(item.label || item.name, f.id),
					cont = CT.dom.div([lab, f]);
				if (opts.extra && opts.extra[item.name])
					cont.unshift(opts.extra[item.name](f));
				cont.opts = f.opts;
				cont.getVal = f.getVal;
				cont.updateField = f.update;
				cont.setLabel = function(lval) {
					CT.dom.setContent(lab, lval);
				};
				cont.upLabel = function() {
					item.labeller && cont.setLabel(item.labeller(vals));
				};
				cont.update = function() {
					cont.upLabel();
					cont.updateField && cont.updateField(vals);
				};
				return cont;
			}
			return f;
		}).concat(opts.numbers.map(function(nitem) { // depped .. embed among items[] instead
			f = CT.dom.numberSelector(nitem);
			nums.push(f);
			return [ CT.dom.div(nitem.name, "right"), f ];
		})), opts.className), i, val, name, vals = {}, nahs = [undefined, ""];
		node.setVal = function(f) {
			val = f.getVal();
			name = f.opts.name;
			if (nahs.includes(val))
				return alert("please provide a " + CT.parse.key2title(name));
			vals[name] = val;
			return val;
		};
		node.value = function() {
			for (i = 0; i < fields.length; i++)
				if (nahs.includes(node.setVal(fields[i])))
					return;
			for (i = 0; i < nums.length; i++)
				vals[opts.numbers[i].name] = nums[i].value();
			opts.cb && opts.cb(vals);
			return vals;
		};
		if (opts.step) {
			node.classList.add("stepper");
			node.nextStep = function(submitter, recenterer) {
				if (node.curStep) {
					if (nahs.includes(node.setVal(node.curStep))) return;
					node.curStep.classList.remove("curstep");
					node.curStep = node.curStep.nextSibling;
					if (node.curStep.type == "submit")
						node.curStep = null;
					while (node.curStep && node.curStep.opts.condition && !vals[node.curStep.opts.condition])
						node.curStep = node.curStep.nextSibling;
				} else
					node.curStep = node.firstChild;
				if (node.curStep) {
					node.curStep.update && node.curStep.update();
					node.curStep.classList.add("curstep");
					recenterer && setTimeout(recenterer, 500);
				} else
					submitter();
			};
			node.nextStep();
		}
		node.continue = function() {
			if (opts.step && node.curStep)
				node.nextStep(node.value);
			else
				node.value();
		};
		if (opts.button)
			node.appendChild(CT.dom.button(opts.bname || "continue", node.continue, opts.buttClass));
		return node;
	}
};<|endoftext|>


<commit_before>/*
This module provides a direct interface with the ctpubsub backend. Here's how to use it.

	CT.pubsub.connect(host, port, uname)
	CT.pubsub.publish(channel, message)
	CT.pubsub.subscribe(channel)
	CT.pubsub.unsubscribe(channel)
	CT.pubsub.pm(user, message)
	CT.pubsub.set_cb(action, cb)
	CT.pubsub.set_reconnect(bool)
	CT.pubsub.isInitialized() (returns bool)
*/

CT.pubsub = {
	"_": {
		"ws": null,
		"args": null,
		"open": false,
		"initialized": false,
		"autohistory": false,
		"autohistory_exemptions": [],
		"reconnect": true,
		"reconnect_interval": 250,
		"protocol": location.protocol == "http:" && "ws" || "wss",
		"log": CT.log.getLogger("CT.pubsub"),
		"queue": [],
		"channels": {},
		"cb": { // default callbacks -- override w/ set_cb()
			"snapshot": CT.log.getLogger("CT.pubsub|snapshot"),
			"pm": CT.log.getLogger("CT.pubsub|pm"),
			"message": CT.log.getLogger("CT.pubsub|message"),
			"subscribe": CT.log.getLogger("CT.pubsub|subscribe"),
			"join": CT.log.getLogger("CT.pubsub|join"),
			"leave": CT.log.getLogger("CT.pubsub|leave"),
			"presence": CT.log.getLogger("CT.pubsub|presence"),
			"open": CT.log.getLogger("CT.pubsub|open"),
			"close": CT.log.getLogger("CT.pubsub|close"),
			"wserror": CT.log.getLogger("CT.pubsub|wserror"), // websocket -- almost same as close
			"error": CT.log.getLogger("CT.pubsub|error") // non-fatal pubsub error
		},
		"process": { // pubsub events
			"channel": function(data) {
				var _ = CT.pubsub._;
				_.channels[data.channel] = data;
				_.cb.subscribe(data);
				if (_.autohistory && (_.autohistory_exemptions.indexOf(data.channel) == -1))
					data.history.forEach(CT.pubsub._.process.publish);
				CT.pubsub._.cb.presence(data.presence.length, data.channel);
			},
			"publish": function(data) {
				CT.pubsub._.cb.message(data);
			},
			"subscribe": function(data) {
				CT.pubsub._.cb.join(data.channel, data.user);
				var chan = CT.pubsub._.channels[data.channel];
				if (chan) {
					CT.data.append(chan.presence, data.user);
					CT.pubsub._.cb.presence(chan.presence.length, data.channel);
				}
			},
			"unsubscribe": function(data) {
				CT.pubsub._.cb.leave(data.channel, data.user);
				var chan = CT.pubsub._.channels[data.channel];
				if (chan) {
					CT.data.remove(chan.presence, data.user);
					CT.pubsub._.cb.presence(chan.presence.length, data.channel);
				}
			},
			"pm": function(data) {
				CT.pubsub._.cb.pm(data.message, data.user);
			},
			"error": function(data) {
				CT.pubsub._.cb.error(data.message);
			},
			"snapshot": function(data) {
				CT.pubsub._.cb.snapshot(data);
				data.channels.forEach(function(c) {
					var cdata = CT.pubsub._.channels[c.name] = {
						channel: c.name,
						presence: c.users
					};
					CT.pubsub._.cb.subscribe(cdata);
				});
			}
		},
		"on": { // websocket events
			"open": function() {
				CT.pubsub._.open = true;
				CT.pubsub._.reconnect_interval = 250;
				CT.pubsub._.write({
					"action": "register",
					"data": CT.pubsub._.args[2]
				});
				for (var channel in CT.pubsub._.channels)
					CT.pubsub.subscribe(channel);
				CT.pubsub._.queue.forEach(CT.pubsub._.write);
				CT.pubsub._.queue.length = 0;
				CT.pubsub._.cb.open();
			},
			"close": function() {
				CT.pubsub._.open = false;
				CT.pubsub._.cb.close();
				if (CT.pubsub._.reconnect)
					CT.pubsub._.try_reconnect()
			},
			"error": function() {
				CT.pubsub._.cb.wserror();
			},
			"message": function(msg) {
				var d = JSON.parse(atob(msg.data));
				CT.pubsub._.process[d.action](d.data);
			}
		},
		"write": function(data) {
			if (CT.pubsub._.open) {
				var dstring = JSON.stringify(data),
					b64str = btoa(dstring);
				CT.pubsub._.ws.send(b64str);
			} else
				CT.pubsub._.queue.push(data);
		},
		"try_reconnect": function() {
			var r_int = CT.pubsub._.reconnect_interval;
			CT.pubsub._.log("RECONNECT", r_int);
			setTimeout(function() {
				CT.pubsub.connect.apply(null, CT.pubsub._.args)
			}, r_int);
			CT.pubsub._.reconnect_interval = Math.min(2 * r_int, 30000);
		}
	},
	"isInitialized": function() {
		return CT.pubsub._.initialized;
	},
	"set_protocol": function(protocol) {
		CT.pubsub._.protocol = protocol;
	},
	"set_reconnect": function(bool) {
		CT.pubsub._.reconnect = bool;
	},
	"set_autohistory": function(bool, exemptions, mergeExemptions) {
		CT.pubsub._.autohistory = bool;
		if (exemptions) {
			if (mergeExemptions) {
				exemptions.forEach(function(e) {
					CT.pubsub._.autohistory_exemptions.push(e);
				});
			} else
				CT.pubsub._.autohistory_exemptions = exemptions;
		}
	},
	"set_cb": function(action, cb) {
		// action: message|subscribe|join|leave|presence|open|close|error|pm
		CT.pubsub._.cb[action] = cb;
	},
	"presence": function(channel) {
		return CT.pubsub._.channels[channel].presence;
	},
	"pm": function(user, message) {
		CT.pubsub._.write({
			"action": "pm",
			"data": {
				"user": user,
				"message": message
			}
		});
	},
	"publish": function(channel, message) {
		CT.pubsub._.write({
			"action": "publish",
			"data": {
				"channel": channel,
				"message": message
			}
		});
	},
	"subscribe": function(channel) {
		CT.pubsub._.write({
			"action": "subscribe",
			"data": channel
		});
	},
	"unsubscribe": function(channel) {
		CT.pubsub._.write({
			"action": "unsubscribe",
			"data": channel
		});
	},
	"connect": function(host, port, uname, onbeforeunload) {
		CT.pubsub._.args = arguments;
		CT.pubsub._.initialized = true;
		CT.pubsub._.ws = new WebSocket(CT.pubsub._.protocol + "://" + host + ":" + port);
		for (var action in CT.pubsub._.on)
			CT.pubsub._.ws["on" + action] = CT.pubsub._.on[action];
		window.addEventListener("beforeunload", function() {
			onbeforeunload && onbeforeunload();
			CT.pubsub._.write({ "action": "close" });
		});
	}
};<commit_msg>CT.pubsub: connect() supports meta arg (used for registration in on.open()); process.channel/subscribe() pass along meta and process metamap{} as necessary<commit_after>/*
This module provides a direct interface with the ctpubsub backend. Here's how to use it.

	CT.pubsub.connect(host, port, uname)
	CT.pubsub.publish(channel, message)
	CT.pubsub.subscribe(channel)
	CT.pubsub.unsubscribe(channel)
	CT.pubsub.pm(user, message)
	CT.pubsub.set_cb(action, cb)
	CT.pubsub.set_reconnect(bool)
	CT.pubsub.isInitialized() (returns bool)
*/

CT.pubsub = {
	"_": {
		"ws": null,
		"args": null,
		"open": false,
		"initialized": false,
		"autohistory": false,
		"autohistory_exemptions": [],
		"reconnect": true,
		"reconnect_interval": 250,
		"protocol": location.protocol == "http:" && "ws" || "wss",
		"log": CT.log.getLogger("CT.pubsub"),
		"queue": [],
		"channels": {},
		"cb": { // default callbacks -- override w/ set_cb()
			"snapshot": CT.log.getLogger("CT.pubsub|snapshot"),
			"pm": CT.log.getLogger("CT.pubsub|pm"),
			"message": CT.log.getLogger("CT.pubsub|message"),
			"subscribe": CT.log.getLogger("CT.pubsub|subscribe"),
			"join": CT.log.getLogger("CT.pubsub|join"),
			"leave": CT.log.getLogger("CT.pubsub|leave"),
			"presence": CT.log.getLogger("CT.pubsub|presence"),
			"open": CT.log.getLogger("CT.pubsub|open"),
			"close": CT.log.getLogger("CT.pubsub|close"),
			"wserror": CT.log.getLogger("CT.pubsub|wserror"), // websocket -- almost same as close
			"error": CT.log.getLogger("CT.pubsub|error") // non-fatal pubsub error
		},
		"process": { // pubsub events
			"channel": function(data) {
				var _ = CT.pubsub._;
				if (data.meta) {
					data.metamap = {};
					data.presence.forEach(function(u, i) {
						data.metamap[u] = data.meta[i];
					});
				}
				_.channels[data.channel] = data;
				_.cb.subscribe(data);
				if (_.autohistory && (_.autohistory_exemptions.indexOf(data.channel) == -1))
					data.history.forEach(CT.pubsub._.process.publish);
				CT.pubsub._.cb.presence(data.presence.length, data.channel);
			},
			"publish": function(data) {
				CT.pubsub._.cb.message(data);
			},
			"subscribe": function(data) {
				CT.pubsub._.cb.join(data.channel, data.user, data.meta);
				var chan = CT.pubsub._.channels[data.channel];
				if (chan) {
					CT.data.append(chan.presence, data.user);
					CT.pubsub._.cb.presence(chan.presence.length, data.channel);
				}
			},
			"unsubscribe": function(data) {
				CT.pubsub._.cb.leave(data.channel, data.user);
				var chan = CT.pubsub._.channels[data.channel];
				if (chan) {
					CT.data.remove(chan.presence, data.user);
					CT.pubsub._.cb.presence(chan.presence.length, data.channel);
				}
			},
			"pm": function(data) {
				CT.pubsub._.cb.pm(data.message, data.user);
			},
			"error": function(data) {
				CT.pubsub._.cb.error(data.message);
			},
			"snapshot": function(data) {
				CT.pubsub._.cb.snapshot(data);
				data.channels.forEach(function(c) {
					var cdata = CT.pubsub._.channels[c.name] = {
						channel: c.name,
						presence: c.users
					};
					CT.pubsub._.cb.subscribe(cdata);
				});
			}
		},
		"on": { // websocket events
			"open": function() {
				CT.pubsub._.open = true;
				CT.pubsub._.reconnect_interval = 250;
				CT.pubsub._.write({
					"action": "register",
					"data": CT.pubsub._.args[2],
					"meta": CT.pubsub._.args[4]
				});
				for (var channel in CT.pubsub._.channels)
					CT.pubsub.subscribe(channel);
				CT.pubsub._.queue.forEach(CT.pubsub._.write);
				CT.pubsub._.queue.length = 0;
				CT.pubsub._.cb.open();
			},
			"close": function() {
				CT.pubsub._.open = false;
				CT.pubsub._.cb.close();
				if (CT.pubsub._.reconnect)
					CT.pubsub._.try_reconnect()
			},
			"error": function() {
				CT.pubsub._.cb.wserror();
			},
			"message": function(msg) {
				var d = JSON.parse(atob(msg.data));
				CT.pubsub._.process[d.action](d.data);
			}
		},
		"write": function(data) {
			if (CT.pubsub._.open) {
				var dstring = JSON.stringify(data),
					b64str = btoa(dstring);
				CT.pubsub._.ws.send(b64str);
			} else
				CT.pubsub._.queue.push(data);
		},
		"try_reconnect": function() {
			var r_int = CT.pubsub._.reconnect_interval;
			CT.pubsub._.log("RECONNECT", r_int);
			setTimeout(function() {
				CT.pubsub.connect.apply(null, CT.pubsub._.args)
			}, r_int);
			CT.pubsub._.reconnect_interval = Math.min(2 * r_int, 30000);
		}
	},
	"isInitialized": function() {
		return CT.pubsub._.initialized;
	},
	"set_protocol": function(protocol) {
		CT.pubsub._.protocol = protocol;
	},
	"set_reconnect": function(bool) {
		CT.pubsub._.reconnect = bool;
	},
	"set_autohistory": function(bool, exemptions, mergeExemptions) {
		CT.pubsub._.autohistory = bool;
		if (exemptions) {
			if (mergeExemptions) {
				exemptions.forEach(function(e) {
					CT.pubsub._.autohistory_exemptions.push(e);
				});
			} else
				CT.pubsub._.autohistory_exemptions = exemptions;
		}
	},
	"set_cb": function(action, cb) {
		// action: message|subscribe|join|leave|presence|open|close|error|pm
		CT.pubsub._.cb[action] = cb;
	},
	"presence": function(channel) {
		return CT.pubsub._.channels[channel].presence;
	},
	"pm": function(user, message) {
		CT.pubsub._.write({
			"action": "pm",
			"data": {
				"user": user,
				"message": message
			}
		});
	},
	"publish": function(channel, message) {
		CT.pubsub._.write({
			"action": "publish",
			"data": {
				"channel": channel,
				"message": message
			}
		});
	},
	"subscribe": function(channel) {
		CT.pubsub._.write({
			"action": "subscribe",
			"data": channel
		});
	},
	"unsubscribe": function(channel) {
		CT.pubsub._.write({
			"action": "unsubscribe",
			"data": channel
		});
	},
	"connect": function(host, port, uname, onbeforeunload, meta) {
		CT.pubsub._.args = arguments;
		CT.pubsub._.initialized = true;
		CT.pubsub._.ws = new WebSocket(CT.pubsub._.protocol + "://" + host + ":" + port);
		for (var action in CT.pubsub._.on)
			CT.pubsub._.ws["on" + action] = CT.pubsub._.on[action];
		window.addEventListener("beforeunload", function() {
			onbeforeunload && onbeforeunload();
			CT.pubsub._.write({ "action": "close" });
		});
	}
};<|endoftext|>


<commit_before>(function () {
  var canvas = document.getElementById('canvas');
  var canvas2 = document.getElementById('canvas2');
  var canvas3 = document.getElementById('canvas3');
  var ctx = canvas.getContext('2d');
  var ctx2 = canvas2.getContext('2d');
  var ctx3 = canvas3.getContext('2d');
  var w = canvas.width;
  var h = canvas.height;
  var img = new Image();
  img.src = 'cosmos.jpg';
  ctx3.drawImage(img, 0, 0, 570, 570);

})();
<commit_msg>Add main animation loop<commit_after>(function () {
  var canvas = document.getElementById('canvas');
  var canvas2 = document.getElementById('canvas2');
  var canvas3 = document.getElementById('canvas3');
  var ctx = canvas.getContext('2d');
  var ctx2 = canvas2.getContext('2d');
  var ctx3 = canvas3.getContext('2d');
  var w = canvas.width;
  var h = canvas.height;
  var img = new Image();
  img.src = 'cosmos.jpg';
  ctx3.drawImage(img, 0, 0, 570, 570);

  function loop () {
    window.requestAnimationFrame(loop, canvas);
  }

  loop();

})();
<|endoftext|>


<commit_before>import platform from '../../util/platform'
import Scrollbar from '../scrollbar/Scrollbar'
import getRelativeBoundingRect from '../../util/getRelativeBoundingRect'
import AbstractScrollPane from './AbstractScrollPane'

/**
  Wraps content in a scroll pane.

  NOTE: It is best practice to put all overlays as direct childs of the ScrollPane
        to reduce the chance that positioning gets messed up (position: relative)

  @prop {String} scrollbarType 'native' or 'substance' for a more advanced visual scrollbar. Defaults to 'native'
  @prop {String} [scrollbarPosition] 'left' or 'right' only relevant when scrollBarType: 'substance'. Defaults to 'right'
  @prop {ui/Highlights} [highlights] object that maintains highlights and can be manipulated from different sources
  @prop {ui/TOCProvider} [tocProvider] object that maintains table of content entries

  @example

  ```js
  $$(ScrollPane, {
    scrollbarType: 'substance', // defaults to native
    scrollbarPosition: 'left', // defaults to right
    onScroll: this.onScroll.bind(this),
    highlights: this.contentHighlights,
    tocProvider: this.tocProvider
  })
  ```
*/
class ScrollPane extends AbstractScrollPane {

  didMount() {
    super.didMount()
    if (this.refs.scrollbar && this.props.highlights) {
      this.props.highlights.on('highlights:updated', this.onHighlightsUpdated, this)
    }
    if (this.refs.scrollbar) {
      this.domObserver = new window.MutationObserver(this._onContentChanged.bind(this))
      this.domObserver.observe(this.el.getNativeElement(), {
        subtree: true,
        attributes: true,
        characterData: true,
        childList: true,
      })
      this.context.editorSession.onPosition(this._onPosition, this)
    }
  }

  dispose() {
    super.dispose()
    if (this.props.highlights) {
      this.props.highlights.off(this)
    }
    this.context.editorSession.off(this)
    this.context.dragManager.off(this)
  }

  render($$) {
    let el = $$('div')
      .addClass('sc-scroll-pane')

    if (platform.isFF) {
      el.addClass('sm-firefox')
    }

    // When noStyle is provided we just use ScrollPane as a container, but without
    // any absolute positioned containers, leaving the body scrollable.
    if (!this.props.noStyle) {
      el.addClass('sm-default-style')
    }

    // Initialize Substance scrollbar (if enabled)
    if (this.props.scrollbarType === 'substance') {
      el.addClass('sm-substance-scrollbar')
      el.addClass('sm-scrollbar-position-' + this.props.scrollbarPosition)

      el.append(
        // TODO: is there a way to pass scrollbar highlights already
        // via props? Currently the are initialized with a delay
        $$(Scrollbar, {
          scrollPane: this
        }).ref('scrollbar')
          .attr('id', 'content-scrollbar')
      )

      // Scanline is debugging purposes, display: none by default.
      el.append(
        $$('div').ref("scanline").addClass('se-scanline')
      )
    }

    el.append(
      $$('div').ref('scrollable').addClass('se-scrollable').append(
        this.renderContent($$)
      ).on('scroll', this.onScroll)
    )
    return el
  }

  renderContent($$) {
    let contentEl = $$('div').ref('content').addClass('se-content')
    contentEl.append(this.props.children)
    if (this.props.contextMenu === 'custom') {
      contentEl.on('contextmenu', this._onContextMenu)
    }
    return contentEl
  }

  _onContentChanged() {
    this._contentChanged = true
  }

  _onPosition() {
    if (this.refs.scrollbar && this._contentChanged) {
      this._contentChanged = false
      this._updateScrollbar()
    }
  }

  _updateScrollbar() {
    if (this.refs.scrollbar) {
      this.refs.scrollbar.updatePositions()
    }
  }

  onHighlightsUpdated(highlights) {
    this.refs.scrollbar.extendProps({
      highlights: highlights
    })
  }

  onScroll() {
    let scrollPos = this.getScrollPosition()
    let scrollable = this.refs.scrollable
    if (this.props.onScroll) {
      this.props.onScroll(scrollPos, scrollable)
    }
    // Update TOCProvider given
    if (this.props.tocProvider) {
      this.props.tocProvider.markActiveEntry(this)
    }
    this.emit('scroll', scrollPos, scrollable)
  }

  /**
    Returns the height of scrollPane (inner content overflows)
  */
  getHeight() {
    let scrollableEl = this.getScrollableElement()
    return scrollableEl.height
  }

  /**
    Returns the cumulated height of a panel's content
  */
  getContentHeight() {
    let contentEl = this.refs.content.el.getNativeElement()
    // Important to use scrollHeight here (e.g. to consider overflowing
    // content, that stretches the content area, such as an overlay or
    // a context menu)
    return contentEl.scrollHeight
  }

  /**
    Get the `.se-content` element
  */
  getContentElement() {
    return this.refs.content.el
  }

  /**
    Get the `.se-scrollable` element
  */
  getScrollableElement() {
    return this.refs.scrollable.el
  }

  /**
    Get current scroll position (scrollTop) of `.se-scrollable` element
  */
  getScrollPosition() {
    let scrollableEl = this.getScrollableElement()
    return scrollableEl.getProperty('scrollTop')
  }

  setScrollPosition(scrollPos) {
    let scrollableEl = this.getScrollableElement()
    scrollableEl.setProperty('scrollTop', scrollPos)
  }

  /**
    Get offset relative to `.se-content`.

    @param {DOMNode} el DOM node that lives inside the
  */
  getPanelOffsetForElement(el) {
    let nativeEl = el.getNativeElement()
    let contentContainerEl = this.refs.content.getNativeElement()
    let rect = getRelativeBoundingRect(nativeEl, contentContainerEl)
    return rect.top
  }

  /**
    Scroll to a given sub component.

    @param {String} componentId component id, must be present in data-id attribute
  */
  scrollTo(componentId, onlyIfNotVisible) {
    let scrollableEl = this.getScrollableElement()
    let targetNode = scrollableEl.find('*[data-id="'+componentId+'"]')
    if (targetNode) {
      const offset = this.getPanelOffsetForElement(targetNode)
      let shouldScroll = true
      if (onlyIfNotVisible) {
        const height = scrollableEl.height
        const oldOffset = scrollableEl.getProperty('scrollTop')
        shouldScroll = (offset < oldOffset || oldOffset+height<offset)
      }
      if (shouldScroll) {
        this.setScrollPosition(offset)
      }
    } else {
      console.warn(componentId, 'not found in scrollable container')
    }
  }

  /*
    Determines the selection bounding rectangle relative to the scrollpane's content.
  */
  _onDomSelectionRendered() {
    super._onDomSelectionRendered()
    this._updateScrollbar()
  }

  _onContextMenu(e) {
    super._onContextMenu(e)
    this._updateScrollbar()
  }

}

export default ScrollPane
<commit_msg>Spreading arguments (surface) to super class<commit_after>import platform from '../../util/platform'
import Scrollbar from '../scrollbar/Scrollbar'
import getRelativeBoundingRect from '../../util/getRelativeBoundingRect'
import AbstractScrollPane from './AbstractScrollPane'

/**
  Wraps content in a scroll pane.

  NOTE: It is best practice to put all overlays as direct childs of the ScrollPane
        to reduce the chance that positioning gets messed up (position: relative)

  @prop {String} scrollbarType 'native' or 'substance' for a more advanced visual scrollbar. Defaults to 'native'
  @prop {String} [scrollbarPosition] 'left' or 'right' only relevant when scrollBarType: 'substance'. Defaults to 'right'
  @prop {ui/Highlights} [highlights] object that maintains highlights and can be manipulated from different sources
  @prop {ui/TOCProvider} [tocProvider] object that maintains table of content entries

  @example

  ```js
  $$(ScrollPane, {
    scrollbarType: 'substance', // defaults to native
    scrollbarPosition: 'left', // defaults to right
    onScroll: this.onScroll.bind(this),
    highlights: this.contentHighlights,
    tocProvider: this.tocProvider
  })
  ```
*/
class ScrollPane extends AbstractScrollPane {

  didMount() {
    super.didMount()
    if (this.refs.scrollbar && this.props.highlights) {
      this.props.highlights.on('highlights:updated', this.onHighlightsUpdated, this)
    }
    if (this.refs.scrollbar) {
      this.domObserver = new window.MutationObserver(this._onContentChanged.bind(this))
      this.domObserver.observe(this.el.getNativeElement(), {
        subtree: true,
        attributes: true,
        characterData: true,
        childList: true,
      })
      this.context.editorSession.onPosition(this._onPosition, this)
    }
  }

  dispose() {
    super.dispose()
    if (this.props.highlights) {
      this.props.highlights.off(this)
    }
    this.context.editorSession.off(this)
    this.context.dragManager.off(this)
  }

  render($$) {
    let el = $$('div')
      .addClass('sc-scroll-pane')

    if (platform.isFF) {
      el.addClass('sm-firefox')
    }

    // When noStyle is provided we just use ScrollPane as a container, but without
    // any absolute positioned containers, leaving the body scrollable.
    if (!this.props.noStyle) {
      el.addClass('sm-default-style')
    }

    // Initialize Substance scrollbar (if enabled)
    if (this.props.scrollbarType === 'substance') {
      el.addClass('sm-substance-scrollbar')
      el.addClass('sm-scrollbar-position-' + this.props.scrollbarPosition)

      el.append(
        // TODO: is there a way to pass scrollbar highlights already
        // via props? Currently the are initialized with a delay
        $$(Scrollbar, {
          scrollPane: this
        }).ref('scrollbar')
          .attr('id', 'content-scrollbar')
      )

      // Scanline is debugging purposes, display: none by default.
      el.append(
        $$('div').ref("scanline").addClass('se-scanline')
      )
    }

    el.append(
      $$('div').ref('scrollable').addClass('se-scrollable').append(
        this.renderContent($$)
      ).on('scroll', this.onScroll)
    )
    return el
  }

  renderContent($$) {
    let contentEl = $$('div').ref('content').addClass('se-content')
    contentEl.append(this.props.children)
    if (this.props.contextMenu === 'custom') {
      contentEl.on('contextmenu', this._onContextMenu)
    }
    return contentEl
  }

  _onContentChanged() {
    this._contentChanged = true
  }

  _onPosition() {
    if (this.refs.scrollbar && this._contentChanged) {
      this._contentChanged = false
      this._updateScrollbar()
    }
  }

  _updateScrollbar() {
    if (this.refs.scrollbar) {
      this.refs.scrollbar.updatePositions()
    }
  }

  onHighlightsUpdated(highlights) {
    this.refs.scrollbar.extendProps({
      highlights: highlights
    })
  }

  onScroll() {
    let scrollPos = this.getScrollPosition()
    let scrollable = this.refs.scrollable
    if (this.props.onScroll) {
      this.props.onScroll(scrollPos, scrollable)
    }
    // Update TOCProvider given
    if (this.props.tocProvider) {
      this.props.tocProvider.markActiveEntry(this)
    }
    this.emit('scroll', scrollPos, scrollable)
  }

  /**
    Returns the height of scrollPane (inner content overflows)
  */
  getHeight() {
    let scrollableEl = this.getScrollableElement()
    return scrollableEl.height
  }

  /**
    Returns the cumulated height of a panel's content
  */
  getContentHeight() {
    let contentEl = this.refs.content.el.getNativeElement()
    // Important to use scrollHeight here (e.g. to consider overflowing
    // content, that stretches the content area, such as an overlay or
    // a context menu)
    return contentEl.scrollHeight
  }

  /**
    Get the `.se-content` element
  */
  getContentElement() {
    return this.refs.content.el
  }

  /**
    Get the `.se-scrollable` element
  */
  getScrollableElement() {
    return this.refs.scrollable.el
  }

  /**
    Get current scroll position (scrollTop) of `.se-scrollable` element
  */
  getScrollPosition() {
    let scrollableEl = this.getScrollableElement()
    return scrollableEl.getProperty('scrollTop')
  }

  setScrollPosition(scrollPos) {
    let scrollableEl = this.getScrollableElement()
    scrollableEl.setProperty('scrollTop', scrollPos)
  }

  /**
    Get offset relative to `.se-content`.

    @param {DOMNode} el DOM node that lives inside the
  */
  getPanelOffsetForElement(el) {
    let nativeEl = el.getNativeElement()
    let contentContainerEl = this.refs.content.getNativeElement()
    let rect = getRelativeBoundingRect(nativeEl, contentContainerEl)
    return rect.top
  }

  /**
    Scroll to a given sub component.

    @param {String} componentId component id, must be present in data-id attribute
  */
  scrollTo(componentId, onlyIfNotVisible) {
    let scrollableEl = this.getScrollableElement()
    let targetNode = scrollableEl.find('*[data-id="'+componentId+'"]')
    if (targetNode) {
      const offset = this.getPanelOffsetForElement(targetNode)
      let shouldScroll = true
      if (onlyIfNotVisible) {
        const height = scrollableEl.height
        const oldOffset = scrollableEl.getProperty('scrollTop')
        shouldScroll = (offset < oldOffset || oldOffset+height<offset)
      }
      if (shouldScroll) {
        this.setScrollPosition(offset)
      }
    } else {
      console.warn(componentId, 'not found in scrollable container')
    }
  }

  /*
    Determines the selection bounding rectangle relative to the scrollpane's content.
  */
  _onDomSelectionRendered() {
    super._onDomSelectionRendered(...arguments)
    this._updateScrollbar()
  }

  _onContextMenu(e) {
    super._onContextMenu(e)
    this._updateScrollbar()
  }

}

export default ScrollPane
<|endoftext|>


<commit_before>import React from 'react';
import AceEditor from 'react-ace';
import brace from 'brace';
import EditorActionCreators from '../actions/EditorActionCreators';
import EditorStore from '../stores/EditorStore';
import EditorToolbar from './EditorToolbar';
import Mousetrap from 'mousetrap';
import smalltalk from 'smalltalk';
import _ from 'lodash';
import ConsoleOutput from './ConsoleOutput';
import RobotActions from '../actions/RobotActions';
import Ansible from '../utils/Ansible';
import {Panel} from 'react-bootstrap';
import { EditorButton } from './EditorClasses';
import ace from 'brace';
import 'brace/ext/language_tools';
import 'brace/ext/searchbox';
import 'brace/mode/python';
// React-ace themes
import 'brace/theme/monokai';
import 'brace/theme/github';
import 'brace/theme/tomorrow';
import 'brace/theme/kuroir';
import 'brace/theme/twilight';
import 'brace/theme/xcode';
import 'brace/theme/textmate';
import 'brace/theme/solarized_dark';
import 'brace/theme/solarized_light';
import 'brace/theme/terminal';
import {remote} from 'electron';
let langtools = ace.acequire('ace/ext/language_tools');
let storage = remote.require('electron-json-storage');

export default React.createClass({
  getInitialState() {
    return {
      showConsole: false,
      filepath: null,
      latestSaveCode: '',
      editorCode: '',
      editorTheme: 'github'
    };
  },
  componentDidMount() {
    this.refs.CodeEditor.editor.setOption('enableBasicAutocompletion', true);

    Mousetrap.prototype.stopCallback = function(e, element, combo) {
      return false; // Always respond to keyboard combos
    };

    Mousetrap.bind(['mod+s'], (e)=>{
      if (e.preventDefault) {
        e.preventDefault();
      }
      this.saveFile();
    });

    // If possible, reopen the last opened file.
    let lastFile = localStorage.getItem('lastFile');
    if (lastFile !== null) {
      EditorActionCreators.readFilepath(lastFile);
    }

    storage.has('editorTheme').then((hasKey)=>{
      if (hasKey) {
        storage.get('editorTheme').then((data)=>{
          this.setState({
            editorTheme: data.theme
          });
        });
      } else {
        storage.set('editorTheme', {
          theme: 'github'
        }, (err)=>{
          if (err) throw err;
        });
      }
    });

    EditorStore.on('change', this.updateEditorData);
  },
  componentWillUnmount() {
    Mousetrap.unbind(['mod+s']);
    EditorStore.removeListener('change', this.updateEditorData);
  },
  updateEditorData() {
    this.setState({
      filepath: EditorStore.getFilepath(),
      latestSaveCode: EditorStore.getLatestSaveCode(),
      editorCode: EditorStore.getEditorCode()
    });
  },
  openFile() {
    if (this.hasUnsavedChanges()) {
      smalltalk.confirm(
        'Are you sure?',
        'You have unsaved changes, opening a new file will discard them!'
      ).then(()=>{
        EditorActionCreators.openFile();
      }, ()=>{
        console.log('Canceled');
      });
    } else {
      EditorActionCreators.openFile();
    }
  },
  saveFile() {
    EditorActionCreators.saveFile(this.state.filepath, this.state.editorCode);
  },
  saveAsFile() {
    EditorActionCreators.saveFile(null, this.state.editorCode);
  },
  createNewFile() {
    if (this.hasUnsavedChanges()) {
      smalltalk.confirm(
        'Are you sure?',
        'You have unsaved changes, creating a new file will discard them!'
      ).then(()=>{
        EditorActionCreators.createNewFile();
      }, ()=>{
        console.log('Canceled');
      });
    } else {
      EditorActionCreators.createNewFile();
    }
  },
  editorUpdate(newVal) {
    EditorActionCreators.editorUpdate(newVal);
  },
  onEditorPaste(pasteData) {
    // Must correct non-ASCII characters, which would crash Runtime.
    let correctedText = pasteData.text;
    // Normalizing will allow us (in some cases) to preserve ASCII equivalents.
    correctedText = correctedText.normalize("NFD");
    // Special case to replace fancy quotes.
    correctedText = correctedText.replace(/[”“]/g,'"');
    correctedText = correctedText.replace(/[^\x00-\x7F]/g, "");
    // TODO: Create some notification that an attempt was made at correcting non-ASCII chars.
    pasteData.text = correctedText;
  },
  toggleConsole() {
    this.setState({showConsole: !this.state.showConsole});
    // must call resize method after changing height of ace editor
    setTimeout(()=>this.refs.CodeEditor.editor.resize(), 0.1);
  },
  clearConsole() {
    RobotActions.clearConsole();
  },
  upload() {
    Ansible.sendMessage('save', {
      code: this.state.editorCode
    });
  },
  startRobot() {
    Ansible.sendMessage('execute', {
      code: this.state.editorCode
    });
  },
  stopRobot() {
    Ansible.sendMessage('stop', {});
  },
  generateButtons() {
    // The buttons which will be in the button toolbar
    return [
      {
        groupId: 'file-operations-buttons',
        buttons: [
          new EditorButton('create', 'New', this.createNewFile, 'file'),
          new EditorButton('open', 'Open', this.openFile, 'folder-open'),
          new EditorButton('save', 'Save', this.saveFile, 'floppy-disk'),
          new EditorButton('saveas', 'Save As', this.saveAsFile, 'floppy-save')
        ],
      }, {
        groupId: 'code-execution-buttons',
        buttons: [
          new EditorButton('run', 'Run', this.startRobot, 'play', (this.props.isRunningCode || !this.props.connectionStatus)),
          new EditorButton('stop', 'Stop', this.stopRobot, 'stop', !(this.props.isRunningCode && this.props.connectionStatus)),
          new EditorButton('upload', 'Upload', this.upload, 'upload', (this.props.isRunningCode || !this.props.connectionStatus)),
          new EditorButton('toggle-console', 'Toggle Console', this.toggleConsole, 'console'),
          new EditorButton('clear-console', 'Clear Console', this.clearConsole, 'remove')
        ]
      }
    ];
  },
  pathToName(filepath) {
    if (filepath !== null) {
      if (process.platform === 'win32') {
        return filepath.split('\\').pop();
      } else {
        return filepath.split('/').pop();
      }
    } else {
      return '[ New File ]';
    }
  },
  hasUnsavedChanges() {
    return (this.state.latestSaveCode !== this.state.editorCode);
  },
  changeTheme(theme) {
    storage.set('editorTheme', {theme: theme}, (err)=>{
      if (err) throw err;
    });
    this.setState({editorTheme: theme});
  },
  themes: [
    'monokai',
    'github',
    'tomorrow',
    'kuroir',
    'twilight',
    'xcode',
    'textmate',
    'solarized_dark',
    'solarized_light',
    'terminal'
  ],
  render() {
    let consoleHeight = 250;
    let editorHeight = window.innerHeight * 0.66;
    return (
      <Panel
        header={'Editing: ' + this.pathToName(this.state.filepath) +
          (this.hasUnsavedChanges() ? '*' : '')}
        bsStyle="primary">
        <EditorToolbar
          buttons={ this.generateButtons() }
          unsavedChanges={ this.hasUnsavedChanges() }
          changeTheme={ this.changeTheme }
          editorTheme={ this.state.editorTheme }
          themes={ this.themes }
        />
        <AceEditor
          mode="python"
          theme={ this.state.editorTheme }
          width="100%"
          fontSize={14}
          ref="CodeEditor"
          name="CodeEditor"
          height={String(
            editorHeight - this.state.showConsole * (consoleHeight + 30)) + 'px'}
          value = { this.state.editorCode }
          onChange={ this.editorUpdate }
	  onPaste={ this.onEditorPaste }
          editorProps={{$blockScrolling: Infinity}}
        />
        <ConsoleOutput
          show={this.state.showConsole}
          height={String(consoleHeight) + 'px'}
          output={this.props.consoleData}/>
      </Panel>
    );
  }
});
<commit_msg>Check text for non-ASCII chars before executing or uploading code<commit_after>import React from 'react';
import AceEditor from 'react-ace';
import brace from 'brace';
import EditorActionCreators from '../actions/EditorActionCreators';
import EditorStore from '../stores/EditorStore';
import AlertActions from '../actions/AlertActions';
import EditorToolbar from './EditorToolbar';
import Mousetrap from 'mousetrap';
import smalltalk from 'smalltalk';
import _ from 'lodash';
import ConsoleOutput from './ConsoleOutput';
import RobotActions from '../actions/RobotActions';
import Ansible from '../utils/Ansible';
import {Panel} from 'react-bootstrap';
import { EditorButton } from './EditorClasses';
import ace from 'brace';
import 'brace/ext/language_tools';
import 'brace/ext/searchbox';
import 'brace/mode/python';
// React-ace themes
import 'brace/theme/monokai';
import 'brace/theme/github';
import 'brace/theme/tomorrow';
import 'brace/theme/kuroir';
import 'brace/theme/twilight';
import 'brace/theme/xcode';
import 'brace/theme/textmate';
import 'brace/theme/solarized_dark';
import 'brace/theme/solarized_light';
import 'brace/theme/terminal';
import {remote} from 'electron';
let langtools = ace.acequire('ace/ext/language_tools');
let storage = remote.require('electron-json-storage');

export default React.createClass({
  getInitialState() {
    return {
      showConsole: false,
      filepath: null,
      latestSaveCode: '',
      editorCode: '',
      editorTheme: 'github'
    };
  },
  componentDidMount() {
    this.refs.CodeEditor.editor.setOption('enableBasicAutocompletion', true);

    Mousetrap.prototype.stopCallback = function(e, element, combo) {
      return false; // Always respond to keyboard combos
    };

    Mousetrap.bind(['mod+s'], (e)=>{
      if (e.preventDefault) {
        e.preventDefault();
      }
      this.saveFile();
    });

    // If possible, reopen the last opened file.
    let lastFile = localStorage.getItem('lastFile');
    if (lastFile !== null) {
      EditorActionCreators.readFilepath(lastFile);
    }

    storage.has('editorTheme').then((hasKey)=>{
      if (hasKey) {
        storage.get('editorTheme').then((data)=>{
          this.setState({
            editorTheme: data.theme
          });
        });
      } else {
        storage.set('editorTheme', {
          theme: 'github'
        }, (err)=>{
          if (err) throw err;
        });
      }
    });

    EditorStore.on('change', this.updateEditorData);
  },
  componentWillUnmount() {
    Mousetrap.unbind(['mod+s']);
    EditorStore.removeListener('change', this.updateEditorData);
  },
  updateEditorData() {
    this.setState({
      filepath: EditorStore.getFilepath(),
      latestSaveCode: EditorStore.getLatestSaveCode(),
      editorCode: EditorStore.getEditorCode()
    });
  },
  openFile() {
    if (this.hasUnsavedChanges()) {
      smalltalk.confirm(
        'Are you sure?',
        'You have unsaved changes, opening a new file will discard them!'
      ).then(()=>{
        EditorActionCreators.openFile();
      }, ()=>{
        console.log('Canceled');
      });
    } else {
      EditorActionCreators.openFile();
    }
  },
  saveFile() {
    EditorActionCreators.saveFile(this.state.filepath, this.state.editorCode);
  },
  saveAsFile() {
    EditorActionCreators.saveFile(null, this.state.editorCode);
  },
  createNewFile() {
    if (this.hasUnsavedChanges()) {
      smalltalk.confirm(
        'Are you sure?',
        'You have unsaved changes, creating a new file will discard them!'
      ).then(()=>{
        EditorActionCreators.createNewFile();
      }, ()=>{
        console.log('Canceled');
      });
    } else {
      EditorActionCreators.createNewFile();
    }
  },
  editorUpdate(newVal) {
    EditorActionCreators.editorUpdate(newVal);
  },
  correctText(text) {
    // Removes non-ASCII characters from text.
    return text.replace(/[^\x00-\x7F]/g, "");
  },
  onEditorPaste(pasteData) {
    // Must correct non-ASCII characters, which would crash Runtime.
    let correctedText = pasteData.text;
    // Normalizing will allow us (in some cases) to preserve ASCII equivalents.
    correctedText = correctedText.normalize("NFD");
    // Special case to replace fancy quotes.
    correctedText = correctedText.replace(/[”“]/g,'"');
    correctedText = this.correctText(correctedText);
    // TODO: Create some notification that an attempt was made at correcting non-ASCII chars.
    pasteData.text = correctedText;
  },
  toggleConsole() {
    this.setState({showConsole: !this.state.showConsole});
    // must call resize method after changing height of ace editor
    setTimeout(()=>this.refs.CodeEditor.editor.resize(), 0.1);
  },
  clearConsole() {
    RobotActions.clearConsole();
  },
  sendCode(command) {
    let correctedText = this.correctText(this.state.editorCode);
    if (correctedText !== this.state.editorCode) {
      AlertActions.addAlert(
	'Invalid characters detected',
	'Your code has non-ASCII characters, which won\'t work on the robot. ' +
	'Please remove them and try again.'
      );
    } else {
      Ansible.sendMessage(command, {
	code: this.state.editorCode
      });
    }
  },
  upload() { this.sendCode('upload'); },
  startRobot() { this.sendCode('execute'); },
  stopRobot() {
    Ansible.sendMessage('stop', {});
  },
  generateButtons() {
    // The buttons which will be in the button toolbar
    return [
      {
        groupId: 'file-operations-buttons',
        buttons: [
          new EditorButton('create', 'New', this.createNewFile, 'file'),
          new EditorButton('open', 'Open', this.openFile, 'folder-open'),
          new EditorButton('save', 'Save', this.saveFile, 'floppy-disk'),
          new EditorButton('saveas', 'Save As', this.saveAsFile, 'floppy-save')
        ],
      }, {
        groupId: 'code-execution-buttons',
        buttons: [
          new EditorButton('run', 'Run', this.startRobot, 'play', (this.props.isRunningCode || !this.props.connectionStatus)),
          new EditorButton('stop', 'Stop', this.stopRobot, 'stop', !(this.props.isRunningCode && this.props.connectionStatus)),
          new EditorButton('upload', 'Upload', this.upload, 'upload', (this.props.isRunningCode || !this.props.connectionStatus)),
          new EditorButton('toggle-console', 'Toggle Console', this.toggleConsole, 'console'),
          new EditorButton('clear-console', 'Clear Console', this.clearConsole, 'remove')
        ]
      }
    ];
  },
  pathToName(filepath) {
    if (filepath !== null) {
      if (process.platform === 'win32') {
        return filepath.split('\\').pop();
      } else {
        return filepath.split('/').pop();
      }
    } else {
      return '[ New File ]';
    }
  },
  hasUnsavedChanges() {
    return (this.state.latestSaveCode !== this.state.editorCode);
  },
  changeTheme(theme) {
    storage.set('editorTheme', {theme: theme}, (err)=>{
      if (err) throw err;
    });
    this.setState({editorTheme: theme});
  },
  themes: [
    'monokai',
    'github',
    'tomorrow',
    'kuroir',
    'twilight',
    'xcode',
    'textmate',
    'solarized_dark',
    'solarized_light',
    'terminal'
  ],
  render() {
    let consoleHeight = 250;
    let editorHeight = window.innerHeight * 0.66;
    return (
      <Panel
        header={'Editing: ' + this.pathToName(this.state.filepath) +
          (this.hasUnsavedChanges() ? '*' : '')}
        bsStyle="primary">
        <EditorToolbar
          buttons={ this.generateButtons() }
          unsavedChanges={ this.hasUnsavedChanges() }
          changeTheme={ this.changeTheme }
          editorTheme={ this.state.editorTheme }
          themes={ this.themes }
        />
        <AceEditor
          mode="python"
          theme={ this.state.editorTheme }
          width="100%"
          fontSize={14}
          ref="CodeEditor"
          name="CodeEditor"
          height={String(
            editorHeight - this.state.showConsole * (consoleHeight + 30)) + 'px'}
          value = { this.state.editorCode }
          onChange={ this.editorUpdate }
	  onPaste={ this.onEditorPaste }
          editorProps={{$blockScrolling: Infinity}}
        />
        <ConsoleOutput
          show={this.state.showConsole}
          height={String(consoleHeight) + 'px'}
          output={this.props.consoleData}/>
      </Panel>
    );
  }
});
<|endoftext|>


<commit_before>/* jshint unused:false */

function makeSettingsEditor(handlers, options, callbacks) {
    var $ = window.$;

    // Step 1. Define default path to file with schema
    var settingsSchemaSrc = options.schemaPath; // default path to json schema of sesstings' fields

    // Step 2. If custom path was defined - is it
    if (settingsSchemaSrc in window) {
        settingsSchemaSrc = window.settingsSchemaSrc;    // use custom path if exists
    }

    // Step 3. Get data from json file. Call onPropertiesSchemaReady() when schema received
    $.getJSON(settingsSchemaSrc, function (propertiesSchema) {
        onPropertiesSchemaReady(propertiesSchema);
    });

    // Step 4. Render form (using cool JSONEditor), init some other tools and listen "click" on form's submit button
    var onPropertiesSchemaReady = function (propertiesSchema) {
        /**
         * JSON Schema -> HTML Editor
         * https://github.com/jdorn/json-editor/
         */
        var editor = new JSONEditor(handlers.form, {
            disable_collapse: true,
            disable_edit_json: true,
            disable_properties: true,
            no_additional_properties: true,
            schema: {
                type: 'object',
                title: options.title,
                properties: propertiesSchema
            },
            required: [],
            required_by_default: true,
            theme: 'bootstrap3'
        });

        // Init IframeHelper
        var inno = new IframeHelper();

        Loader.show();
        inno.onReady(function () {
            callbacks.callbackGetSettings(inno, editor);
        });

        // Listen submit button click event
        handlers.submit.on('click', function () {
            var errors = editor.validate();
            if (errors.length) {
                errors = errors.map(function (error) {
                    var field = editor.getEditor(error.path),
                        title = field.schema.title;
                    return title + ': ' + error.message;
                });
                console.log(errors.join('\n'));
            } else {
                callbacks.callbackSetSettings(inno, editor);
            }
        });
    };

}
<commit_msg>fix jscs for jenkins<commit_after>/* jshint unused:false */

function makeSettingsEditor (handlers, options, callbacks) {
    var $ = window.$;

    // Step 1. Define default path to file with schema
    var settingsSchemaSrc = options.schemaPath; // default path to json schema of sesstings' fields

    // Step 2. If custom path was defined - is it
    if (settingsSchemaSrc in window) {
        settingsSchemaSrc = window.settingsSchemaSrc;    // use custom path if exists
    }

    // Step 3. Get data from json file. Call onPropertiesSchemaReady() when schema received
    $.getJSON(settingsSchemaSrc, function (propertiesSchema) {
        onPropertiesSchemaReady(propertiesSchema);
    });

    // Step 4. Render form (using cool JSONEditor), init some other tools and listen "click" on form's submit button
    var onPropertiesSchemaReady = function (propertiesSchema) {
        /**
         * JSON Schema -> HTML Editor
         * https://github.com/jdorn/json-editor/
         */
        var editor = new JSONEditor(handlers.form, {
            disable_collapse: true,
            disable_edit_json: true,
            disable_properties: true,
            no_additional_properties: true,
            schema: {
                type: 'object',
                title: options.title,
                properties: propertiesSchema
            },
            required: [],
            required_by_default: true,
            theme: 'bootstrap3'
        });

        // Init IframeHelper
        var inno = new IframeHelper();

        Loader.show();
        inno.onReady(function () {
            callbacks.callbackGetSettings(inno, editor);
        });

        // Listen submit button click event
        handlers.submit.on('click', function () {
            var errors = editor.validate();
            if (errors.length) {
                errors = errors.map(function (error) {
                    var field = editor.getEditor(error.path),
                        title = field.schema.title;
                    return title + ': ' + error.message;
                });
                console.log(errors.join('\n'));
            } else {
                callbacks.callbackSetSettings(inno, editor);
            }
        });
    };

}
<|endoftext|>


<commit_before>'use strict';

const helpers = require('./helpers/helpers.js');
const Table = require('cli-table');

module.exports = format;

const FORMATTERS = {
    'console': formatConsole,
    'dataForConsole': formatForConsole,
    'updatesForConsole': formatUpdates,
    'reportMusic': formatReportMusic,
    'reportGeneral': formatReportGeneral,
    'reportUpdates': formatReportUpdates
};

function format(type) {
    const formatter = FORMATTERS[type];
    if (typeof formatter === 'function') {
        let args = [].slice.call(arguments, 1);
        return formatter.apply(null, args);
    }

    console.error('No formatter for type', item.type);
}

function formatConsole(data) {
    let result = '';
    result += formatForConsole(data.user, data.logs_written);
    if (data.updates) {
        result += formatUpdates(data.updates);
    }

    return result;
}

function formatForConsole(data, logs_written = 0) {
    let launch_date = helpers.getProcessLaunchDate();
    let name = data.Name.replace('  ', ' ');
    let result = `App launched on ${launch_date}\n`;
    result += `User name: ${name}\n`;
    result += `User ID: ${data.user_id}\n`;
    result += `Logs written: ${logs_written}\n\n`;
    result += `>>> Checked on ${new Date(data.timestamp)} <<<\n\n`;
    result += `${name} -- ${data['Last seen']}`;
    if (data.isFromMobile) {
        result += ' [Mobile]';
    }
    result += '\n';
    result += `Current status: ${data['Current status']}\n`;

    return result;
}

function formatUpdates(updates) {
    if (typeof updates !== 'object') {
        return '';
    }

    let result = '\nUPDATES\n';
    for (let k in updates) {
        result += `${k}: ${updates[k].current}\n`;
    }

    return result;
}

function formatReportMusic(docs) {
    if (!docs.length) {
        return 'No music tracks were found';
    }

    let table = new Table({
        head: ['#', 'Track', 'Times played', 'Last played']
    });

    docs.forEach((doc, i) => {
        table.push([i + 1, doc.track, doc.play_count, new Date(doc.last_played).toString()]);
    });

    return table.toString();
}

function formatReportGeneral(doc) {
    let result = '';
    for (let k in doc) {
        if (k === '_id') continue;
        result += `${k.trim().replace(':', '')}: ${doc[k]}\n`;
    }

    return result;
}

function formatReportUpdates(docs) {
    if (!docs.length) {
        return 'No updates tracks were found';
    }

    let result = '';

    docs.forEach(doc => {
        let update = '\n\n' + Array(40).join('-');
        update += '\n' + new Date(doc.timestamp).toString();
        for (let k in doc.updates) {
            update += `\n\n${k}`;
            update += `\nOld: ${doc.updates[k].old}`;
            update += `\nNew: ${doc.updates[k].current}`;
        }

        result += update;
    });

    return result;
}
<commit_msg>Rename formatForConsole -> formatStalkData<commit_after>'use strict';

const helpers = require('./helpers/helpers.js');
const Table = require('cli-table');

module.exports = format;

const FORMATTERS = {
    'console': formatConsole,
    'dataForConsole': formatStalkData,
    'updatesForConsole': formatUpdates,
    'reportMusic': formatReportMusic,
    'reportGeneral': formatReportGeneral,
    'reportUpdates': formatReportUpdates
};

function format(type) {
    const formatter = FORMATTERS[type];
    if (typeof formatter === 'function') {
        let args = [].slice.call(arguments, 1);
        return formatter.apply(null, args);
    }

    console.error('No formatter for type', item.type);
}

function formatConsole(data) {
    let result = '';
    result += formatStalkData(data.user, data.logs_written);
    if (data.updates) {
        result += formatUpdates(data.updates);
    }

    return result;
}

function formatStalkData(data, logs_written = 0) {
    let launch_date = helpers.getProcessLaunchDate();
    let name = data.Name.replace('  ', ' ');
    let result = `App launched on ${launch_date}\n`;
    result += `User name: ${name}\n`;
    result += `User ID: ${data.user_id}\n`;
    result += `Logs written: ${logs_written}\n\n`;
    result += `>>> Checked on ${new Date(data.timestamp)} <<<\n\n`;
    result += `${name} -- ${data['Last seen']}`;
    if (data.isFromMobile) {
        result += ' [Mobile]';
    }
    result += '\n';
    result += `Current status: ${data['Current status']}\n`;

    return result;
}

function formatUpdates(updates) {
    if (typeof updates !== 'object') {
        return '';
    }

    let result = '\nUPDATES\n';
    for (let k in updates) {
        result += `${k}: ${updates[k].current}\n`;
    }

    return result;
}

function formatReportMusic(docs) {
    if (!docs.length) {
        return 'No music tracks were found';
    }

    let table = new Table({
        head: ['#', 'Track', 'Times played', 'Last played']
    });

    docs.forEach((doc, i) => {
        table.push([i + 1, doc.track, doc.play_count, new Date(doc.last_played).toString()]);
    });

    return table.toString();
}

function formatReportGeneral(doc) {
    let result = '';
    for (let k in doc) {
        if (k === '_id') continue;
        result += `${k.trim().replace(':', '')}: ${doc[k]}\n`;
    }

    return result;
}

function formatReportUpdates(docs) {
    if (!docs.length) {
        return 'No updates tracks were found';
    }

    let result = '';

    docs.forEach(doc => {
        let update = '\n\n' + Array(40).join('-');
        update += '\n' + new Date(doc.timestamp).toString();
        for (let k in doc.updates) {
            update += `\n\n${k}`;
            update += `\nOld: ${doc.updates[k].old}`;
            update += `\nNew: ${doc.updates[k].current}`;
        }

        result += update;
    });

    return result;
}
<|endoftext|>


<commit_before>
/////////////////////////////////////////////////////////////////////
// DEVELOPMENT configuration
//
/////////////////////////////////////////////////////////////////////
const config = {

  client: {
    // this the public host name of your server for the
    // client socket to connect.
    // eg. https://myforgeapp.mydomain.com
    host: 'https://bim360-visual-reports.herokuapp.com/',
    env: 'production',
    port: 443
  },

  forge: {

    oauth: {
      clientSecret: process.env.FORGE_CLIENT_SECRET,
      clientId: process.env.FORGE_CLIENT_ID,
      scope: [
        'data:read',
        'data:create',
        'data:write',
        'bucket:read',
        'bucket:create'
      ]
    },

    viewer: {
      viewer3D: 'https://developer.api.autodesk.com/viewingservice/v1/viewers/viewer3D.min.js?v=2.13',
      threeJS:  'https://developer.api.autodesk.com/viewingservice/v1/viewers/three.min.js?v=2.13',
      style:    'https://developer.api.autodesk.com/viewingservice/v1/viewers/style.min.css?v=2.13'
    }
  }
}

module.exports = config

<commit_msg>changin prod link<commit_after>
/////////////////////////////////////////////////////////////////////
// DEVELOPMENT configuration
//
/////////////////////////////////////////////////////////////////////
const config = {

  client: {
    // this the public host name of your server for the
    // client socket to connect.
    // eg. https://myforgeapp.mydomain.com
    host: 'https://bim360-visual-report.herokuapp.com/',
    env: 'production',
    port: 443
  },

  forge: {

    oauth: {
      clientSecret: process.env.FORGE_CLIENT_SECRET,
      clientId: process.env.FORGE_CLIENT_ID,
      scope: [
        'data:read',
        'data:create',
        'data:write',
        'bucket:read',
        'bucket:create'
      ]
    },

    viewer: {
      viewer3D: 'https://developer.api.autodesk.com/viewingservice/v1/viewers/viewer3D.min.js?v=2.13',
      threeJS:  'https://developer.api.autodesk.com/viewingservice/v1/viewers/three.min.js?v=2.13',
      style:    'https://developer.api.autodesk.com/viewingservice/v1/viewers/style.min.css?v=2.13'
    }
  }
}

module.exports = config

<|endoftext|>


<commit_before>(function () {
    'use strict';

    var socket_url,
        web_socket,
        chat_div = document.getElementById('chat-content'),
        chat_input = document.getElementById('chat-input');

    if (window.location.protocol === 'https:') {
        socket_url = 'wss:';
    } else {
        socket_url = 'ws:';
    }
    socket_url += '//' + window.location.host;
    socket_url += socket_url_path;

    web_socket = new WebSocket(socket_url);
    web_socket.onmessage = function (msg) {
        chat_div.textContent += msg.data + "\n";
    };

    document.getElementById('chat-input-form').addEventListener('submit', function(e) {
        e.preventDefault();
        var message = chat_input.value;
        chat_input.value = '';
        web_socket.send(JSON.stringify({
            'type': 'message',
            'data': message
        }));
        chat_input.focus();
    });

})();
<commit_msg>add protocol processing to receipt of server messages<commit_after>(function () {
    'use strict';

    var socket_url,
        web_socket,
        chat_div = document.getElementById('chat-content'),
        chat_input = document.getElementById('chat-input'),
        connected_users = {};

    function addMessage(message) {
        chat_div.textContent += message + '\n';
    }

    if (window.location.protocol === 'https:') {
        socket_url = 'wss:';
    } else {
        socket_url = 'ws:';
    }
    socket_url += '//' + window.location.host;
    socket_url += socket_url_path;

    web_socket = new WebSocket(socket_url);
    web_socket.onmessage = function (msg) {
        var obj = JSON.parse(msg.data),
            data = obj['data'];
        if (obj['type'] === 'curstate') {
            var userlist = data['userlist'],
                history = data['history'];
            for (var i in userlist) {
                var user = userlist[i];
                connected_users[user['connection_id']] = {
                    'nickname': user['nickname'],
                    'is_asker': user['is_asker']
                };
            }
            for (var i in history) {
                var userName = connected_users[history[i][0]]['nickname'],
                    message = history[i][1];
                addMessage(userName + ': ' + message);
            }
        } else if (obj['type'] === 'message') {
            var userName = connected_users[data['connection_id']]['nickname'],
                message = data['message'];
            addMessage(userName + ': ' + message);
        } else if (obj['type'] === 'connect') {
            connected_users[data['connection_id']] = {
                'nickname': data['nickname'],
                'is_asker': data['is_asker']
            };
            addMessage(data['nickname'] + ' connected.');
        } else if (obj['type'] === 'disconnect') {
            addMessage(connected_users[data['connection_id']]['nickname'] + ' disconnected.');
            delete connected_users[data['connection_id']];
        }
    };

    document.getElementById('chat-input-form').addEventListener('submit', function(e) {
        e.preventDefault();
        var message = chat_input.value;
        chat_input.value = '';
        web_socket.send(JSON.stringify({
            'type': 'message',
            'data': message
        }));
        chat_input.focus();
    });

})();
<|endoftext|>