<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>JS Bin</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/mithril/0.1.28/mithril.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/radio/0.2.0/radio.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.8.0/ramda.min.js"></script>
</head>
<body>
<div class="container">
<div id="app"></div>
</div>
</body>
</html>
.line {
text-decoration: line-through;
}
.pointy {
cursor: pointer;
}
var Dialog = {}
Dialog.controller = function() {
var self = this
self.noopContentProvider = {
header: function() {},
body: function() {},
footer: function() {}
}
self.dialogContentProvider = self.noopContentProvider
self.visible = m.prop(false)
self.onclose = function() {
self.visible(false)
self.dialogContentProvider.onclose()
self.dialogContentProvider = self.noopContentProvider
}
self.onopen = function(dialogContentProvider) {
self.dialogContentProvider = dialogContentProvider
self.visible(true)
}
radio('dialog-open').subscribe(self.onopen)
radio('dialog-close').subscribe(self.onclose)
self.documentHeight = function() {
var doc = document.documentElement
return Math.max(doc.clientHeight, window.innerHeight || 0)
}
}
Dialog.view = function(ctrl) {
var smallClose = m('span',
{onclick: ctrl.onclose},
m.trust('×'))
var header = m('.modal-header', [
m('button[type=button].close', [smallClose]),
m('h4.modal-title', ctrl.dialogContentProvider.header())
])
var body = m('.modal-body', [
m('p', ctrl.dialogContentProvider.body())
])
var footer = m('.modal-footer', [
m('button[type=button].btn.btn-default',
{onclick: ctrl.onclose},
'Cancel'),
ctrl.dialogContentProvider.footer()
])
var modalClass = ctrl.visible() ? '.show' : ''
var dialog = m('.modal' + modalClass, [
m('.modal-dialog', [
m('.modal-content', [header, body, footer])
])
])
var maybeOverlay = ''
if (ctrl.visible()) {
maybeOverlay = m('.modal-backdrop.fade.in',
{style: 'height:' + ctrl.documentHeight() + 'px'})
}
return m('.row', [
maybeOverlay,
m('.col-lg-6.col-md-12', [dialog])
])
}
// -----------------------------------------------------------------
var TodoEditDialog = function(todo) {
var self = this
var todoCopy = {
name: m.prop(todo.name),
completed: m.prop(todo.completed)
}
var onSaveClicked = function() {
todo.name = todoCopy.name()
todo.completed = todoCopy.completed()
radio('dialog-close').broadcast()
}
self.header = function() {
return 'Editing: ' + todoCopy.name()
}
self.body = function() {
return [
m('input.form-control',
{onchange: m.withAttr('value', todoCopy.name),
value: todoCopy.name()}),
m('.checkbox', [
m('label', [
m('input[type=checkbox]',
{onchange: m.withAttr('checked', todoCopy.completed),
checked: todoCopy.completed()}),
'Completed'
])
])
]
}
self.footer = function() {
return m('button[type=button].btn.btn-primary',
{onclick: onSaveClicked},
'Save changes')
}
self.onclose = function() {
console.log('TodoEditDialog closed!')
}
}
// -----------------------------------------------------------------
var Todo = function(name) {
var self = this
self.id = 'todo_' + Todo._id++
self.name = name
self.completed = false
}
Todo._id = 0
Todo.controller = function() {
var self = this
self.todoList = [
new Todo('laundry'),
new Todo('fix car'),
new Todo('take out trash')
]
self.onTodoClicked = function() {
radio('dialog-open').broadcast(new TodoEditDialog(this))
}
}
Todo.view = function(ctrl) {
var mkTodo = function(t) {
return m('h4.pointy' + (t.completed ? '.line' : ''),
{key: t.id,
onclick: ctrl.onTodoClicked.bind(t)},
t.name)
}
return m('.todo-list', R.map(mkTodo, ctrl.todoList))
}
// -----------------------------------------------------------------
App = {}
App.controller = function() {
var self = this
self.dialogCtrl = new Dialog.controller()
self.todoCtrl = new Todo.controller()
}
App.view = function(ctrl) {
return m('div', [
Dialog.view(ctrl.dialogCtrl),
Todo.view(ctrl.todoCtrl)
])
}
m.module(document.getElementById('app'), App)
Output
300px
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |