-
Notifications
You must be signed in to change notification settings - Fork 13.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
15bdca9
commit 1916c93
Showing
17 changed files
with
2,132 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
.DS_Store | ||
/node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# TodoMVC: jQuery | ||
## Description | ||
|
||
This application uses jQuery (3.6.3) to implement a todo application. | ||
|
||
jQuery is a fast, small, and feature-rich JavaScript library. It makes things like HTML document traversal and manipulation, event handling, animation, and Ajax much simpler with an easy-to-use API that works across a multitude of browsers. With a combination of versatility and extensibility, jQuery has changed the way that millions of people write JavaScript. | ||
|
||
[jQuery.com](https://jquery.com/) | ||
|
||
## Implementation details | ||
|
||
In contrast to mv(*) patterns, this implementation keeps display, state and app logic all within one file. | ||
Many apps that utilize jQuery tend to keep most or all aspects within a single file for simplicity. | ||
|
||
## Build Steps | ||
|
||
A simple build script copies all necessary files to a `dist` folder. | ||
It does not rely on compilers or transpilers and serves raw html, css and js files to the user. | ||
|
||
``` | ||
npm run build | ||
``` | ||
|
||
## Requirements | ||
|
||
The only requirement is an installation of Node, to be able to install dependencies and run scripts to serve a local server. | ||
|
||
``` | ||
* Node (min version: 18.13.0) | ||
* NPM (min version: 8.19.3) | ||
``` | ||
|
||
## Local preview | ||
|
||
``` | ||
terminal: | ||
1. npm install | ||
2. npm run dev | ||
browser: | ||
1. http://localhost:7001/ | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
#main, | ||
#footer { | ||
display: none; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
/*global jQuery, Handlebars, Router */ | ||
jQuery(function ($) { | ||
'use strict'; | ||
|
||
Handlebars.registerHelper('eq', function (a, b, options) { | ||
return a === b ? options.fn(this) : options.inverse(this); | ||
}); | ||
|
||
var util = { | ||
uuid: function () { | ||
/*jshint bitwise:false */ | ||
var i, random; | ||
var uuid = ''; | ||
|
||
for (i = 0; i < 32; i++) { | ||
random = Math.random() * 16 | 0; | ||
if (i === 8 || i === 12 || i === 16 || i === 20) { | ||
uuid += '-'; | ||
} | ||
uuid += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random)).toString(16); | ||
} | ||
|
||
return uuid; | ||
}, | ||
pluralize: function (count, word) { | ||
return count === 1 ? word : word + 's'; | ||
}, | ||
store: function (namespace, data) { | ||
return []; | ||
} | ||
}; | ||
|
||
var App = { | ||
ENTER_KEY: 13, | ||
ESCAPE_KEY: 27, | ||
init: function () { | ||
this.todos = util.store('todos-jquery'); | ||
this.todoTemplate = Handlebars.compile($('#todo-template').html()); | ||
this.footerTemplate = Handlebars.compile($('#footer-template').html()); | ||
this.bindEvents(); | ||
|
||
new Router({ | ||
'/:filter': function (filter) { | ||
this.filter = filter; | ||
this.render(); | ||
}.bind(this) | ||
}).init('/all'); | ||
|
||
var dummyNodeToNotifyAppIsReady = document.createElement('div'); | ||
dummyNodeToNotifyAppIsReady.id = 'appIsReady'; | ||
document.body.appendChild(dummyNodeToNotifyAppIsReady); | ||
}, | ||
bindEvents: function () { | ||
$('#new-todo').on('keyup', this.create.bind(this)); | ||
$('#toggle-all').on('change', this.toggleAll.bind(this)); | ||
$('#footer').on('click', '.clear-completed', this.destroyCompleted.bind(this)); | ||
$('#todo-list') | ||
.on('change', '.toggle', this.toggle.bind(this)) | ||
.on('dblclick', 'label', this.edit.bind(this)) | ||
.on('keyup', '.edit', this.editKeyup.bind(this)) | ||
.on('focusout', '.edit', this.update.bind(this)) | ||
.on('click', '.destroy', this.destroy.bind(this)); | ||
}, | ||
render: function () { | ||
var todos = this.getFilteredTodos(); | ||
$('#todo-list').html(this.todoTemplate(todos)); | ||
$('#main').toggle(todos.length > 0); | ||
$('#toggle-all').prop('checked', this.getActiveTodos().length === 0); | ||
this.renderFooter(); | ||
$('#new-todo').focus(); | ||
util.store('todos-jquery', this.todos); | ||
}, | ||
renderFooter: function () { | ||
var todoCount = this.todos.length; | ||
var activeTodoCount = this.getActiveTodos().length; | ||
var template = this.footerTemplate({ | ||
activeTodoCount: activeTodoCount, | ||
activeTodoWord: util.pluralize(activeTodoCount, 'item'), | ||
completedTodos: todoCount - activeTodoCount, | ||
filter: this.filter | ||
}); | ||
|
||
$('#footer').toggle(todoCount > 0).html(template); | ||
}, | ||
toggleAll: function (e) { | ||
var isChecked = $(e.target).prop('checked'); | ||
|
||
this.todos.forEach(function (todo) { | ||
todo.completed = isChecked; | ||
}); | ||
|
||
this.render(); | ||
}, | ||
getActiveTodos: function () { | ||
return this.todos.filter(function (todo) { | ||
return !todo.completed; | ||
}); | ||
}, | ||
getCompletedTodos: function () { | ||
return this.todos.filter(function (todo) { | ||
return todo.completed; | ||
}); | ||
}, | ||
getFilteredTodos: function () { | ||
if (this.filter === 'active') { | ||
return this.getActiveTodos(); | ||
} | ||
|
||
if (this.filter === 'completed') { | ||
return this.getCompletedTodos(); | ||
} | ||
|
||
return this.todos; | ||
}, | ||
destroyCompleted: function () { | ||
this.todos = this.getActiveTodos(); | ||
this.filter = 'all'; | ||
this.render(); | ||
}, | ||
// accepts an element from inside the `.item` div and | ||
// returns the corresponding index in the `todos` array | ||
indexFromEl: function (el) { | ||
var id = $(el).closest('li').data('id'); | ||
var todos = this.todos; | ||
var i = todos.length; | ||
|
||
while (i--) { | ||
if (todos[i].id === id) { | ||
return i; | ||
} | ||
} | ||
}, | ||
create: function (e) { | ||
var $input = $(e.target); | ||
var val = $.trim($input.val()); | ||
|
||
if (e.which !== this.ENTER_KEY || !val) { | ||
return; | ||
} | ||
|
||
this.todos.push({ | ||
id: util.uuid(), | ||
title: val, | ||
completed: false | ||
}); | ||
|
||
$input.val(''); | ||
|
||
this.render(); | ||
}, | ||
toggle: function (e) { | ||
var i = this.indexFromEl(e.target); | ||
this.todos[i].completed = !this.todos[i].completed; | ||
this.render(); | ||
}, | ||
edit: function (e) { | ||
var $input = $(e.target).closest('li').addClass('editing').find('.edit'); | ||
const title = $(e.target).text(); | ||
$input.trigger("focus").val("").val(title); | ||
}, | ||
editKeyup: function (e) { | ||
if (e.which === this.ENTER_KEY) { | ||
e.target.blur(); | ||
} | ||
|
||
if (e.which === this.ESCAPE_KEY) { | ||
$(e.target).data('abort', true).blur(); | ||
} | ||
}, | ||
update: function (e) { | ||
var el = e.target; | ||
var $el = $(el); | ||
var val = $el.val().trim(); | ||
|
||
if (!val) { | ||
this.destroy(e); | ||
return; | ||
} | ||
|
||
if ($el.data('abort')) { | ||
$el.data('abort', false); | ||
} else { | ||
this.todos[this.indexFromEl(el)].title = val; | ||
} | ||
|
||
this.render(); | ||
}, | ||
destroy: function (e) { | ||
this.todos.splice(this.indexFromEl(e.target), 1); | ||
this.render(); | ||
} | ||
}; | ||
|
||
window.app = App; | ||
window.app.init(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
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; | ||
} | ||
} |
Oops, something went wrong.