Commit fbf1b38b authored by Mathieu Lorber's avatar Mathieu Lorber Committed by Pascal Hartig

Update for Dart 1.0 release

parent 8a023841
packages
out
app.dart.js.deps
app.dart.js.map
pubspec.lock
\ No newline at end of file
name: todomvc_dart
version: 0.0.1
description: TodoMVC built with Dart
# Dart TodoMVC Example
> Dart is a class-based, object-oriented language with lexical scoping, closures, and optional static typing. Dart helps you build structured modern web apps and is easy to learn for a wide range of developers.
> _[Dart - dartlang.org](http://dartlang.org)_
## 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
```
cd web/dart
dart2js app.dart -oapp.dart.js
```
The dart2js compilator can be found in the SDK.
The currently provided JS is minified (dart2js [...] --minify).
## Syntax Analysis
```
cd web/dart
dart_analyzer app.dart --fatal-type-errors --fatal-warnings
```
Dart SDK is still under active development, and new releases include breaking changes. The application is built by drone.io, which proposes a specific build trigger for Dart SDK updates.
[![Build Status](https://drone.io/mlorber/todomvc-dart/status.png)](https://drone.io/mlorber/todomvc-dart/latest)
Build history can be seen [here](https://drone.io/mlorber/todomvc-dart)
## Credit
This TodoMVC application was created by [Mathieu Lorber](http://mlorber.net).
library todomvc;
import 'dart:html';
import 'dart:math';
import 'dart:convert';
part 'TodoWidget.dart';
part 'TodoApp.dart';
void main() {
new TodoApp();
}
class Todo {
String id;
String title;
bool completed;
Todo(String this.id, String this.title, {bool this.completed : false});
Todo.fromJson(Map json) {
id = json['id'];
title = json['title'];
completed = json['completed'];
}
// this is automatically called by JSON.encode
Map toJson() {
return {'id': id, 'title': title, 'completed': completed};
}
}
class UUID {
static Random random = new Random();
static String createUuid() {
return "${S4()}${S4()}-${S4()}-${S4()}-${S4()}-${S4()}${S4()}${S4()}";
}
static String S4() {
return random.nextInt(65536).toRadixString(16);
}
}
/**
* Escapes HTML-special characters of [text] so that the result can be
* included verbatim in HTML source code, either in an element body or in an
* attribute value.
*/
String htmlEscape(String text) {
return text.replaceAll("&", "&")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&apos;");
}
This source diff could not be displayed because it is too large. You can view the blob instead.
// Copyright (c) 2012, 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.
// Bootstrap support for Dart scripts on the page as this script.
if (navigator.webkitStartDart) {
if (!navigator.webkitStartDart()) {
document.body.innerHTML = 'This build has expired. Please download a new Dartium at http://www.dartlang.org/dartium/index.html';
}
} else {
// TODO:
// - Support in-browser compilation.
// - Handle inline Dart scripts.
window.addEventListener("DOMContentLoaded", function (e) {
// 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 + '.js';
var parent = scripts[i].parentNode;
parent.replaceChild(script, scripts[i]);
}
}
}
}, false);
}
// ---------------------------------------------------------------------------
// Experimental support for JS interoperability
// ---------------------------------------------------------------------------
function SendPortSync() {
}
function ReceivePortSync() {
this.id = ReceivePortSync.id++;
ReceivePortSync.map[this.id] = this;
}
(function() {
// Serialize the following types as follows:
// - primitives / null: unchanged
// - lists: [ 'list', internal id, list of recursively serialized elements ]
// - maps: [ 'map', internal id, map of keys and recursively serialized values ]
// - send ports: [ 'sendport', type, isolate id, port id ]
//
// Note, internal id's are for cycle detection.
function serialize(message) {
var visited = [];
function checkedSerialization(obj, serializer) {
// Implementation detail: for now use linear search.
// Another option is expando, but it may prohibit
// VM optimizations (like putting object into slow mode
// on property deletion.)
var id = visited.indexOf(obj);
if (id != -1) return [ 'ref', id ];
var id = visited.length;
visited.push(obj);
return serializer(id);
}
function doSerialize(message) {
if (message == null) {
return null; // Convert undefined to null.
} else if (typeof(message) == 'string' ||
typeof(message) == 'number' ||
typeof(message) == 'boolean') {
return message;
} else if (message instanceof Array) {
return checkedSerialization(message, function(id) {
var values = new Array(message.length);
for (var i = 0; i < message.length; i++) {
values[i] = doSerialize(message[i]);
}
return [ 'list', id, values ];
});
} else if (message instanceof LocalSendPortSync) {
return [ 'sendport', 'nativejs', message.receivePort.id ];
} else if (message instanceof DartSendPortSync) {
return [ 'sendport', 'dart', message.isolateId, message.portId ];
} else {
return checkedSerialization(message, function(id) {
var keys = Object.getOwnPropertyNames(message);
var values = new Array(keys.length);
for (var i = 0; i < keys.length; i++) {
values[i] = doSerialize(message[keys[i]]);
}
return [ 'map', id, keys, values ];
});
}
}
return doSerialize(message);
}
function deserialize(message) {
return deserializeHelper(message);
}
function deserializeHelper(message) {
if (message == null ||
typeof(message) == 'string' ||
typeof(message) == 'number' ||
typeof(message) == 'boolean') {
return message;
}
switch (message[0]) {
case 'map': return deserializeMap(message);
case 'sendport': return deserializeSendPort(message);
case 'list': return deserializeList(message);
default: throw 'unimplemented';
}
}
function deserializeMap(message) {
var result = { };
var id = message[1];
var keys = message[2];
var values = message[3];
for (var i = 0, length = keys.length; i < length; i++) {
var key = deserializeHelper(keys[i]);
var value = deserializeHelper(values[i]);
result[key] = value;
}
return result;
}
function deserializeSendPort(message) {
var tag = message[1];
switch (tag) {
case 'nativejs':
var id = message[2];
return new LocalSendPortSync(ReceivePortSync.map[id]);
case 'dart':
var isolateId = message[2];
var portId = message[3];
return new DartSendPortSync(isolateId, portId);
default:
throw 'Illegal SendPortSync type: $tag';
}
}
function deserializeList(message) {
var values = message[2];
var length = values.length;
var result = new Array(length);
for (var i = 0; i < length; i++) {
result[i] = deserializeHelper(values[i]);
}
return result;
}
window.registerPort = function(name, port) {
var stringified = JSON.stringify(serialize(port));
window.localStorage['dart-port:' + name] = stringified;
};
window.lookupPort = function(name) {
var stringified = window.localStorage['dart-port:' + name];
return deserialize(JSON.parse(stringified));
};
ReceivePortSync.id = 0;
ReceivePortSync.map = {};
ReceivePortSync.dispatchCall = function(id, message) {
// TODO(vsm): Handle and propagate exceptions.
var deserialized = deserialize(message);
var result = ReceivePortSync.map[id].callback(deserialized);
return serialize(result);
};
ReceivePortSync.prototype.receive = function(callback) {
this.callback = callback;
};
ReceivePortSync.prototype.toSendPort = function() {
return new LocalSendPortSync(this);
};
ReceivePortSync.prototype.close = function() {
delete ReceivePortSync.map[this.id];
};
if (navigator.webkitStartDart) {
window.addEventListener('js-sync-message', function(event) {
var data = JSON.parse(getPortSyncEventData(event));
var deserialized = deserialize(data.message);
var result = ReceivePortSync.map[data.id].callback(deserialized);
// TODO(vsm): Handle and propagate exceptions.
dispatchEvent('js-result', serialize(result));
}, false);
}
function LocalSendPortSync(receivePort) {
this.receivePort = receivePort;
}
LocalSendPortSync.prototype = new SendPortSync();
LocalSendPortSync.prototype.callSync = function(message) {
// TODO(vsm): Do a direct deepcopy.
message = deserialize(serialize(message));
return this.receivePort.callback(message);
}
function DartSendPortSync(isolateId, portId) {
this.isolateId = isolateId;
this.portId = portId;
}
DartSendPortSync.prototype = new SendPortSync();
function dispatchEvent(receiver, message) {
var string = JSON.stringify(message);
var event = document.createEvent('CustomEvent');
event.initCustomEvent(receiver, false, false, string);
window.dispatchEvent(event);
}
function getPortSyncEventData(event) {
return event.detail;
}
DartSendPortSync.prototype.callSync = function(message) {
var serialized = serialize(message);
var target = 'dart-port-' + this.isolateId + '-' + this.portId;
// TODO(vsm): Make this re-entrant.
// TODO(vsm): Set this up set once, on the first call.
var source = target + '-result';
var result = null;
var listener = function (e) {
result = JSON.parse(getPortSyncEventData(e));
};
window.addEventListener(source, listener, false);
dispatchEvent(target, [source, serialized]);
window.removeEventListener(source, listener, false);
return deserialize(result);
}
})();
......@@ -159,7 +159,7 @@
<a href="architecture-examples/spine/" data-source="http://spinejs.com" data-content="Spine is a lightweight framework for building JavaScript web applications. Spine gives you an MVC structure and then gets out of your way, allowing you to concentrate on the fun stuff, building awesome web applications.">Spine</a>
</li>
<li class="routing">
<a href="architecture-examples/dart/web/" data-source="http://dartlang.org" 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. It has two run modes : it can be compiled to JS, and will later run in native VM in compliant browsers (just in a dedicated Chromium provided with Dart SDK for the moment).">Dart</a>
<a href="vanilla-examples/vanilladart/build/" data-source="http://dartlang.org" 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. It has two run modes : it can be compiled to JS, and will later run in native VM in compliant browsers (just in a dedicated Chromium provided with Dart SDK for the moment).">Dart</a>
</li>
<li class="routing">
<a href="architecture-examples/gwt/" data-source="https://developers.google.com/web-toolkit/" data-content="Google Web Toolkit (GWT) is an MVP development toolkit for building and optimizing complex browser-based applications. GWT is used by many products at Google, including Google AdWords.">GWT</a>
......
This diff is collapsed.
......@@ -40,6 +40,6 @@
</footer>
<script src="bower_components/todomvc-common/base.js"></script>
<script type="application/dart" src="dart/app.dart"></script>
<script src="sdk/dart.js"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
// 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 models;
class Todo {
String id;
String title;
bool completed;
Todo(this.id, this.title, {this.completed : false});
Todo.fromJson(Map json) {
id = json['id'];
title = json['title'];
completed = json['completed'];
}
// this is automatically called by JSON.encode
Map toJson() {
return {
'id': id,
'title': title,
'completed': completed
};
}
}
\ No newline at end of file
name: todomvc_vanilladart
version: 1.0.0
description: TodoMVC built with Dart. Simple, vanilla Dart.
dependencies:
browser: any
uuid: 0.2.0
\ No newline at end of file
# Dart TodoMVC Example
> Dart is a class-based, object-oriented language with lexical scoping,
> closures, and optional static typing. Dart helps you build structured modern
> web apps and is easy to learn for a wide range of developers.
> _[Dart - dartlang.org][dart]_
This version of TodoMVC is a "vanilla" Dart implementation. The
core [Dart SDK][sdk] is required. No web frameworks are used. However,
this implementation uses two packages from the [pub package repository][pub].
# Running the app
Simply open the `build/index.html` file in your favorite modern browser. We
have taken the liberty of pre-compiling the Dart app into JavaScript for you.
# Directory layout
This app follows the [pub package layout conventions][pkglayout].
`build` - the pre-built example, ready to run in any modern browser. This
directory is generated by the build system. Do not modify files in this
directory.
`pubspec.yaml` - project metadata and a list of dependencies.
`lib` - Dart code, such as models.
`web` - web resources, such as HTML and CSS.
`web/dart` - Dart code specific to this web app.
# Editing and rebuilding
If you want to make changes to the app, follow these instructions. For very
fast development cycles, we recommend using [Dartium][] (a
custom build of Chromium with an embedded Dart VM). With Dartium, you can
simply reload the Dart code to instantly see your changes. However, you
need to run `pub build` to prep the app for browsers without Dart VM.
## Installing the SDK
First, download [Dart Editor][editor] (which includes the Dart SDK)
or the [Dart SDK][sdk]. Put the SDK's bin directory on your PATH.
## Installing packages
Dart Editor automatically runs Dart's package manager to install
package dependencies. If you did not open the project in Dart Editor,
you need to manually install the dependencies:
```
cd vanilla-examples/vanilladart
pub get
```
## Building the app
You need to compile the app into JavaScript to run in your browser.
```
cd vanilla-examples/vanilladart
pub build
```
The above command creates a `build` directory, complete with HTML files
and a compiled-to-JavaScript version of the app.
# Learning Dart
The [Dart website][dart] is a great resource for learning
Dart.
Getting started:
* [Try Dart](https://www.dartlang.org/codelabs/darrrt/)
Here are some links you may find helpful:
* [API Reference](https://api.dartlang.org/)
* [A Tour of the Dart Language][langtour]
* [Articles](http://www.dartlang.org/articles)
* [Tutorials](http://www.dartlang.org/docs/tutorials)
* [FAQ](http://www.dartlang.org/support/faq.html)
Get help from other Dart users:
* [Dart on StackOverflow](http://stackoverflow.com/questions/tagged/dart)
* [Support](https://www.dartlang.org/support/)
Follow the Dart project:
* [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)._
[![Build Status](https://drone.io/mlorber/todomvc-dart/status.png)][buildstatus]
Build history can be seen [here][builds].
## Credit
This TodoMVC application was created by [Mathieu Lorber](http://mlorber.net).
[langtour]: http://www.dartlang.org/docs/dart-up-and-running/contents/ch02.html
[dart]: https://www.dartlang.org
[builds]: https://drone.io/mlorber/todomvc-dart
[buildstatus]: https://drone.io/mlorber/todomvc-dart/latest
[sdk]: https://www.dartlang.org/tools/sdk/
[pub]: http://pub.dartlang.org
[editor]: https://www.dartlang.org/tools/editor/
[pkglayout]: http://pub.dartlang.org/doc/package-layout.html
[Dartium]: https://www.dartlang.org/tools/dartium/
\ No newline at end of file
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);
})();
part of todomvc;
class TodoApp {
static final Uuid uuid = new Uuid();
List<TodoWidget> todoWidgets = new List<TodoWidget>();
Element todoListElement = querySelector('#todo-list');
......@@ -26,12 +29,10 @@ class TodoApp {
var jsonList = window.localStorage['todos-vanilladart'];
if (jsonList != null) {
try {
var todos = JSON.decode(jsonList);
for (Map todo in todos) {
addTodo(new Todo.fromJson(todo));
}
List<Map> todos = JSON.decode(jsonList);
todos.forEach((todo) => addTodo(new Todo.fromJson(todo)));
} catch (e) {
window.console.log('Could not load todos form local storage.');
print('Could not load todos form local storage.');
}
}
}
......@@ -39,20 +40,20 @@ class TodoApp {
void initElementEventListeners() {
InputElement newTodoElement = querySelector('#new-todo');
newTodoElement.onKeyPress.listen((KeyboardEvent e) {
if (e.keyCode == KeyCode.ENTER) {
var title = newTodoElement.value.trim();
if (title != '') {
addTodo(new Todo(UUID.createUuid(), title));
newTodoElement.value = '';
updateFooterDisplay();
save();
}
}
newTodoElement.onKeyDown.listen((KeyboardEvent e) {
if (e.keyCode == KeyCode.ENTER) {
var title = newTodoElement.value.trim();
if (title.isNotEmpty) {
addTodo(new Todo(uuid.v4(), title));
newTodoElement.value = '';
updateFooterDisplay();
save();
}
}
});
checkAllCheckboxElement.onClick.listen((e) {
for (TodoWidget todoWidget in todoWidgets) {
for (var todoWidget in todoWidgets) {
if (todoWidget.todo.completed != checkAllCheckboxElement.checked) {
todoWidget.toggle();
}
......@@ -61,7 +62,7 @@ class TodoApp {
save();
});
clearCompletedElement.onClick.listen((e) {
clearCompletedElement.onClick.listen((_) {
var newList = new List<TodoWidget>();
for (TodoWidget todoWidget in todoWidgets) {
if (todoWidget.todo.completed) {
......@@ -91,20 +92,15 @@ class TodoApp {
}
void updateCounts() {
var complete = 0;
for (TodoWidget todoWidget in todoWidgets) {
if (todoWidget.todo.completed) {
complete++;
}
}
var complete = todoWidgets.where((w) => w.todo.completed).length;
checkAllCheckboxElement.checked = (complete == todoWidgets.length);
var left = todoWidgets.length - complete;
countElement.innerHtml = '<strong>${left}</strong> item${left != 1 ? 's' : ''} left';
countElement.innerHtml = '<strong>$left</strong> item${left != 1 ? 's' : ''} left';
if (complete == 0) {
clearCompletedElement.style.display = 'none';
} else {
clearCompletedElement.style.display = 'block';
clearCompletedElement.text = 'Clear completed (${complete})';
clearCompletedElement.text = 'Clear completed ($complete)';
}
updateFilter();
}
......@@ -129,21 +125,21 @@ class TodoApp {
void showAll() {
setSelectedFilter(showAllElement);
for (TodoWidget todoWidget in todoWidgets) {
for (var todoWidget in todoWidgets) {
todoWidget.visible = true;
}
}
void showActive() {
setSelectedFilter(showActiveElement);
for (TodoWidget todoWidget in todoWidgets) {
for (var todoWidget in todoWidgets) {
todoWidget.visible = !todoWidget.todo.completed;
}
}
void showCompleted() {
setSelectedFilter(showCompletedElement);
for (TodoWidget todoWidget in todoWidgets) {
for (var todoWidget in todoWidgets) {
todoWidget.visible = todoWidget.todo.completed;
}
}
......@@ -157,7 +153,7 @@ class TodoApp {
void save() {
var todos = new List<Todo>();
for (TodoWidget todoWidget in todoWidgets) {
for (var todoWidget in todoWidgets) {
todos.add(todoWidget.todo);
}
window.localStorage['todos-vanilladart'] = JSON.encode(todos);
......
part of todomvc;
class TodoWidget {
static const HtmlEscape htmlEscape = const HtmlEscape();
TodoApp todoApp;
Todo todo;
Element element;
......@@ -13,25 +16,25 @@ class TodoWidget {
<li ${todo.completed ? 'class="completed"' : ''}>
<div class='view'>
<input class='toggle' type='checkbox' ${todo.completed ? 'checked' : ''}>
<label class='todo-content'>${htmlEscape(todo.title)}</label>
<label class='todo-content'>${htmlEscape.convert(todo.title)}</label>
<button class='destroy'></button>
</div>
<input class='edit' value='${htmlEscape(todo.title)}'>
<input class='edit' value='${htmlEscape.convert(todo.title)}'>
</li>
''');
Element contentElement = element.query('.todo-content');
InputElement editElement = element.query('.edit');
Element contentElement = element.querySelector('.todo-content');
InputElement editElement = element.querySelector('.edit');
toggleElement = element.query('.toggle');
toggleElement = element.querySelector('.toggle');
toggleElement.onClick.listen((e) {
toggleElement.onClick.listen((_) {
toggle();
todoApp.updateCounts();
todoApp.save();
});
contentElement.onDoubleClick.listen((e) {
contentElement.onDoubleClick.listen((_) {
element.classes.add('editing');
editElement.selectionStart = todo.title.length;
editElement.focus();
......@@ -43,14 +46,15 @@ class TodoWidget {
todoApp.updateFooterDisplay();
}
element.query('.destroy').onClick.listen((e) {
element.querySelector('.destroy').onClick.listen((_) {
removeTodo();
todoApp.save();
});
void doneEditing() {
todo.title = editElement.value.trim();
if (todo.title != '') {
editElement.value = editElement.value.trim();
todo.title = editElement.value;
if (todo.title.isNotEmpty) {
contentElement.text = todo.title;
element.classes.remove('editing');
} else {
......@@ -59,13 +63,23 @@ class TodoWidget {
todoApp.save();
}
void undoEditing() {
element.classes.remove('editing');
editElement.value = todo.title;
}
editElement
..onKeyPress.listen((KeyboardEvent e) {
if (e.keyCode == KeyCode.ENTER) {
doneEditing();
..onKeyDown.listen((KeyboardEvent e) {
switch (e.keyCode) {
case KeyCode.ENTER:
doneEditing();
break;
case KeyCode.ESC:
undoEditing();
break;
}
})
..onBlur.listen((e) => doneEditing());
..onBlur.listen((_) => doneEditing());
return element;
}
......
library todomvc;
import 'dart:html'
show Element, InputElement, KeyCode, KeyboardEvent, querySelector, window;
import 'dart:convert' show HtmlEscape, JSON;
import 'package:uuid/uuid.dart' show Uuid;
import 'package:todomvc_vanilladart/models.dart' show Todo;
part 'TodoWidget.dart';
part 'TodoApp.dart';
void main() {
new TodoApp();
}
<!doctype html>
<html lang="en" data-framework="dart">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Dart • TodoMVC</title>
<link rel="stylesheet" href="bower_components/todomvc-common/base.css">
</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">
<span id="todo-count"><strong>0</strong> item left</span>
<ul id="filters">
<li>
<a class="selected" href="#/">All</a>
</li>
<li>
<a href="#/active">Active</a>
</li>
<li>
<a href="#/completed">Completed</a>
</li>
</ul>
<button id="clear-completed">Clear completed</button>
</footer>
</section>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Created by <a href="http://mlorber.net">Mathieu Lorber</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
<script src="bower_components/todomvc-common/base.js"></script>
<script type="application/dart" src="dart/app.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
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