Commit 41a93a95 authored by Arthur Verschaeve's avatar Arthur Verschaeve

(Temporarily) drop montage app

The app is getting outdated and is these days even totally broken sowe
decided to remove it, till someone volunteers to maintain it and PRs it
back with all remaining issues fixed.

There was an open PR with some updates but it went inactive:
https://github.com/tastejs/todomvc/pull/1241

Close #1241
Fix #1222
Ref #1234
Ref #1110
parent a500cab9
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Motorola Mobility LLC nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
================================================================================
Copyright (c) 2013, António Afonso.
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Motorola Mobility LLC nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
#montage-todomvc .visible {
display: block;
}
{
"name": "todomvc-montage",
"version": "0.0.0",
"dependencies": {
"todomvc-common": "~0.3.0"
}
}
html,
body {
margin: 0;
padding: 0;
}
button {
margin: 0;
padding: 0;
border: 0;
background: none;
font-size: 100%;
vertical-align: baseline;
font-family: inherit;
color: inherit;
-webkit-appearance: none;
-ms-appearance: none;
-o-appearance: none;
appearance: none;
}
body {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em;
background: #eaeaea url('bg.png');
color: #4d4d4d;
width: 550px;
margin: 0 auto;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
-o-font-smoothing: antialiased;
font-smoothing: antialiased;
}
button,
input[type="checkbox"] {
outline: none;
}
#todoapp {
background: #fff;
background: rgba(255, 255, 255, 0.9);
margin: 130px 0 40px 0;
border: 1px solid #ccc;
position: relative;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2),
0 25px 50px 0 rgba(0, 0, 0, 0.15);
}
#todoapp:before {
content: '';
border-left: 1px solid #f5d6d6;
border-right: 1px solid #f5d6d6;
width: 2px;
position: absolute;
top: 0;
left: 40px;
height: 100%;
}
#todoapp input::-webkit-input-placeholder {
font-style: italic;
}
#todoapp input::-moz-placeholder {
font-style: italic;
color: #a9a9a9;
}
#todoapp h1 {
position: absolute;
top: -120px;
width: 100%;
font-size: 70px;
font-weight: bold;
text-align: center;
color: #b3b3b3;
color: rgba(255, 255, 255, 0.3);
text-shadow: -1px -1px rgba(0, 0, 0, 0.2);
-webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility;
-ms-text-rendering: optimizeLegibility;
-o-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility;
}
#header {
padding-top: 15px;
border-radius: inherit;
}
#header:before {
content: '';
position: absolute;
top: 0;
right: 0;
left: 0;
height: 15px;
z-index: 2;
border-bottom: 1px solid #6c615c;
background: #8d7d77;
background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8)));
background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670');
border-top-left-radius: 1px;
border-top-right-radius: 1px;
}
#new-todo,
.edit {
position: relative;
margin: 0;
width: 100%;
font-size: 24px;
font-family: inherit;
line-height: 1.4em;
border: 0;
outline: none;
color: inherit;
padding: 6px;
border: 1px solid #999;
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
-o-font-smoothing: antialiased;
font-smoothing: antialiased;
}
#new-todo {
padding: 16px 16px 16px 60px;
border: none;
background: rgba(0, 0, 0, 0.02);
z-index: 2;
box-shadow: none;
}
#main {
position: relative;
z-index: 2;
border-top: 1px dotted #adadad;
}
label[for='toggle-all'] {
display: none;
}
#toggle-all {
position: absolute;
top: -42px;
left: -4px;
width: 40px;
text-align: center;
/* Mobile Safari */
border: none;
}
#toggle-all:before {
content: '»';
font-size: 28px;
color: #d9d9d9;
padding: 0 25px 7px;
}
#toggle-all:checked:before {
color: #737373;
}
#todo-list {
margin: 0;
padding: 0;
list-style: none;
}
#todo-list li {
position: relative;
font-size: 24px;
border-bottom: 1px dotted #ccc;
}
#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: 13px 17px 12px 17px;
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;
/* Mobile Safari */
border: none;
-webkit-appearance: none;
-ms-appearance: none;
-o-appearance: none;
appearance: none;
}
#todo-list li .toggle:after {
content: '✔';
/* 40 + a couple of pixels visual adjustment */
line-height: 43px;
font-size: 20px;
color: #d9d9d9;
text-shadow: 0 -1px 0 #bfbfbf;
}
#todo-list li .toggle:checked:after {
color: #85ada7;
text-shadow: 0 1px 0 #669991;
bottom: 1px;
position: relative;
}
#todo-list li label {
white-space: pre;
word-break: break-word;
padding: 15px 60px 15px 15px;
margin-left: 45px;
display: block;
line-height: 1.2;
-webkit-transition: color 0.4s;
transition: color 0.4s;
}
#todo-list li.completed label {
color: #a9a9a9;
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: 22px;
color: #a88a8a;
-webkit-transition: all 0.2s;
transition: all 0.2s;
}
#todo-list li .destroy:hover {
text-shadow: 0 0 1px #000,
0 0 10px rgba(199, 107, 107, 0.8);
-webkit-transform: scale(1.3);
transform: scale(1.3);
}
#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: 0 15px;
position: absolute;
right: 0;
bottom: -31px;
left: 0;
height: 20px;
z-index: 1;
text-align: center;
}
#footer:before {
content: '';
position: absolute;
right: 0;
bottom: 31px;
left: 0;
height: 50px;
z-index: -1;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3),
0 6px 0 -3px rgba(255, 255, 255, 0.8),
0 7px 1px -3px rgba(0, 0, 0, 0.3),
0 43px 0 -6px rgba(255, 255, 255, 0.8),
0 44px 2px -6px rgba(0, 0, 0, 0.2);
}
#todo-count {
float: left;
text-align: left;
}
#filters {
margin: 0;
padding: 0;
list-style: none;
position: absolute;
right: 0;
left: 0;
}
#filters li {
display: inline;
}
#filters li a {
color: #83756f;
margin: 2px;
text-decoration: none;
}
#filters li a.selected {
font-weight: bold;
}
#clear-completed {
float: right;
position: relative;
line-height: 20px;
text-decoration: none;
background: rgba(0, 0, 0, 0.1);
font-size: 11px;
padding: 0 10px;
border-radius: 3px;
box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2);
}
#clear-completed:hover {
background: rgba(0, 0, 0, 0.15);
box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3);
}
#info {
margin: 65px auto 0;
color: #a6a6a6;
font-size: 12px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
text-align: center;
}
#info a {
color: inherit;
}
/*
Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox and Opera
*/
@media screen and (-webkit-min-device-pixel-ratio:0) {
#toggle-all,
#todo-list li .toggle {
background: none;
}
#todo-list li .toggle {
height: 40px;
}
#toggle-all {
top: -56px;
left: -15px;
width: 65px;
height: 41px;
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
-webkit-appearance: none;
appearance: none;
}
}
.hidden {
display: none;
}
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;
}
.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);
-webkit-transition-property: left;
transition-property: left;
-webkit-transition-duration: 500ms;
transition-duration: 500ms;
}
@media (min-width: 899px) {
.learn-bar {
width: auto;
margin: 0 0 0 300px;
}
.learn-bar > .learn {
left: 8px;
}
.learn-bar #todoapp {
width: 550px;
margin: 130px auto 40px auto;
}
}
(function () {
'use strict';
// Underscore's Template Module
// Courtesy of underscorejs.org
var _ = (function (_) {
_.defaults = function (object) {
if (!object) {
return object;
}
for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {
var iterable = arguments[argsIndex];
if (iterable) {
for (var key in iterable) {
if (object[key] == null) {
object[key] = iterable[key];
}
}
}
}
return object;
}
// By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters.
_.templateSettings = {
evaluate : /<%([\s\S]+?)%>/g,
interpolate : /<%=([\s\S]+?)%>/g,
escape : /<%-([\s\S]+?)%>/g
};
// When customizing `templateSettings`, if you don't want to define an
// interpolation, evaluation or escaping regex, we need one that is
// guaranteed not to match.
var noMatch = /(.)^/;
// Certain characters need to be escaped so that they can be put into a
// string literal.
var escapes = {
"'": "'",
'\\': '\\',
'\r': 'r',
'\n': 'n',
'\t': 't',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
_.template = function(text, data, settings) {
var render;
settings = _.defaults({}, settings, _.templateSettings);
// Combine delimiters into one regular expression via alternation.
var matcher = new RegExp([
(settings.escape || noMatch).source,
(settings.interpolate || noMatch).source,
(settings.evaluate || noMatch).source
].join('|') + '|$', 'g');
// Compile the template source, escaping string literals appropriately.
var index = 0;
var source = "__p+='";
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
source += text.slice(index, offset)
.replace(escaper, function(match) { return '\\' + escapes[match]; });
if (escape) {
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
}
if (interpolate) {
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
}
if (evaluate) {
source += "';\n" + evaluate + "\n__p+='";
}
index = offset + match.length;
return match;
});
source += "';\n";
// If a variable is not specified, place data values in local scope.
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'');};\n" +
source + "return __p;\n";
try {
render = new Function(settings.variable || 'obj', '_', source);
} catch (e) {
e.source = source;
throw e;
}
if (data) return render(data, _);
var template = function(data) {
return render.call(this, data, _);
};
// Provide the compiled function source as a convenience for precompilation.
template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
return template;
};
return _;
})({});
if (location.hostname === 'todomvc.com') {
window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script'));
}
function redirect() {
if (location.hostname === 'tastejs.github.io') {
location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com');
}
}
function findRoot() {
var base = location.href.indexOf('examples/');
return location.href.substr(0, base);
}
function getFile(file, callback) {
if (!location.host) {
return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.');
}
var xhr = new XMLHttpRequest();
xhr.open('GET', findRoot() + file, true);
xhr.send();
xhr.onload = function () {
if (xhr.status === 200 && callback) {
callback(xhr.responseText);
}
};
}
function Learn(learnJSON, config) {
if (!(this instanceof Learn)) {
return new Learn(learnJSON, config);
}
var template, framework;
if (typeof learnJSON !== 'object') {
try {
learnJSON = JSON.parse(learnJSON);
} catch (e) {
return;
}
}
if (config) {
template = config.template;
framework = config.framework;
}
if (!template && learnJSON.templates) {
template = learnJSON.templates.todomvc;
}
if (!framework && document.querySelector('[data-framework]')) {
framework = document.querySelector('[data-framework]').dataset.framework;
}
this.template = template;
if (learnJSON.backend) {
this.frameworkJSON = learnJSON.backend;
this.append({
backend: true
});
} else if (learnJSON[framework]) {
this.frameworkJSON = learnJSON[framework];
this.append();
}
}
Learn.prototype.append = function (opts) {
var aside = document.createElement('aside');
aside.innerHTML = _.template(this.template, this.frameworkJSON);
aside.className = 'learn';
if (opts && opts.backend) {
// Remove demo link
var sourceLinks = aside.querySelector('.source-links');
var heading = sourceLinks.firstElementChild;
var sourceLink = sourceLinks.lastElementChild;
// Correct link path
var href = sourceLink.getAttribute('href');
sourceLink.setAttribute('href', href.substr(href.lastIndexOf('http')));
sourceLinks.innerHTML = heading.outerHTML + sourceLink.outerHTML;
} else {
// Localize demo links
var demoLinks = aside.querySelectorAll('.demo-link');
Array.prototype.forEach.call(demoLinks, function (demoLink) {
if (demoLink.getAttribute('href').substr(0, 4) !== 'http') {
demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href'));
}
});
}
document.body.className = (document.body.className + ' learn-bar').trim();
document.body.insertAdjacentHTML('afterBegin', aside.outerHTML);
};
redirect();
getFile('learn.json', Learn);
})();
var Montage = require('montage').Montage;
exports.Todo = Montage.specialize({
constructor: {
value: function Todo() {
this.super();
}
},
initWithTitle: {
value: function (title) {
this.title = title;
return this;
}
},
title: {
value: null
},
completed: {
value: false
}
});
<!doctype html>
<html lang="en" data-framework="montage" id="montage-todomvc">
<head>
<meta charset="utf-8">
<title>Montage • TodoMVC</title>
<link rel="stylesheet" href="bower_components/todomvc-common/base.css">
<link rel="stylesheet" href="assets/app.css">
</head>
<body>
<div data-montage-id="todo-container" id="todo-container"></div>
<script src="bower_components/todomvc-common/base.js"></script>
<script src="index.html.bundle-0.js" data-montage-location="packages/montage@6364dae/" data-promise-location="packages/q@2847ee2/" data-montage-hash="6364dae" data-promise-hash="2847ee2" data-application-hash="6607c26"></script>
<script type="text/montage-serialization">
{
"owner": {
"prototype": "montage/ui/loader.reel",
"properties": {
"element": {"#": "todo-container"}
}
}
}
</script>
</body>
</html>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
# MontageJS TodoMVC Example
> MontageJS is a client-side HTML5 framework for building rich single-page applications. It offers time-tested patterns and principles, a modular architecture, and a friendly method to achieve a clean separation of concerns.
>
> _[MontageJS - montagejs.org](http://montagejs.org)_
## Learning MontageJS
The [MontageJS](http://montagejs.org) website is a great resource for getting started.
Here are some links you may find helpful:
* [Quick Start](http://montagejs.org/docs/montagejs-setup.html)
* [Demos](http://montagejs.org/docs/montagejs-examples.html)
* [API Reference](http://montagejs.org/api/)
* [Applications built with MontageJS](http://montagejs.org/apps/)
* [Blog](http://montagejs.org/blog/)
* [FAQ](http://montagejs.org/docs/faq.html)
* [MontageJS on GitHub](https://github.com/montagejs/montage)
Articles and guides from the community:
* [YouTube - Getting Started](http://www.youtube.com/watch?v=JfT1ML200JI)
* [My First MontageJS Application](http://renaun.com/blog/2013/05/my-first-montagejs-application/)
Get help from other Montage users:
* [IRC](http://webchat.freenode.net/?channels=montage)
* [MontageJS on Google Groups](https://groups.google.com/forum/?hl=en&fromgroups#!forum/montagejs)
* [MontageJS on Twitter](http://twitter.com/montagejs)
* [MontageJS on Google +](https://plus.google.com/116915300739108010954)
## Application Structure
MontageJS applications follow a unified directory structure that makes it easy to look for and add files. The following table provides a brief description of the TodoMVC application's directory structure.
Folder / File | Description |
------------ | -------------
assets | Contains global styles and the background image for the TodoMVC application.
core | Contains the data model.
index.html | Is the entry-point HTML document.
LICENSE.md | Contains copyright information.
package.json | Describes your app and its dependencies.
README.md | Describes the TodoMVC application.
ui | Contains the user interface components of the TodoMVC application, main.reel and todo-view.reel.
## Running the TodoMVC Example
MontageJS application development depends on npm, the Node package manager, which is distributed with Node.js. If you haven't done so already, be sure to [download](http://nodejs.org/download/) and run the prebuilt Node.js installer for your platform from the Node.js website. Then, to run the TodoMVC example locally, follow these steps:
1. Clone the todo-mvc [GitHub repo](https://github.com/montagejs/todo-mvc) in your desktop.
2. Use your command line tool to navigate to the cloned todo-mvc directory and install the modules required to run the demo:
```
cd todo-mvc
npm update
```
3. Spin up your preferred HTTP server and point your browser to the associated port.
> During development MontageJS applications rely on XHR to load their various components and modules, which is why you will need a web server to serve the demo.
> If you happen to have [minit](https://github.com/montagejs/minit), the Montage Initializer, installed (`npm install minit -g`) you can run `minit serve` from within the demo directory to set up a server on demand.
## A Note about the Source
You are looking at the nonminified source code of the application. MontageJS application development is divided into a development (creating the app) phase and a production (compiling the app) phase. During production—before submitting the application to the TodoMVC site—we use the Montage Optimizer (Mop) to minify the source code and create "bundles" (files) that consist of the application code and its dependencies, ready for deployment.
## Credit
This TodoMVC application was created by [Montagejs](http://montagejs.org).
#main,
#footer,
#clear-completed-container {
display: none;
}
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Main</title>
<link rel="stylesheet" href="main.css">
<script type="text/montage-serialization">
{
"owner": {
"properties": {
"element": {"#": "mainComponent"},
"_newTodoForm": {"#": "newTodoForm"},
"_newTodoInput": {"#": "newTodoField"}
}
},
"todoRepetition": {
"prototype": "montage/ui/repetition.reel",
"properties": {
"element": {"#": "todo-list"}
},
"bindings": {
"contentController": {"<-": "@owner.todoListController"}
}
},
"todoView": {
"prototype": "ui/todo-view.reel",
"properties": {
"element": {"#": "todoView"}
},
"bindings": {
"todo": {"<-": "@todoRepetition.objectAtCurrentIteration"}
}
},
"main": {
"prototype": "matte/ui/dynamic-element.reel",
"properties": {
"element": {"#": "main"}
},
"bindings": {
"classList.has('visible')": {
"<-": "@owner.todos.length > 0"
}
}
},
"footer": {
"prototype": "matte/ui/dynamic-element.reel",
"properties": {
"element": {"#": "footer"}
},
"bindings": {
"classList.has('visible')": {
"<-": "@owner.todos.length > 0"
}
}
},
"toggleAllCheckbox": {
"prototype": "native/ui/input-checkbox.reel",
"properties": {
"element": {"#": "toggle-all"}
},
"bindings": {
"checked": {"<->": "@owner.allCompleted"}
}
},
"todoCount": {
"prototype": "montage/ui/text.reel",
"properties": {
"element": {"#": "todo-count"}
},
"bindings": {
"value": {
"<-": "@owner.todosLeft.length"
}
}
},
"todoCountWording": {
"prototype": "montage/ui/text.reel",
"properties": {
"element": {"#": "todo-count-wording"}
},
"bindings": {
"value": {"<-": "@owner.todosLeft.length == 1 ? 'item' : 'items'"}
}
},
"completedCount": {
"prototype": "montage/ui/text.reel",
"properties": {
"element": {"#": "completed-count"}
},
"bindings": {
"value": {
"<-": "@owner.todosCompleted.length"
}
}
},
"clearCompletedContainer": {
"prototype": "matte/ui/dynamic-element.reel",
"properties": {
"element": {"#": "clear-completed-container"}
},
"bindings": {
"classList.has('visible')": {
"<-": "@owner.todosCompleted.length"
}
}
},
"clearCompletedButton": {
"prototype": "native/ui/button.reel",
"properties": {
"element": {"#": "clear-completed"}
},
"listeners": [
{
"type": "action",
"listener": {"@": "owner"},
"capture": false
}
]
}
}
</script>
</head>
<body>
<div data-montage-id="mainComponent">
<section id="todoapp">
<header id="header">
<h1>todos</h1>
<form data-montage-id="newTodoForm">
<input type="text" data-montage-id="newTodoField" id="new-todo" placeholder="What needs to be done?" autofocus="">
</form>
</header>
<section data-montage-id="main" id="main">
<input type="checkbox" data-montage-id="toggle-all" id="toggle-all">
<label for="toggle-all">Mark all as complete</label>
<ul data-montage-id="todo-list" id="todo-list">
<li data-montage-id="todoView"></li>
</ul>
</section>
<footer data-montage-id="footer" id="footer">
<span id="todo-count"><strong data-montage-id="todo-count">0</strong> <span data-montage-id="todo-count-wording">items</span> left</span>
<div data-montage-id="clear-completed-container" id="clear-completed-container">
<button data-montage-id="clear-completed" id="clear-completed">Clear completed (<span data-montage-id="completed-count">0</span>)</button>
</div>
</footer>
</section>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Created with <a href="http://github.com/montagejs/montage">Montage</a> </p>
<p>Source available at <a href="http://github.com/montagejs/todo-mvc">Montage-TodoMVC</a> </p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
</div>
</body>
</html>
\ No newline at end of file
var Component = require('montage/ui/component').Component;
var RangeController = require('montage/core/range-controller').RangeController;
var Todo = require('core/todo').Todo;
var Serializer = require('montage/core/serialization').Serializer;
var Deserializer = require('montage/core/serialization').Deserializer;
var LOCAL_STORAGE_KEY = 'todos-montage';
exports.Main = Component.specialize({
_newTodoForm: {
value: null
},
_newTodoInput: {
value: null
},
todoListController: {
value: null
},
constructor: {
value: function Main() {
this.todoListController = new RangeController();
this.addPathChangeListener('todos.every{completed}', this, 'handleTodosCompletedChanged');
this.defineBindings({
'todos': {'<-': 'todoListController.organizedContent'},
'todosLeft': {'<-': 'todos.filter{!completed}'},
'todosCompleted': {'<-': 'todos.filter{completed}'}
});
}
},
templateDidLoad: {
value: function () {
this.load();
}
},
load: {
value: function () {
if (localStorage) {
var todoSerialization = localStorage.getItem(LOCAL_STORAGE_KEY);
if (todoSerialization) {
var deserializer = new Deserializer(),
self = this;
deserializer.init(todoSerialization, require)
.deserializeObject()
.then(function (todos) {
self.todoListController.content = todos;
}).fail(function (error) {
console.error('Could not load saved tasks.');
console.debug('Could not deserialize', todoSerialization);
console.log(error.stack);
});
}
}
}
},
save: {
value: function () {
if (localStorage) {
var todos = this.todoListController.content,
serializer = new Serializer().initWithRequire(require);
localStorage.setItem(LOCAL_STORAGE_KEY, serializer.serializeObject(todos));
}
}
},
enterDocument: {
value: function (firstTime) {
if (firstTime) {
this._newTodoForm.identifier = 'newTodoForm';
this._newTodoForm.addEventListener('submit', this, false);
this.addEventListener('destroyTodo', this, true);
window.addEventListener('beforeunload', this, true);
}
}
},
captureDestroyTodo: {
value: function (evt) {
this.destroyTodo(evt.detail.todo);
}
},
createTodo: {
value: function (title) {
var todo = new Todo().initWithTitle(title);
this.todoListController.add(todo);
return todo;
}
},
destroyTodo: {
value: function (todo) {
this.todoListController.delete(todo);
return todo;
}
},
_allCompleted: {
value: null
},
allCompleted: {
get: function () {
return this._allCompleted;
},
set: function (value) {
this._allCompleted = value;
this.todoListController.organizedContent.forEach(function (member) {
member.completed = value;
});
}
},
todos: {
value: null
},
todosLeft: {
value: null
},
todosCompleted: {
value: null
},
// Handlers
handleNewTodoFormSubmit: {
value: function (evt) {
evt.preventDefault();
var title = this._newTodoInput.value.trim();
if (title === '') {
return;
}
this.createTodo(title);
this._newTodoInput.value = null;
}
},
handleTodosCompletedChanged: {
value: function (value) {
this._allCompleted = value;
this.dispatchOwnPropertyChange('allCompleted', value);
}
},
handleClearCompletedButtonAction: {
value: function () {
var completedTodos = this.todoListController.organizedContent.filter(function (todo) {
return todo.completed;
});
if (completedTodos.length > 0) {
this.todoListController.deleteEach(completedTodos);
}
}
},
captureBeforeunload: {
value: function () {
this.save();
}
}
});
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>TodoView</title>
<script type="text/montage-serialization">
{
"owner": {
"properties": {
"element": {"#": "todoView"},
"editInput": {"@": "editInput"}
}
},
"todoTitle": {
"prototype": "montage/ui/text.reel",
"properties": {
"element": {"#": "todoTitle"}
},
"bindings": {
"value": {"<-": "@owner.todo.title"}
}
},
"todoCompletedCheckbox": {
"prototype": "native/ui/input-checkbox.reel",
"properties": {
"element": {"#": "todoCompletedCheckbox"}
},
"bindings": {
"checked": {"<->": "@owner.todo.completed"}
}
},
"destroyButton": {
"prototype": "native/ui/button.reel",
"properties": {
"element": {"#": "destroyButton"}
},
"listeners": [
{
"type": "action",
"listener": {"@": "owner"},
"capture": true
}
]
},
"editInput": {
"prototype": "native/ui/input-text.reel",
"properties": {
"element": {"#": "edit-input"}
},
"bindings": {
"value": {"<-": "@owner.todo.title"}
}
}
}
</script>
</head>
<body>
<li data-montage-id="todoView">
<div class="view">
<input type="checkbox" data-montage-id="todoCompletedCheckbox" class="toggle">
<label data-montage-id="todoTitle"></label>
<button data-montage-id="destroyButton" class="destroy"></button>
</div>
<form data-montage-id="edit">
<input type="text" data-montage-id="edit-input" class="edit" value="Rule the web">
</form>
</li>
</body>
</html>
\ No newline at end of file
var Component = require('montage/ui/component').Component;
exports.TodoView = Component.specialize({
todo: {
value: null
},
editInput: {
value: null
},
constructor: {
value: function TodoView() {
this.defineBinding('isCompleted', {
'<-': 'todo.completed'
});
}
},
enterDocument: {
value: function (firstTime) {
if (firstTime) {
this.element.addEventListener('dblclick', this, false);
this.element.addEventListener('blur', this, true);
this.element.addEventListener('submit', this, false);
}
}
},
captureDestroyButtonAction: {
value: function () {
this.dispatchDestroy();
}
},
dispatchDestroy: {
value: function () {
this.dispatchEventNamed('destroyTodo', true, true, {todo: this.todo});
}
},
handleDblclick: {
value: function () {
this.isEditing = true;
}
},
_isEditing: {
value: false
},
isEditing: {
get: function () {
return this._isEditing;
},
set: function (value) {
if (value === this._isEditing) {
return;
}
if (value) {
this.classList.add('editing');
} else {
this.classList.remove('editing');
}
this._isEditing = value;
this.needsDraw = true;
}
},
_isCompleted: {
value: false
},
isCompleted: {
get: function () {
return this._isCompleted;
},
set: function (value) {
if (value === this._isCompleted) {
return;
}
if (value) {
this.classList.add('completed');
} else {
this.classList.remove('completed');
}
this._isCompleted = value;
this.needsDraw = true;
}
},
captureBlur: {
value: function (evt) {
if (this.isEditing && this.editInput.element === evt.target) {
this._submitTitle();
}
}
},
handleSubmit: {
value: function (evt) {
if (this.isEditing) {
evt.preventDefault();
this._submitTitle();
}
}
},
_submitTitle: {
value: function () {
var title = this.editInput.value.trim();
if ('' === title) {
this.dispatchDestroy();
} else {
this.todo.title = title;
}
this.isEditing = false;
}
},
draw: {
value: function () {
if (this.isEditing) {
this.editInput.element.focus();
} else {
this.editInput.element.blur();
}
}
}
});
......@@ -206,9 +206,6 @@
<li>
<a href="examples/cujo/index.html" data-source="http://cujojs.com" data-content="cujoJS is an architectural framework for building highly modular, scalable, maintainable applications in Javascript. It provides architectural plumbing, such as modules (AMD and CommonJS), declarative application composition, declarative connections, and aspect oriented programming.">cujoJS</a>
</li>
<li>
<a href="examples/montage/" data-source="http://montagejs.org" data-content="Montage simplifies the development of rich HTML5 applications by providing modular components, real-time two-way data binding, CommonJS dependency management, and many more conveniences.">Montage</a>
</li>
<li class="routing">
<a href="examples/sammyjs/" data-source="http://sammyjs.org" data-content="Sammy.js is a tiny JavaScript framework developed to ease the pain and provide a basic structure for developing JavaScript applications.">Sammy.js</a>
</li>
......
......@@ -1455,64 +1455,6 @@
}]
}]
},
"montage": {
"name": "MontageJS",
"description": "MontageJS is a framework for building rich HTML5 applications optimized for today and tomorrow’s range of connected devices. It offers time-tested design patterns and software principles, a modular architecture, a friendly method to achieve a clean separation of concerns, and supports sharing packages and modules with your NodeJS server.",
"homepage": "montagejs.org",
"examples": [{
"name": "Example",
"url": "examples/montage"
}],
"link_groups": [{
"heading": "Official Resources",
"links": [{
"name": "Quick Start",
"url": "http://montagejs.org/docs/montagejs-setup.html"
}, {
"name": "Documentation",
"url": "http://montagejs.org/docs"
}, {
"name": "API Reference",
"url": "http://montagejs.org/api"
}, {
"name": "Applications built with MontageJS",
"url": "http://montagejs.org/apps"
}, {
"name": "MontageJS on GitHub",
"url": "https://github.com/montagejs/montage"
}, {
"name": "Minit - MontageJS Initializer",
"url": "https://github.com/montagejs/minit"
}, {
"name": "MOP - MontageJS Optimizer",
"url": "https://github.com/montagejs/mop"
}]
}, {
"heading": "Articles and Guides",
"links": [{
"name": "YouTube - Getting Started",
"url": "http://www.youtube.com/watch?v=JfT1ML200JI"
}, {
"name": "My First MontageJS Application",
"url": "http://renaun.com/blog/2013/05/my-first-montagejs-application/"
}]
}, {
"heading": "Community",
"links": [{
"name": "IRC",
"url": "http://webchat.freenode.net/?channels=montage"
}, {
"name": "Mailing list on Google Groups",
"url": "https://groups.google.com/forum/?fromgroups#!forum/montagejs"
}, {
"name": "Montage on Twitter",
"url": "http://twitter.com/montagejs"
}, {
"name": "Montage on Google+",
"url": "https://plus.google.com/116915300739108010954"
}]
}]
},
"olives": {
"name": "Olives.js",
"description": "A JS Framework for creating realtime and scalable applications. Based on Emily.js and socket.io.",
......
......@@ -15,7 +15,7 @@ var excludedFrameworks = [
// YUI is a special case here, it is not hosted, but fetches JS files dynamically
'yui',
// these frameworks take a long time to start-up, and there is no easy way to determine when they are ready
'cujo', 'montage',
'cujo',
// sammyjs fails intermittently, it would appear that its state is sometimes updated asynchronously?
'sammyjs',
// elm-html batches UI updates with requestAnimationFrame which the tests
......
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