Commit 6e93883b authored by Pascal Hartig's avatar Pascal Hartig Committed by Sindre Sorhus

Add AngularDart example

Add an example app to the labs for AngularDart without routing.

AngularDart is heavily inspired by AngularJS and is supercharged for Dart. Core
Angular features such as directives, data binding, and dependency injection, are
all there, and they've taken advantage of Dart's features like metadata, types,
and classes to feel natural for Dart developers. AngularDart is also the first
version of Angular to be built on emerging web standards like Shadow DOM.
parent 59cabea6
......@@ -7,6 +7,7 @@
- Exoskeleton
- Atma.js
- ComponentJS
- AngularDart
- Updates since 1.2:
- CanJS 2.0
......
......@@ -170,6 +170,9 @@
<li class="routing">
<a href="architecture-examples/closure/" data-source="http://code.google.com/closure/library/" data-content="The Closure Library is a broad, well-tested, modular, and cross-browser JavaScript library. You can pull just what you need from a large set of reusable UI widgets and controls, and from lower-level utilities for DOM manipulation, server communication, animation, data structures, unit testing, rich-text editing, and more.">Closure</a>
</li>
<li class="labs">
<a href="labs/architecture-examples/angular-dart/web/" data-source="https://github.com/angular/angular.dart" data-content="Dart firstly targets the development of modern and large scale browser-side web apps. It's an object oriented language with a C-style syntax. AngularDart is a port of Angular to Dart.">AngularDart</a>
</li>
<li class="routing labs">
<a href="labs/architecture-examples/batman/" data-source="http://batmanjs.org" data-content="Batman.js is a framework for building rich web applications with CoffeeScript or JavaScript. App code is concise and declarative, thanks to a powerful system of view bindings and observable properties. The API is designed with developer and designer happiness as its first priority.">Batman.js</a>
</li>
......
{
"name": "angular-dart-todomvc",
"version": "0.0.0",
"authors": [
"Pascal Hartig <passy@twitter.com>"
],
"license": "MIT",
"homepage": "https://github.com/passy/angular-dart-todomvc",
"private": true,
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"dependencies": {
"todomvc-common": "~0.1.9"
}
}
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);
-ms-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);
-ms-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;
[/labs/, /\w*-examples/].forEach(function (href) {
var match = location.href.match(href);
if (!base && match) {
base = location.href.indexOf(match);
}
});
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]').getAttribute('data-framework');
}
if (template && learnJSON[framework]) {
this.frameworkJSON = learnJSON[framework];
this.template = template;
this.append();
}
}
Learn.prototype.append = function () {
var aside = document.createElement('aside');
aside.innerHTML = _.template(this.template, this.frameworkJSON);
aside.className = 'learn';
// Localize demo links
var demoLinks = aside.querySelectorAll('.demo-link');
Array.prototype.forEach.call(demoLinks, function (demoLink) {
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);
})();
# Generated by pub
# See http://pub.dartlang.org/doc/glossary.html#lockfile
packages:
analyzer:
description: analyzer
source: hosted
version: "0.10.5"
angular:
description: angular
source: hosted
version: "0.9.3"
args:
description: args
source: hosted
version: "0.9.0"
browser:
description: browser
source: hosted
version: "0.9.1"
collection:
description: collection
source: hosted
version: "0.9.0"
di:
description: di
source: hosted
version: "0.0.32"
html5lib:
description: html5lib
source: hosted
version: "0.9.1"
intl:
description: intl
source: hosted
version: "0.9.1"
js:
description: js
source: hosted
version: "0.2.1"
logging:
description: logging
source: hosted
version: "0.9.1+1"
path:
description: path
source: hosted
version: "1.0.0"
perf_api:
description: perf_api
source: hosted
version: "0.0.8"
route_hierarchical:
description: route_hierarchical
source: hosted
version: "0.4.10"
source_maps:
description: source_maps
source: hosted
version: "0.9.0"
stack_trace:
description: stack_trace
source: hosted
version: "0.9.1"
unittest:
description: unittest
source: hosted
version: "0.9.2+1"
unmodifiable_collection:
description: unmodifiable_collection
source: hosted
version: "0.9.2"
utf:
description: utf
source: hosted
version: "0.9.0"
name: angular-dart-todomvc
version: 0.0.0
environment:
sdk: '>=1.0.0 <1.1.0'
dependencies:
angular: '>=0.9.0 <0.10.0'
browser: '>=0.9.0 <0.10.0'
js: '>=0.2.0'
# AngularDart TodoMVC Example
> AngularDart is heavily inspired by AngularJS and is supercharged for Dart.
> Core Angular features such as directives, data binding, and dependency
> injection, are all there, and they've taken advantage of Dart's features
> like metadata, types, and classes to feel natural for Dart developers.
> AngularDart is also the first version of Angular to be built on emerging
> web standards like Shadow DOM.
> _[AngularDart](http://news.dartlang.org/2013/11/angular-announces-angulardart.html)_
## Learning Dart
The [Dart website](http://www.dartlang.org) is a great resource for getting started.
Here are some links you may find helpful:
* [Documentation](http://www.dartlang.org/docs/technical-overview)
* [API Reference](http://api.dartlang.org/docs/releases/latest)
* [A Tour of the Dart Language](http://www.dartlang.org/docs/dart-up-and-running/contents/ch02.html)
* [Articles](http://www.dartlang.org/articles)
* [Tutorials](http://www.dartlang.org/docs/tutorials)
* [FAQ](http://www.dartlang.org/support/faq.html)
Articles and guides from the community:
* [Getting started with Google Dart](http://www.techrepublic.com/blog/webmaster/getting-started-with-google-dart/931)
Get help from other Dart users:
* [Dart on StackOverflow](http://stackoverflow.com/questions/tagged/dart)
* [Dart on Twitter](http://twitter.com/dart_lang)
* [Dart on Google +](https://plus.google.com/+dartlang/posts)
_If you have other helpful links to share, or find any of the links above no longer work, please [let us know](https://github.com/tastejs/todomvc/issues)._
## Running
Dart compiles to JavaScript and thus runs across modern browsers. Dart also can
run in its own virtual machine.
Both Dart files and JS compilation result are provided in this sample,
therefore it actually works in any browser.
To edit and debug the code, you can use Dart Editor. The editor ships with the
[SDK](http://dartlang.org) and [Dartium](http://www.dartlang.org/dartium/), a
dedicated version of Chromium with an embedded Dart VM.
## Compiling
```
dart2js src/main.dart -o dist/main.js
```
The dart2js compilator can be found in the SDK.
The currently provided JS is minified (dart2js [...] --minify).
import 'dart:html' as dom;
import 'package:angular/angular.dart';
@NgDirective(
selector: '[todo-escape]',
map: const {'todo-escape': '&onEscape'}
)
@NgDirective(
selector: '[todo-focus]',
map: const {'todo-focus': '@todoFocus'}
)
class TodoDOMEventDirective {
final Map<int, Function> listeners = {};
final dom.Element element;
final Scope scope;
TodoDOMEventDirective(this.element, this.scope);
void initHandler(stream, value, [bool predicate(event)]) {
final int key = stream.hashCode;
if (!listeners.containsKey(key)) {
listeners[key] = value;
stream.listen((event) => scope.$apply(() {
if (predicate == null || predicate(event)) {
event.preventDefault();
value({r'$event': event});
}
}));
}
}
set onEscape(value) {
initHandler(element.onKeyDown, value, (event) => event.keyCode ==
dom.KeyCode.ESC);
}
set todoFocus(watchExpr) {
scope.$watch(watchExpr, (value) {
if (value) {
element.focus();
}
});
}
}
<!doctype html>
<html lang="en" ng-app data-framework="angulardart">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>AngularDart • TodoMVC</title>
<link rel="stylesheet" href="../bower_components/todomvc-common/base.css">
<style>
[ng-cloak] { display: none; }
.ng-show { display: block; }
.ng-hide { display: none; }
</style>
</head>
<body>
<section todo-controller id="todoapp">
<header id="header">
<h1>todos</h1>
<form id="todo-form" ng-submit="todo.add()">
<input type="text" id="new-todo" placeholder="What needs to be done?" ng-model="todo.newItem.title" autofocus>
</form>
</header>
<section id="main" ng-if="todo.total()" ng-cloak>
<input id="toggle-all" type="checkbox" ng-model="todo.allChecked">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list">
<li ng-repeat="item in todo.items | filter:statusFilter" ng-class="{completed: item.completed, editing: item == todo.editedItem}">
<div class="view">
<input type="checkbox" class="toggle" ng-model="item.completed">
<label ng-doubleclick="todo.editTodo(item)">{{item.title}}</label>
<button class="destroy" ng-click="todo.remove(item)"></button>
</div>
<form ng-submit="todo.doneEditing()">
<input type="text" class="edit" ng-trim="false" ng-model="item.title" todo-escape="todo.revertEditing(item)" ng-blur="todo.doneEditing()" todo-focus="item == todo.editedItem">
</form>
</li>
</ul>
</section>
<footer id="footer" ng-hide="!todo.total()" ng-cloak>
<span id="todo-count"><strong>{{todo.remaining()}}</strong> {{todo.itemsLeftText}}</span>
<button id="clear-completed" ng-click="todo.clearCompleted()" ng-if="todo.completed() > 0">Clear completed ({{todo.completed()}})</button>
</footer>
</section>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Written by <a href="http://passy.me/">Pascal Hartig</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
<script src="../bower_components/todomvc-common/base.js"></script>
<script src="main.dart" type="application/dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
import 'package:di/di.dart';
import 'package:angular/angular.dart';
import 'todo.dart';
import 'directives.dart';
main() {
var module = new Module()
..type(StorageService)
..type(TodoController)
..type(TodoDOMEventDirective);
ngBootstrap(module: module);
}
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
(function() {
// Bootstrap support for Dart scripts on the page as this script.
if (navigator.userAgent.indexOf('(Dart)') === -1) {
// TODO:
// - Support in-browser compilation.
// - Handle inline Dart scripts.
// Fall back to compiled JS. Run through all the scripts and
// replace them if they have a type that indicate that they source
// in Dart code.
//
// <script type="application/dart" src="..."></script>
//
var scripts = document.getElementsByTagName("script");
var length = scripts.length;
for (var i = 0; i < length; ++i) {
if (scripts[i].type == "application/dart") {
// Remap foo.dart to foo.dart.js.
if (scripts[i].src && scripts[i].src != '') {
var script = document.createElement('script');
script.src = scripts[i].src.replace(/\.dart(?=\?|$)/, '.dart.js');
var parent = scripts[i].parentNode;
// TODO(vsm): Find a solution for issue 8455 that works with more
// than one script.
document.currentScript = script;
parent.replaceChild(script, scripts[i]);
}
}
}
}
})();
library todo;
import 'dart:html' as dom;
import 'dart:convert' as convert;
import 'package:angular/angular.dart';
class StorageService {
final dom.Storage _storage = dom.window.localStorage;
static const String STORAGE_KEY = 'todos-angulardart';
List<Item> loadItems() {
final String data = _storage[STORAGE_KEY];
if (data == null) {
return [];
}
final List<Map> rawItems = convert.JSON.decode(data);
return rawItems.map((item) => new Item.fromJson(item)).toList();
}
void saveItems(List<Item> items) {
_storage[STORAGE_KEY] = convert.JSON.encode(items);
}
}
class Item {
String title;
bool completed;
Item([this.title = '', this.completed = false]);
Item.fromJson(Map obj) {
this.title = obj['title'];
this.completed = obj['completed'];
}
bool get isEmpty => title.trim().isEmpty;
Item clone() => new Item(this.title, this.completed);
String toString() => completed ? '[X]' : '[ ]' + ' ${this.title}';
void normalize() {
title = title.trim();
}
// This is method is called when from JSON.encode.
Map toJson() => { 'title': title, 'completed': completed };
}
@NgDirective(
selector: '[todo-controller]',
publishAs: 'todo'
)
class TodoController {
List<Item> items = [];
Item newItem = new Item();
Item editedItem = null;
Item previousItem = null;
StorageService _storageService;
TodoController(Scope scope, StorageService storage) {
items = storage.loadItems();
_storageService = storage;
// Since there is no native support for deeply watching collections, we
// instead watch over the JSON-serialized string representing the items.
// While hugely inefficient, this is a very simple work-around.
scope.$watch((Scope scope) => convert.JSON.encode(items), save);
}
void save() {
_storageService.saveItems(items);
}
void add() {
if (newItem.isEmpty) {
return;
}
newItem.normalize();
items.add(newItem);
newItem = new Item();
}
void remove(Item item) {
items.remove(item);
}
void clearCompleted() {
items.removeWhere((i) => i.completed);
}
int remaining() {
return items.where((item) => !item.completed).length;
}
int completed() {
return items.where((item) => item.completed).length;
}
int total() {
return items.length;
}
bool get allChecked {
return items.every((i) => i.completed);
}
void set allChecked(value) {
items.forEach((i) => i.completed = value);
}
String get itemsLeftText {
return 'item' + (remaining() != 1 ? 's' : '') + ' left';
}
void editTodo(Item item) {
editedItem = item;
previousItem = item.clone();
}
void doneEditing() {
if (editedItem == null) {
return;
}
if (editedItem.isEmpty) {
items.remove(editedItem);
}
editedItem.normalize();
editedItem = null;
previousItem = null;
}
void revertEditing(Item item) {
editedItem = null;
item.title = previousItem.title;
previousItem = null;
}
}
......@@ -118,6 +118,46 @@
}]
}]
},
"angulardart": {
"name": "AngularDart",
"description": "Dart is a class-based, object-oriented language with lexical scoping, closures, and optional static typing. AngularDart is a port of Angular to Dart.",
"homepage": "github.com/angular/angular.dart",
"examples": [{
"name": "Architecture Example",
"url": "labs/architecture-examples/angular-dart"
}],
"link_groups": [{
"heading": "Official Resources",
"links": [{
"name": "API Reference",
"url": "http://ci.angularjs.org/view/Dart/job/angular.dart-master/javadoc/"
}, {
"name": "Tutorial",
"url": "https://github.com/angular/angular.dart.tutorial"
}, {
"name": "A Tour of the Dart Language",
"url": "http://www.dartlang.org/docs/dart-up-and-running/contents/ch02.html"
}]
}, {
"heading": "Community",
"links": [{
"name": "AngularDart Mailing List",
"url": "https://groups.google.com/forum/#!forum/angular-dart"
}, {
"name": "AngularDart on StackOverflow",
"url": "http://stackoverflow.com/questions/tagged/angulardart"
}, {
"name": "+AngularDart on Google+",
"url": "https://plus.google.com/+AngularJS"
}, {
"name": "@angularjs on Twitter",
"url": "https://twitter.com/angularjs"
}, {
"name": "Bugtracker on GitHub",
"url": "https://github.com/angular/angular.dart/issues?state=open"
}]
}]
},
"ariatemplates": {
"name": "Aria Templates",
"description": "Aria Templates (aka AT) is an application framework written in JavaScript for building rich and large-scaled enterprise web applications.",
......
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