Commit f189c265 authored by Romuald Quantin's avatar Romuald Quantin Committed by Sindre Sorhus

Close GH-162: Somajs.

parent 352327f7
......@@ -92,6 +92,9 @@ had it been designed for web apps">AngularJS</a>
<li>
<a href="reference-examples/vanillajs/index.html" data-source="https://developer.mozilla.org/en/JavaScript" data-content="You know JavaScript right? :P">Vanilla JS</a>
</li>
<li>
<a href="architecture-examples/somajs/index.html" data-source="http://somajs.github.com/somajs/" data-content="soma.js is a javascript model-view-controller (MVC) framework that is meant to help developers to write loosely-coupled applications to increase scalability and maintainability.">soma.js</a>
</li>
</ul>
</div>
<div class="span4">
......
# [soma.js](http://somajs.github.com/somajs) • [TodoMVC](http://todomvc.com)
soma.js is a javascript model-view-controller (MVC) framework that is meant to help developers to write loosely-coupled applications to increase scalability and maintainability.
The main idea behind the MVC pattern is to separate the data (model), the user interface (view) and the logic of the application (controller). They must be independent and should not know about each other in order to increase the scalability of the application.
soma.js is providing tools to make the three parts "talk" to each other, keeping the view and the model free of framework code, using only native events that can be dispatched from either the framework or the DOM itself.
## Credits
Created by [Romuald Quantin](http://www.soundstep.com) using [soma.js](http://somajs.github.com/somajs)
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>soma.js • 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" placeholder="What needs to be done?" autofocus>
</header>
<section id="main">
<input id="toggle-all" type="checkbox">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list"></ul>
</section>
<footer id="footer"></footer>
</section>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Template by <a href="http://github.com/sindresorhus">Sindre Sorhus</a></p>
<p>Created by <a href="http://soundstep.com">Romuald Quantin</a></p>
<p>With <a href="http://somajs.github.com/somajs/">soma.js</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
<script id="todo-list-template" type="text/x-handlebars-template">
{{#this}}
<li {{#if completed}}class="completed"{{/if}} data-id="{{id}}">
<div class="view">
<input class="toggle" type="checkbox" {{#if completed}}checked{{/if}}>
<label>{{title}}</label>
<button class="destroy"></button>
</div>
<input class="edit" value="{{title}}">
</li>
{{/this}}
</script>
<script id="footer-template" type="text/x-handlebars-template">
<span id="todo-count"><strong>{{active}}</strong> {{itemLabel}} left</span>
<button id="clear-completed">Clear completed ({{completed}})</button>
</script>
<script src="../../../assets/base.js"></script>
<script src="../../../assets/handlebars.min.js"></script>
<script src="../../../assets/jquery.min.js"></script>
<script src="js/lib/soma-native_v1.0.2_min.js"></script>
<script src="js/todos/models/models.js"></script>
<script src="js/todos/views/views.js"></script>
<script src="js/todos/controllers/controllers.js"></script>
<script src="js/app.js"></script>
</body>
</html>
\ No newline at end of file
var todo = window.todo || {};
(function( window ) {
'use strict';
todo.TodoApp = new soma.Application.extend({
init: function() {
this.addModel( todo.TodoModel.NAME, new todo.TodoModel() );
this.addCommand( todo.TodoEvent.RENDER, todo.TodoCommand );
this.addCommand( todo.TodoEvent.CREATE, todo.TodoCommand );
this.addCommand( todo.TodoEvent.DELETE, todo.TodoCommand );
this.addCommand( todo.TodoEvent.TOGGLE, todo.TodoCommand );
this.addCommand( todo.TodoEvent.UPDATE, todo.TodoCommand );
this.addCommand( todo.TodoEvent.TOGGLE_ALL, todo.TodoCommand );
this.addCommand( todo.TodoEvent.CLEAR_COMPLETED, todo.TodoCommand );
this.addView( todo.TodoListView.NAME, new todo.TodoListView( $('#todo-list')[0] ) );
this.addView( todo.FooterView.NAME, new todo.FooterView( $('#footer')[0] ) );
this.addView( todo.TodoInputView.NAME, new todo.TodoInputView( $('#new-todo')[0] ) );
},
start: function() {
this.dispatchEvent( new todo.TodoEvent( todo.TodoEvent.RENDER ) );
}
});
var app = new todo.TodoApp();
})( window );
var todo = window.todo || {};
(function( window ) {
'use strict';
todo.TodoCommand = soma.Command.extend({
execute: function( event ) {
var model = this.getModel( todo.TodoModel.NAME );
switch( event.type ) {
case todo.TodoEvent.RENDER:
this.getView( todo.TodoListView.NAME ).render( model.data, model.getActiveLength() );
this.getView( todo.FooterView.NAME ).render( model.dataFooter );
break;
case todo.TodoEvent.CREATE:
model.addItem( event.params.todoTitle );
break;
case todo.TodoEvent.DELETE:
model.removeItem( event.params.todoId );
break;
case todo.TodoEvent.TOGGLE:
model.toggleItem( event.params.todoId );
break;
case todo.TodoEvent.TOGGLE_ALL:
model.toggleAll( event.params.toggleAll );
break;
case todo.TodoEvent.UPDATE:
model.updateItem( event.params.todoId, event.params.todoTitle );
break;
case todo.TodoEvent.CLEAR_COMPLETED:
model.clearCompleted();
break;
}
}
});
todo.TodoEvent = soma.Event.extend( {
constructor: function( type, todoTitle, todoId, toggleAll ) {
return soma.Event.call( this, type, {
todoTitle: todoTitle,
todoId: todoId,
toggleAll: toggleAll
});
}
} );
todo.TodoEvent.RENDER = 'TodoEvent.RENDER';
todo.TodoEvent.CREATE = 'TodoEvent.CREATE';
todo.TodoEvent.DELETE = 'TodoEvent.DELETE';
todo.TodoEvent.UPDATE = 'TodoEvent.UPDATE';
todo.TodoEvent.TOGGLE = 'TodoEvent.TOGGLE';
todo.TodoEvent.TOGGLE_ALL = 'TodoEvent.TOGGLE_ALL';
todo.TodoEvent.CLEAR_COMPLETED = 'TodoEvent.CLEAR_COMPLETED';
})( window );
\ No newline at end of file
var todo = window.todo || {};
(function( window ) {
'use strict';
todo.TodoModel = new soma.Model.extend({
dataFooter: null,
init: function() {
this.storeKey = 'todos-somajs';
this.data = JSON.parse( this.getStore() ) || [];
this.updateDataFooter();
},
updateDataFooter: function() {
var active = this.getActiveLength();
this.dataFooter = {
active: active,
itemLabel: active === 1 ? 'item' : 'items',
completed: this.data.length - active,
length: this.data.length
};
},
addItem: function( title ) {
this.data.push({
id: this.uuid(),
title: title,
completed: false
});
this.update();
},
removeItem: function( id ) {
this.data.splice( this.getIndexById( id ), 1 );
this.update();
},
toggleItem: function( id ) {
var item = this.data[ this.getIndexById( id ) ];
item.completed = !item.completed;
this.update();
},
updateItem: function( id, title ) {
this.data[ this.getIndexById( id ) ].title = title;
this.update();
},
toggleAll: function( toggleValue ) {
for (var i = 0; i < this.data.length; i++) {
this.data[i].completed = toggleValue;
}
this.update();
},
clearCompleted: function() {
var i = this.data.length;
while( i-- ) {
if ( this.data[i].completed ) {
this.data.splice( i, 1 );
}
}
this.update();
},
getIndexById: function( id ) {
for ( var i = 0; i < this.data.length; i++ ) {
if ( this.data[i].id === id ) {
return i;
}
}
return -1;
},
getActiveLength: function() {
var count = 0;
for ( var i = 0; i < this.data.length; i++ ) {
if ( !this.data[i].completed ) {
count++;
}
}
return count;
},
update: function() {
this.updateDataFooter();
this.setStore( this.data );
this.dispatchEvent( new todo.TodoEvent( todo.TodoEvent.RENDER ) );
},
getStore: function() {
return localStorage.getItem( this.storeKey );
},
setStore: function() {
localStorage.setItem( this.storeKey, JSON.stringify( this.data ) );
},
// https://gist.github.com/1308368
uuid: function(a,b){for(b=a='';a++<36;b+=a*51&52?(a^15?8^Math.random()*(a^20?16:4):4).toString(16):'-');return b;}
});
todo.TodoModel.NAME = 'TodoModel';
})( window );
\ No newline at end of file
var todo = window.todo || {};
(function( window ) {
'use strict';
var ENTER_KEY = 13;
todo.TodoListView = soma.View.extend({
template: null,
init: function() {
this.template = Handlebars.compile( $('#' + this.domElement.id + '-template').html() );
$(this.domElement).on( 'click', '.destroy', this.destroy.bind(this) );
$(this.domElement).on( 'click', '.toggle', this.toggle.bind(this) );
$(this.domElement).on( 'dblclick', '.view', this.edit );
$(this.domElement).on( 'blur', '.edit', this.update.bind(this) );
$(this.domElement).on( 'keypress', '.edit', this.blurInput );
$('#toggle-all').click( this.toggleAll );
},
render: function( data, activeCount ) {
$(this.domElement).html( this.template( data ) );
$('#toggle-all').prop( 'checked', !activeCount );
$('#main').toggle( !!data.length );
},
destroy: function( event ) {
var id = $(event.target).closest('li').attr('data-id');
this.dispatchEvent( new todo.TodoEvent( todo.TodoEvent.DELETE, null, id ) );
},
toggle: function( event ) {
var id = $(event.target).closest('li').attr('data-id');
this.dispatchEvent( new todo.TodoEvent( todo.TodoEvent.TOGGLE, null, id ) );
},
toggleAll: function() {
this.dispatchEvent( new todo.TodoEvent( todo.TodoEvent.TOGGLE_ALL, null, null, $(this).prop('checked') ) );
},
edit: function( event ) {
$(this).closest('li').addClass('editing').find('.edit').select();
},
update: function( event ) {
var li = $(event.target).closest('li').removeClass('editing');
var id = li.attr('data-id');
var val = li.find('.edit').val().trim();
if ( val ) {
this.dispatchEvent( new todo.TodoEvent( todo.TodoEvent.UPDATE, val, id ) );
}
else {
this.dispatchEvent( new todo.TodoEvent( todo.TodoEvent.DELETE, null, id ) );
}
},
blurInput: function( event ) {
if (event.which === ENTER_KEY) {
event.target.blur();
}
}
});
todo.TodoListView.NAME = 'TodoListView';
todo.TodoInputView = soma.View.extend({
init: function() {
$(this.domElement).keypress( this.keyPressHandler.bind(this) );
$(this.domElement).blur( this.blur );
},
keyPressHandler: function( event ) {
if (event.which === ENTER_KEY) {
this.createItem();
}
},
createItem: function() {
var value = this.domElement.value.trim();
if (value) {
this.dispatchEvent( new todo.TodoEvent( todo.TodoEvent.CREATE, value ) );
}
this.domElement.value = '';
},
blur: function( event ) {
if (!this.value.trim()) {
this.value = '';
}
}
});
todo.TodoInputView.NAME = 'TodoInputView';
todo.FooterView = soma.View.extend({
template: null,
init: function() {
this.template = Handlebars.compile( $('#' + this.domElement.id + '-template').html() );
$(this.domElement).on( 'click', '#clear-completed', this.clearCompleted.bind(this) );
},
render: function( data ) {
$(this.domElement).html( this.template( data ) );
$(this.domElement).toggle( !!data.length );
$('#clear-completed').toggle( !!data.completed );
},
clearCompleted: function(event) {
this.dispatchEvent( new todo.TodoEvent( todo.TodoEvent.CLEAR_COMPLETED ) );
}
});
todo.FooterView.NAME = 'FooterView';
})( window );
\ No newline at end of file
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