Commit 1af400dd authored by Eugene Shen's avatar Eugene Shen

Add RenderJS example, without npm

parent 4521175a
hr {
margin: 20px 0;
border: 0;
border-top: 1px dashed #c5c5c5;
border-bottom: 1px dashed #f7f7f7;
}
.learn a {
font-weight: normal;
text-decoration: none;
color: #b83f45;
}
.learn a:hover {
text-decoration: underline;
color: #787e7e;
}
.learn h3,
.learn h4,
.learn h5 {
margin: 10px 0;
font-weight: 500;
line-height: 1.2;
color: #000;
}
.learn h3 {
font-size: 24px;
}
.learn h4 {
font-size: 18px;
}
.learn h5 {
margin-bottom: 0;
font-size: 14px;
}
.learn ul {
padding: 0;
margin: 0 0 30px 25px;
}
.learn li {
line-height: 20px;
}
.learn p {
font-size: 15px;
font-weight: 300;
line-height: 1.3;
margin-top: 0;
margin-bottom: 0;
}
#issue-count {
display: none;
}
.quote {
border: none;
margin: 20px 0 60px 0;
}
.quote p {
font-style: italic;
}
.quote p:before {
content: '“';
font-size: 50px;
opacity: .15;
position: absolute;
top: -20px;
left: 3px;
}
.quote p:after {
content: '”';
font-size: 50px;
opacity: .15;
position: absolute;
bottom: -42px;
right: 3px;
}
.quote footer {
position: absolute;
bottom: -40px;
right: 0;
}
.quote footer img {
border-radius: 3px;
}
.quote footer a {
margin-left: 5px;
vertical-align: middle;
}
.speech-bubble {
position: relative;
padding: 10px;
background: rgba(0, 0, 0, .04);
border-radius: 5px;
}
.speech-bubble:after {
content: '';
position: absolute;
top: 100%;
right: 30px;
border: 13px solid transparent;
border-top-color: rgba(0, 0, 0, .04);
}
.learn-bar > .learn {
position: absolute;
width: 272px;
top: 8px;
left: -300px;
padding: 10px;
border-radius: 5px;
background-color: rgba(255, 255, 255, .6);
transition-property: left;
transition-duration: 500ms;
}
@media (min-width: 899px) {
.learn-bar {
width: auto;
padding-left: 300px;
}
.learn-bar > .learn {
left: 8px;
}
}
\ No newline at end of file
<!doctype html>
<html>
<head>
<title>Model Gadget</title>
<script src="rsvp.js"></script>
<script src="renderjs.js"></script>
<script src="jio.js"></script>
<script src="gadget_model.js"></script>
</head>
<body>
</body>
</html>
\ No newline at end of file
/*global define, App, window, RSVP, rJS, jIO */
/*jshint unused:false */
(function (window, RSVP, rJS, jIO) {
'use strict';
rJS(window)
// Initialize the gadget as soon as it is loaded in memory,
// blocking all other methods in itself and its ancestors.
.ready(function () {
var gadget = this;
// Create a new jIO storage that supports queries,
// automatic IDs, and documents on local storage.
return gadget.changeState({
storage: jIO.createJIO({
type: 'query',
sub_storage: {
type: 'uuid',
sub_storage: {
type: 'document',
document_id: '/',
sub_storage: {
type: 'local'
}
}
}
})
});
})
// Put a new todo into jIO storage, with an auto-generated ID.
.declareMethod('postTodo', function (title) {
var gadget = this;
var storage = gadget.state.storage;
return storage.post({
title: title,
completed: false,
creation_date: Date.now()
});
})
// Update the properties of an existing todo in jIO storage.
.declareMethod('putTodo', function (id, todo) {
var gadget = this;
var storage = gadget.state.storage;
// Get todo from storage first to get all its properties.
return storage.get(id)
.push(function (result) {
var key;
// Only overwrite the given properties.
for (key in todo) {
if (todo.hasOwnProperty(key)) {
result[key] = todo[key];
}
}
return result;
},
// Reject callback if todo is not found in storage.
function () {
return todo;
})
.push(function (todo) {
return storage.put(id, todo);
});
})
// Return a list of all todos in storage that match the given query.
.declareMethod('getTodos', function (query) {
var gadget = this;
var storage = gadget.state.storage;
// Get a list of all todos in storage that match the given query,
// in chronological order, with 'title' and 'completed' properties.
return storage.allDocs({
query: query,
sort_on: [['creation_date', 'ascending']],
select_list: ['title', 'completed']
})
// Add the todo IDs into the list.
.push(function (result_list) {
var todo_list = [], todo, i;
for (i = 0; i < result_list.data.total_rows; i += 1) {
todo = result_list.data.rows[i];
todo_list.push({
id: todo.id,
title: todo.value.title,
completed: todo.value.completed
});
}
return todo_list;
});
})
// Get the count of all total todos and all active todos.
.declareMethod('getTodoCountDict', function () {
var gadget = this;
var storage = gadget.state.storage;
// Get a list of all todos in storage with the 'completed' property
return storage.allDocs({select_list: ['completed']})
.push(function (result_list) {
var todo_count_dict = {
total: result_list.data.total_rows,
active: 0
};
// Iterate through all todos to count only the active ones.
for (var i = 0; i < result_list.data.total_rows; i += 1) {
if (!result_list.data.rows[i].value.completed) {
todo_count_dict.active += 1;
}
}
return todo_count_dict;
});
})
// Change the title of a todo.
.declareMethod('changeTitle', function (id, title) {
var gadget = this;
return gadget.putTodo(id, {title: title});
})
// Change the completion status of a todo.
.declareMethod('toggleOne', function (id, completed) {
var gadget = this;
return gadget.putTodo(id, {completed: completed});
})
// Change the completion status of all todos.
.declareMethod('toggleAll', function (completed) {
var gadget = this;
var storage = gadget.state.storage;
// Get all todos, and change the completion status of each one.
return storage.allDocs()
.push(function (result_list) {
var promise_list = [];
for (var i = 0; i < result_list.data.total_rows; i += 1) {
promise_list.push(gadget.toggleOne(
result_list.data.rows[i].id, completed
));
}
return RSVP.all(promise_list);
});
})
// Remove one todo from the storage.
.declareMethod('removeOne', function (id) {
var gadget = this;
var storage = gadget.state.storage;
return storage.remove(id);
})
// Remove all completed todos from the storage.
.declareMethod('removeCompleted', function () {
var gadget = this;
// Get a list of all todos, and only remove the completed ones.
return gadget.getTodos()
.push(function (todo_list) {
var promise_list = [];
for (var i = 0; i < todo_list.length; i += 1) {
if (todo_list[i].completed) {
promise_list.push(gadget.removeOne(todo_list[i].id));
}
}
return RSVP.all(promise_list);
});
});
}(window, RSVP, rJS, jIO));
<!doctype html>
<html>
<head>
<title>Router Gadget</title>
<script src="rsvp.js"></script>
<script src="renderjs.js"></script>
<script src="gadget_router.js"></script>
</head>
<body>
</body>
</html>
\ No newline at end of file
/*global define, App, window, RSVP, rJS */
/*jshint unused:false */
(function (window, RSVP, rJS) {
'use strict';
// Get the appropriate query for allDocs() based on the given hash.
function getQueryFromHash(hash) {
switch (hash) {
case '#/active':
return 'completed: "false"';
case '#/completed':
return 'completed: "true"';
default:
return '';
}
}
// Return an infinite promise for event listening purposes.
// Copied directly from RenderJS implementation; don't worry about it.
function loopEventListener(
target, type, useCapture, callback, prevent_default) {
var handle_event_callback;
var callback_promise;
if (prevent_default === undefined) {
prevent_default = true;
}
function cancelResolver() {
if ((callback_promise !== undefined) &&
(typeof callback_promise.cancel === 'function')) {
callback_promise.cancel();
}
}
function canceller() {
if (handle_event_callback !== undefined) {
target.removeEventListener(
type, handle_event_callback, useCapture);
}
cancelResolver();
}
function itsANonResolvableTrap(resolve, reject) {
var result;
handle_event_callback = function (evt) {
if (prevent_default) {
evt.stopPropagation();
evt.preventDefault();
}
cancelResolver();
try {
result = callback(evt);
} catch (e) {
result = RSVP.reject(e);
}
callback_promise = result;
new RSVP.Queue()
.push(function () {
return result;
})
.push(undefined, function (error) {
if (!(error instanceof RSVP.CancellationError)) {
canceller();
reject(error);
}
});
};
target.addEventListener(type, handle_event_callback, useCapture);
}
return new RSVP.Promise(itsANonResolvableTrap, canceller);
}
rJS(window)
// Initialize the gadget as soon as it is loaded in memory,
// blocking all other methods in itself and its ancestors.
.ready(function () {
var gadget = this;
return gadget.setQuery(getQueryFromHash(window.location.hash));
})
// Initialize the gadget as soon as it is loaded in the DOM,
// but only after ready() has finished and stopped blocking.
.declareService(function () {
var gadget = this;
return loopEventListener(window, 'hashchange', false,
function () {
return gadget.setQuery(
getQueryFromHash(window.location.hash)
);
});
}, false)
// Declare an acquired method from the parent gadget to use it.
.declareAcquiredMethod('setQuery', 'setQuery');
}(window, RSVP, rJS));
This diff is collapsed.
html,
body {
margin: 0;
padding: 0;
}
button {
margin: 0;
padding: 0;
border: 0;
background: none;
font-size: 100%;
vertical-align: baseline;
font-family: inherit;
font-weight: inherit;
color: inherit;
-webkit-appearance: none;
appearance: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em;
background: #f5f5f5;
color: #4d4d4d;
min-width: 230px;
max-width: 550px;
margin: 0 auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-weight: 300;
}
:focus {
outline: 0;
}
.hidden {
display: none;
}
.todoapp {
background: #fff;
margin: 130px 0 40px 0;
position: relative;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.todoapp input::-webkit-input-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
.todoapp input::-moz-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
.todoapp input::input-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
.todoapp h1 {
position: absolute;
top: -155px;
width: 100%;
font-size: 100px;
font-weight: 100;
text-align: center;
color: rgba(175, 47, 47, 0.15);
-webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility;
}
.new-todo,
.edit {
position: relative;
margin: 0;
width: 100%;
font-size: 24px;
font-family: inherit;
font-weight: inherit;
line-height: 1.4em;
border: 0;
color: inherit;
padding: 6px;
border: 1px solid #999;
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.new-todo {
padding: 16px 16px 16px 60px;
border: none;
background: rgba(0, 0, 0, 0.003);
box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
}
.main {
position: relative;
z-index: 2;
border-top: 1px solid #e6e6e6;
}
.toggle-all {
text-align: center;
border: none; /* Mobile Safari */
opacity: 0;
position: absolute;
}
.toggle-all + label {
width: 60px;
height: 34px;
font-size: 0;
position: absolute;
top: -52px;
left: -13px;
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
}
.toggle-all + label:before {
content: '❯';
font-size: 22px;
color: #e6e6e6;
padding: 10px 27px 10px 27px;
}
.toggle-all:checked + label:before {
color: #737373;
}
.todo-list {
margin: 0;
padding: 0;
list-style: none;
}
.todo-list li {
position: relative;
font-size: 24px;
border-bottom: 1px solid #ededed;
}
.todo-list li:last-child {
border-bottom: none;
}
.todo-list li.editing {
border-bottom: none;
padding: 0;
}
.todo-list li.editing .edit {
display: block;
width: 506px;
padding: 12px 16px;
margin: 0 0 0 43px;
}
.todo-list li.editing .view {
display: none;
}
.todo-list li .toggle {
text-align: center;
width: 40px;
/* auto, since non-WebKit browsers doesn't support input styling */
height: auto;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
border: none; /* Mobile Safari */
-webkit-appearance: none;
appearance: none;
}
.todo-list li .toggle {
opacity: 0;
}
.todo-list li .toggle + label {
/*
Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433
IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/
*/
background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E');
background-repeat: no-repeat;
background-position: center left;
}
.todo-list li .toggle:checked + label {
background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E');
}
.todo-list li label {
word-break: break-all;
padding: 15px 15px 15px 60px;
display: block;
line-height: 1.2;
transition: color 0.4s;
}
.todo-list li.completed label {
color: #d9d9d9;
text-decoration: line-through;
}
.todo-list li .destroy {
display: none;
position: absolute;
top: 0;
right: 10px;
bottom: 0;
width: 40px;
height: 40px;
margin: auto 0;
font-size: 30px;
color: #cc9a9a;
margin-bottom: 11px;
transition: color 0.2s ease-out;
}
.todo-list li .destroy:hover {
color: #af5b5e;
}
.todo-list li .destroy:after {
content: '×';
}
.todo-list li:hover .destroy {
display: block;
}
.todo-list li .edit {
display: none;
}
.todo-list li.editing:last-child {
margin-bottom: -1px;
}
.footer {
color: #777;
padding: 10px 15px;
height: 20px;
text-align: center;
border-top: 1px solid #e6e6e6;
}
.footer:before {
content: '';
position: absolute;
right: 0;
bottom: 0;
left: 0;
height: 50px;
overflow: hidden;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
0 8px 0 -3px #f6f6f6,
0 9px 1px -3px rgba(0, 0, 0, 0.2),
0 16px 0 -6px #f6f6f6,
0 17px 2px -6px rgba(0, 0, 0, 0.2);
}
.todo-count {
float: left;
text-align: left;
}
.todo-count strong {
font-weight: 300;
}
.filters {
margin: 0;
padding: 0;
list-style: none;
position: absolute;
right: 0;
left: 0;
}
.filters li {
display: inline;
}
.filters li a {
color: inherit;
margin: 3px;
padding: 3px 7px;
text-decoration: none;
border: 1px solid transparent;
border-radius: 3px;
}
.filters li a:hover {
border-color: rgba(175, 47, 47, 0.1);
}
.filters li a.selected {
border-color: rgba(175, 47, 47, 0.2);
}
.clear-completed,
html .clear-completed:active {
float: right;
position: relative;
line-height: 20px;
text-decoration: none;
cursor: pointer;
}
.clear-completed:hover {
text-decoration: underline;
}
.info {
margin: 65px auto 0;
color: #bfbfbf;
font-size: 10px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
text-align: center;
}
.info p {
line-height: 1;
}
.info a {
color: inherit;
text-decoration: none;
font-weight: 400;
}
.info a:hover {
text-decoration: underline;
}
/*
Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox
*/
@media screen and (-webkit-min-device-pixel-ratio:0) {
.toggle-all,
.todo-list li .toggle {
background: none;
}
.todo-list li .toggle {
height: 40px;
}
}
@media (max-width: 430px) {
.footer {
height: 50px;
}
.filters {
bottom: 10px;
}
}
\ No newline at end of file
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>OfficeJS App</title>
<script src="rsvp.js"></script>
<script src="renderjs.js"></script>
<script src="jio.js"></script>
<script src="handlebars.js"></script>
<script src="index.js"></script>
<link href="base.css" rel="stylesheet">
<link href="index.css" rel="stylesheet">
<link href="manifest.json" rel="manifest">
</head>
<body>
<div data-gadget-url="gadget_model.html"
data-gadget-scope="model"
data-gadget-sandbox="public">
</div>
<main class="handlebars">
</main>
<script class="handlebars-template" type="text/x-handlebars-template">
<section class="todoapp">
<header class="header">
<h1>todos</h1>
<form>
<input class="new-todo" placeholder="What needs to be done?" autofocus>
</form>
</header>
<section class="main {{#unless todo_exists}}hidden{{/unless}}">
<input class="toggle-all" type="checkbox" {{#if all_completed}}checked="true"{{/if}}>
<label for="toggle-all" class="toggle-label">Mark all as complete</label>
<ul class="todo-list">
{{#each todo_list}}
<li class="todo-item {{#if this.completed}}completed{{/if}} {{#if this.editing}}editing{{/if}}"
data-jio-id="{{this.id}}">
<div class="view {{#if this.edit}}hidden{{/if}}">
<input class="toggle" type="checkbox"{{#if this.completed}} checked="true"{{/if}}>
<label class="todo-label">{{this.title}}</label>
<button class="destroy"></button>
</div>
<input class="edit{{#unless this.editing}} hidden{{/unless}}">
</li>
{{/each}}
</ul>
</section>
<footer class="footer {{#unless todo_exists}}hidden{{/unless}}">
<span class="todo-count">{{todo_count}}</span>
<div class="filters">
<a href="#/" class="selected">All</a>
<a href="#/active">Active</a>
<a href="#/completed">Completed</a>
</div>
<button class="clear-completed">Clear completed</button>
</footer>
</section>
<footer class="info">
<p>Double-click to edit a todo</p>
</footer>
</script>
</body>
</html>
\ No newline at end of file
/*global define, App, window, document, rJS, Handlebars */
/*jshint unused:false */
(function (window, document, rJS, Handlebars) {
'use strict';
// Constants
var ENTER_KEY = 13;
var ESCAPE_KEY = 27;
// Global Variables
var handlebars_template; // = Handlebars.compile(template.innerHTML);
rJS(window)
// Initiaize the state of the gadget as soon as it is loaded in memory.
.setState({
update: false,
clear_input: false,
editing_jio_id: '',
query: ''
})
// Initialize the gadget as soon as it is loaded in the DOM,
// but only after ready() has finished in its child gadgets.
.declareService(function () {
var gadget = this;
var temp = gadget.element.querySelector('.handlebars-template');
// Create a new empty element for the router gadget.
var div = document.createElement('div');
gadget.element.appendChild(div);
// Compile the Handlebars template only once, on page load.
handlebars_template = Handlebars.compile(temp.innerHTML);
// Declare the router gadget in JavaScript instead of HTML.
return gadget.declareGadget('gadget_router.html', {
scope: 'router',
sandbox: 'public',
element: div
})
// Render the state for the first time.
.push(function () {
return gadget.changeState({update: true});
});
})
// Declare an acquirable method to allow child gadgets to use it.
.allowPublicAcquisition('setQuery', function (param_list) {
var gadget = this;
// Keep the given query in the state when the router gadget calls.
gadget.changeState({query: param_list[0]});
})
// Render the entire todo app every time the state changes.
.onStateChange(function (modification_dict) {
var gadget = this;
var model_gadget;
var todo_count_dict;
// Get the model gadget and todo count dict to store for later.
return gadget.getDeclaredGadget('model')
.push(function (subgadget) {
model_gadget = subgadget;
return model_gadget.getTodoCountDict();
})
.push(function (count_dict) {
todo_count_dict = count_dict;
return model_gadget.getTodos(gadget.state.query);
})
// Get the list of todos from storage.
.push(function (todo_list) {
var plural = todo_list.length === 1 ? ' item' : ' items';
var focus_query = '.new-todo';
var edit_value = '';
var post_value = '';
// If a todo is currently being edited,
// set the focus to its edit input.
// Otherwise, the focus remains on the new todo input.
if (gadget.state.editing_jio_id) {
focus_query = 'li[data-jio-id="' +
gadget.state.editing_jio_id + '"] .edit';
}
// If the new todo input has not yet been submitted and
// it exists, then keep its current value in post_value.
if (!modification_dict.hasOwnProperty('clear_input') &&
gadget.element.querySelector('.new-todo')) {
post_value =
gadget.element.querySelector('.new-todo').value;
}
// Set at most one todo as currently being edited.
for (var i = 0; i < todo_list.length; i += 1) {
if (todo_list[i].id === gadget.state.editing_jio_id) {
todo_list[i].editing = true;
edit_value = todo_list[i].title;
} else {
todo_list[i].editing = false;
}
}
// Apply the Handlebars template on the todo list.
gadget.element.querySelector('.handlebars').innerHTML =
handlebars_template({
todo_list: todo_list,
todo_exists: todo_count_dict.total >= 1,
todo_count:
todo_count_dict.active.toString() + plural,
all_completed: todo_count_dict.active === 0
});
// Focus the proper element and copy the previous values
// of the currently editing input and the new todo input
// back into them, since Handlebars reset all input values
gadget.element.querySelector(focus_query).focus();
if (edit_value) {
gadget.element.querySelector(focus_query).value =
edit_value;
}
if (post_value) {
gadget.element.querySelector('.new-todo').value =
post_value;
}
gadget.state.update = false;
gadget.state.clear_input = false;
});
})
// Post a new todo when the new todo input is submitted.
.onEvent('submit', function (event) {
var gadget = this;
var item = event.target.elements[0].value.trim();
// Trim the input and reject blank values.
if (!item) {
return;
}
// Change clear_input in state to clear the new todo input.
return gadget.getDeclaredGadget('model')
.push(function (model_gadget) {
return model_gadget.postTodo(item);
})
.push(function () {
return gadget.changeState({clear_input: true});
});
}, false, true)
// Do the correct action when anything is clicked once.
.onEvent('click', function (event) {
var gadget = this;
var todo_item = event.target.parentElement.parentElement;
var jio_id = todo_item.getAttribute('data-jio-id');
// Delegate all responsibility to the model gadget.
return gadget.getDeclaredGadget('model')
.push(function (model_gadget) {
switch (event.target.className) {
// Set completed to the opposite of its current status.
case 'toggle':
return model_gadget.toggleOne(
jio_id,
!todo_item.classList.contains('completed')
);
// Set completed to the state of the toggle all checkbox.
case 'toggle-all':
return model_gadget.toggleAll(event.target.checked);
// Set completed to the opposite of the current state
// of the checkbox when the label is clicked, because
// the event is sent before the checkbox changes state
case 'toggle-label':
return model_gadget.toggleAll(
!gadget.element
.querySelector('.toggle-all').checked
);
// Remove the todo with the given ID.
case 'destroy':
return model_gadget.removeOne(jio_id);
// Remove all completed todos.
case 'clear-completed':
return model_gadget.removeCompleted();
// If the user clicked anywhere else, don't do anything,
// unless it was outside the currently editing input.
default:
if (gadget.state.editing_jio_id &&
event.target.className !== 'edit') {
return 'cancel editing';
}
return 'default';
}
})
// Only update the user if the user clicked outside the default.
.push(function (path) {
if (path !== 'default') {
return gadget.changeState({
update: true,
editing_jio_id: ''
});
}
});
}, false, false)
// Do the correct action when anything is clicked twice.
.onEvent('dblclick', function (event) {
var gadget = this;
// If a todo label is clicked twice, then edit it.
if (event.target.className === 'todo-label') {
return gadget.changeState({
editing_jio_id: event.target.parentElement
.parentElement.getAttribute('data-jio-id')
});
}
}, false, false)
// Do the correct action when any keys are pressed in an input.
.onEvent('keydown', function (event) {
var gadget = this;
// Reset everything if the escape key is pressed.
if (event.target.className === 'edit') {
if (event.keyCode === ESCAPE_KEY) {
return gadget.changeState({
update: true,
editing_jio_id: ''
});
}
// Change the title of the todo if the enter key is pressed.
var item = event.target.value.trim();
if (event.keyCode === ENTER_KEY && item) {
return gadget.getDeclaredGadget('model')
.push(function (model_gadget) {
return model_gadget.changeTitle(
event.target.parentElement
.getAttribute('data-jio-id'),
item
);
})
.push(function () {
return gadget.changeState({
update: true,
editing_jio_id: ''
});
});
}
}
}, false, false);
}(window, document, rJS, Handlebars));
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment