added pure javascript version of codelab (in progress)

This commit is contained in:
Renato Mangini 2013-02-22 20:11:12 -03:00
Родитель c12d952014
Коммит 3bd0250ac0
24 изменённых файлов: 976 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,70 @@
/**
A poor man's controller - a very simple module with basic MVC functionaliies
**/
(function(exports) {
var TodoModel = function() {
this.contents = '';
this.listeners = [];
}
TodoModel.prototype.setContents = function(contents) {
if (this.contents !== contents) {
this.contents = contents;
this.notifyListeners();
}
}
TodoModel.prototype.bindToInput = function(input) {
var _this = this;
var eventHandler = function(e) {
if (typeof(e.target.value) != 'undefined') {
_this.setContents(e.target.value);
}
};
input.addEventListener('keyup', eventHandler);
input.addEventListener('change', eventHandler);
}
TodoModel.prototype.bindToView = function(view) {
this.addListener(function(model) {
if (view.setValue) {
view.setValue(model.contents);
} else {
view.innerText=model.contents;
}
});
}
TodoModel.prototype.addListener = function(listener) {
this.listeners.push(listener);
}
TodoModel.prototype.notifyListeners = function() {
var this_ = this;
this.listeners.forEach(function(listener) {
listener(this_);
});
}
exports.TodoModel = TodoModel;
})(window);
window.addEventListener('DOMContentLoaded', function() {
var model = new TodoModel();
var newTodo = document.getElementById('newTodo');
var todoText = document.getElementById('todoText');
model.bindToView(todoText);
model.bindToInput(newTodo);
})

Двоичные данные
lab3_mvc/javascript/simpleview/icon.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 6.4 KiB

Просмотреть файл

@ -0,0 +1,18 @@
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="todo.css">
</head>
<body>
<h2>Todo</h2>
<div>
<ul>
<li id="todoText">
</li>
</ul>
<input type="text" id="newTodo" size="30"
placeholder="type your todo here">
</div>
<script src="controller.js"></script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,5 @@
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('index.html',
{width: 500, height: 309});
});

Просмотреть файл

@ -0,0 +1,12 @@
{
"manifest_version": 2,
"name": "Lab3a Simple MVC",
"version": "1",
"app": {
"background": {
"scripts": ["main.js"]
}
},
"icons": { "128": "icon.png" }
}

Просмотреть файл

@ -0,0 +1,21 @@
body {
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
}
ul {
list-style: none;
}
button, input[type=submit] {
background-color: #0074CC;
background-image: linear-gradient(top, #08C, #05C);
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
color: white;
}
.done-true {
text-decoration: line-through;
color: grey;
}

Просмотреть файл

@ -0,0 +1,164 @@
/**
A poor man's controller - a very simple module with basic MVC functionaliies
**/
(function(exports) {
var nextId = 1;
var TodoModel = function() {
this.todos = {};
this.listeners = [];
}
TodoModel.prototype.clearTodos = function() {
this.todos = {};
this.notifyListeners('removed');
}
TodoModel.prototype.archiveDone = function() {
var oldTodos = this.todos;
this.todos={};
for (var id in oldTodos) {
if ( ! oldTodos[id].isDone ) {
this.todos[id] = oldTodos[id];
}
}
this.notifyListeners('archived');
}
TodoModel.prototype.setTodoState = function(id, isDone) {
if ( this.todos[id].isDone != isDone ) {
this.todos[id].isDone = isDone;
this.notifyListeners('stateChanged', id);
}
}
TodoModel.prototype.addTodo = function(text, isDone) {
var id = nextId++;
this.todos[id]={'id': id, 'text': text, 'isDone': isDone};
this.notifyListeners('added', id);
}
TodoModel.prototype.addListener = function(listener) {
this.listeners.push(listener);
}
TodoModel.prototype.notifyListeners = function(change, param) {
var this_ = this;
this.listeners.forEach(function(listener) {
listener(this_, change, param);
});
}
exports.TodoModel = TodoModel;
})(window);
window.addEventListener('DOMContentLoaded', function() {
var model = new TodoModel();
var form = document.querySelector('form');
var archive = document.getElementById('archive');
var list = document.getElementById('list');
var todoTemplate = document.querySelector('#templates > [data-name="list"]');
/**
* When the form is submitted, we need to add a new todo and clear the input
**/
form.addEventListener('submit', function(e) {
var textEl = e.target.querySelector('input[type="text"]');
model.addTodo(textEl.value, false);
textEl.value=null;
e.preventDefault();
});
/**
* A simple handler for the archive link
**/
archive.addEventListener('click', function(e) {
model.archiveDone();
e.preventDefault();
});
/**
* Listen to changes in the model and trigger the appropriate changes in the view
**/
model.addListener(function(model, changeType, param) {
if ( changeType === 'removed' || changeType === 'archived') {
redrawUI(model);
} else if ( changeType === 'added' ) {
drawTodo(model.todos[param], list);
} else if ( changeType === 'stateChanged') {
updateTodo(model.todos[param]);
}
updateCounters(model);
});
/**
* Clean the current ToDo list and add elements again
**/
var redrawUI = function(model) {
list.innerHTML='';
for (var id in model.todos) {
drawTodo(model.todos[id], list);
}
};
/**
* Deep clone a template node, set its ID and add it to the DOM container.
* Add a listener to the newly added checkbox, so it can trigger the state flip
* when the checkbox is clicked.
**/
var drawTodo = function(todoObj, container) {
var el = todoTemplate.cloneNode(true);
el.setAttribute('data-id', todoObj.id);
container.appendChild(el);
updateTodo(todoObj);
var checkbox = el.querySelector('input[type="checkbox"]');
checkbox.addEventListener('change', function(e) {
model.setTodoState(todoObj.id, e.target.checked);
});
}
/**
* Find the element corresponding to the given ToDo model object and update its
* state and description from the model.
*/
var updateTodo = function(model) {
var todoElement = list.querySelector('li[data-id="'+model.id+'"]');
if (todoElement) {
var checkbox = todoElement.querySelector('input[type="checkbox"]');
var desc = todoElement.querySelector('span');
checkbox.checked = model.isDone;
desc.innerText = model.text;
desc.className = "done-"+model.isDone;
}
}
/**
* Recalculate total number of ToDos and remaining ToDos and update
* appropriate elements in the DOM.
**/
var updateCounters = function(model) {
var count = 0;
var notDone = 0;
for (var id in model.todos) {
count++;
if ( ! model.todos[id].isDone ) {
notDone ++;
}
}
document.getElementById('remaining').innerText = notDone;
document.getElementById('length').innerText = count;
}
updateCounters(model);
});

Двоичные данные
lab3_mvc/javascript/withcontroller/icon.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 6.4 KiB

Просмотреть файл

@ -0,0 +1,30 @@
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="todo.css">
</head>
<body>
<h2>Todo</h2>
<div>
<span><span id="remaining"></span> of <span id="length"></span> remaining</span>
[ <a href="" id="archive">archive</a> ]
<ul class="unstyled" id="list">
</ul>
<form>
<input type="text" size="30"
placeholder="add new todo here">
<input class="btn-primary" type="submit" value="add">
</form>
</div>
<!-- poor man's template -->
<div id="templates" style="display: none;">
<li data-name="list">
<input type="checkbox">
<span></span>
</li>
</div>
<script src="controller.js"></script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,5 @@
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('index.html',
{width: 500, height: 309});
});

Просмотреть файл

@ -0,0 +1,12 @@
{
"manifest_version": 2,
"name": "Lab3b MVC with controller",
"version": "1",
"app": {
"background": {
"scripts": ["main.js"]
}
},
"icons": { "128": "icon.png" }
}

Просмотреть файл

@ -0,0 +1,21 @@
body {
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
}
ul {
list-style: none;
}
button, input[type=submit] {
background-color: #0074CC;
background-image: linear-gradient(top, #08C, #05C);
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
color: white;
}
.done-true {
text-decoration: line-through;
color: grey;
}

Просмотреть файл

@ -0,0 +1,193 @@
/**
A poor man's controller - a very simple module with basic MVC functionaliies
**/
(function(exports) {
var nextId = 1;
var TodoModel = function() {
this.todos = {};
this.listeners = [];
}
TodoModel.prototype.setTodos = function(todos) {
this.todos = todos;
var maxId = 0;
for (var id in todos) {
var idInt = parseInt(id);
maxId = idInt > maxId ? idInt : maxId;
}
nextId = maxId + 1;
this.notifyListeners('reset');
}
TodoModel.prototype.clearTodos = function() {
this.todos = {};
this.notifyListeners('removed');
}
TodoModel.prototype.archiveDone = function() {
var oldTodos = this.todos;
this.todos={};
for (var id in oldTodos) {
if ( ! oldTodos[id].isDone ) {
this.todos[id] = oldTodos[id];
}
}
this.notifyListeners('archived');
}
TodoModel.prototype.setTodoState = function(id, isDone) {
if ( this.todos[id].isDone != isDone ) {
this.todos[id].isDone = isDone;
this.notifyListeners('stateChanged', id);
}
}
TodoModel.prototype.addTodo = function(text, isDone) {
var id = nextId++;
this.todos[id]={'id': id, 'text': text, 'isDone': isDone};
this.notifyListeners('added', id);
}
TodoModel.prototype.addListener = function(listener) {
this.listeners.push(listener);
}
TodoModel.prototype.notifyListeners = function(change, param) {
var this_ = this;
this.listeners.forEach(function(listener) {
listener(this_, change, param);
});
}
exports.TodoModel = TodoModel;
})(window);
window.addEventListener('DOMContentLoaded', function() {
var model = new TodoModel();
var form = document.querySelector('form');
var archive = document.getElementById('archive');
var list = document.getElementById('list');
var todoTemplate = document.querySelector('#templates > [data-name="list"]');
/**
* When the form is submitted, we need to add a new todo and clear the input
**/
form.addEventListener('submit', function(e) {
var textEl = e.target.querySelector('input[type="text"]');
model.addTodo(textEl.value, false);
textEl.value=null;
e.preventDefault();
});
/**
* A simple handler for the archive link
**/
archive.addEventListener('click', function(e) {
model.archiveDone();
e.preventDefault();
});
/**
* Listen to changes in the model and trigger the appropriate changes in the view
**/
model.addListener(function(model, changeType, param) {
if ( changeType === 'removed' || changeType === 'archived' || changeType === 'reset') {
redrawUI(model);
} else if ( changeType === 'added' ) {
drawTodo(model.todos[param], list);
} else if ( changeType === 'stateChanged') {
updateTodo(model.todos[param]);
}
storageSave();
updateCounters(model);
});
// If there is saved data in storage, use it. Otherwise, bootstrap with sample todos
var storageLoad = function() {
chrome.storage.sync.get('todolist', function(value) {
if (value && value.todolist) {
model.setTodos(value.todolist);
} else {
model.addTodo('learn Chrome Apps', true);
model.addTodo('build a Chrome App', false);
}
});
}
var storageSave = function() {
chrome.storage.sync.set({'todolist': model.todos});
};
/**
* Clean the current ToDo list and add elements again
**/
var redrawUI = function(model) {
list.innerHTML='';
for (var id in model.todos) {
drawTodo(model.todos[id], list);
}
};
/**
* Deep clone a template node, set its ID and add it to the DOM container.
* Add a listener to the newly added checkbox, so it can trigger the state flip
* when the checkbox is clicked.
**/
var drawTodo = function(todoObj, container) {
var el = todoTemplate.cloneNode(true);
el.setAttribute('data-id', todoObj.id);
container.appendChild(el);
updateTodo(todoObj);
var checkbox = el.querySelector('input[type="checkbox"]');
checkbox.addEventListener('change', function(e) {
model.setTodoState(todoObj.id, e.target.checked);
});
}
/**
* Find the element corresponding to the given ToDo model object and update its
* state and description from the model.
*/
var updateTodo = function(model) {
var todoElement = list.querySelector('li[data-id="'+model.id+'"]');
if (todoElement) {
var checkbox = todoElement.querySelector('input[type="checkbox"]');
var desc = todoElement.querySelector('span');
checkbox.checked = model.isDone;
desc.innerText = model.text;
desc.className = "done-"+model.isDone;
}
}
/**
* Recalculate total number of ToDos and remaining ToDos and update
* appropriate elements in the DOM.
**/
var updateCounters = function(model) {
var count = 0;
var notDone = 0;
for (var id in model.todos) {
count++;
if ( ! model.todos[id].isDone ) {
notDone ++;
}
}
document.getElementById('remaining').innerText = notDone;
document.getElementById('length').innerText = count;
}
storageLoad();
});

Двоичные данные
lab5_data/javascript/1_storage_sync/icon.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 6.1 KiB

Просмотреть файл

@ -0,0 +1,30 @@
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="todo.css">
</head>
<body>
<h2>Todo</h2>
<div>
<span><span id="remaining"></span> of <span id="length"></span> remaining</span>
[ <a href="" id="archive">archive</a> ]
<ul class="unstyled" id="list">
</ul>
<form>
<input type="text" size="30"
placeholder="add new todo here">
<input class="btn-primary" type="submit" value="add">
</form>
</div>
<!-- poor man's template -->
<div id="templates" style="display: none;">
<li data-name="list">
<input type="checkbox">
<span></span>
</li>
</div>
<script src="controller.js"></script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,5 @@
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('index.html',
{width: 500, height: 309});
});

Просмотреть файл

@ -0,0 +1,13 @@
{
"manifest_version": 2,
"name": "Lab5a Storage sync",
"version": "1",
"app": {
"background": {
"scripts": ["main.js"]
}
},
"permissions": ["storage"],
"icons": { "128": "icon.png" }
}

Просмотреть файл

@ -0,0 +1,23 @@
body {
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
}
ul {
list-style: none;
max-height: 120px;
overflow-y: auto;
}
button, input[type=submit] {
background-color: #0074CC;
background-image: linear-gradient(top, #08C, #05C);
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
color: white;
}
.done-true {
text-decoration: line-through;
color: grey;
}

Просмотреть файл

@ -0,0 +1,261 @@
/**
A poor man's controller - a very simple module with basic MVC functionaliies
**/
(function(exports) {
var nextId = 1;
var TodoModel = function() {
this.todos = {};
this.listeners = [];
}
TodoModel.prototype.setTodos = function(todos) {
this.todos = todos;
var maxId = 0;
for (var id in todos) {
var idInt = parseInt(id);
maxId = idInt > maxId ? idInt : maxId;
}
nextId = maxId + 1;
this.notifyListeners('reset');
}
TodoModel.prototype.clearTodos = function() {
this.todos = {};
this.notifyListeners('removed');
}
TodoModel.prototype.archiveDone = function() {
var oldTodos = this.todos;
this.todos={};
for (var id in oldTodos) {
if ( ! oldTodos[id].isDone ) {
this.todos[id] = oldTodos[id];
}
}
this.notifyListeners('archived');
}
TodoModel.prototype.setTodoState = function(id, isDone) {
if ( this.todos[id].isDone != isDone ) {
this.todos[id].isDone = isDone;
this.notifyListeners('stateChanged', id);
}
}
TodoModel.prototype.addTodo = function(text, isDone, extras) {
var id = nextId++;
this.todos[id]={'id': id, 'text': text, 'isDone': isDone, 'extras': extras};
this.notifyListeners('added', id);
}
TodoModel.prototype.addListener = function(listener) {
this.listeners.push(listener);
}
TodoModel.prototype.notifyListeners = function(change, param) {
var this_ = this;
this.listeners.forEach(function(listener) {
listener(this_, change, param);
});
}
exports.TodoModel = TodoModel;
})(window);
(function(exports) {
var defaultDropText = "Or drop files here...";
var dropText = document.getElementById('dropText');
dropText.innerText = defaultDropText;
// on dragOver, we will change the style and text accordingly, depending on
// the data being transfered
var dragOver = function(e) {
e.stopPropagation();
e.preventDefault();
var valid = isValid(e.dataTransfer);
if (valid) {
dropText.innerText="Drop files and remote images and they will become Todos";
document.body.classList.add("dragging");
} else {
dropText.innerText="Can only drop files and remote images here";
document.body.classList.add("invalid-dragging");
}
}
var isValid = function(dataTransfer) {
return dataTransfer && dataTransfer.types
&& ( dataTransfer.types.indexOf('Files') >= 0
|| dataTransfer.types.indexOf('text/uri-list') >=0 )
}
// reset style and text to the default
var dragLeave = function(e) {
dropText.innerText=defaultDropText;
document.body.classList.remove('dragging');
document.body.classList.remove('invalid-dragging');
}
// on drop, we create the appropriate TODOs using dropped data
var drop = function(e, model) {
e.preventDefault();
e.stopPropagation();
if (isValid(e.dataTransfer)) {
if (e.dataTransfer.types.indexOf('Files') >= 0) {
var files = e.dataTransfer.files;
for (var i = 0; i < files.length; i++) {
var text = files[i].name+', '+files[i].size+' bytes';
model.addTodo(text, false, {file: files[i]});
}
} else { // uris
var uri=e.dataTransfer.getData("text/uri-list");
model.addTodo(uri, false, {uri: uri});
}
}
dragLeave();
}
exports.setDragHandlers = function(model) {
document.body.addEventListener("dragover", dragOver, false);
document.body.addEventListener("dragleave", dragLeave, false);
document.body.addEventListener("drop", function(e) {
drop(e, model);
}, false);
}
})(window);
window.addEventListener('DOMContentLoaded', function() {
var model = new TodoModel();
var form = document.querySelector('form');
var archive = document.getElementById('archive');
var list = document.getElementById('list');
var todoTemplate = document.querySelector('#templates > [data-name="list"]');
/**
* When the form is submitted, we need to add a new todo and clear the input
**/
form.addEventListener('submit', function(e) {
var textEl = e.target.querySelector('input[type="text"]');
model.addTodo(textEl.value, false);
textEl.value=null;
e.preventDefault();
});
/**
* A simple handler for the archive link
**/
archive.addEventListener('click', function(e) {
model.archiveDone();
e.preventDefault();
});
/**
* Listen to changes in the model and trigger the appropriate changes in the view
**/
model.addListener(function(model, changeType, param) {
if ( changeType === 'removed' || changeType === 'archived' || changeType === 'reset') {
redrawUI(model);
} else if ( changeType === 'added' ) {
drawTodo(model.todos[param], list);
} else if ( changeType === 'stateChanged') {
updateTodo(model.todos[param]);
}
storageSave();
updateCounters(model);
});
// If there is saved data in storage, use it. Otherwise, bootstrap with sample todos
var storageLoad = function() {
chrome.storage.sync.get('todolist', function(value) {
if (value && value.todolist) {
model.setTodos(value.todolist);
} else {
model.addTodo('learn Chrome Apps', true);
model.addTodo('build a Chrome App', false);
}
});
}
var storageSave = function() {
chrome.storage.sync.set({'todolist': model.todos});
};
/**
* Clean the current ToDo list and add elements again
**/
var redrawUI = function(model) {
list.innerHTML='';
for (var id in model.todos) {
drawTodo(model.todos[id], list);
}
};
/**
* Deep clone a template node, set its ID and add it to the DOM container.
* Add a listener to the newly added checkbox, so it can trigger the state flip
* when the checkbox is clicked.
**/
var drawTodo = function(todoObj, container) {
var el = todoTemplate.cloneNode(true);
el.setAttribute('data-id', todoObj.id);
container.appendChild(el);
updateTodo(todoObj);
var checkbox = el.querySelector('input[type="checkbox"]');
checkbox.addEventListener('change', function(e) {
model.setTodoState(todoObj.id, e.target.checked);
});
}
/**
* Find the element corresponding to the given ToDo model object and update its
* state and description from the model.
*/
var updateTodo = function(model) {
var todoElement = list.querySelector('li[data-id="'+model.id+'"]');
if (todoElement) {
var checkbox = todoElement.querySelector('input[type="checkbox"]');
var desc = todoElement.querySelector('span');
checkbox.checked = model.isDone;
desc.innerText = model.text;
desc.className = "done-"+model.isDone;
}
}
/**
* Recalculate total number of ToDos and remaining ToDos and update
* appropriate elements in the DOM.
**/
var updateCounters = function(model) {
var count = 0;
var notDone = 0;
for (var id in model.todos) {
count++;
if ( ! model.todos[id].isDone ) {
notDone ++;
}
}
document.getElementById('remaining').innerText = notDone;
document.getElementById('length').innerText = count;
}
storageLoad();
setDragHandlers(model);
});

Двоичные данные
lab5_data/javascript/2_drop_files/icon.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 6.1 KiB

Просмотреть файл

@ -0,0 +1,32 @@
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="todo.css">
</head>
<body>
<h2>Todo</h2>
<div>
<span><span id="remaining"></span> of <span id="length"></span> remaining</span>
[ <a href="" id="archive">archive</a> ]
<ul class="unstyled" id="list">
</ul>
<form>
<input type="text" size="30"
placeholder="add new todo here">
<input class="btn-primary" type="submit" value="add">
</form>
<div id="dropText">
</div>
</div>
<!-- poor man's template -->
<div id="templates" style="display: none;">
<li data-name="list">
<input type="checkbox">
<span></span>
</li>
</div>
<script src="controller.js"></script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,5 @@
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('index.html',
{width: 500, height: 309});
});

Просмотреть файл

@ -0,0 +1,13 @@
{
"manifest_version": 2,
"name": "Lab5b Drop files",
"version": "1",
"app": {
"background": {
"scripts": ["main.js"]
}
},
"permissions": ["storage"],
"icons": { "128": "icon.png" }
}

Просмотреть файл

@ -0,0 +1,43 @@
html {
height: 100%;
overflow: hidden;
}
body {
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
height: 100%;
}
@-webkit-keyframes switch-green {
from { background-color: white;} to {background-color: rgb(163, 255, 163);}
}
@-webkit-keyframes switch-red {
from { background-color: white;} to {background-color: rgb(255, 203, 203);}
}
.dragging {
-webkit-animation: switch-green 0.5s ease-in-out 0 infinite alternate;
}
.invalid-dragging {
-webkit-animation: switch-red 0.5s ease-in-out 0 infinite alternate;
}
ul {
list-style: none;
max-height: 120px;
overflow-y: auto;
}
button, input[type=submit] {
background-color: #0074CC;
background-image: linear-gradient(top, #08C, #05C);
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
color: white;
}
.done-true {
text-decoration: line-through;
color: grey;
}