Commit 2d765605 authored by Sam Saccone's avatar Sam Saccone Committed by GitHub

Symlink fixup (#1644)

* Delete ancient ember version

* Rename ember-cli example to emberjs

* Update image min dep

* Use vinyl fs directly for symlink fix

Fixes #1635
parent 31a5b943
todomvc/dist/index.html
\ No newline at end of file
node_modules/.bin
node_modules/jquery
!node_modules/jquery/dist/jquery.js
node_modules/components-ember
!node_modules/components-ember/ember.js
node_modules/ember-data
!node_modules/ember-data.js
node_modules/ember-localstorage-adapter
!node_modules/ember-localstorage-adapter/localstorage_adapter.js
node_modules/handlebars
!node_modules/handlebars/dist/handlebars.js
node_modules/todomvc-app-css
!node_modules/todomvc-app-css/index.css
node_modules/todomvc-common
!node_modules/todomvc-common/base.css
!node_modules/todomvc-common/base.js
<!doctype html>
<html lang="en" data-framework="emberjs">
<head>
<meta charset="utf-8">
<title>ember.js • TodoMVC</title>
<link rel="stylesheet" href="node_modules/todomvc-common/base.css">
<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
</head>
<body>
<script type="text/x-handlebars" data-template-name="todo-list">
{{#if length}}
<section id="main">
{{#if canToggle}}
{{input type="checkbox" id="toggle-all" checked=allTodos.allAreDone}}
{{/if}}
<ul id="todo-list">
{{#each}}
<li {{bind-attr class="isCompleted:completed isEditing:editing"}}>
{{#if isEditing}}
{{todo-input type="text" class="edit" value=bufferedTitle focus-out="doneEditing" insert-newline="doneEditing" escape-press="cancelEditing"}}
{{else}}
{{input type="checkbox" class="toggle" checked=isCompleted}}
<label {{action "editTodo" on="doubleClick"}}>{{title}}</label>
<button {{action "removeTodo"}} class="destroy"></button>
{{/if}}
</li>
{{/each}}
</ul>
</section>
{{/if}}
</script>
<script type="text/x-handlebars" data-template-name="todos">
<section id="todoapp">
<header id="header">
<h1>todos</h1>
{{todo-input id="new-todo" type="text" value=newTitle action="createTodo" placeholder="What needs to be done?"}}
</header>
{{outlet}}
{{#if length}}
<footer id="footer">
<span id="todo-count"><strong>{{remaining.length}}</strong> {{pluralize 'item' remaining.length}} left</span>
<ul id="filters">
<li>
{{#link-to "todos.index" activeClass="selected"}}All{{/link-to}}
</li>
<li>
{{#link-to "todos.active" activeClass="selected"}}Active{{/link-to}}
</li>
<li>
{{#link-to "todos.completed" activeClass="selected"}}Completed{{/link-to}}
</li>
</ul>
{{#if completed.length}}
<button id="clear-completed" {{action "clearCompleted"}}>Clear completed</button>
{{/if}}
</footer>
{{/if}}
</section>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>
Created by
<a href="http://github.com/tomdale">Tom Dale</a>,
<a href="http://github.com/addyosmani">Addy Osmani</a>
</p>
<p>
Updated by
<a href="http://github.com/bantic">Cory Forsyth</a>
</p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
</script>
<script src="node_modules/todomvc-common/base.js"></script>
<script src="node_modules/jquery/dist/jquery.js"></script>
<script src="node_modules/handlebars/dist/handlebars.js"></script>
<script src="node_modules/components-ember/ember.js"></script>
<script src="node_modules/ember-data/ember-data.js"></script>
<script src="node_modules/ember-localstorage-adapter/localstorage_adapter.js"></script>
<script src="js/app.js"></script>
<script src="js/router.js"></script>
<script src="js/models/todo.js"></script>
<script src="js/controllers/todos_controller.js"></script>
<script src="js/controllers/todos_list_controller.js"></script>
<script src="js/controllers/todo_controller.js"></script>
<script src="js/views/todo_input_component.js"></script>
<script src="js/helpers/pluralize.js"></script>
</body>
</html>
todomvc/dist/index.html
\ No newline at end of file
/*global Ember, DS, Todos:true */
window.Todos = Ember.Application.create();
Todos.ApplicationAdapter = DS.LSAdapter.extend({
namespace: 'todos-emberjs'
});
/*global Todos, Ember */
(function () {
'use strict';
Todos.TodoController = Ember.ObjectController.extend({
isEditing: false,
// We use the bufferedTitle to store the original value of
// the model's title so that we can roll it back later in the
// `cancelEditing` action.
bufferedTitle: Ember.computed.oneWay('title'),
actions: {
editTodo: function () {
this.set('isEditing', true);
},
doneEditing: function () {
var bufferedTitle = this.get('bufferedTitle').trim();
if (Ember.isEmpty(bufferedTitle)) {
// The `doneEditing` action gets sent twice when the user hits
// enter (once via 'insert-newline' and once via 'focus-out').
//
// We debounce our call to 'removeTodo' so that it only gets
// made once.
Ember.run.debounce(this, 'removeTodo', 0);
} else {
var todo = this.get('model');
todo.set('title', bufferedTitle);
todo.save();
}
// Re-set our newly edited title to persist its trimmed version
this.set('bufferedTitle', bufferedTitle);
this.set('isEditing', false);
},
cancelEditing: function () {
this.set('bufferedTitle', this.get('title'));
this.set('isEditing', false);
},
removeTodo: function () {
this.removeTodo();
}
},
removeTodo: function () {
var todo = this.get('model');
todo.deleteRecord();
todo.save();
},
saveWhenCompleted: function () {
this.get('model').save();
}.observes('isCompleted')
});
})();
/*global Todos, Ember */
(function () {
'use strict';
Todos.TodosController = Ember.ArrayController.extend({
actions: {
createTodo: function () {
var title, todo;
// Get the todo title set by the "New Todo" text field
title = this.get('newTitle').trim();
if (!title) {
return;
}
// Create the new Todo model
todo = this.store.createRecord('todo', {
title: title,
isCompleted: false
});
todo.save();
// Clear the "New Todo" text field
this.set('newTitle', '');
},
clearCompleted: function () {
var completed = this.get('completed');
completed.invoke('deleteRecord');
completed.invoke('save');
}
},
/* properties */
remaining: Ember.computed.filterBy('model', 'isCompleted', false),
completed: Ember.computed.filterBy('model', 'isCompleted', true),
allAreDone: function (key, value) {
if (value !== undefined) {
this.setEach('isCompleted', value);
return value;
} else {
var length = this.get('length');
var completedLength = this.get('completed.length');
return length > 0 && length === completedLength;
}
}.property('length', 'completed.length')
});
})();
/*global Todos, Ember */
(function () {
'use strict';
Todos.TodosListController = Ember.ArrayController.extend({
needs: ['todos'],
allTodos: Ember.computed.alias('controllers.todos'),
itemController: 'todo',
canToggle: function () {
var anyTodos = this.get('allTodos.length');
var isEditing = this.isAny('isEditing');
return anyTodos && !isEditing;
}.property('allTodos.length', '@each.isEditing')
});
})();
/*global Ember */
(function () {
'use strict';
Ember.Handlebars.helper('pluralize', function (singular, count) {
/* From Ember-Data */
var inflector = Ember.Inflector.inflector;
return count === 1 ? singular : inflector.pluralize(singular);
});
})();
/*global Todos, DS */
(function () {
'use strict';
Todos.Todo = DS.Model.extend({
title: DS.attr('string'),
isCompleted: DS.attr('boolean')
});
})();
/*global Ember, Todos */
(function () {
'use strict';
Todos.Router.map(function () {
this.resource('todos', { path: '/' }, function () {
this.route('active');
this.route('completed');
});
});
Todos.TodosRoute = Ember.Route.extend({
model: function () {
return this.store.find('todo');
}
});
Todos.TodosIndexRoute = Todos.TodosRoute.extend({
templateName: 'todo-list',
controllerName: 'todos-list'
});
Todos.TodosActiveRoute = Todos.TodosIndexRoute.extend({
model: function () {
return this.store.filter('todo', function (todo) {
return !todo.get('isCompleted');
});
}
});
Todos.TodosCompletedRoute = Todos.TodosIndexRoute.extend({
model: function () {
return this.store.filter('todo', function (todo) {
return todo.get('isCompleted');
});
}
});
})();
/*global Todos, Ember */
(function () {
'use strict';
Todos.TodoInputComponent = Ember.TextField.extend({
focusOnInsert: function () {
// Re-set input value to get rid of a redundant text selection
this.$().val(this.$().val());
this.$().focus();
}.on('didInsertElement')
});
})();
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
/*global Ember*/
/*global DS*/
(function () {
'use strict';
DS.LSSerializer = DS.JSONSerializer.extend({
serializeHasMany: function(record, json, relationship) {
var key = relationship.key;
var payloadKey = this.keyForRelationship ? this.keyForRelationship(key, "hasMany") : key;
var relationshipType = record.constructor.determineRelationshipType(relationship);
if (relationshipType === 'manyToNone' ||
relationshipType === 'manyToMany' ||
relationshipType === 'manyToOne') {
json[payloadKey] = record.get(key).mapBy('id');
// TODO support for polymorphic manyToNone and manyToMany relationships
}
},
/**
* Extracts whatever was returned from the adapter.
*
* If the adapter returns relationships in an embedded way, such as follows:
*
* ```js
* {
* "id": 1,
* "title": "Rails Rambo",
*
* "_embedded": {
* "comment": [{
* "id": 1,
* "comment_title": "FIRST"
* }, {
* "id": 2,
* "comment_title": "Rails is unagi"
* }]
* }
* }
*
* this method will create separated JSON for each resource and then push
* them individually to the Store.
*
* In the end, only the main resource will remain, containing the ids of its
* relationships. Given the relations are already in the Store, we will
* return a JSON with the main resource alone. The Store will sort out the
* associations by itself.
*
* @method extractSingle
* @private
* @param {DS.Store} store the returned store
* @param {DS.Model} type the type/model
* @param {Object} payload returned JSON
*/
extractSingle: function(store, type, payload) {
if (payload && payload._embedded) {
for (var relation in payload._embedded) {
var relType = type.typeForRelationship(relation);
var typeName = relType.typeKey,
embeddedPayload = payload._embedded[relation];
if (embeddedPayload) {
if (Ember.isArray(embeddedPayload)) {
store.pushMany(typeName, embeddedPayload);
} else {
store.push(typeName, embeddedPayload);
}
}
}
delete payload._embedded;
}
return this.normalize(type, payload);
},
/**
* This is exactly the same as extractSingle, but used in an array.
*
* @method extractSingle
* @private
* @param {DS.Store} store the returned store
* @param {DS.Model} type the type/model
* @param {Array} payload returned JSONs
*/
extractArray: function(store, type, payload) {
return payload.map(function(json) {
return this.extractSingle(store, type, json);
}, this);
}
});
DS.LSAdapter = DS.Adapter.extend(Ember.Evented, {
/**
This is the main entry point into finding records. The first parameter to
this method is the model's name as a string.
@method find
@param {DS.Model} type
@param {Object|String|Integer|null} id
*/
find: function(store, type, id, opts) {
var allowRecursive = true;
var namespace = this._namespaceForType(type);
var record = Ember.A(namespace.records[id]);
/**
* In the case where there are relationships, this method is called again
* for each relation. Given the relations have references to the main
* object, we use allowRecursive to avoid going further into infinite
* recursiveness.
*
* Concept from ember-indexdb-adapter
*/
if (opts && typeof opts.allowRecursive !== 'undefined') {
allowRecursive = opts.allowRecursive;
}
if (!record || !record.hasOwnProperty('id')) {
return Ember.RSVP.reject(new Error("Couldn't find record of"
+ " type '" + type.typeKey
+ "' for the id '" + id + "'."));
}
if (allowRecursive) {
return this.loadRelationships(type, record);
} else {
return Ember.RSVP.resolve(record);
}
},
findMany: function (store, type, ids, opts) {
var namespace = this._namespaceForType(type);
var adapter = this,
allowRecursive = true,
results = [], record;
/**
* In the case where there are relationships, this method is called again
* for each relation. Given the relations have references to the main
* object, we use allowRecursive to avoid going further into infinite
* recursiveness.
*
* Concept from ember-indexdb-adapter
*/
if (opts && typeof opts.allowRecursive !== 'undefined') {
allowRecursive = opts.allowRecursive;
}
for (var i = 0; i < ids.length; i++) {
record = namespace.records[ids[i]];
if (!record || !record.hasOwnProperty('id')) {
return Ember.RSVP.reject(new Error("Couldn't find record of type '" + type.typeKey
+ "' for the id '" + ids[i] + "'."));
}
results.push(Ember.copy(record));
}
if (results.get('length') && allowRecursive) {
return this.loadRelationshipsForMany(type, results);
} else {
return Ember.RSVP.resolve(results);
}
},
// Supports queries that look like this:
//
// {
// <property to query>: <value or regex (for strings) to match>,
// ...
// }
//
// Every property added to the query is an "AND" query, not "OR"
//
// Example:
//
// match records with "complete: true" and the name "foo" or "bar"
//
// { complete: true, name: /foo|bar/ }
findQuery: function (store, type, query, recordArray) {
var namespace = this._namespaceForType(type);
var results = this.query(namespace.records, query);
if (results.get('length')) {
return this.loadRelationshipsForMany(type, results);
} else {
return Ember.RSVP.reject();
}
},
query: function (records, query) {
var results = [], record;
function recordMatchesQuery(record) {
return Ember.keys(query).every(function(property) {
var test = query[property];
if (Object.prototype.toString.call(test) === '[object RegExp]') {
return test.test(record[property]);
} else {
return record[property] === test;
}
});
}
for (var id in records) {
record = records[id];
if (recordMatchesQuery(record)) {
results.push(Ember.copy(record));
}
}
return results;
},
findAll: function (store, type) {
var namespace = this._namespaceForType(type),
results = [];
for (var id in namespace.records) {
results.push(Ember.copy(namespace.records[id]));
}
return Ember.RSVP.resolve(results);
},
createRecord: function (store, type, record) {
var namespaceRecords = this._namespaceForType(type);
var recordHash = record.serialize({includeId: true});
namespaceRecords.records[recordHash.id] = recordHash;
this.persistData(type, namespaceRecords);
return Ember.RSVP.resolve();
},
updateRecord: function (store, type, record) {
var namespaceRecords = this._namespaceForType(type);
var id = record.get('id');
namespaceRecords.records[id] = record.serialize({ includeId: true });
this.persistData(type, namespaceRecords);
return Ember.RSVP.resolve();
},
deleteRecord: function (store, type, record) {
var namespaceRecords = this._namespaceForType(type);
var id = record.get('id');
delete namespaceRecords.records[id];
this.persistData(type, namespaceRecords);
return Ember.RSVP.resolve();
},
generateIdForRecord: function () {
return Math.random().toString(32).slice(2).substr(0, 5);
},
// private
adapterNamespace: function () {
return this.get('namespace') || 'DS.LSAdapter';
},
loadData: function () {
var storage = localStorage.getItem(this.adapterNamespace());
return storage ? JSON.parse(storage) : {};
},
persistData: function(type, data) {
var modelNamespace = this.modelNamespace(type);
var localStorageData = this.loadData();
localStorageData[modelNamespace] = data;
localStorage.setItem(this.adapterNamespace(), JSON.stringify(localStorageData));
},
_namespaceForType: function (type) {
var namespace = this.modelNamespace(type);
var storage = this.loadData();
return storage[namespace] || {records: {}};
},
modelNamespace: function(type) {
return type.url || type.typeKey;
},
/**
* This takes a record, then analyzes the model relationships and replaces
* ids with the actual values.
*
* Stolen from ember-indexdb-adapter
*
* Consider the following JSON is entered:
*
* ```js
* {
* "id": 1,
* "title": "Rails Rambo",
* "comments": [1, 2]
* }
*
* This will return:
*
* ```js
* {
* "id": 1,
* "title": "Rails Rambo",
* "comments": [1, 2]
*
* "_embedded": {
* "comment": [{
* "_id": 1,
* "comment_title": "FIRST"
* }, {
* "_id": 2,
* "comment_title": "Rails is unagi"
* }]
* }
* }
*
* This way, whenever a resource returned, its relationships will be also
* returned.
*
* @method loadRelationships
* @private
* @param {DS.Model} type
* @param {Object} record
*/
loadRelationships: function(type, record) {
var adapter = this,
resultJSON = {},
typeKey = type.typeKey,
relationshipNames, relationships,
relationshipPromises = [];
/**
* Create a chain of promises, so the relationships are
* loaded sequentially. Think of the variable
* `recordPromise` as of the accumulator in a left fold.
*/
var recordPromise = Ember.RSVP.resolve(record);
relationshipNames = Ember.get(type, 'relationshipNames');
relationships = relationshipNames.belongsTo
.concat(relationshipNames.hasMany);
relationships.forEach(function(relationName) {
var relationModel = type.typeForRelationship(relationName);
var relationEmbeddedId = record[relationName];
var relationProp = adapter.relationshipProperties(type, relationName);
var relationType = relationProp.kind;
var foreignAdapter = type.store.adapterFor(relationModel);
var opts = {allowRecursive: false};
/**
* embeddedIds are ids of relations that are included in the main
* payload, such as:
*
* {
* cart: {
* id: "s85fb",
* customer: "rld9u"
* }
* }
*
* In this case, cart belongsTo customer and its id is present in the
* main payload. We find each of these records and add them to _embedded.
*/
if (relationEmbeddedId && foreignAdapter === adapter)
{
recordPromise = recordPromise.then(function(recordPayload) {
var promise;
if (relationType === 'belongsTo' || relationType === 'hasOne') {
promise = adapter.find(null, relationModel, relationEmbeddedId, opts);
} else if (relationType == 'hasMany') {
promise = adapter.findMany(null, relationModel, relationEmbeddedId, opts);
}
return promise.then(function(relationRecord) {
return adapter.addEmbeddedPayload(recordPayload, relationName, relationRecord);
});
});
}
});
return recordPromise;
},
/**
* Given the following payload,
*
* {
* cart: {
* id: "1",
* customer: "2"
* }
* }
*
* With `relationshipName` being `customer` and `relationshipRecord`
*
* {id: "2", name: "Rambo"}
*
* This method returns the following payload:
*
* {
* cart: {
* id: "1",
* customer: "2"
* },
* _embedded: {
* customer: {
* id: "2",
* name: "Rambo"
* }
* }
* }
*
* which is then treated by the serializer later.
*
* @method addEmbeddedPayload
* @private
* @param {Object} payload
* @param {String} relationshipName
* @param {Object} relationshipRecord
*/
addEmbeddedPayload: function(payload, relationshipName, relationshipRecord) {
var objectHasId = (relationshipRecord && relationshipRecord.id);
var arrayHasIds = (relationshipRecord.length && relationshipRecord.everyBy("id"));
var isValidRelationship = (objectHasId || arrayHasIds);
if (isValidRelationship) {
if (!payload['_embedded']) {
payload['_embedded'] = {};
}
payload['_embedded'][relationshipName] = relationshipRecord;
if (relationshipRecord.length) {
payload[relationshipName] = relationshipRecord.mapBy('id');
} else {
payload[relationshipName] = relationshipRecord.id;
}
}
if (this.isArray(payload[relationshipName])) {
payload[relationshipName] = payload[relationshipName].filter(function(id) {
return id;
});
}
return payload;
},
isArray: function(value) {
return Object.prototype.toString.call(value) === '[object Array]';
},
/**
* Same as `loadRelationships`, but for an array of records.
*
* @method loadRelationshipsForMany
* @private
* @param {DS.Model} type
* @param {Object} recordsArray
*/
loadRelationshipsForMany: function(type, recordsArray) {
var adapter = this,
promise = Ember.RSVP.resolve([]);
/**
* Create a chain of promises, so the records are loaded sequentially.
* Think of the variable promise as of the accumulator in a left fold.
*/
recordsArray.forEach(function(record) {
promise = promise.then(function(records) {
return adapter.loadRelationships(type, record)
.then(function(loadedRecord) {
records.push(loadedRecord);
return records;
});
});
});
return promise;
},
/**
*
* @method relationshipProperties
* @private
* @param {DS.Model} type
* @param {String} relationName
*/
relationshipProperties: function(type, relationName) {
var relationships = Ember.get(type, 'relationshipsByName');
if (relationName) {
return relationships.get(relationName);
} else {
return relationships;
}
}
});
}());
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
html,
body {
margin: 0;
padding: 0;
}
button {
margin: 0;
padding: 0;
border: 0;
background: none;
font-size: 100%;
vertical-align: baseline;
font-family: inherit;
font-weight: inherit;
color: inherit;
-webkit-appearance: none;
appearance: none;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
font-smoothing: antialiased;
}
body {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em;
background: #f5f5f5;
color: #4d4d4d;
min-width: 230px;
max-width: 550px;
margin: 0 auto;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
font-smoothing: antialiased;
font-weight: 300;
}
button,
input[type="checkbox"] {
outline: none;
}
.hidden {
display: none;
}
#todoapp {
background: #fff;
margin: 130px 0 40px 0;
position: relative;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
#todoapp input::-webkit-input-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
#todoapp input::-moz-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
#todoapp input::input-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
#todoapp h1 {
position: absolute;
top: -155px;
width: 100%;
font-size: 100px;
font-weight: 100;
text-align: center;
color: rgba(175, 47, 47, 0.15);
-webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility;
}
#new-todo,
.edit {
position: relative;
margin: 0;
width: 100%;
font-size: 24px;
font-family: inherit;
font-weight: 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);
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
font-smoothing: antialiased;
}
#new-todo {
padding: 16px 16px 16px 60px;
border: none;
background: rgba(0, 0, 0, 0.003);
box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
}
#main {
position: relative;
z-index: 2;
border-top: 1px solid #e6e6e6;
}
label[for='toggle-all'] {
display: none;
}
#toggle-all {
position: absolute;
top: -55px;
left: -12px;
width: 60px;
height: 34px;
text-align: center;
border: none; /* Mobile Safari */
}
#toggle-all:before {
content: '❯';
font-size: 22px;
color: #e6e6e6;
padding: 10px 27px 10px 27px;
}
#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 solid #ededed;
}
#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;
border: none; /* Mobile Safari */
-webkit-appearance: none;
appearance: none;
}
#todo-list li .toggle:after {
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#ededed" stroke-width="3"/></svg>');
}
#todo-list li .toggle:checked:after {
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#bddad5" stroke-width="3"/><path fill="#5dc2af" d="M72 25L42 71 27 56l-4 4 20 20 34-52z"/></svg>');
}
#todo-list li label {
white-space: pre-line;
word-break: break-all;
padding: 15px 60px 15px 15px;
margin-left: 45px;
display: block;
line-height: 1.2;
transition: color 0.4s;
}
#todo-list li.completed label {
color: #d9d9d9;
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: 30px;
color: #cc9a9a;
margin-bottom: 11px;
transition: color 0.2s ease-out;
}
#todo-list li .destroy:hover {
color: #af5b5e;
}
#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: 10px 15px;
height: 20px;
text-align: center;
border-top: 1px solid #e6e6e6;
}
#footer:before {
content: '';
position: absolute;
right: 0;
bottom: 0;
left: 0;
height: 50px;
overflow: hidden;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
0 8px 0 -3px #f6f6f6,
0 9px 1px -3px rgba(0, 0, 0, 0.2),
0 16px 0 -6px #f6f6f6,
0 17px 2px -6px rgba(0, 0, 0, 0.2);
}
#todo-count {
float: left;
text-align: left;
}
#todo-count strong {
font-weight: 300;
}
#filters {
margin: 0;
padding: 0;
list-style: none;
position: absolute;
right: 0;
left: 0;
}
#filters li {
display: inline;
}
#filters li a {
color: inherit;
margin: 3px;
padding: 3px 7px;
text-decoration: none;
border: 1px solid transparent;
border-radius: 3px;
}
#filters li a.selected,
#filters li a:hover {
border-color: rgba(175, 47, 47, 0.1);
}
#filters li a.selected {
border-color: rgba(175, 47, 47, 0.2);
}
#clear-completed,
html #clear-completed:active {
float: right;
position: relative;
line-height: 20px;
text-decoration: none;
cursor: pointer;
position: relative;
}
#clear-completed:hover {
text-decoration: underline;
}
#info {
margin: 65px auto 0;
color: #bfbfbf;
font-size: 10px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
text-align: center;
}
#info p {
line-height: 1;
}
#info a {
color: inherit;
text-decoration: none;
font-weight: 400;
}
#info a:hover {
text-decoration: underline;
}
/*
Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox
*/
@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 {
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
-webkit-appearance: none;
appearance: none;
}
}
@media (max-width: 430px) {
#footer {
height: 50px;
}
#filters {
bottom: 10px;
}
}
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;
}
#issue-count {
display: none;
}
.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);
transition-property: left;
transition-duration: 500ms;
}
@media (min-width: 899px) {
.learn-bar {
width: auto;
padding-left: 300px;
}
.learn-bar > .learn {
left: 8px;
}
}
/* global _ */
(function () {
'use strict';
/* jshint ignore:start */
// 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'));
}
/* jshint ignore:end */
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.frameworkJSON.issueLabel = framework;
this.append({
backend: true
});
} else if (learnJSON[framework]) {
this.frameworkJSON = learnJSON[framework];
this.frameworkJSON.issueLabel = framework;
this.append();
}
this.fetchIssueCount();
}
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);
};
Learn.prototype.fetchIssueCount = function () {
var issueLink = document.getElementById('issue-count-link');
if (issueLink) {
var url = issueLink.href.replace('https://github.com', 'https://api.github.com/repos');
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onload = function (e) {
var parsedResponse = JSON.parse(e.target.responseText);
if (parsedResponse instanceof Array) {
var count = parsedResponse.length
if (count !== 0) {
issueLink.innerHTML = 'This app has ' + count + ' open issues';
document.getElementById('issue-count').style.display = 'inline';
}
}
};
xhr.send();
}
};
redirect();
getFile('learn.json', Learn);
})();
{
"private": true,
"dependencies": {
"todomvc-app-css": "^1.0.0",
"todomvc-common": "^1.0.1",
"jquery": "^2.1.0",
"handlebars": "^2.0.0",
"ember": "components/ember#1.10.0-beta.3",
"ember-localstorage-adapter": "^0.5.0"
}
}
# Ember.js TodoMVC Example
> A framework for creating ambitious web applications.
> _[Ember.js - emberjs.com](http://emberjs.com)_
## Learning Ember.js
The [Ember.js website](http://emberjs.com) is a great resource for getting started.
Here are some links you may find helpful:
* [Guides](http://emberjs.com/guides)
* [API Reference](http://emberjs.com/api)
* [Screencast - Building an App with Ember.js](https://www.youtube.com/watch?v=Ga99hMi7wfY)
* [Applications built with Ember.js](http://emberjs.com/ember-users)
* [Blog](http://emberjs.com/blog)
Articles and guides from the community:
* [Getting Into Ember.js](http://net.tutsplus.com/tutorials/javascript-ajax/getting-into-ember-js)
* [EmberWatch](http://emberwatch.com)
* [CodeSchool course Warming Up With Ember.js](https://www.codeschool.com/courses/warming-up-with-emberjs)
Get help from other Ember.js users:
* [Ember.js on StackOverflow](http://stackoverflow.com/questions/tagged/ember.js)
* [Ember.js on Twitter](http://twitter.com/emberjs)
* [Ember.js on Google +](https://plus.google.com/communities/106387049790387471205)
_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)._
......@@ -6,21 +6,21 @@
<title>Todomvc</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<base href="/examples/ember-cli/index.html" />
<base href="/examples/emberjs/index.html" />
<meta name="todomvc/config/environment" content="%7B%22modulePrefix%22%3A%22todomvc%22%2C%22environment%22%3A%22production%22%2C%22baseURL%22%3Anull%2C%22locationType%22%3A%22auto%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%7D%2C%22APP%22%3A%7B%22name%22%3A%22todomvc%22%2C%22version%22%3A%220.0.0+b72d5e59%22%7D%2C%22exportApplicationGlobal%22%3Afalse%7D" />
<link rel="stylesheet" href="assets/vendor-7b5c98520910afa58d74e05ec86cd873.css">
<link rel="stylesheet" href="assets/todomvc-d41d8cd98f00b204e9800998ecf8427e.css">
</head>
<body>
<script src="assets/vendor-fe6aaf6bf08a00247e9bb45b00c6c98c.js"></script>
<script src="assets/todomvc-abee913429a66e32e34b282e3460218c.js"></script>
</body>
</html>
......@@ -7,6 +7,7 @@ var del = require('del');
var runSequence = require('run-sequence');
var pagespeed = require('psi');
var app = require('./server');
var vinylfs = require('vinyl-fs');
var AUTOPREFIXER_BROWSERS = [
'ie >= 10',
......@@ -40,7 +41,7 @@ gulp.task('images', function () {
// Copy All Files At The Root Level (app)
gulp.task('copy', function () {
return gulp.src([
return vinylfs.src([
'examples/**',
'bower_components/**',
'learn.json',
......@@ -49,8 +50,10 @@ gulp.task('copy', function () {
'site-assets/favicon.ico'
], {
dot: true,
base: './'
}).pipe(gulp.dest('dist'))
base: './',
followSymlinks: false,
})
.pipe(vinylfs.dest('dist'))
.pipe($.size({title: 'copy'}));
});
......
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