Last active
January 10, 2020 05:16
-
-
Save jimdoescode/4c974cfae29d6a117b2a to your computer and use it in GitHub Desktop.
An early version of CodeMana
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var React = require("react"); | |
var Router = require('react-router'); | |
var GistForm = require("./GistForm.js"); | |
var AppHeader = require("./AppHeader.js"); | |
var AppFooter = require("./AppFooter.js"); | |
var Gist = require("./Gist.js"); | |
var Config = require("./Config.js"); | |
var DefaultRoute = Router.DefaultRoute; | |
var NotFoundRoute = Router.NotFoundRoute; | |
var Route = Router.Route; | |
var RouteHandler = Router.RouteHandler; | |
var App = React.createClass({ | |
render: function() { | |
return ( | |
<div className="app"> | |
<AppHeader origin={Config.origin}/> | |
<RouteHandler/> | |
<AppFooter/> | |
</div> | |
); | |
} | |
}); | |
var GistRoute = React.createClass({ | |
contextTypes: { | |
router: React.PropTypes.func | |
}, | |
render: function() { | |
return ( | |
<Gist id={this.context.router.getCurrentParams().gistId}/> | |
); | |
} | |
}); | |
var HomeRoute = React.createClass({ | |
render: function() { | |
return ( | |
<div className="container main"> | |
<section className="hero"> | |
<h1> | |
<div className="fa fa-flask fa-3x"/> | |
<p>Up your Gist magic <i className="fa fa-magic"/></p> | |
</h1> | |
<p> | |
CodeMana lets you comment in line on your Gists, simply click the line you want to talk about. | |
It works entirely in your browser, only calling GitHub to post comments and retrieve Gists. | |
</p> | |
<p> | |
If you're curious <a href="https://github.com/jimdoescode/codemana">peruse the code</a>. It's comprised mostly of React components. | |
</p> | |
<GistForm className="pure-form" showButton="true"/> | |
</section> | |
</div> | |
); | |
} | |
}); | |
var FourOhFourRoute = React.createClass({ | |
render: function() { | |
return ( | |
<div className="container main"> | |
NOT FOUND!! | |
</div> | |
); | |
} | |
}); | |
var routes = ( | |
<Route name="app" path={Config.root} handler={App}> | |
<Route name="gist" path=":gistId" handler={GistRoute}/> | |
<DefaultRoute handler={HomeRoute}/> | |
<NotFoundRoute handler={FourOhFourRoute}/> | |
</Route> | |
); | |
Router.run(routes, Router.HistoryLocation, function (Handler) { | |
React.render(<Handler/>, document.getElementById("mount-point")); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var React = require("react"); | |
var GistForm = require("./GistForm.js"); | |
module.exports = React.createClass({ | |
getDefaultProps: function() { | |
return {origin: window.location.origin}; | |
}, | |
updateStyle: function(event) { | |
event.preventDefault(); | |
document.getElementById('highlight-style').href = event.target.value; | |
}, | |
shouldComponentUpdate: function(newProps, newState) { | |
return false; //No need to update this thing, it's static | |
}, | |
render: function() { | |
return ( | |
<header className="app-header"> | |
<nav className="pure-menu pure-menu-horizontal pure-menu-fixed"> | |
<div className="container"> | |
<a className="pure-menu-heading pull-left logo" href={this.props.origin}> | |
<span>CODE</span><i className="fa fa-flask"/><span>MANA</span> | |
</a> | |
<GistForm className="pure-form pull-left pure-u-2-3"/> | |
<ul className="pure-menu-list pull-right"> | |
<li className="pure-menu-item"> | |
<select onChange={this.updateStyle}> | |
<option value="css/default.css">Default Highlighting</option> | |
<option value="css/funky.css">Funky Highlighting</option> | |
<option value="css/okaidia.css">Okaidia Highlighting</option> | |
<option value="css/dark.css">Dark Highlighting</option> | |
</select> | |
</li> | |
</ul> | |
</div> | |
</nav> | |
</header> | |
); | |
} | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module.exports = { | |
root: '/', | |
gistApi: 'https://api.github.com', | |
origin: window.location.origin | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var React = require("react"); | |
module.exports = React.createClass({ | |
getDefaultProps: function() { | |
return { | |
name: '', | |
lines: [], | |
comments: [], | |
onCommentFormOpen: function() {}, | |
onCommentFormCancel: function() {}, | |
onCommentFormSubmit: function() {} | |
}; | |
}, | |
render: function() { | |
var rows = []; | |
var lineCount = this.props.lines.length; | |
for (var i=0; i < lineCount; i++) { | |
var num = i + 1; | |
if (this.props.comments[num] && this.props.comments[num].length > 0) { | |
rows.push(<Line key={this.props.name + num} | |
onClick={this.props.onCommentFormOpen} | |
file={this.props.name} | |
number={num} | |
content={this.props.lines[i]} | |
toggle={LineComments.generateNodeId(this.props.name, num)}/>); | |
rows.push(<LineComments key={this.props.name + num + 'comments'} | |
onEditOrReply={this.props.onCommentFormOpen} | |
onCancel={this.props.onCommentFormCancel} | |
onSubmit={this.props.onCommentFormSubmit} | |
file={this.props.name} | |
number={num} | |
comments={this.props.comments[num]}/>); | |
} else { | |
rows.push(<Line key={this.props.name + num} | |
onClick={this.props.onCommentFormOpen} | |
file={this.props.name} | |
number={num} | |
content={this.props.lines[i]}/>); | |
} | |
} | |
return ( | |
<section className="code-file-container"> | |
<table id={this.props.name} className="code-file"> | |
<tbody> | |
<tr className="spacer line"> | |
<td className="line-marker"/> | |
<td className="line-num"/> | |
<td className="line-content"/> | |
</tr> | |
{rows} | |
<tr className="spacer line"> | |
<td className="line-marker"/> | |
<td className="line-num"/> | |
<td className="line-content"/> | |
</tr> | |
</tbody> | |
</table> | |
</section> | |
); | |
} | |
}); | |
var Line = React.createClass({ | |
getDefaultProps: function() { | |
return { | |
number: 0, | |
content: '', | |
file: '', | |
toggle: false, | |
onClick: function() {} | |
} | |
}, | |
//Lines only need to rerender when a new file is set. | |
shouldComponentUpdate: function(newProps, newState) { | |
return this.props.content !== newProps.content || | |
this.props.file !== newProps.file || | |
this.props.toggle !== newProps.toggle; | |
}, | |
render: function() { | |
var toggleCol = this.props.toggle ? <td className="line-marker"><CommentToggle toggle={this.props.toggle}/></td> : <td className="line-marker"/> | |
return ( | |
<tr id={this.props.file+"-L"+this.props.number} className="line"> | |
{toggleCol} | |
<td className="line-num">{this.props.number}</td> | |
<td className="line-content" onClick={this.props.onClick.bind(null, this.props.file, this.props.number, 0)}> | |
<pre> | |
<code dangerouslySetInnerHTML={{__html: this.props.content}}/> | |
</pre> | |
</td> | |
</tr> | |
); | |
} | |
}); | |
var LineComments = React.createClass({ | |
statics: { | |
generateNodeId: function(filename, number) { | |
return filename + "-C" + number; | |
} | |
}, | |
getDefaultProps: function() { | |
return { | |
number: 0, | |
file: '', | |
comments: [], | |
onEditOrReply: function() {}, | |
onCancel: function() {}, | |
onSubmit: function() {} | |
} | |
}, | |
render: function() { | |
var comments = this.props.comments.map(function(comment) { | |
return comment.showForm ? | |
<CommentForm user={comment.user} text={comment.body} key="comment-form" onCancel={this.props.onCancel} onSubmit={this.props.onSubmit}/> : | |
<Comment user={comment.user} text={comment.body} key={comment.id} onEditOrReply={this.props.onEditOrReply} id={this.props.file+"-L"+this.props.number+"-C"+comment.id}/>; | |
}, this); | |
return ( | |
<tr id={LineComments.generateNodeId(this.props.file, this.props.number)} className="line comment-row"> | |
<td className="line-marker"/> | |
<td className="line-num"/> | |
<td className="line-comments">{comments}</td> | |
</tr> | |
); | |
} | |
}); | |
var Comment = React.createClass({ | |
getDefaultProps: function() { | |
return { | |
id: '', | |
text: '', | |
user: null, | |
onEditOrReply: function() {} | |
}; | |
}, | |
//Comments only need to rerender when new text is set. | |
shouldComponentUpdate: function(newProps, newState) { | |
return this.props.text !== newProps.text; | |
}, | |
render: function() { | |
return ( | |
<div className="line-comment"> | |
<a className="avatar pull-left" href={this.props.user.html_url}><img src={this.props.user.avatar_url} alt=""/></a> | |
<div className="pull-left content"> | |
<header className="comment-header"> | |
<a href={this.props.user.html_url}>{this.props.user.login}</a> | |
</header> | |
<p className="comment-body">{this.props.text}</p> | |
</div> | |
</div> | |
); | |
} | |
}); | |
var CommentForm = React.createClass({ | |
getDefaultProps: function() { | |
return { | |
text: '', | |
user: null, | |
onSubmit: function() {}, | |
onCancel: function() {} | |
} | |
}, | |
render: function() { | |
return ( | |
<div className="line-comment"> | |
<a className="avatar pull-left" href={this.props.user.html_url}><img src={this.props.user.avatar_url} alt=""/></a> | |
<div className="pull-left content"> | |
<header className="comment-header"> | |
<a href={this.props.user.html_url}>{this.props.user.login}</a> | |
</header> | |
<form action="#" onSubmit={this.props.onSubmit} className="comment-body"> | |
<textarea name="text" placeholder="Enter your comment..." defaultValue={this.props.text}/> | |
<button type="submit" className="pure-button button-primary"> | |
<i className="fa fa-comment"/> Comment | |
</button> | |
<button type="button" className="pure-button button-error" onClick={this.props.onCancel}> | |
<i className="fa fa-times-circle"/> Cancel | |
</button> | |
</form> | |
</div> | |
</div> | |
); | |
} | |
}); | |
var CommentToggle = React.createClass({ | |
getInitialState: function() { | |
return { | |
display: 'none', | |
symbolClass: this.props.closeIcon | |
}; | |
}, | |
getDefaultProps: function() { | |
return { | |
toggle: '', | |
closeIcon: 'fa-comment-o fa-flip-horizontal', | |
openIcon: 'fa-comment fa-flip-horizontal' | |
}; | |
}, | |
handleClick: function(event) { | |
var elm = document.getElementById(this.props.toggle); | |
var display = elm.style.display; | |
event.preventDefault(); | |
elm.style.display = this.state.display; | |
this.setState({ | |
symbolClass: display === 'none' ? this.props.closeIcon : this.props.openIcon, | |
display: display | |
}); | |
}, | |
render: function() { | |
return ( | |
<a href='#' onClick={this.handleClick}> | |
<i className={"fa " + this.state.symbolClass + " fa-fw"}/> | |
</a> | |
); | |
} | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var React = require("react"); | |
var Qwest = require("qwest"); | |
var Modal = require("react-modal"); | |
var File = require("./File.js"); | |
var Utils = require("./Utils.js"); | |
var Spinner = require("./Spinner.js"); | |
var Config = require("./Config.js"); | |
Modal.setAppElement(document.getElementById("mount-point")); | |
Modal.injectCSS(); | |
Qwest.base = Config.gistApi; | |
module.exports = React.createClass({ | |
getHeaders: function() { | |
var headers = {}; | |
if (this.state.user !== null) | |
headers["Authorization"] = 'Basic ' + btoa(this.state.user.login + ':' + this.state.user.password); | |
return headers; | |
}, | |
getInitialState: function() { | |
return { | |
files: [], | |
comments: [], | |
openComment: null, | |
showLoginModal: false, | |
user: Utils.getUserFromStorage(sessionStorage), | |
processing: true | |
}; | |
}, | |
fetchGist: function(gistId) { | |
var self = this; | |
var options = { | |
headers: this.getHeaders(), | |
responseType: 'json' | |
}; | |
Qwest.get('/gists/'+gistId, null, options).then(function(xhr, gist) { | |
var files = []; | |
for (var name in gist.files) | |
files.push(Utils.parseFile(gist.files[name])); | |
if (self.isMounted()) | |
self.setState({ | |
files: files, | |
processing: false | |
}); | |
}).catch(function(xhr, response, e) { | |
console.log(xhr, response, e); | |
alert('There was a problem fetching and or parsing this Gist.'); | |
self.setState({processing: false}); | |
}); | |
Qwest.get('/gists/'+gistId+'/comments', null, options).then(function(xhr, comments) { | |
var parsedComments = {}; | |
var commentCount = comments.length; | |
for (var i=0; i < commentCount; i++) { | |
var parsed = Utils.parseComment(comments[i]); | |
if (parsedComments[parsed.filename] === undefined) | |
parsedComments[parsed.filename] = []; | |
if (parsedComments[parsed.filename][parsed.line] === undefined) | |
parsedComments[parsed.filename][parsed.line] = []; | |
parsedComments[parsed.filename][parsed.line].push(parsed); | |
} | |
if (self.isMounted()) { | |
self.setState({ | |
comments: parsedComments | |
}); | |
} | |
}).catch(function(xhr, response, e) { | |
console.log(xhr, response, e); | |
alert('There was a problem fetching the comments for this Gist.'); | |
}); | |
}, | |
componentDidMount: function() { | |
this.fetchGist(this.props.id); | |
}, | |
componentWillReceiveProps: function(newProps) { | |
this.setState({processing: true}); | |
this.fetchGist(newProps.id); | |
}, | |
componentDidUpdate: function(prevProps, prevState) { | |
//If there is a hash specified then attempt to scroll there. | |
if (window.location.hash) { | |
var elm = document.getElementById(window.location.hash.substring(1)); | |
if (elm) | |
window.scrollTo(0, elm.offsetTop); | |
} | |
}, | |
postGistComment: function(event) { | |
var text = event.target.children.namedItem("text").value.trim(); | |
var comments = this.state.comments; | |
var open = this.state.openComment; | |
var options = { | |
headers: this.getHeaders(), | |
dataType: 'json', | |
responseType: 'json' | |
}; | |
event.preventDefault(); | |
if (text !== "" && open !== null) { | |
open.body = text; | |
open.showForm = false; | |
comments[open.filename][open.line].splice(open.replyTo, 1, open); | |
//Send the comment to GitHub. We only need to handle the case where it doesn't make it | |
Qwest.post('/gists/'+this.props.id+'/comments', {body: Utils.createCommentLink(this.props.id, open.filename, open.line) + ' ' + text}, options); | |
this.setState({ | |
comments: comments, | |
openComment: null | |
}); | |
} | |
}, | |
insertCommentForm: function(filename, line, replyTo, event) { | |
var comments = this.state.comments; | |
var open = this.state.openComment; | |
var newOpen = Utils.createComment(this.props.id, 0, filename, line, '', this.state.user, replyTo, true); | |
if (this.state.user === null) { | |
this.setState({showLoginModal: true}); | |
return; | |
} | |
event.preventDefault(); | |
if (open !== null) | |
comments[open.filename][open.line].splice(open.replyTo, 1); | |
if (!comments[filename]) | |
comments[filename] = []; | |
if (!comments[filename][line]) | |
comments[filename][line] = []; | |
if (open === null || open.filename !== newOpen.filename || open.line !== newOpen.line || open.replyTo !== newOpen.replyTo) { | |
newOpen.id = comments[filename][line].length; | |
comments[filename][line].splice(replyTo, 0, newOpen); | |
} else { | |
newOpen = null; | |
} | |
this.setState({ | |
comments: comments, | |
openComment: newOpen | |
}); | |
}, | |
removeCommentForm: function(event) { | |
var comments = this.state.comments; | |
var open = this.state.openComment; | |
event.preventDefault(); | |
if (open !== null) { | |
comments[open.filename][open.line].splice(open.replyTo, 1); | |
this.setState({ | |
comments: comments, | |
openComment: null | |
}); | |
} | |
}, | |
handleLogin: function(user) { | |
this.setState({ | |
user: user, | |
showLoginModal: false | |
}); | |
}, | |
closeModal: function(event) { | |
event.preventDefault(); | |
this.setState({showLoginModal: false}); | |
}, | |
render: function() { | |
var body = this.state.processing ? | |
<Spinner/> : this.state.files.map(function(file) { | |
return <File onCommentFormOpen={this.insertCommentForm} | |
onCommentFormCancel={this.removeCommentForm} | |
onCommentFormSubmit={this.postGistComment} | |
key={file.name} | |
name={file.name} | |
lines={file.parsedLines} | |
comments={this.state.comments[file.name]}/> | |
}, this); | |
return ( | |
<div className="container main"> | |
<LoginModal show={this.state.showLoginModal} onSuccess={this.handleLogin} onClose={this.closeModal}/> | |
{body} | |
</div> | |
); | |
} | |
}); | |
var LoginModal = React.createClass({ | |
getDefaultProps: function() { | |
return { | |
show: false, | |
onSuccess: function(user) {}, | |
onClose: function() {} | |
}; | |
}, | |
getInitialState: function() { | |
return { | |
processing: false | |
}; | |
}, | |
attemptLogin: function(event) { | |
var username = event.target.elements.namedItem("username").value.trim(); | |
var password = event.target.elements.namedItem("password").value; | |
var store = event.target.elements.namedItem("store").checked; | |
var self = this; | |
event.preventDefault(); | |
if (username && password) { | |
var options = { | |
headers: { | |
Authorization: 'Basic ' + btoa(username + ':' + password) | |
}, | |
responseType: 'json' | |
}; | |
this.setState({processing: true}); | |
Qwest.get('/user', null, options).then(function(xhr, user) { | |
user.password = password; | |
if (store) | |
Utils.saveUserToStorage(user, sessionStorage); | |
self.props.onSuccess(user); | |
}).complete(function(xhr, user) { | |
self.setState({processing: false}); | |
}); | |
} | |
}, | |
render: function() { | |
var form = ( | |
<form className="pure-form pure-form-stacked" onSubmit={this.attemptLogin}> | |
<fieldset> | |
<input name="username" className="pure-input-1" type="text" placeholder="GitHub User Name..." required="true"/> | |
<input name="password" className="pure-input-1" type="password" placeholder="GitHub Password or Token..." required="true"/> | |
<label><input name="store" type="checkbox"/> Store in Memory</label> | |
</fieldset> | |
<fieldset> | |
<button type="submit" className="pure-button button-primary"><i className="fa fa-save"/> Save</button> | |
<button className="pure-button button-error" onClick={this.props.onClose}><i className="fa fa-times-circle"/> Cancel</button> | |
</fieldset> | |
</form> | |
); | |
return ( | |
<Modal isOpen={this.props.show} onRequestClose={this.props.onClose} className="react-modal-content" overlayClassName="react-modal-overlay"> | |
<h2><i className="fa fa-github"/> GitHub Access</h2> | |
<p>To leave a comment you need to enter your GitHub user name and GitHub password. This is <strong>only</strong> used to post Gist comments to GitHub.</p> | |
<p>If you prefer not to enter your password you can use a <a target="_blank" href="https://github.com/settings/tokens/new">personal access token</a>. Make sure it has Gist access.</p> | |
<hr/> | |
{ this.state.processing ? <Spinner className="fa-github-alt"/> : form } | |
</Modal> | |
); | |
} | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var React = require("react"); | |
var Router = require('react-router'); | |
module.exports = React.createClass({ | |
mixins: [Router.Navigation], | |
getDefaultProps: function() { | |
return { | |
className: "", | |
showButton: false | |
}; | |
}, | |
handleSubmit: function(event) { | |
event.preventDefault(); | |
var gistId = event.target.elements.namedItem("gistId").value.trim(); | |
if (gistId.length > 0) | |
this.transitionTo('gist', {gistId: gistId}); | |
}, | |
render: function() { | |
var body = ( | |
this.props.showButton ? | |
<fieldset> | |
<input name="gistId" type="text" className="pure-input-1-3" placeholder="Enter a Gist ID..." required="true"/> | |
<button type="submit" className="pure-button button-primary">Go <i className="fa fa-sign-in"/></button> | |
</fieldset> | |
: | |
<input className="pure-input-1-3" name="gistId" type="text" placeholder="Enter a Gist ID..." required="true"/> | |
); | |
return ( | |
<form className={this.props.className} onSubmit={this.handleSubmit} action="#">{body}</form> | |
); | |
} | |
}); | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var React = require("react"); | |
module.exports = React.createClass({ | |
getDefaultProps: function() { | |
return { | |
className: 'fa-spinner' | |
}; | |
}, | |
render: function() { | |
var classes = 'fa ' + this.props.className + ' fa-spin fa-5x'; | |
return ( | |
<p className="spinner"><i className={classes}/></p> | |
); | |
} | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var Prism = require("./Prism.js"); | |
module.exports = { | |
tokenizeNewLines: function(str) { | |
var tokens = []; | |
var strlen = str.length; | |
var lineCount = 0; | |
for (var i=0; i < strlen; i++) | |
{ | |
if (tokens[lineCount]) | |
tokens[lineCount] += str[i]; | |
else | |
tokens[lineCount] = str[i]; | |
if (str[i] === '\n') | |
lineCount++; | |
} | |
return tokens; | |
}, | |
tokenize: function(code, lang) { | |
var processed = []; | |
var tokens = Prism.tokenize(code, lang); | |
var token = tokens.shift(); | |
while (token) | |
{ | |
var isObj = typeof token === 'object'; | |
var lines = this.tokenizeNewLines(isObj ? token.content : token); | |
var count = lines.length; | |
for (var i=0; i < count; i++) | |
{ | |
if (isObj) | |
processed.push(new Prism.Token(token.type, Prism.util.encode(lines[i]), token.alias)); | |
else | |
processed.push(Prism.util.encode(lines[i])); | |
} | |
token = tokens.shift(); | |
} | |
return processed; | |
}, | |
syntaxHighlight: function(code, lang) { | |
var lineCount = 0; | |
var lines = ['']; | |
var prismLang = this.getPrismCodeLanguage(lang); | |
var tokens = this.tokenize(code, prismLang); | |
var token = tokens.shift(); | |
while (token) | |
{ | |
code = (typeof token === 'object') ? Prism.Token.stringify(token, prismLang) : token; | |
lines[lineCount] += code.replace(/\n/g, ''); | |
if (code.indexOf('\n') !== -1) | |
lines[++lineCount] = ''; | |
token = tokens.shift(); | |
} | |
return lines; | |
}, | |
getPrismCodeLanguage: function(gistLang) { | |
var lang = gistLang.toLowerCase().replace(/#/, 'sharp').replace(/\+/g, 'p'); | |
if (Prism.languages[lang]) { | |
return Prism.languages[lang]; | |
} | |
console.log("CodeMana Error - Prism doesn't support the language: "+gistLang+". Help them and us out by adding it http://prismjs.com/"); | |
throw ({msg: "CodeMana Error - Prism doesn't support the language: " + gistLang}); | |
}, | |
getUserFromStorage: function(store) { | |
return JSON.parse(store.getItem('user')); | |
}, | |
saveUserToStorage: function(user, store) { | |
store.setItem('user', JSON.stringify(user)); | |
}, | |
createComment: function(gistId, commentId, filename, lineNumber, commentBody, commentUser, replyTo, showForm) { | |
return { | |
gistId: gistId, | |
id: commentId, | |
filename: filename, | |
line: parseInt(lineNumber, 10), | |
body: commentBody, | |
user: commentUser, | |
replyTo: replyTo, | |
showForm: showForm | |
}; | |
}, | |
createFile: function(name, parsedLines) { | |
return { | |
name: name, | |
parsedLines: parsedLines | |
}; | |
}, | |
parseComment: function(comment) { | |
//Annoyingly I couldn't get a single regex to separate everything out... | |
var split = comment.body.match(/(\S+)\s(.*)/); | |
var data = split[1].match(/http:\/\/codemana\.com\/(.*)#(.+)-L(\d+)/); | |
return data !== null ? this.createComment(data[1], comment.id, data[2], parseInt(data[3], 10), split[2], comment.user, 0, false) : null; | |
}, | |
parseFile: function(file) { | |
var lines = this.syntaxHighlight(file.content, file.language); | |
return this.createFile(file.filename, lines) | |
}, | |
createCommentLink: function(id, filename, lineNumber) { | |
return 'http://codemana.com/'+id+'#'+filename+'-L'+lineNumber; | |
} | |
}; |
http://localhost:3000/4c974cfae29d6a117b2a#App.js-L12 Replying to this one.
http://localhost:3000/4c974cfae29d6a117b2a#App.js-L17 Testing the new comment form edit
testing it now
http://localhost:3000/4c974cfae29d6a117b2a#App.js-L17 Replying to a comment
http://localhost:3000/4c974cfae29d6a117b2a#Config.js-L5 Make it scroll a little
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
http://localhost:3000/4c974cfae29d6a117b2a#App.js-L12 Testing some stuff