Commit a7fa2f2b authored by Sindre Sorhus's avatar Sindre Sorhus

Merge pull request #436 from kendo-labs/gh-pages

Kendo UI Implementation
parents 87305cde 566d0d58
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Kendo UI • TodoMVC</title>
<link rel="stylesheet" href="../../../assets/base.css">
<!--[if IE]>
<script src="../../../assets/ie.js"></script>
<![endif]-->
</head>
<body>
<section id="todoapp">
<header id="header">
<h1>todos</h1>
<input id="new-todo" data-bind="enter: saveTodo"
placeholder="What needs to be done?" autofocus>
</header>
<section id="main" data-bind="visible: isVisible">
<input id="toggle-all" type="checkbox" data-bind="click: toggleAll, checked: allCompleted">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list" data-role="listview" data-template="item-template" data-bind="source: todos"></ul>
</section>
<footer id="footer" data-bind="visible: isVisible">
<span id="todo-count">
<strong data-bind="text: activeCount"></strong>
<span data-bind="text: activeCountText"></span> left
</span>
<ul id="filters">
<li>
<a href="#/" data-bind="attr: { class: allFilterClass }">All</a>
</li>
<li>
<a href="#/active" data-bind="attr: { class: activeFilterClass }">Active</a>
</li>
<li>
<a href="#/completed" data-bind="attr: { class: completedFilterClass }">Completed</a>
</li>
</ul>
<button id="clear-completed"
data-bind="click: destroyCompleted,
visible: completedCount,
text: clearCompletedText">
</button>
</footer>
</section>
<div id="info">
<p>Double-click to edit a todo</p>
<p>Credits <a href="https://github.com/bsatrom">Brandon Satrom</a>, <a href="https://github.com/burkeholland">Burke Holland</a> &nbsp; <a href="https://github.com/akorchev">Atanas Korchev</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</div>
<script type="text/template" id="item-template">
<li data-bind="attr: { class: todoItemClass }">
<div class="view">
<input class="toggle" type="checkbox" data-bind="checked: completed">
<label data-bind="text: title, events: { dblclick: startEdit } "></label>
<button class="destroy" data-bind="click: destroy">
</button>
</div>
<input class="edit" data-bind="value: title, events: { change: endEdit, blur: endEdit }, enter: endEdit">
</li>
</script>
<script src="../../../assets/base.js"></script>
<script src="../../../assets/jquery.min.js"></script>
<script src="js/lib/kendo.all.min.js"></script>
<script src="js/lib/kendo.bindings.custom.js"></script>
<script src="js/lib/kendo.data.localstoragedatasource.js"></script>
<script src="js/app.js"></script>
</body>
</html>
var app = app || {};
(function ($, kendo) {
'use strict';
var filterBase = {
field: 'completed',
operator: 'eq'
};
// Route object to manage filtering the todo item list
var routes = {
'/': function () {
app.todoData.filter({});
app.todoViewModel.set('filter', '');
},
'/active': function () {
filterBase.value = false;
app.todoData.filter(filterBase);
app.todoViewModel.set('filter', 'active');
},
'/completed': function () {
filterBase.value = true;
app.todoData.filter(filterBase);
app.todoViewModel.set('filter', 'completed');
}
};
// Todo Model Object
app.Todo = kendo.data.Model.define({
id: 'id',
fields: {
id: { editable: false, nullable: true },
title: { type: 'string' },
completed: { type: 'boolean', nullable: false, defaultValue: false },
edit: { type: 'boolean', nullable: false, defaultValue: false }
}
});
// The Todo DataSource. This is a custom DataSource that extends the
// Kendo UI DataSource and adds custom transports for saving data to
// localStorage.
// Implementation in js/lib/kendo.data.localstoragedatasource.ds
app.todoData = new kendo.data.extensions.LocalStorageDataSource({
itemBase: 'todos-kendo',
autoSync: true,
schema: {
model: app.Todo
}
});
// The core ViewModel for our todo app
app.todoViewModel = kendo.observable({
todos: app.todoData,
filter: null,
// Handle route changes and direct to the appropriate handler in our
// local routes object.
routeChanged: function (url) {
routes[url || '/'].call(this);
},
// Main element visibility handler
isVisible: function () {
return this.get('todos').data().length;
},
// Core CRUD Methods
saveTodo: function () {
var todos = this.get('todos');
var newTodo = $('#new-todo');
var todo = new app.Todo({
title: newTodo.val().trim(),
completed: false,
edit: false
});
todos.add(todo);
newTodo.val('');
},
toggleAll: function () {
var completed = this.completedTodos().length === this.get('todos').data().length;
$.grep(this.get('todos').data(), function (el) {
el.set('completed', !completed);
});
},
startEdit: function (e) {
e.data.set('edit', true);
$('li[data-uid=' + e.data.uid + ']').find('input').focus();
},
endEdit: function (e) {
var editData = e;
if (e.data) {
editData = e.data;
// If the todo has a title, set it's edit property
// to false. Otherwise, delete it.
if (editData.title.trim()) {
editData.set('edit', false);
} else {
this.destroy(e);
}
}
editData.set('edit', false);
},
destroy: function (e) {
this.todos.remove(e.data);
},
destroyCompleted: function () {
$.each(this.completedTodos(), function (index, value) {
this.todos.remove(value);
}.bind(this));
},
// Methods for retrieving filtered todos and count values
activeTodos: function () {
return $.grep(this.get('todos').data(), function (el) {
return !el.get('completed');
});
},
activeCount: function () {
return this.activeTodos().length;
},
completedTodos: function () {
return $.grep(this.get('todos').data(), function (el) {
return el.get('completed');
});
},
completedCount: function () {
return this.completedTodos().length;
},
allCompleted: function () {
return this.completedTodos().length === this.get('todos').data().length;
},
// Text value bound methods
activeCountText: function () {
return this.activeCount() === 1 ? 'item' : 'items';
},
clearCompletedText: function () {
return 'Clear completed (' + this.completedCount() + ')';
},
// Class attribute bound methods
todoItemClass: function (item) {
if (item.get('edit')) {
return 'editing';
}
return (item.get('completed') ? 'completed' : 'active');
},
allFilterClass: function () {
return this.get('filter') ? '' : 'selected';
},
activeFilterClass: function () {
return this.get('filter') === 'active' ? 'selected' : '';
},
completedFilterClass: function () {
return this.get('filter') === 'completed' ? 'selected' : '';
}
});
// Kendo History object for capturing hash changes and triggering
// our route-changed handler
kendo.history.start({
ready: function (e) {
app.todoViewModel.routeChanged(e.url);
},
change: function (e) {
app.todoViewModel.routeChanged(e.url);
}
});
// Bind the ViewModel to the todoapp DOM element
kendo.bind($('#todoapp'), app.todoViewModel);
}($, kendo));
This diff is collapsed.
(function ($, kendo) {
'use strict';
var ENTER_KEY = 13;
// Create a custom "enter" binding by extending the kendo.data.Binder
// object with a custom init function that binds to the keyup event and,
// if the enter key is pressed, will call a bound function.
kendo.data.binders.enter = kendo.data.Binder.extend({
init: function (widget, bindings, options) {
// Call the "base" init method
kendo.data.Binder.fn.init.call(this, widget, bindings, options);
$(this.element).bind('keyup', function (e) {
// If the keypressed is not the enter key, return
if (e.which !== ENTER_KEY || !this.element.value.trim()) {
return;
}
// Otherwise, call the function specified in the enter binding
this.bindings['enter'].get();
}.bind(this));
},
// The refresh function must be specified in a custom binding,
// even when empty.
refresh: function () {}
});
})($, kendo);
\ No newline at end of file
(function ($, kendo) {
'use strict';
var itemBase, separator, idField;
kendo.data.extensions = kendo.data.extensions || {};
// Function to create a quasi-unique GUID for localStorage
var getGuid = function () {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
});
};
// Obtains the list of keys from localStorage
var getKeys = function () {
var keysList = localStorage.getItem(itemBase);
return keysList ? keysList.split(',') : [];
};
// Checks the localStorage key list for the current id and,
// if it doesn't exist, adds that key to the list and saves
// the list back to localStorage.
var addKeyIfNew = function (id) {
var keys = getKeys(),
matchingKey = $.grep(keys, function (key) { return key === id; });
if (!matchingKey.length) {
keys.push(id);
localStorage.setItem(itemBase, keys.join(','));
}
};
// Fetches an array of objects from localStorage
var getFromLocalStorage = function () {
var keys = getKeys(),
todos = [];
$.each(keys, function (index, value) {
var item = localStorage.getItem(itemBase + separator + value);
if (item) {
todos.push(JSON.parse(item));
}
});
return todos;
};
// Saves the current item to localStorage
var saveToLocalStorage = function (data) {
if (!data[idField]) {
data[idField] = getGuid();
}
addKeyIfNew(data[idField]);
localStorage.setItem(itemBase + separator + data[idField], JSON.stringify(data));
};
// Removes the current item from localStorage
var removeFromLocalStorage = function (data) {
var keys = getKeys();
var index = keys.indexOf(data[idField]);
if (index >= 0) {
keys.splice(index, 1);
localStorage.setItem(itemBase, keys.join(','));
localStorage.removeItem(itemBase + separator + data[idField]);
}
};
// Specify a CRUD transport object for our custom Kendo DataSource
var localTransports = {
read: function (options) {
var todos = getFromLocalStorage();
options.success(todos);
},
create: function (options) {
saveToLocalStorage(options.data);
options.success(options.data);
},
update: function (options) {
saveToLocalStorage(options.data);
options.success(options.data);
},
destroy: function (options) {
removeFromLocalStorage(options.data);
options.success(options.data);
}
};
// Create the custom DataSource by extending a kendo.data.DataSource
// and specify an init method that wires up needed functionality.
kendo.data.extensions.LocalStorageDataSource = kendo.data.DataSource.extend({
init: function (options) {
// DataSource consumers can specify custom itemBase and separator
// strings when initializing the DataSource. These values are
// used when saving records to localStorage.
itemBase = options.itemBase || 'kendo-ds';
separator = options.separator || '-';
idField = options.schema.model.idField;
// The idField is required. If not specified on the model, throw an error
if (!idField) {
throw new Error('An id field is required in order to work with localStorage. Please specify an id on your Model.');
}
// Call the "base" DataSource init function and provide our custom transport object
kendo.data.DataSource.fn.init.call(this, $.extend(true, {}, { transport: localTransports }, options));
}
});
})($, kendo);
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