Skip to content

Commit

Permalink
Showing 45 changed files with 2,394 additions and 0 deletions.
1 change: 1 addition & 0 deletions examples/leaderboard/.skybreak/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
local
6 changes: 6 additions & 0 deletions examples/leaderboard/.skybreak/packages
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Skybreak packages used by this project, one per line.
#
# 'skybreak add' and 'skybreak remove' will edit this file for you,
# but you can also edit it by hand.

jquery
56 changes: 56 additions & 0 deletions examples/leaderboard/leaderboard.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
body {
font-family: 'Helvetica Neue', Helvetica, Arial, san-serif;
margin: 50px auto;
width: 420px;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-o-user-select: none;
user-select: none;
}

.leaderboard {
margin: 50px 0px;
}

.player {
padding: 5px 10px;
}

.player .name {
display: inline-block;
width: 300px;
font-weight: 200;
font-size: 1.75em;
}

.player .score {
font-weight: bold;
display: inline-block;
width: 100px;
color: #777;
font-size: 2em;
text-align: right;
}

.player.selected {
background-color: yellow;
}

.player.selected .score {
color: black;
}

.details, .none {
font-weight: bold;
font-size: 2em;
border-style: dashed none none none;
border-color: #ccc;
border-width: 4px;
margin: 50px 10px;
padding: 10px 0px;
}

.none {
color: #777;
}
6 changes: 6 additions & 0 deletions examples/leaderboard/leaderboard.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<head>
<title>Leaderboard</title>
</head>

<body>
</body>
74 changes: 74 additions & 0 deletions examples/leaderboard/leaderboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Set up a collection to contain player information. On the server,
// it is backed by a MongoDB collection named "players."
Players = Sky.Collection("players");

/*** Client ***/

if (Sky.is_client) {
// Get the top 10 players from the server, updated continuously.
Sky.subscribe("top10");

// Start with no player selected.
Session.set("selected_player", null);

$(document).ready(function () {
// List the players by score. You can click to select a player.
var leaderboard_elt = $('<div class="leaderboard"></div>')[0];
Sky.ui.renderList(Players, leaderboard_elt, {
sort: {score: -1}, // sort from high to low score
render: function (player) {
if (Session.equals("selected_player", player._id))
var style = "player selected";
else
var style = "player";

return $('<div class="' + style + '">' +
'<div class="name">' + player.name + '</div>' +
'<div class="score">' + player.score + '</div></div>')[0];
},
events: {
"click": function () {
Session.set("selected_player", this._id);
}
}
});
$('body').append(leaderboard_elt);

// Details area, showing the currently selected player.
var details_elt = Sky.ui.render(function () {
var selected_player = Session.get("selected_player");
if (!selected_player)
return $('<div class="none">Click a player to select</div>')[0];

var player = Players.find(selected_player);
return $('<div class="details"><div class="name">' + player.name +
'</div><input type="button" value="Give 5 points"></div>')[0];
}, {
'click input': function () {
Players.update(Session.get("selected_player"), {$inc: {score: 5}});
}
});
$('body').append(details_elt);
});
}

/*** Server ***/

if (Sky.is_server) {
// Publish the top 10 players, live, to any client that wants them.
Sky.publish("top10", {collection: Players, sort: {score: -1},
limit: 10});

// On server startup, create some players if the database is empty.
Sky.startup(function () {
if (Players.find().length === 0) {
var names = ["Glinnes Hulden", "Shira Hulden", "Denzel Warhound",
"Lute Casagave", "Akadie", "Thammas, Lord Gensifer",
"Ervil Savat", "Duissane Trevanyi", "Sagmondo Bandolio",
"Rhyl Shermatz", "Yalden Wirp", "Tyran Lucho",
"Bump Candolf", "Wilmer Guff", "Carbo Gilweg"];
for (var i = 0; i < names.length; i++)
Players.insert({name: names[i], score: Math.floor(Math.random()*10)*5});
}
});
}
1 change: 1 addition & 0 deletions examples/todos/.skybreak/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
local
7 changes: 7 additions & 0 deletions examples/todos/.skybreak/packages
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Skybreak packages used by this project, one per line.
#
# 'skybreak add' and 'skybreak remove' will edit this file for you,
# but you can also edit it by hand.

jquery-layout
backbone
30 changes: 30 additions & 0 deletions examples/todos/client/reset.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, font, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td {
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-weight: inherit;
font-style: inherit;
font-size: 100%;
font-family: inherit;
vertical-align: baseline;
}
body {
line-height: 1;
color: black;
background: white;
}
ol, ul {
list-style: none;
}
a img {
border: none;
}

205 changes: 205 additions & 0 deletions examples/todos/client/todos.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 1.4em;
background: #eeeeee;
color: #333333;
}

.hidden {
display: none;
}

.ui-layout-north {
background: #dddddd;
}

#tag-filter {
margin: 8px;
}

#items-view {
margin: 10px;
}

#new-todo {
width: 466px;
font-size: 24px;
font-family: inherit;
line-height: 1.4em;
border: 0;
outline: none;
padding: 6px;
border: 1px solid #999999;
margin-left: 75px;
}

.ui-layout-west {
padding: 10px;
border-right: solid 1px #cccccc;
}

.ui-layout-south {
border-top: solid 1px black;
padding: 10px;
background: #cccccc;
}

#help p {
margin: 8px;
}

.ui-layout-center {
overflow: auto;
}

#lists .list {
margin: 2px;
font-weight: bold;
}

#lists .list-name .empty {
font-size: 0.9em;
font-style: italic;
}

#lists .editing input {
font-family: inherit;
margin: 0;
line-height: 1.6em;
border: 0;
outline: none;
padding: 10px 7px 0px 27px;
border: 1px solid #999999;
}
#lists .selected {
background-color: lightblue;
}

/* todo items */

#item-list {
margin-top: 10px;
}

#item-list li {
margin: 12px;
font-size: 24px;
line-height: 1.1em;
border-bottom: 1px solid #cccccc;
height: 50px;
}

#item-list li:after {
content: "\0020";
display: block;
height: 0;
clear: both;
overflow: hidden;
visibility: hidden;
}

#item-list .destroy {
float: left;
width: 20px;
height: 20px;
cursor: pointer;
margin-top: 12px;
margin-left: 5px;
}

#item-list li:hover .destroy {
background: url('/destroy.png') no-repeat 0 0;
}

#item-list li .destroy:hover {
background-position: 0 -20px;
}

#item-list .display {
float: left;
margin: 9px;
}

#item-list .check {
float: left;
margin: 9px;
}

#item-list .edit {
float: left;
}

#item-list .todo-text {
float: left;
}

#item-list li.editing {
padding: 0;
}

#item-list .editing input {
width: 444px;
font-size: 24px;
font-family: inherit;
margin-left: 38px;
line-height: 1.6em;
border: 0;
outline: none;
border: 1px solid #999999;
}

#item-list .done .todo-text {
text-decoration: line-through;
color: #777777;
}

#item-list .item-tags {
float: right;
}

/* tags */

.tag {
float: left;

color: black;
background: #aaaaaa;
font-size: 16px;
font-weight: bold;

cursor: pointer;

border: 1px solid black;
border-radius: 2px;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;

padding: 1px 3px 1px 3px;
margin: 4px;
}

.tag.addtag {
background: lightblue;
border: 1px dashed black;
}

.tag.edittag {
}

.tag.selected {
background: lightblue;
}

.tag .name {
float: left;
}

.tag .remove {
margin-top: 5px;
margin-left: 5px;
float: left;
width: 16px;
height: 16px;
background-image: url("/close_16.png");
}
130 changes: 130 additions & 0 deletions examples/todos/client/todos.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<head>
<title>Todos</title>
</head>

<body>

<div class="ui-layout-north">
{{> tag_filter}}
</div>

<div class="ui-layout-center">
{{> todos}}
</div>

<div class="ui-layout-west">
{{> create_list}}
{{> lists}}
</div>

<div class="ui-layout-south">
<div id="help">
<p>
To get started, create a new todo list in the left sidebar by
typing its name in the text box. Select a list by clicking on its
name, and rename by double clicking. The active list appears in
the main window pane. You can do the usual here: add items, check
them off as completed, and destroy items. You can also tag items
with one or more tags, by clicking the blue <b>Add new tag</b>
button to the right. All your in-use tags appear at the top. You
can filter the list items by selecting a tag, or click the
leftmost button to return to the full list.
</p>

<p>
Inspired by Backbone's
<a href="http://documentcloud.github.com/backbone/examples/todos/index.html">Todo Demo</a>,
with credit to
<a href="http://jgn.me/">J&eacute;r&ocirc;me Gravel-Niquet</a>.
</p>
</div>
</div>

</body>

<template name="tag_filter">
<div id="tag-filter" class="tag-list">
{{#each tags}}
{{> tag_item}}
{{/each}}
</div>
</template>

<template name="tag_item">
<div class="tag {{selected}}">
{{tag_text}}
</div>
</template>

<template name="lists">
<div id="lists">
{{#each lists}}
{{> list_item}}
{{/each}}
</div>
</template>

<template name="list_item">
<div class="list {{selected}}">
<div class="display {{display_hidden}}">
<div class="list-name {{name_class}}">
{{name}}
</div>
</div>
<div class="edit {{edit_hidden}}">
<input class="list-name-input" type="text" value="" />
</div>
</div>
</template>

<template name="create_list">
<div id="createList">
<input type="text" id="new-list" placeholder="New list" />
</div>
</template>

<template name="todos">
<div id="items-view" class="{{hidden_class}}">
<input type="text" id="new-todo" placeholder="New item" />
{{> todo_list}}
</div>
</template>

<template name="todo_list">
<ul id="item-list">
{{#each todos}}
{{> todo_item}}
{{/each}}
</ul>
</template>

<template name="todo_item">
<li class="todo {{done_class}}">
<div class="destroy"></div>
<div class="display {{display_hidden}}">
<input class="check" type="checkbox" {{done_checkbox}} />
<div class="todo-text">{{text}}</div>
</div>
<div class="edit {{edit_hidden}}">
<input class="todo-input" type="text" value="" />
</div>
<div class="item-tags">
{{#each tag_objs}}
{{> todo_tag}}
{{/each}}
<div class="tag addtag {{addtag_hidden}}">
Add new tag
</div>
<div class="tag edittag {{edittag_hidden}}">
<input type="text" value="" />
</div>
</div>
</li>
</template>

<template name="todo_tag">
<div class="tag">
<div class="name">{{tag}}</div>
<div class="remove"></div>
</div>
</template>
249 changes: 249 additions & 0 deletions examples/todos/client/todos.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
Session.set('list_id', null);
Session.set('tag_filter', null);
Session.set('editing_addtag', null);
Session.set('editing_listname', null);
Session.set('editing_itemname', null);

Sky.subscribe('lists', {}, function () {
// Once the lists have loaded, select the first one.
if (!Session.get('list_id')) {
var lists = Lists.find({}, {sort: {name: 1}, limit: 1});
if (lists.length)
Router.setList(lists[0]._id);
}
});

Sky.autosubscribe(function () {
var list_id = Session.get('list_id');
if (list_id)
Sky.subscribe('todos', {list: list_id});
});

////////// Tag Filter //////////

Template.tag_filter.tags = function () {
// Pick out the unique tags from all tasks.
var tags = _(Todos.find())
.chain().pluck('tags').compact().flatten().sort().uniq(true).value()
.map(function (tag) { return {tag: tag} });

tags.unshift({tag: null}); // "show all" button
return tags;
};

Template.tag_item.selected = function () {
return Session.equals('tag_filter', this.tag) ? 'selected' : '';
};

Template.tag_item.tag_text = function () {
return this.tag || "Show all";
};

Template.tag_item.events = {
'mousedown': function () {
if (Session.equals('tag_filter', this.tag))
Session.set('tag_filter', null);
else
Session.set('tag_filter', this.tag);
}
};

////////// Lists //////////

Template.lists.lists = function () {
return Lists.find({}, {sort: {name: 1}});
};

Template.list_item.selected = function () {
return Session.equals('list_id', this._id) ? 'selected' : '';
};

Template.list_item.name_class = function () {
return this.name ? '' : 'empty';
};

Template.list_item.display_hidden = function () {
return Session.equals('editing_listname', this._id) ? 'hidden' : '';
};

Template.list_item.edit_hidden = function () {
return Session.equals('editing_listname', this._id) ? '' : 'hidden';
};

Template.list_item.events = {
'mousedown': function (evt) { // select list
Router.setList(this._id);
},
'dblclick': function (evt) { // start editing list name
var top = $(evt.target).parents('.list');
Session.set('editing_listname', this._id);
top.find('.edit input').val(this.name).focus().select();
},
'blur .edit input, keypress .edit input': function (evt) {
// rename list
if (evt.type === "blur" || evt.keyCode === 13) {
var target = $(evt.target);
var val = target.val();
if (val)
Lists.update(this._id, {$set: {name: val}});
Session.set('editing_listname', null);
}
}
};

Template.create_list.events = {
'keypress #new-list': function (evt) {
var target = $(evt.target);
var text = target.val();
if (evt.keyCode === 13 && text) {
var list = Lists.insert({name: text});
Router.setList(list._id);
target.val('');
}
}
};

////////// Todos //////////

Template.todos.hidden_class = function () {
return Session.get('list_id') ? '' : 'hidden';
};

Template.todos.events = {
'keypress #new-todo': function (evt) {
var target = $(evt.target);
var text = target.val();
if (evt.keyCode === 13 && text) {
var tag = Session.get('tag_filter');
Todos.insert({
text: text,
list_id: Session.get('list_id'),
done: false,
timestamp: (new Date()).getTime(),
tags: tag ? [tag] : []
});
target.val('');
}
}
};

Template.todo_list.todos = function () {
var list_id = Session.get('list_id');
if (!list_id)
return {};

var sel = {list_id: list_id};
var tag_filter = Session.get('tag_filter');
if (tag_filter)
sel.tags = tag_filter;

return Todos.find(sel, {sort: {timestamp: 1}});
};

Template.todo_item.tag_objs = function () {
var todo_id = this._id;
return _.map(this.tags || [], function (tag) {
return {todo_id: todo_id, tag: tag};
});
};

Template.todo_item.done_class = function () {
return this.done ? 'done' : '';
};

Template.todo_item.done_checkbox = function () {
return this.done ? 'checked="checked"' : '';
};

Template.todo_item.display_hidden = function () {
return Session.equals('editing_itemname', this._id) ? 'hidden' : '';
};

Template.todo_item.edit_hidden = function () {
return Session.equals('editing_itemname', this._id) ? '' : 'hidden';
};

Template.todo_item.addtag_hidden = function () {
return Session.equals('editing_addtag', this._id) ? 'hidden' : '';
};

Template.todo_item.edittag_hidden = function () {
return Session.equals('editing_addtag', this._id) ? '' : 'hidden';
};

Template.todo_item.events = {
'click .check': function () {
Todos.update(this._id, {$set: {done: !this.done}});
},

'click .destroy': function () {
Todos.remove(this._id);
},

'click .addtag': function (evt) {
var top = $(evt.target).closest('li.todo');
Session.set('editing_addtag', this._id);
top.find('.edittag input').focus();
},

'dblclick': function (evt) {
var top = $(evt.target).closest('li.todo');
Session.set('editing_itemname', this._id);
top.find('.edit input').val(this.text).focus().select();
},

'blur .edit input, keypress .edit input': function (evt) {
if (evt.type === "blur" || evt.keyCode === 13) {
var target = $(evt.target);
if (target.val())
Todos.update(this._id, {$set: {text: target.val()}});
Session.set('editing_itemname', null);
}
},

'blur .edittag input, keypress .edittag input': function (evt) {
if (evt.type === "blur" || evt.keyCode === 13) {
var target = $(evt.target);
if (target.val())
Todos.update(this._id, {$addToSet: {tags: target.val()}});
Session.set('editing_addtag', null);
}
}
};

Template.todo_tag.events = {
'click .remove': function (evt) {
var tag = this.tag;
var id = this.todo_id;

$(evt.target).parent().fadeOut(500, function () {
Todos.update({_id: id}, {$pull: {tags: tag}});
});
}
};

////////// Tracking selected list in URL //////////

var TodosRouter = Backbone.Router.extend({
routes: {
":list_id": "main"
},
main: function (list_id) {
Session.set("list_id", list_id);
},
setList: function (list_id) {
this.navigate(list_id, true);
}
});

Router = new TodosRouter;

////////// Startup //////////

$(function () {
$('body').layout({north__minSize: 50,
spacing_open: 10,
north__fxSettings: { direction: "vertical" }});

Backbone.history.start({pushState: true});
});
19 changes: 19 additions & 0 deletions examples/todos/model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Lists = Sky.Collection("lists");

Todos = Sky.Collection("todos");

/* Schema support coming soon!
Lists.schema({text: String});
Todos.schema({text: String,
done: Boolean,
tags: [String]});
*/

Sky.publish('lists');
Sky.publish('todos', {
selector: function (params) {
return {list_id: params.list};
}
});
Binary file added examples/todos/public/close_16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/todos/public/destroy.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 51 additions & 0 deletions examples/todos/server/bootstrap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// if the database is empty on server start, create some sample data.
Sky.startup(function () {
if (Lists.find().length === 0) {
var data = [
{name: "* Seven Principles *",
contents: [
["Data on the Wire", "Simplicity", "Better UX", "Fun"],
["One Language", "Simplicity", "Fun"],
["Database Everywhere", "Simplicity"],
["Latency Compensation", "Better UX"],
["Full Stack Reactivity", "Better UX", "Fun"],
["Embrace the Ecosystem", "Fun"],
["Simplicity Equals Productivity", "Simplicity", "Fun"]
]
},
{name: "Next-gen frameworks",
contents: [
["Skybreak"],
["Derby + Racer"],
["Capsule + Thoonk from &yet"],
["Flatiron from Nodejitsu"],
["Socketstream"],
["Sencha.io Data"]
]
},
{name: "Client-side MVC options",
contents: [
["Backbone", "Minimal"],
["Spine", "Minimal", "Coffeescript"],
["Angular", "Minimal", "Templating"],
["Batman", "Minimal", "Coffeescript"],
["Knockout", "Minimal", "Templating"],
["Sproutcore", "Widgets", "Templating"],
["Sencha", "Widgets", "GUI builder", "Mobile"],
["Kendo UI", "Widgets", "Mobile"],
["boltjs", "Minimal"]
]
}
];

for (var i = 0; i < data.length; i++) {
var list_id = Lists.insert({name: data[i].name})._id;
for (var j = 0; j < data[i].contents.length; j++) {
var info = data[i].contents[j];
Todos.insert({list_id: list_id,
text: info[0],
tags: info.slice(1)});
}
}
}
});
1 change: 1 addition & 0 deletions examples/unfinished/azrael/.skybreak/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
local
8 changes: 8 additions & 0 deletions examples/unfinished/azrael/.skybreak/packages
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Skybreak packages used by this project, one per line.
#
# 'skybreak add' and 'skybreak remove' will edit this file for you,
# but you can also edit it by hand.

underscore
jquery
jquery-layout
26 changes: 26 additions & 0 deletions examples/unfinished/azrael/client/azrael.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#room-list .room.selected {
color: white;
background-color: black;
}

.room .name {
display: inline;
}

.room .delete {
float: right;
display: none;
}

.room:hover .delete {
display: block;
}

.add-room {
margin-top: 20px;
font-style: italic;
}

.add-room:hover {
text-decoration: underline;
}
61 changes: 61 additions & 0 deletions examples/unfinished/azrael/client/azrael.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<body>

<div class="ui-layout-center" id="chat-view">
{{> center_pane }}
</div>
<div class="ui-layout-east">East</div>
<div class="ui-layout-west">
{{> room_list}}
{{> add_room}}
</div>
<div class="ui-layout-north">Azrael</div>

</body>

<template name="room_list">
<div id="room-list">
{{#each rooms}}
{{> room}}
{{/each}}
</div>
</template>

<template name="add_room">
<div class="add-room">
Create new room
</div>
</template>

<template name="room">
<div class="room {{maybe_selected}}">
{{#if editing}}
<input id="room_name_input" value="{{name}}">
{{else}}
<div class="name">{{name}}</div>
<div class="delete">(x)</div>
{{/if}}
</div>
</template>

<template name="center_pane">
<div id="center-pane">
{{#if any_room_selected}}
<div id="chat">
{{#each messages}}
{{> chat_message}}
{{else}}
No chat yet!
{{/each}}
</div>
<input id="chat-entry">
{{else}}
No room selected
{{/if}}
</div>
</template>

<template name="chat_message">
<div>
{{username}}: {{message}}
</div>
</template>
124 changes: 124 additions & 0 deletions examples/unfinished/azrael/client/azrael.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
Sky.subscribe('rooms');

Session.set('current_room', null);
Session.set('editing_room_name', false);

Sky.autosubscribe(function () {
var room_id = Session.get('current_room');
if (room_id) Sky.subscribe('room-detail', {room: room_id});
});

// XXX would be nice to eliminate this function and have people just
// call Session.set("current_room", foo) directly instead
var selectRoom = function (room_id) {
// XXX pushstate
var room = Rooms.find(room_id);
Session.set('current_room', room_id);
};

$(document).ready(function () {
$('body').layout({applyDefaultStyles: true})
});

Template.room_list.rooms = function () {
// XXX it would be nice if this were find instead of findLive (ie,
// if they were unified in some sane way)
return Rooms.findLive({}, {sort: {name: 1}});
};

Template.add_room.events = {
'click': function () {
// XXX should put up dialog to get name
// XXX should support automatically set created/updated timestamps
var obj = Rooms.insert({name: "New room",
// XXX horrid syntax
created: (new Date()).getTime()});
selectRoom(obj._id);
// XXX XXX XXX this fails to work -- it leaves edit mode after
// 1RTT. what happens is, the server echos the insert back to us,
// and that is currently wired up to trigger a changed event on
// the findlive, which redraws the element, which triggers blur,
// which causes us to set editing_room_name to false.
//
// one option is to have the rendering function (maybe in a
// post-render routine?) decide if it currently wants
// focus. (should that be within the recomputation envelope, I
// wonder?)
//
// another is to suppress blur on rerender. probably the only
// principled way to do this is to narrow the scope of the
// rerender to not include the <input>.
Session.set('editing_room_name', true);
Sky.ui.focus('#room_name_input');
}
};

Template.room.events = {
'mousedown': function (evt) {
selectRoom(this._id);
},
'dblclick': function (evt) {
Session.set('editing_room_name', true);
// XXX XXX doesn't generalize.. the element might very reasonably
// not have a unique id. may need a different strategy..
Sky.ui.focus('#room_name_input');
},
'blur input': function (evt) {
Session.set('editing_room_name', false);
},
'keypress input': function (evt) {
// XXX should really have a binding/validator-based pattern
// XXX check to see this pattern works if you are saving
// continuously (on every keystroke)
var value = $(evt.target).val();
if (evt.which === 13 && value.length)
Rooms.update(this._id, {$set: {name: value}});
if (evt.which === 13 || evt.which === 27)
Session.set('editing_room_name', false);
},
// If you make this event be click (rather than mousedown), then
// delete doesn't work if the room isn't already selected. what
// happens is, the mousedown triggers the selection, which redraws
// the room, meaning that the elements are replaced out from under
// the event, and the click event is lost.. bleh. needs
// reconsideration.
'mousedown .delete': function (evt) {
Rooms.remove('rooms', this._id);
Session.set('current_room', null);
},
};

Template.room.editing = function (options) {
// Check current_room first, before editing_room_name, to minimize
// number of redraws
return (Session.equals('current_room', this._id) &&
Session.equals('editing_room_name', true));
};

Template.room.maybe_selected = function () {
return Session.equals('current_room', this._id) ? "selected" : "";
};

Template.center_pane.messages = function () {
return Chat.findLive({room: Session.get("current_room")},
{sort: {created: 1}});
};

Template.center_pane.any_room_selected = function () {
return !Session.equals('current_room', null);
};

Template.center_pane.events = {
'keydown #chat-entry': function (evt) {
if (evt.which === 13) {
var room_id = Session.get('current_room');
if (!room_id)
return;

Chat.insert({room: room_id, message: $(evt.target).val(),
username: "someone",
created: (new Date()).getTime()});
$(evt.target).val('');
}
}
};
18 changes: 18 additions & 0 deletions examples/unfinished/azrael/model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// XXX it is actually very dangerous to store times as Number. use
// Date type once it's implemented in minimongo
Rooms = Sky.Collection("rooms");
Rooms.schema({name: String, created: Number});

Chat = Sky.Collection("chat");
Chat.schema({room: String, message: String,
username: String, created: Number});

Sky.publish('rooms');

// XXX should limit to just a certain amount of recent chat ..
Sky.publish('room-detail', {
collection: Chat,
selector: function (params) {
return {room: params.room};
}
});
1 change: 1 addition & 0 deletions examples/unfinished/coffeeless/.skybreak/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
local
7 changes: 7 additions & 0 deletions examples/unfinished/coffeeless/.skybreak/packages
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Skybreak packages used by this project, one per line.
#
# 'skybreak add' and 'skybreak remove' will edit this file for you,
# but you can also edit it by hand.

less
coffeescript
8 changes: 8 additions & 0 deletions examples/unfinished/coffeeless/client/coffeeless.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Sky.subscribe 'presses'

Template.button_demo.events =
'click input': ->
console.log "press"
Presses.insert {}

Template.button_demo.press_count = -> Presses.find({}).length
36 changes: 36 additions & 0 deletions examples/unfinished/coffeeless/client/coffeeless.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<head>
<title>coffeeless</title>
</head>

<body>
<h1>coffeeless</h1>

Welcome to Skybreak! Here is a button for you to press.

{{> button_demo }}

<!-- Skybreak is an incredibly fast way to create incredibly great
websites
- in pure Javascript
- using your tools of choice, from jQuery and backbone to
Coffeescript and handlebars
-->

<img class="banner" src="/4201645142_ec2e3bb3f8_b.jpg">

</body>

<template name="button_demo">
<div>
<input type="button" value="Button"><br>

This delightful button has

{{#if never_pressed}}
never been pressed! Won't you be the first?
{{else}}
been pressed {{press_count}} times.
{{/if}}
</div>
</template>
12 changes: 12 additions & 0 deletions examples/unfinished/coffeeless/client/coffeeless.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@red: #842210;

h1 {
color: @red;
}

img {
display: block;
position: absolute;
top: 0px;
right: 0px;
}
4 changes: 4 additions & 0 deletions examples/unfinished/coffeeless/model.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
root = exports ? this # export Presses globally.
root.Presses = Sky.Collection 'presses'

Sky.publish 'presses'
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions examples/unfinished/todos-backbone/.skybreak/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
local
7 changes: 7 additions & 0 deletions examples/unfinished/todos-backbone/.skybreak/packages
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Skybreak packages used by this project, one per line.
#
# 'skybreak add' and 'skybreak remove' will edit this file for you,
# but you can also edit it by hand.

jquery
backbone
75 changes: 75 additions & 0 deletions examples/unfinished/todos-backbone/body.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<head>
<title>Todos</title>
</head>

<body>

<!-- Todo App Interface -->

<div id="todoapp">

<div class="title">
<h1>Todos</h1>
</div>

<div class="content">

<div id="create-todo">
<input id="new-todo" placeholder="What needs to be done?" type="text" />
<span class="ui-tooltip-top" style="display:none;">Press Enter to save this task</span>
</div>

<div id="todos">
<ul id="todo-list"></ul>
</div>

<div id="todo-stats"></div>

</div>

</div>

<ul id="instructions">
<li>Double-click to edit a todo.</li>
<li><a href="../../docs/todos.html">View the annotated source.</a></li>
</ul>

<div id="credits">
Created by
<br />
<a href="http://jgn.me/">J&eacute;r&ocirc;me Gravel-Niquet</a>
</div>

<!-- Templates -->

<script type="text/template" id="item-template">
<div class="todo <%= done ? 'done' : '' %>">
<div class="display">
<input class="check" type="checkbox" <%= done ? 'checked="checked"' : '' %> />
<div class="todo-text"></div>
<span class="todo-destroy"></span>
</div>
<div class="edit">
<input class="todo-input" type="text" value="" />
</div>
</div>
</script>

<script type="text/template" id="stats-template">
<% if (total) { %>
<span class="todo-count">
<span class="number"><%= remaining %></span>
<span class="word"><%= remaining == 1 ? 'item' : 'items' %></span> left.
</span>
<% } %>
<% if (done) { %>
<span class="todo-clear">
<a href="#">
Clear <span class="number-done"><%= done %></span>
completed <span class="word-done"><%= done == 1 ? 'item' : 'items' %></span>
</a>
</span>
<% } %>
</script>

</body>
201 changes: 201 additions & 0 deletions examples/unfinished/todos-backbone/client/todos.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// An example Backbone application contributed by
// [Jérôme Gravel-Niquet](http://jgn.me/). This demo uses a simple
// [LocalStorage adapter](backbone-localstorage.html)
// to persist Backbone models within your browser.

// Load the application once the DOM is ready, using `jQuery.ready`:
$(function(){
// ask for all the todos in my cache
Sky.subscribe('todos');

// helper functions

function all () {
return Todos.find();
};

function all_done () {
return Todos.find({done: true});
};

function all_remaining () {
return Todos.find({done: false});
};

function nextOrder () {
var todos = Todos.find({}, {sort: {order: -1}, limit: 1});
return todos[0] ? todos[0].order + 1 : 1;
};

// Todo Item View
// --------------

// The DOM element for a todo item...
window.TodoView = Backbone.View.extend({

//... is a list tag.
tagName: "li",

// Cache the template function for a single item.
template: _.template($('#item-template').html()),

// The DOM events specific to an item.
events: {
"click .check" : "toggleDone",
"dblclick div.todo-text" : "edit",
"click span.todo-destroy" : "clear",
"keypress .todo-input" : "updateOnEnter"
},

// Re-render the contents of the todo item.
render: function() {
$(this.el).html(this.template(this.model));
this.setText();
return this;
},

// To avoid XSS (not that it would be harmful in this particular app),
// we use `jQuery.text` to set the contents of the todo item.
setText: function() {
this.$('.todo-text').text(this.model.text);
this.input = this.$('.todo-input');
this.input.bind('blur', _.bind(this.close, this)).val(this.model.text);
},

// Toggle the `"done"` state of the object.
toggleDone: function() {
Todos.update(this.model._id, {$set: {done: !this.model.done}});
},

// Switch this view into `"editing"` mode, displaying the input field.
edit: function() {
$(this.el).addClass("editing");
this.input.focus();
},

// Close the `"editing"` mode, saving changes to the todo.
// findLive callback will update this view.
close: function() {
Todos.update(this.model._id, {$set: {text: this.input.val()}});
$(this.el).removeClass("editing");
},

// If you hit `enter`, we're through editing the item.
updateOnEnter: function(e) {
if (e.keyCode == 13) this.close();
},

// Remove this view from the DOM.
remove: function() {
$(this.el).remove();
},

// destroy the todo object. the findLive callback will g/c this view.
clear: function() {
Todos.remove(this.model._id);
}
});

// The Application
// ---------------

// Our overall **AppView** is the top-level piece of UI.
window.AppView = Backbone.View.extend({

// Instead of generating a new element, bind to the existing skeleton of
// the App already present in the HTML.
el: $("#todoapp"),

// Our template for the line of statistics at the bottom of the app.
statsTemplate: _.template($('#stats-template').html()),

// Delegated events for creating new items, and clearing done ones.
events: {
"keypress #new-todo": "createOnEnter",
"keyup #new-todo": "showTooltip",
"click .todo-clear a": "clearCompleted"
},

todos: [],

// At initialization we bind to the relevant events on the `Todos`
// collection, when items are added or changed. Kick things off by
// loading any preexisting todos that might be saved in *localStorage*.
initialize: function() {
var self = this;

this.input = this.$("#new-todo");

// spin up the live query. ignore the return value since we never
// stop the query.
Todos.findLive({}, {
added: function (obj, before_idx) {
// add a view node to the DOM
var view = new TodoView({model: obj});
self.todos.splice(before_idx, 0, view);
self.$("#todo-list").append(view.render().el);
self.render();
},
removed: function (id, at_idx) {
// remove the view node from the DOM
var view = self.todos.splice(at_idx, 1);
view[0].remove();
self.render();
},
changed: function (obj, at_idx) {
// update obj in existing view and rerender
self.todos[at_idx].model = obj;
self.todos[at_idx].render();
self.render();
},
moved: function (old_idx, new_idx) {
// unimplemented -- items don't ever move
},
sort: {'order': 1}
});
},

// Re-rendering the App just means refreshing the statistics -- the rest
// of the app doesn't change.
render: function() {
console.log("RENDER", all().length, all_done().length, all_remaining().length);

this.$('#todo-stats').html(this.statsTemplate({
total: all().length,
done: all_done().length,
remaining: all_remaining().length
}));
},

// If you hit return in the main input field, and there is text to save,
// create new **Todo** model.
createOnEnter: function(e) {
var text = this.input.val();
if (!text || e.keyCode != 13) return;
Todos.insert({text: text, done: false, order: nextOrder()});
this.input.val('');
},

// Clear all done todo items, destroying their models.
clearCompleted: function() {
_.each(all_done(), function (todo) { Todos.remove(todo._id); });
return false;
},

// Lazily show the tooltip that tells you to press `enter` to save
// a new todo item, after one second.
showTooltip: function(e) {
var tooltip = this.$(".ui-tooltip-top");
var val = this.input.val();
tooltip.fadeOut();
if (this.tooltipTimeout) clearTimeout(this.tooltipTimeout);
if (val == '' || val == this.input.attr('placeholder')) return;
var show = function(){ tooltip.show().fadeIn(); };
this.tooltipTimeout = _.delay(show, 1000);
}
});

// Finally, we kick things off by creating the **App**.
window.App = new AppView;

});
4 changes: 4 additions & 0 deletions examples/unfinished/todos-backbone/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Todos = Sky.Collection("todos");
Todos.schema({text: String, done: Boolean, order: Number});

Sky.publish('todos');
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
311 changes: 311 additions & 0 deletions examples/unfinished/todos-backbone/todos.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,311 @@
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, font, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td {
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-weight: inherit;
font-style: inherit;
font-size: 100%;
font-family: inherit;
vertical-align: baseline;
}
body {
line-height: 1;
color: black;
background: white;
}
ol, ul {
list-style: none;
}
a img {
border: none;
}

html {
background: #eeeeee;
}
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 1.4em;
background: #eeeeee;
color: #333333;
}

#todoapp {
width: 480px;
margin: 0 auto 40px;
background: white;
padding: 20px;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 5px 6px 0;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 5px 6px 0;
-o-box-shadow: rgba(0, 0, 0, 0.2) 0 5px 6px 0;
box-shadow: rgba(0, 0, 0, 0.2) 0 5px 6px 0;
}
#todoapp h1 {
font-size: 36px;
font-weight: bold;
text-align: center;
padding: 20px 0 30px 0;
line-height: 1;
}

#create-todo {
position: relative;
}
#create-todo input {
width: 466px;
font-size: 24px;
font-family: inherit;
line-height: 1.4em;
border: 0;
outline: none;
padding: 6px;
border: 1px solid #999999;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
-o-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
}
#create-todo input::-webkit-input-placeholder {
font-style: italic;
}
#create-todo span {
position: absolute;
z-index: 999;
width: 170px;
left: 50%;
margin-left: -85px;
}

#todo-list {
margin-top: 10px;
}
#todo-list li {
padding: 12px 20px 11px 0;
position: relative;
font-size: 24px;
line-height: 1.1em;
border-bottom: 1px solid #cccccc;
}
#todo-list li:after {
content: "\0020";
display: block;
height: 0;
clear: both;
overflow: hidden;
visibility: hidden;
}
#todo-list li.editing {
padding: 0;
border-bottom: 0;
}
#todo-list .editing .display,
#todo-list .edit {
display: none;
}
#todo-list .editing .edit {
display: block;
}
#todo-list .editing input {
width: 444px;
font-size: 24px;
font-family: inherit;
margin: 0;
line-height: 1.6em;
border: 0;
outline: none;
padding: 10px 7px 0px 27px;
border: 1px solid #999999;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
-o-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
}
#todo-list .check {
position: relative;
top: 9px;
margin: 0 10px 0 7px;
float: left;
}
#todo-list .done .todo-text {
text-decoration: line-through;
color: #777777;
}
#todo-list .todo-destroy {
position: absolute;
right: 5px;
top: 14px;
display: none;
cursor: pointer;
width: 20px;
height: 20px;
background: url(destroy.png) no-repeat 0 0;
}
#todo-list li:hover .todo-destroy {
display: block;
}
#todo-list .todo-destroy:hover {
background-position: 0 -20px;
}

#todo-stats {
*zoom: 1;
margin-top: 10px;
color: #777777;
}
#todo-stats:after {
content: "\0020";
display: block;
height: 0;
clear: both;
overflow: hidden;
visibility: hidden;
}
#todo-stats .todo-count {
float: left;
}
#todo-stats .todo-count .number {
font-weight: bold;
color: #333333;
}
#todo-stats .todo-clear {
float: right;
}
#todo-stats .todo-clear a {
color: #777777;
font-size: 12px;
}
#todo-stats .todo-clear a:visited {
color: #777777;
}
#todo-stats .todo-clear a:hover {
color: #336699;
}

#instructions {
width: 520px;
margin: 10px auto;
color: #777777;
text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0;
text-align: center;
}
#instructions a {
color: #336699;
}

#credits {
width: 520px;
margin: 30px auto;
color: #999;
text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0;
text-align: center;
}
#credits a {
color: #888;
}


/*
* François 'cahnory' Germain
*/
.ui-tooltip, .ui-tooltip-top, .ui-tooltip-right, .ui-tooltip-bottom, .ui-tooltip-left {
color:#ffffff;
cursor:normal;
display:-moz-inline-stack;
display:inline-block;
font-size:12px;
font-family:arial;
padding:.5em 1em;
position:relative;
text-align:center;
text-shadow:0 -1px 1px #111111;
-webkit-border-top-left-radius:4px ;
-webkit-border-top-right-radius:4px ;
-webkit-border-bottom-right-radius:4px ;
-webkit-border-bottom-left-radius:4px ;
-khtml-border-top-left-radius:4px ;
-khtml-border-top-right-radius:4px ;
-khtml-border-bottom-right-radius:4px ;
-khtml-border-bottom-left-radius:4px ;
-moz-border-radius-topleft:4px ;
-moz-border-radius-topright:4px ;
-moz-border-radius-bottomright:4px ;
-moz-border-radius-bottomleft:4px ;
border-top-left-radius:4px ;
border-top-right-radius:4px ;
border-bottom-right-radius:4px ;
border-bottom-left-radius:4px ;
-o-box-shadow:0 1px 2px #000000, inset 0 0 0 1px #222222, inset 0 2px #666666, inset 0 -2px 2px #444444;
-moz-box-shadow:0 1px 2px #000000, inset 0 0 0 1px #222222, inset 0 2px #666666, inset 0 -2px 2px #444444;
-khtml-box-shadow:0 1px 2px #000000, inset 0 0 0 1px #222222, inset 0 2px #666666, inset 0 -2px 2px #444444;
-webkit-box-shadow:0 1px 2px #000000, inset 0 0 0 1px #222222, inset 0 2px #666666, inset 0 -2px 2px #444444;
box-shadow:0 1px 2px #000000, inset 0 0 0 1px #222222, inset 0 2px #666666, inset 0 -2px 2px #444444;
background-color:#3b3b3b;
background-image:-moz-linear-gradient(top,#555555,#222222);
background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#555555),color-stop(1,#222222));
filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#555555,EndColorStr=#222222);
-ms-filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#555555,EndColorStr=#222222);
}
.ui-tooltip:after, .ui-tooltip-top:after, .ui-tooltip-right:after, .ui-tooltip-bottom:after, .ui-tooltip-left:after {
content:"\25B8";
display:block;
font-size:2em;
height:0;
line-height:0;
position:absolute;
}
.ui-tooltip:after, .ui-tooltip-bottom:after {
color:#2a2a2a;
bottom:0;
left:1px;
text-align:center;
text-shadow:1px 0 2px #000000;
-o-transform:rotate(90deg);
-moz-transform:rotate(90deg);
-khtml-transform:rotate(90deg);
-webkit-transform:rotate(90deg);
width:100%;
}
.ui-tooltip-top:after {
bottom:auto;
color:#4f4f4f;
left:-2px;
top:0;
text-align:center;
text-shadow:none;
-o-transform:rotate(-90deg);
-moz-transform:rotate(-90deg);
-khtml-transform:rotate(-90deg);
-webkit-transform:rotate(-90deg);
width:100%;
}
.ui-tooltip-right:after {
color:#222222;
right:-0.375em;
top:50%;
margin-top:-.05em;
text-shadow:0 1px 2px #000000;
-o-transform:rotate(0);
-moz-transform:rotate(0);
-khtml-transform:rotate(0);
-webkit-transform:rotate(0);
}
.ui-tooltip-left:after {
color:#222222;
left:-0.375em;
top:50%;
margin-top:.1em;
text-shadow:0 -1px 2px #000000;
-o-transform:rotate(180deg);
-moz-transform:rotate(180deg);
-khtml-transform:rotate(180deg);
-webkit-transform:rotate(180deg);
}
1 change: 1 addition & 0 deletions examples/unfinished/todos-underscore/.skybreak/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
local
8 changes: 8 additions & 0 deletions examples/unfinished/todos-underscore/.skybreak/packages
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Skybreak packages used by this project, one per line.
#
# 'skybreak add' and 'skybreak remove' will edit this file for you,
# but you can also edit it by hand.

jquery
jquery-layout
jquery-history
98 changes: 98 additions & 0 deletions examples/unfinished/todos-underscore/body.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<head>
<title>Todos</title>
</head>

<body>

<div class="ui-layout-north">
<div id="tag-filter" class="tag-list">
</div>
</div>

<div class="ui-layout-center">
<div id="items-view">
<input type="text" id="new-todo" placeholder="New item, press enter to save." />
<ul id="item-list"></ul>
</div>
</div>

<div class="ui-layout-west">
<div id="createList">
<input type="text" id="new-list" placeholder="New list name." />
</div>

<div id="lists"></div>
</div>

<div class="ui-layout-south">
<div id="help">
<p>
To get started, create a new todo list in the left sidebar by
typing its name in the text box. Select a list by clicking on its
name, and rename by double clicking. The active list appears in
the main window pane. You can do the usual here: add items, check
them off as completed, and destroy items. You can also tag items
with one or more tags, by clicking the blue <b>Add new tag</b>
button to the right. All your in-use tags appear at the top. You
can filter the list items by selecting a tag, or click the
leftmost button to return to the full list.
</p>

<p>
Inspired by Backbone's
<a href="http://documentcloud.github.com/backbone/examples/todos/index.html">Todo Demo</a>,
with credit to
<a href="http://jgn.me/">J&eacute;r&ocirc;me Gravel-Niquet</a>.
</p>
</div>
</div>

<!-- underscore templates -->

<script type="text/template" id="tag-filter-template">
<div class="tag <%= Session.equals('tag_filter', tag) ? 'selected' : '' %>">
<%= tag || "Show all todo items" %>
</div>
</script>

<script type="text/template" id="list-template">
<div class="list <%= Session.equals('list_id', _id) ? 'selected' : '' %>">
<div class="display">
<div class="list-name">
<%= name || '<div class="empty">empty</i>' %>
</div>
</div>
<div class="edit">
<input class="list-name-input" type="text" value="" />
</div>
</div>
</script>

<script type="text/template" id="item-template">
<li class="todo <%= typeof(done) !== 'undefined' && done ? 'done' : '' %>">
<div class="destroy"></div>
<div class="display">
<input class="check" type="checkbox" <%= typeof(done) !== 'undefined' && done ? 'checked="checked"' : '' %> />
<div class="todo-text"></div>
</div>
<div class="edit">
<input class="todo-input" type="text" value="" />
</div>
<div class="item-tags">
<% _.each(typeof(tags) === 'undefined' ? [] : tags, function (tag) { %>
<div class="tag">
<div class="name"><%= tag %></div>
<div class="remove" name="<%= tag %>"></div>
</div>
<% }); %>
<div class="tag addtag">
Add new tag
</div>
<div class="tag edittag">
<input type="text" value="" />
</div>
</div>
</li>
</script>

</body>
260 changes: 260 additions & 0 deletions examples/unfinished/todos-underscore/client/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
// quick jquery extension to bind text inputs to blur and RET.
$.fn.onBlurOrEnter = function (callback) {
this.bind('blur', callback);
this.bind('keypress', function (evt) {
if (evt.keyCode === 13 && $(this).val())
callback.call(this, evt);
});
};

// everything else happens after DOM is ready
$(function () {
$('body').layout({north__minSize: 50,
spacing_open: 10,
north__fxSettings: { direction: "vertical" }});

// cache the template function for a single item.
var item_template = _.template($('#item-template').html());

// this render function could be replaced with a handlebars
// template. underscore template isn't safe for user-entered data
// like the item text (XSS).
function renderItem (obj) {
// generate template for todo
var elt = $(item_template(obj));

// set text through jquery for XSS protection
elt.find('.todo-text').text(obj.text);

// clicking the checkbox toggles done state
elt.find('.check').click(function () {
Todos.update(obj._id, {$set: {done: !obj.done}});
});

// clicking destroy button removes the item
elt.find('.destroy').click(function () {
Todos.remove(obj._id);
});

// wire up tag destruction links
elt.find('.tag .remove').click(function () {
var tag = $(this).attr('name');
$(this).parent().fadeOut(500, function () {
Todos.update(obj._id, {$pull: {tags: tag}});
});
});

// wire up add tag
elt.find('.addtag').click(function () {
$(this).hide();
elt.find('.edittag').show();
elt.find('.edittag input').focus();
});

// wire up edit tag
elt.find('.edittag input').onBlurOrEnter(function () {
elt.find('.edittag').hide();
elt.find('.addtag').show();
if ($(this).val() !== '')
Todos.update(obj._id, {$addToSet: {tags: $(this).val()}});
});

// doubleclick on todo text brings up the editor
elt.find('.todo-text').dblclick(function () {
elt.addClass('editing');

var input = elt.find('.todo-input');
input.val(obj.text);
input.focus();
input.select();

input.onBlurOrEnter(function () {
elt.removeClass('editing');
if ($(this).val() !== '')
Todos.update(obj._id, {$set: {text: elt.find('.todo-input').val()}});
});
});

return elt[0];
};

// construct new todo from text box
$('#new-todo').bind('keypress', function (evt) {
var list_id = Session.get('list_id');
var tag = Session.get('tag_filter');

// prevent creation of a new todo if nothing is selected
if (!list_id) return;

var text = $('#new-todo').val();

if (evt.keyCode === 13 && text) {
var obj = {text: text,
list_id: list_id,
done: false,
timestamp: (new Date()).getTime()};
if (tag) obj.tags = [tag];

Todos.insert(obj);
$('#new-todo').val('');
}
});

var current_list_stop;
function setCurrentList (list_id) {
Session.set('list_id', list_id);

$('#items-view').show();

// kill current findLive render
if (current_list_stop)
current_list_stop.stop();

var query = {list_id: list_id};
if (Session.get('tag_filter'))
query.tags = Session.get('tag_filter')

// render individual todo list, stash kill function
current_list_stop =
Sky.ui.renderList(Todos, $('#item-list'), {
query: query,
sort: {timestamp: 1},
render: renderItem,
events: {}
});
};

// render list of lists in the left sidebar.
Sky.ui.renderList(Lists, $('#lists'), {
query: {}, // everything
sort: {name: 1},
template: $('#list-template'),
events: {
'click': function (evt) {
window.History.pushState({list_id: this._id},
"Todos: " + this.name,
"/" + this._id);
},
'dblclick': function (evt) {
var list_elt = $(evt.currentTarget);
var input = list_elt.find('.list-name-input');

list_elt.addClass('editing');

input.val(this.name);
input.focus();
input.select();

var _id = this._id;
input.onBlurOrEnter(function () {
list_elt.removeClass('editing');
if (input.val() !== '')
Lists.update(_id, {$set: {name: input.val()}});
});
}
}
});

// construct new todo list from text box
$('#new-list').bind('keypress', function (evt) {
var text = $('#new-list').val();

if (evt.keyCode === 13 && text) {
var list = Lists.insert({name: text});
$('#new-list').val('');
window.History.pushState({list_id: list._id},
"Todos: " + list.name,
"/" + list._id);
}
});

// tags and filters

// the tag filter bar is easy to generate using a simple
// renderList() against a minimongo query. since minimongo doesn't
// support aggregate queries, construct a local collection to serve
// the same purpose, and drive the renderList() off of it.

var LocalTags = Sky.Collection();
(function () {
function updateLocalTags() {
var real = _(Todos.find()).chain().pluck('tags').compact().flatten().uniq().value();
real.unshift(null); // XXX fake tag

var computed = _(LocalTags.find()).pluck('tag');

_.each(_.difference(real, computed), function (new_tag) {
LocalTags.insert({tag: new_tag});
});

_.each(_.difference(computed, real), function (dead_tag) {
LocalTags.remove({tag: dead_tag});
});
};

Todos.findLive({}, {
added: function (obj, before_idx) { _.defer(updateLocalTags); },
removed: function (id, at_idx) { _.defer(updateLocalTags); },
changed: function (obj, at_idx) { _.defer(updateLocalTags); },
});
})();

// findLive() against the computed tag table. since we also want a
// show-all button, arrange for the computed table to always include
// a null placeholder tag, and for the template to render that as
// "Show all". always begin the user session with a null filter.

Session.set('tag_filter', null);

Sky.ui.renderList(LocalTags, $('#tag-filter'), {
query: {},
sort: {tag: 1},
template: $('#tag-filter-template'),
events: {
'click': function (evt) {
if (Session.equals('tag_filter', this.tag))
Session.set('tag_filter', null);
else
Session.set('tag_filter', this.tag);

setCurrentList(Session.get('list_id'));
}
}
});

// load list on statechange (which we drive from several places).
window.History.Adapter.bind(window, 'statechange', function () {
var state = window.History.getState();
var list = Lists.find(state.data.list_id);
setCurrentList(list._id);
});

// subscribe to all available todo lists. once the inital load
// completes, navigate to the list specified by URL, if any.
Sky.subscribe('lists', {}, function () {
var initial_list_id = window.location.pathname.split('/')[1];
var list;

if (initial_list_id) {
list = Lists.find(initial_list_id);
} else {
var lists = Lists.find({}, {sort: {name: 1}, limit: 1});
list = lists[0];
}

if (list) {
window.History.replaceState({list_id: list._id},
"Todos: " + list.name,
"/" + list._id);
// replaceState doesn't always trigger statechange on reload. if
// you last reloaded the same page and the state is the same, it
// won't fire. so call this here. double calling is not great, but
// OK.
setCurrentList(list._id);
}
});

// subscribe to all the items in each list. no need for a callback
// here: todo items are never queried using collection.find().
Sky.subscribe('todos');
});
15 changes: 15 additions & 0 deletions examples/unfinished/todos-underscore/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Lists = Sky.Collection("lists");

Todos = Sky.Collection("todos");

/* Schema support coming soon!
Lists.schema({text: String});
Todos.schema({text: String,
done: Boolean,
tags: [String]});
*/

Sky.publish('lists');
Sky.publish('todos');
221 changes: 221 additions & 0 deletions examples/unfinished/todos-underscore/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 1.4em;
background: #eeeeee;
color: #333333;
}

.ui-layout-north {
background: #dddddd;
}

#tag-filter {
margin: 8px;
}

#items-view {
display: none;
margin: 10px;
}

#new-todo {
width: 466px;
font-size: 24px;
font-family: inherit;
line-height: 1.4em;
border: 0;
outline: none;
padding: 6px;
border: 1px solid #999999;
margin-left: 75px;
}

.ui-layout-west {
padding: 10px;
border-right: solid 1px #cccccc;
}

.ui-layout-south {
border-top: solid 1px black;
padding: 10px;
background: #cccccc;
}

#help p {
margin: 8px;
}

.ui-layout-center {
overflow: auto;
}

#lists .list {
margin: 2px;
font-weight: bold;
}

#lists .list-name .empty {
font-size: 0.9em;
font-style: italic;
}

#lists .editing .display,
#lists .edit {
display: none;
}
#lists .editing .edit {
display: block;
}
#lists .editing input {
font-family: inherit;
margin: 0;
line-height: 1.6em;
border: 0;
outline: none;
padding: 10px 7px 0px 27px;
border: 1px solid #999999;
}
#lists .selected {
background-color: lightblue;
}

/* todo items */

#item-list {
margin-top: 10px;
}

#item-list li {
margin: 12px;
font-size: 24px;
line-height: 1.1em;
border-bottom: 1px solid #cccccc;
height: 50px;
}

#item-list li:after {
content: "\0020";
display: block;
height: 0;
clear: both;
overflow: hidden;
visibility: hidden;
}

#item-list .destroy {
float: left;
width: 20px;
height: 20px;
cursor: pointer;
margin-top: 12px;
margin-left: 5px;
}

#item-list li:hover .destroy {
background: url('/destroy.png') no-repeat 0 0;
}

#item-list li .destroy:hover {
background-position: 0 -20px;
}

#item-list .display {
float: left;
margin: 9px;
}

#item-list .check {
float: left;
margin: 9px;
}

#item-list .edit {
float: left;
}

#item-list .todo-text {
float: left;
}

#item-list li.editing {
padding: 0;
}

#item-list .editing .display,
#item-list .edit {
display: none;
}

#item-list .editing .edit {
display: block;
}

#item-list .editing input {
width: 444px;
font-size: 24px;
font-family: inherit;
margin-left: 38px;
line-height: 1.6em;
border: 0;
outline: none;
border: 1px solid #999999;
}

#item-list .done .todo-text {
text-decoration: line-through;
color: #777777;
}

#item-list .item-tags {
float: right;
}

/* tags */

.tag {
float: left;

color: black;
background: #aaaaaa;
font-size: 16px;
font-weight: bold;

cursor: pointer;

border: 1px solid black;
border-radius: 2px;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;

padding: 1px 3px 1px 3px;
margin: 4px;
}

.tag.addtag {
background: lightblue;
border: 1px dashed black;
}

.tag.edittag {
display: none;
}

.tag.selected {
background: lightblue;
}

.tag .name {
float: left;
}

.tag .remove {
margin-top: 5px;
margin-left: 5px;
float: left;
width: 16px;
height: 16px;
background-image: url("/close_16.png");
}


Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions examples/unfinished/todos-underscore/reset.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, font, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td {
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-weight: inherit;
font-style: inherit;
font-size: 100%;
font-family: inherit;
vertical-align: baseline;
}
body {
line-height: 1;
color: black;
background: white;
}
ol, ul {
list-style: none;
}
a img {
border: none;
}

21 changes: 21 additions & 0 deletions examples/unfinished/todos-underscore/server/bootstrap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// if the database is empty on server start, create some sample data.
Sky.startup(function () {
if (Lists.find().length === 0) {
var list1 = Lists.insert({name: 'Things to do'});
Todos.insert({list_id: list1._id,
text: 'Write Skybreak app', tags: ['fun']});
Todos.insert({list_id: list1._id,
text: 'Drink beer', tags: ['fun', 'yum']});

var list2 = Lists.insert({name: 'Places to see'});
Todos.insert({list_id: list2._id, text: 'San Francisco',
tags: ['yum']});
Todos.insert({list_id: list2._id, text: 'Paris',
tags: ['fun']});
Todos.insert({list_id: list2._id, text: 'Tokyo'});

var list3 = Lists.insert({name: 'People to meet'});
Todos.insert({list_id: list3._id,
text: 'All the cool kids'});
}
});

0 comments on commit 0599d51

Please sign in to comment.