Merge pull request #4183 from FreeCodeCamp/fix/job-copy

Fix job board copy
This commit is contained in:
Logan Tegman
2015-11-05 20:43:48 -08:00
22 changed files with 236 additions and 690 deletions

View File

@@ -1 +1,2 @@
public/**/*.js
client/main.js

View File

@@ -224,8 +224,8 @@
"no-plusplus": 0,
"react/display-name": 1,
"react/jsx-boolean-value": 1,
"react/jsx-quotes": [1, "single", "avoid-escape"],
"react/jsx-boolean-value": [1, "always"],
"jsx-quotes": [1, "prefer-single"],
"react/jsx-no-undef": 1,
"react/jsx-sort-props": 1,
"react/jsx-uses-react": 1,

View File

@@ -1,7 +1,7 @@
import React, { createClass, PropTypes } from 'react';
import React, { PropTypes } from 'react';
import { Alert } from 'react-bootstrap';
export default createClass({
export default React.createClass({
displayName: 'FlashQueue',
propTypes: {
@@ -9,9 +9,9 @@ export default createClass({
},
renderMessages(messages) {
return messages.map(message => {
return messages.map(() => {
return (
<Alert>
<Alert />
);
});
},

View File

@@ -8,11 +8,19 @@ export default Actions({
return { title: title + '| Free Code Camp' };
},
setUser({ username, picture, progressTimestamps = [] }) {
setUser({
username,
picture,
progressTimestamps = [],
isFrontEndCert,
isFullStackCert
}) {
return {
username,
picture,
points: progressTimestamps.length
points: progressTimestamps.length,
isFrontEndCert,
isFullStackCert
};
},

View File

@@ -1,63 +0,0 @@
var Action = require('thundercats').Action,
executeBonfire = require('./executeBonfire'),
getModel = require('../../utils/getModel'),
debug = require('debug')('freecc:common:bonfires');
var BonfireActions = Action.createActions([
'setUserCode',
'testUserCode',
'setResults',
'setDisplay',
'setBonfire',
'getBonfire',
'handleBonfireError',
'openCompletionModal'
]);
BonfireActions
.getBonfire
.subscribe(function(params) {
var Bonfire = getModel('bonfire');
var bonfireName = params.bonfireName ?
params.bonfireName.replace(/\-/g, ' ') :
'meet bonfire';
debug('getting bonfire for: ', bonfireName);
var regQuery = { name: { like: bonfireName, options: 'i' } };
Bonfire.find(
{ where: regQuery },
function(err, bonfire) {
if (err) {
return debug('bonfire get err', err);
}
if (!bonfire || bonfire.length < 1) {
return debug('404 no bonfire found for ', bonfireName);
}
bonfire = bonfire.pop();
if (bonfire) {
debug(
'found bonfire %s for route %s',
bonfire.name,
bonfireName
);
}
BonfireActions.setBonfire(bonfire);
}
);
});
BonfireActions
.testUserCode
.subscribe(function({ userCode, tests }) {
debug('test bonfire');
executeBonfire(userCode, tests, function(err, { output, results }) {
if (err) {
debug('error running tests', err);
return BonfireActions.setDisplay(err);
}
BonfireActions.setDisplay(output);
BonfireActions.setResults(results);
});
});
module.exports = BonfireActions;

View File

@@ -1,67 +0,0 @@
var BonfiresActions = require('./Actions');
var { Store, setStateUtil } = require('thundercats');
var BonfiresStore = Store.create({
getInitialValue: function() {
return {
userCode: 'console.log(\'FreeCodeCamp!\')',
difficulty: 0,
description: [
'default state'
],
tests: [],
results: null
};
},
getOperations: function() {
var {
setBonfire,
setUserCode,
setResults,
setDisplay
} = BonfiresActions;
return [
setBonfire
.map(function(bonfire) {
var {
name,
description,
difficulty,
tests
} = bonfire;
var userCode = bonfire.challengeSeed;
return {
name,
userCode,
tests,
description,
difficulty
};
})
.map(setStateUtil),
setUserCode
.map(function(userCode) {
return { userCode };
})
.map(setStateUtil),
setDisplay
.map(function(display) {
return { display };
})
.map(setStateUtil),
setResults
.map(function(results) {
return { results };
})
.map(setStateUtil)
];
}
});
module.exports = BonfiresStore;

View File

@@ -1,99 +0,0 @@
var React = require('react'),
// ## mixins
{ ObservableStateMixin } = require('thundercats'),
// ## components
SidePanel = require('./SidePanel.jsx'),
Results = require('./Results.jsx'),
Display = require('../displayCode'),
Editor = require('../editor'),
{ Grid, Row, Col } = require('react-bootstrap'),
// ## flux
BonfireActions = require('./Actions'),
BonfireStore = require('./Store');
var Bonfire = React.createClass({
mixins: [ObservableStateMixin],
contextTypes: {
makePath: React.PropTypes.func.isRequired,
replaceWith: React.PropTypes.func.isRequired
},
getObservable: function() {
return BonfireStore;
},
componentDidMount: function() {
// get history object
var his = typeof window !== 'undefined' ? window.history : null;
// spinal-case bonfireName
var bonfireName = this.state.name.toLowerCase().replace(/\s/g, '-');
// create proper URI from react-router
var path = this.context.makePath('bonfires', { bonfireName: bonfireName });
// if html5 push state exists, update URI
// else we are using hash location and should just cause a re render
if (his) {
his.replaceState({ path: path }, '', path);
} else {
this.context.replaceWith('bonfires', { bonfireName: bonfireName});
}
},
_onTestBonfire: function() {
BonfireActions.testUserCode({
userCode: this.state.userCode,
tests: this.state.tests
});
},
render: function() {
var {
name,
userCode,
difficulty,
description,
results,
display
} = this.state;
var brief = description.slice(0, 1).pop();
// convert bonfire difficulty from floating point string
// to integer.
var difficultyInt = Math.floor(+difficulty);
return (
<Grid>
<Row>
<Col
xs={ 12 }
md={ 4 }>
<SidePanel
name={ name }
brief={ brief }
difficulty={ difficultyInt }
onTestBonfire={ this._onTestBonfire }
description={ description.length > 1 ? description : [] }/>
<Display
value={ display }/>
<Results
results={ results }/>
</Col>
<Col
xs={ 12 }
md={ 8 }>
<Editor
onValueChange={ BonfireActions.setUserCode }
value={ userCode }/>
</Col>
</Row>
</Grid>
);
}
});
module.exports = Bonfire;

View File

@@ -1,62 +0,0 @@
var React = require('react'),
classNames = require('classnames'),
{ Grid, Row, Col } = require('react-bootstrap');
var Results = React.createClass({
propTypes: {
results: React.PropTypes.array
},
_renderText: function(text, textClass) {
return (
<Col
xs={ 11 }
className={ classNames(textClass) }>
{ text }
</Col>
);
},
_renderResult: function(results) {
return results.map(function(result, idx) {
var err = result.err;
var iconClass = {
'ion-close-circled big-error-icon': err,
'ion-checkmark-circled big-success-icon': !err
};
var textClass = {
'test-output wrappable': true,
'test-vertical-center': !err
};
return (
<div key={ idx }>
<Row>
<Col
xs={ 1 }
className='text-center'>
<i className={ classNames(iconClass) }></i>
</Col>
{ this._renderText(result.text, textClass) }
{ err ? this._renderText(err, textClass) : null }
</Row>
<div className='ten-pixel-break'></div>
</div>
);
}.bind(this));
},
render: function() {
var results = this.props.results;
if (!results || results.length && results.length === 0) {
return null;
}
return (
<Grid>
{ this._renderResult(this.props.results) }
</Grid>
);
}
});
module.exports = Results;

View File

@@ -1,129 +0,0 @@
var React = require('react'),
// ## components
{
Well,
Row,
Col,
Button,
} = require('react-bootstrap');
var SidePanel = React.createClass({
propTypes: {
name: React.PropTypes.string,
brief: React.PropTypes.string,
description: React.PropTypes.array,
difficulty: React.PropTypes.number,
onTestBonfire: React.PropTypes.func
},
getDefaultProps: function() {
return {
name: 'Welcome to Bonfires!',
difficulty: 5,
brief: 'This is a brief description'
};
},
getInitialState: function() {
return {
isMoreInfoOpen: false
};
},
_toggleMoreInfo: function() {
this.setState({
isMoreInfoOpen: !this.state.isMoreInfoOpen
});
},
_renderFlames: function() {
var difficulty = this.props.difficulty;
return [1, 2, 3, 4, 5].map(num => {
var className = 'ion-ios-flame';
if (num > difficulty) {
className += '-outline';
}
return (
<i
key={ num }
className={ className }/>
);
});
},
_renderMoreInfo: function(isDescription) {
var description = this.props.description.map((sentance, index) => {
return <p key={ index }>{ sentance }</p>;
});
if (isDescription && this.state.isMoreInfoOpen) {
return (
<Row>
<Col xs={ 12 }>
{ description }
</Col>
</Row>
);
}
return null;
},
_renderMoreInfoButton: function(isDescription) {
if (isDescription) {
return (
<Button
onClick={ this._toggleMoreInfo }
bsStyle='primary'
block={ true }
className='btn-primary-ghost'>
<span className='ion-arrow-down-b'></span>
More information
</Button>
);
}
return null;
},
render: function() {
var isDescription = this.props.description &&
this.props.description.length > 1;
return (
<div>
<h1 className='text-center'>{ this.props.name }</h1>
<h2 className='text-center'>
<div className='bonfire-flames'>
Difficulty:&nbsp;
{ this._renderFlames() }
</div>
</h2>
<Well>
<Row>
<Col xs={ 12 }>
<div className='bonfire-instructions'>
<p>{ this.props.brief }</p>
<div>
{ this._renderMoreInfo(isDescription) }
{ this._renderMoreInfoButton(isDescription) }
</div>
</div>
</Col>
</Row>
</Well>
<Button
bsStyle='primary'
block={ true }
className='btn-big'
onClick={ this.props.onTestBonfire }>
Run Code (ctrl + enter)
</Button>
<br />
</div>
);
}
});
module.exports = SidePanel;

View File

@@ -1,27 +0,0 @@
var debug = require('debug')('freecc:executebonfire');
var {
addTests,
runTests,
testCode
} = require('../../utils');
module.exports = executeBonfire;
function executeBonfire(userCode, tests, cb) {
// TODO: move this into componentDidMount
// ga('send', 'event', 'Bonfire', 'ran-code', bonfireName);
var testSalt = Math.random();
var { preppedCode, userTests } = addTests(userCode, tests, testSalt);
debug('sending code to web worker for testing');
testCode(preppedCode, function(err, data) {
if (err) { return cb(err); }
var results = runTests(userTests, data, testSalt);
debug('testing complete', results);
cb(null, {
output: data.output,
results
});
});
}

View File

@@ -1,8 +0,0 @@
/*
export default {
path: 'bonfires/(:bonfireName)'
getComponents(cb) {
// TODO(berks): add bonfire component
}
};
*/

View File

@@ -1,8 +1,8 @@
import React, { createClass } from 'react';
import React from 'react';
import { LinkContainer } from 'react-router-bootstrap';
import { Button, Row, Col, Panel } from 'react-bootstrap';
export default createClass({
export default React.createClass({
displayName: 'NoJobFound',
render() {

View File

@@ -74,8 +74,8 @@ export default contain(
mdOffset= { 1 }
xs={ 12 }>
<h1 className='text-center'>
Talented web developers with strong portfolios are eager
to work for your company
Hire JavaScript engineers experienced in
HTML5, Node.js, MongoDB, and Agile Development
</h1>
<Row className='text-center'>
<Col

View File

@@ -45,17 +45,6 @@ const hightlightCopy = `
Highlight my post to make it stand out. (+$50)
`;
const foo = `
This will narrow the field substantially with higher quality applicants
`;
const isFullStackCopy = `
Applicants must have earned Free Code Camps Full Stack Certification to apply.*
`;
const isFrontEndCopy = `
Applicants must have earned Free Code Camps Front End Certification to apply.*
`;
const isRemoteCopy = `
This job can be performed remotely.
@@ -125,9 +114,9 @@ export default contain({
url,
logo,
company,
isHighlighted,
isFrontEndCert = true,
isFullStackCert,
isFrontEndCert,
isHighlighted,
isRemoteOk,
howToApply
} = form;
@@ -140,10 +129,10 @@ export default contain({
logo: formatValue(formatUrl(logo), isValidURL),
company: formatValue(company, makeRequired(isAscii)),
isHighlighted: formatValue(isHighlighted, null, 'bool'),
isFullStackCert: formatValue(isFullStackCert, null, 'bool'),
isFrontEndCert: formatValue(isFrontEndCert, null, 'bool'),
isRemoteOk: formatValue(isRemoteOk, null, 'bool'),
howToApply: formatValue(howToApply, makeRequired(isAscii))
howToApply: formatValue(howToApply, makeRequired(isAscii)),
isFrontEndCert,
isFullStackCert
};
},
subscribeOnWillMount() {
@@ -163,9 +152,9 @@ export default contain({
logo: PropTypes.object,
company: PropTypes.object,
isHighlighted: PropTypes.object,
isFullStackCert: PropTypes.object,
isFrontEndCert: PropTypes.object,
isRemoteOk: PropTypes.object,
isFrontEndCert: PropTypes.bool,
isFullStackCert: PropTypes.bool,
howToApply: PropTypes.object
},
@@ -173,16 +162,16 @@ export default contain({
handleSubmit(e) {
e.preventDefault();
const props = this.props;
const pros = this.props;
let valid = true;
checkValidity.forEach((prop) => {
// if value exist, check if it is valid
if (props[prop].value && props[prop].type !== 'boolean') {
valid = valid && !!props[prop].valid;
if (pros[prop].value && pros[prop].type !== 'boolean') {
valid = valid && !!pros[prop].valid;
}
});
if (!valid) {
if (!valid || !pros.isFrontEndCert && !pros.isFullStackCert ) {
debug('form not valid');
return;
}
@@ -198,9 +187,9 @@ export default contain({
url,
logo,
company,
isHighlighted,
isFullStackCert,
isFrontEndCert,
isFullStackCert,
isHighlighted,
isRemoteOk,
howToApply
} = this.props;
@@ -215,10 +204,10 @@ export default contain({
logo: formatUrl(uriInSingleQuotedAttr(logo.value), false),
company: inHTMLData(company.value),
isHighlighted: !!isHighlighted.value,
isFrontEndCert: !!isFrontEndCert.value,
isFullStackCert: !!isFullStackCert.value,
isRemoteOk: !!isRemoteOk.value,
howToApply: inHTMLData(howToApply.value)
howToApply: inHTMLData(howToApply.value),
isFrontEndCert,
isFullStackCert
};
const job = Object.keys(jobValues).reduce((accu, prop) => {
@@ -245,6 +234,18 @@ export default contain({
handleForm({ [name]: value });
},
handleCertClick(name) {
const { jobActions: { handleForm } } = this.props;
const otherButton = name === 'isFrontEndCert' ?
'isFullStackCert' :
'isFrontEndCert';
handleForm({
[name]: true,
[otherButton]: false
});
},
render() {
const {
position,
@@ -255,12 +256,13 @@ export default contain({
logo,
company,
isHighlighted,
isFrontEndCert,
isFullStackCert,
isRemoteOk,
howToApply,
isFrontEndCert,
isFullStackCert,
jobActions: { handleForm }
} = this.props;
const { handleChange } = this;
const labelClass = 'col-sm-offset-1 col-sm-2';
const inputClass = 'col-sm-6';
@@ -277,7 +279,52 @@ export default contain({
onSubmit={ this.handleSubmit }>
<div className='spacer'>
<h2>First, tell us about the position</h2>
<h2>First, select your ideal applicant: </h2>
</div>
<Row>
<Col
xs={ 6 }
xsOffset={ 3 }>
<Row>
<Button
className={ isFrontEndCert ? 'active' : '' }
onClick={ () => {
if (!isFrontEndCert) {
this.handleCertClick('isFrontEndCert');
}
}}>
<h4>Front End Development Certified</h4>
You can expect each applicant
to have a code portfolio using the
following technologies:
HTML5, CSS, jQuery, API integrations
<br />
<br />
</Button>
</Row>
<div className='button-spacer' />
<Row>
<Button
className={ isFullStackCert ? 'active' : ''}
onClick={ () => {
if (!isFullStackCert) {
this.handleCertClick('isFullStackCert');
}
}}>
<h4>Full Stack Development Certified</h4>
You can expect each applicant to have a code
portfolio using the following technologies:
HTML5, CSS, jQuery, API integrations, MVC Framework,
JavaScript, Node.js, MongoDB, Express.js
<br />
<br />
</Button>
</Row>
</Col>
</Row>
<div className='spacer'>
<h2>Tell us about the position</h2>
</div>
<Input
bsStyle={ position.bsStyle }
@@ -311,26 +358,6 @@ export default contain({
type='textarea'
value={ description.value }
wrapperClassName={ inputClass } />
<Input
checked={ isFrontEndCert.value }
label={ isFrontEndCopy }
onChange={
({ target: { checked } }) => handleForm({
isFrontEndCert: !!checked
})
}
type='checkbox'
wrapperClassName={ checkboxClass } />
<Input
checked={ isFullStackCert.value }
label={ isFullStackCopy }
onChange={
({ target: { checked } }) => handleForm({
isFullStackCert: !!checked
})
}
type='checkbox'
wrapperClassName={ checkboxClass } />
<Input
checked={ isRemoteOk.value }
label={ isRemoteCopy }
@@ -341,9 +368,6 @@ export default contain({
}
type='checkbox'
wrapperClassName={ checkboxClass } />
<Row>
<small>* { foo }</small>
</Row>
<div className='spacer' />
<Row>
<div>

View File

@@ -1,4 +1,4 @@
import React, { createClass } from 'react';
import React, { PropTypes } from 'react';
import { History } from 'react-router';
import { contain } from 'thundercats-react';
@@ -6,12 +6,66 @@ import ShowJob from './ShowJob.jsx';
import JobNotFound from './JobNotFound.jsx';
import { isJobValid } from '../utils';
function shouldShowApply(
{
isFrontEndCert: isFrontEndCertReq = false,
isFullStackCert: isFullStackCertReq = false
}, {
isFrontEndCert = false,
isFullStackCert = false
}
) {
return (!isFrontEndCertReq && !isFullStackCertReq) ||
(isFullStackCertReq && isFullStackCert) ||
(isFrontEndCertReq && isFrontEndCert);
}
function generateMessage(
{
isFrontEndCert: isFrontEndCertReq = false,
isFullStackCert: isFullStackCertReq = false
},
{
isFrontEndCert = false,
isFullStackCert = false,
isSignedIn = false
}
) {
if (!isSignedIn) {
return 'Must be signed in to apply';
}
if (isFrontEndCertReq && !isFrontEndCert) {
return 'This employer requires Free Code Camps Front ' +
'End Development Certification in order to apply';
}
if (isFullStackCertReq && !isFullStackCert) {
return 'This employer requires Free Code Camps Full ' +
'Stack Development Certification in order to apply';
}
if (isFrontEndCertReq && isFrontEndCertReq) {
return 'This employer requires the Front End Development Certification. ' +
"You've earned it, so feel free to apply.";
}
return 'This employer requires the Full Stack Development Certification. ' +
"You've earned it, so feel free to apply.";
}
export default contain(
{
store: 'jobsStore',
stores: ['appStore', 'jobsStore'],
fetchWaitFor: 'jobsStore',
fetchAction: 'jobActions.getJob',
map({ currentJob }) {
return { job: currentJob };
combineLatest(
{ username, isFrontEndCert, isFullStackCert },
{ currentJob }
) {
return {
username,
job: currentJob,
isFrontEndCert,
isFullStackCert
};
},
getPayload({ params: { id }, job = {} }) {
return {
@@ -25,9 +79,16 @@ export default contain(
return job.id !== id;
}
},
createClass({
React.createClass({
displayName: 'Show',
propTypes: {
job: PropTypes.object,
isFullStackCert: PropTypes.bool,
isFrontEndCert: PropTypes.bool,
username: PropTypes.string
},
mixins: [History],
componentDidMount() {
@@ -39,12 +100,36 @@ export default contain(
},
render() {
const { job } = this.props;
const {
isFullStackCert,
isFrontEndCert,
job,
username
} = this.props;
if (!isJobValid(job)) {
return <JobNotFound />;
}
return <ShowJob { ...this.props }/>;
const isSignedIn = !!username;
const showApply = shouldShowApply(
job,
{ isFrontEndCert, isFullStackCert }
);
const message = generateMessage(
job,
{ isFrontEndCert, isFullStackCert, isSignedIn }
);
return (
<ShowJob
message={ message }
preview={ false }
showApply={ showApply }
{ ...this.props }/>
);
}
})
);

View File

@@ -22,7 +22,10 @@ export default React.createClass({
displayName: 'ShowJob',
propTypes: {
job: PropTypes.object,
params: PropTypes.object
params: PropTypes.object,
showApply: PropTypes.bool,
preview: PropTypes.bool,
message: PropTypes.string
},
renderHeader({ company, position }) {
@@ -38,8 +41,45 @@ export default React.createClass({
);
},
renderHowToApply(showApply, preview, message, howToApply) {
if (!showApply) {
return (
<Row>
<Col
md={ 6 }
mdOffset={ 3 }>
<h4 className='bg-info text-center'>{ message }</h4>
</Col>
</Row>
);
}
return (
<Row>
<Col
md={ 6 }
mdOffset={ 3 }>
<Well>
<bold>{ preview ? 'How do I apply?' : message }</bold>
<br />
<br />
<span dangerouslySetInnerHTML={{
__html: addATags(howToApply)
}} />
</Well>
</Col>
</Row>
);
},
render() {
const { job = {} } = this.props;
const {
showApply = true,
message,
preview = true,
job = {}
} = this.props;
const {
logo,
position,
@@ -93,20 +133,7 @@ export default React.createClass({
<p>{ description }</p>
</Col>
</Row>
<Well>
<Row>
<Col
md={ 6 }
mdOffset={ 3 }>
<bold>How do I apply?</bold>
<br />
<br />
<span dangerouslySetInnerHTML={{
__html: addATags(howToApply)
}} />
</Col>
</Row>
</Well>
{ this.renderHowToApply(showApply, preview, message, howToApply) }
</Panel>
</Col>
</Row>

View File

@@ -1,4 +1,4 @@
import React, { createClass, PropTypes } from 'react';
import React, { PropTypes } from 'react';
import { Button } from 'react-bootstrap';
const followLink = 'https://twitter.com/intent/follow?' +
@@ -9,7 +9,7 @@ function commify(count) {
return Number(count).toLocaleString('en');
}
export default createClass({
export default React.createClass({
displayName: 'FollowButton',

View File

@@ -1,51 +0,0 @@
var React = require('react'),
Tailspin = require('tailspin');
var Editor = React.createClass({
propTypes: {
value: React.PropTypes.string
},
getDefaultProps: function() {
return {
value: [
'/**',
'* Your output will go here.',
'* Console.log() -type statements',
'* will appear in your browser\'s',
'* DevTools JavaScript console.',
'**/'
].join('\n')
};
},
render: function() {
var value = this.props.value;
var options = {
lineNumbers: false,
lineWrapping: true,
mode: 'text',
readOnly: 'noCursor',
textAreaClassName: 'hide-textarea',
theme: 'monokai',
value: value
};
var config = {
setSize: ['100%', '100%']
};
return (
<form className='code'>
<div className='form-group codeMirrorView'>
<Tailspin
{ ...options }
config={ config }/>
</div>
</form>
);
}
});
module.exports = Editor;

View File

@@ -1 +0,0 @@
module.exports = require('./Display.jsx');

View File

@@ -1,91 +0,0 @@
var React = require('react'),
debug = require('debug')('freecc:comp:editor'),
jshint = require('jshint').JSHINT,
Tailspin = require('tailspin');
var Editor = React.createClass({
propTypes: {
onValueChange: React.PropTypes.func,
value: React.PropTypes.string
},
getDefaultProps: function() {
return {
value: 'console.log(\'freeCodeCamp is awesome\')'
};
},
getInitialState: function() {
return {
value: this.props.value
};
},
render: function() {
var options = {
autoCloseBrackets: true,
gutters: ['CodeMirror-lint-markers'],
lint: true,
linter: jshint,
lineNumbers: true,
lineWrapping: true,
mode: 'javascript',
matchBrackets: true,
runnable: true,
scrollbarStyle: 'null',
theme: 'monokai',
textAreaClassName: 'hide-textarea',
value: this.state.value,
onChange: e => {
this.setState({ value: e.target.value});
if (typeof this.props.onValueChange === 'function') {
this.props.onValueChange(e.target.value);
}
}
};
var config = {
setSize: ['100%', 'auto'],
extraKeys: {
Tab: function(cm) {
debug('tab pressed');
if (cm.somethingSelected()) {
cm.indentSelection('add');
} else {
var spaces = new Array(cm.getOption('indentUnit') + 1).join(' ');
cm.replaceSelection(spaces);
}
},
'Shift-Tab': function(cm) {
debug('shift-tab pressed');
if (cm.somethingSelected()) {
cm.indentSelection('subtract');
} else {
var spaces = new Array(cm.getOption('indentUnit') + 1).join(' ');
cm.replaceSelection(spaces);
}
},
'Ctrl-Enter': function() {
debug('C-enter pressed');
// execute bonfire action
return false;
}
}
};
return (
<div id='mainEditorPanel'>
<form className='code'>
<div className='form-group codeMirrorView'>
<Tailspin
{ ...options }
config={ config }/>
</div>
</form>
</div>
);
}
});
module.exports = Editor;

View File

@@ -1 +0,0 @@
module.exports = require('./Editor.jsx');

View File

@@ -18,7 +18,7 @@
"lint-server": "jsonlint -q server/*.json",
"lint-resources": "jsonlint -q server/resources/*.json",
"lint-utils": "jsonlint -q server/utils/*.json",
"lint-js": "eslint --ext=.js,.jsx server/ common/models common/utils config/",
"lint-js": "eslint --ext=.js,.jsx server/ common/ common/utils config/ client/",
"lint-json": "npm run lint-server && npm run lint-nonprofits && npm run lint-challenges && npm run lint-resources && npm run lint-utils",
"test-challenges": "babel-node seed/test-challenges.js | tnyan",
"pretest": "npm run lint",