Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

サンプルサーバレスシングルページアプリケーション実装 #40

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
107 changes: 107 additions & 0 deletions public/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
'use strict';
let learnjs = {};
learnjs.problems = [
{
description: 'What is truth?',
code: 'function problem() { return __; }',
},
{
description: 'Simple Math',
code: 'function problem() { return 42 === 6 * __; }',
},
];
learnjs.appOnReady = () => {
window.onhashchange = () => {
learnjs.showView(window.location.hash);
};
learnjs.showView(window.location.hash);
};

learnjs.applyObject = function(obj, elem) {
for (let key in obj) {
elem.find('[data-name="' + key + '"]').text(obj[key]);
}
};

learnjs.problemView = (data) => {
let problemNumber = parseInt(data, 10);
let view = learnjs.template('problem-view');
let problemData = learnjs.problems[problemNumber - 1];
let resultFlash = view.find('.result');

if (problemNumber < learnjs.problems.length) {
let buttonItem = learnjs.template('skip-btn');
buttonItem.find('a').attr('href', '#problem-' + (problemNumber + 1));
$('.nav-list').append(buttonItem);
view.bind('removingView', () => {
buttonItem.remove();
});
}

let checkAnswer = () => {
let answer = view.find('.answer').val();
let test = problemData.code.replace('__', answer) + '; problem();';
return eval(test);
};

let checkAnswerClick = () => {
if (checkAnswer()) {
let correctFlash = learnjs.buildCorrectFlash(problemNumber);
learnjs.flashElement(resultFlash, correctFlash);
} else {
learnjs.flashElement(resultFlash, 'Incorrect!');
}
return false;
};

view.find('.check-btn').click(checkAnswerClick);
view.find('.title').text('Problem #' + problemNumber);
learnjs.applyObject(problemData, view);
return view;
};

learnjs.showView = (hash) => {
let routes = {
'#problem': learnjs.problemView,
'#': learnjs.landingView,
'': learnjs.landingView,
};
let hashParts = hash.split('-');
let viewFn = routes[hashParts[0]];
if (viewFn) {
learnjs.triggerEvent('removingView', []);
$('.view-container').empty().append(viewFn(hashParts[1]));
}
};

learnjs.flashElement = (elem, content) => {
elem.fadeOut('fast', () => {
elem.html(content);
elem.fadeIn();
});
};

learnjs.template = (name) => {
return $('.templates .' + name).clone();
};

learnjs.buildCorrectFlash = (problemNumber) => {
let correctFlash = learnjs.template('correct-flash');
let link = correctFlash.find('a');
if (problemNumber < learnjs.problems.length) {
link.attr('href', '#problem-' + (problemNumber + 1));
} else {
link.attr('href', '');
link.text('You\'re Finished!');
}
return correctFlash;
};

learnjs.landingView = function() {
return learnjs.template('landing-view');
};

learnjs.triggerEvent = (name, args) => {
$('.view-container>*').trigger(name, args);
};

109 changes: 97 additions & 12 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!DOCTYPE html>
<html>
<head>
<head>
<meta charset='utf-8'>
<title>Learn JS!</title>
<link href='//fonts.googleapis.com/css?family=Raleway:400,300,600' rel='stylesheet' type='text/css'>
Expand All @@ -9,17 +9,102 @@
<script src='https://code.jquery.com/jquery-2.1.4.min.js'></script>
<script src='/vendor.js'></script>
<script src='/app.js'></script>
<style type="text/css" media="all">
body { margin-top: 30px; }
<style type='text/css' media='all'>
body {
margin-top: 30px;
}

.text-1g {
font-size: x-large;
}

.templates {
display: none;
}

.inline-list {
margin-bottom: 0px; /* Skleton reset */
}

.inline-list li {
display: inline;
margin: 0 20px 0 0;
}

.fixed-top {
position: fixed;
top: 0px;
z-index: 1024;
}

.no-select {
user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
-moz-user-select: none;
}

.hover-links a {
text-decoration: none;
}

.hover-links a:hover {
text-decoration: underline;
}

.nav-container {
padding-left: 40px;
background: #666;
}

.nav-container a {
color: white;
}
</style>
</head>
<body>
<div class='container'>
<h1>It works!</h1>
<div>
<span>You're ready to start!</span>
<span>Skeleton 2, jQuery 2, and AWS Libraries are included.</span>
</div>
</head>
<body>
<div class='markup'>
<div class='nav-container no-select fixed-top u-full-width'>
<ul class='inline-list hover-links nav-list six columns'>
<li><a class='text-1g' href='#'>LearnJS</a></li>
<li><a href='#problem-1'>Start</a></li>
</ul>
</div>
</body>
<div class='view-container container'></div>
<div class='templates'>
<div class='landing-view'>
<div class='row'>
<div class='one-half column'>
<h3>Learn JavaScript, one puzzle at a time.</h3>
<a href='#problem-1' class='button button-primary'>Start Now!</a>
</div>
<div class='one-half column'>
<img src='/images/HeroImage.jpg'/>
</div>
</div>
</div>
<div class='problem-view'>
<h3 class='title'></h3>
<p data-name='description'></p>
<pre><code data-name='code'></code></pre>
<form>
<textarea class='u-full-width answer'></textarea>
<div>
<button class='button-primary check-btn'>Check Answer</button>
<p class='result'></p>
</div>
</form>
</div>
<div class='correct-flash'>
<span>Correct!</span> <a>Next Problem</a>
</div>
<li class='skip-btn'>
<a>Skip This Problem</a>
</li>
</div>
</div>
<script type='text/javascript'>
$(window).ready(learnjs.appOnReady);
</script>
</body>
</html>
97 changes: 97 additions & 0 deletions public/tests/app_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
describe('LearnJS', function() {
it('can show a problem view', function() {
learnjs.showView('#problem-1');
expect($('.view-container .problem-view').length).toEqual(1);
});
it('shows the landing page view when there is no hash', function() {
learnjs.showView('');
expect($('.view-container .landing-view').length).toEqual(1);
});
it('passes the hash view parameter to the view function', () => {
spyOn(learnjs, 'problemView');
learnjs.showView('#problem-42');
expect(learnjs.problemView).toHaveBeenCalledWith('42');
});
describe('problem view', () => {
it('has a title that includes the problem number', () => {
let view = learnjs.problemView('1');
expect(view.find('.title').text().trim()).toEqual('Problem #1');
});
it('show the description that binds data', () => {
let view = learnjs.problemView('1');
expect(view.find('[data-name="description"]').text().trim()).
toEqual(learnjs.problems[0].description);
});
it('show the problem code that binds data', () => {
let view = learnjs.problemView('1');
expect(view.find('[data-name="code"]').text().trim()).
toEqual(learnjs.problems[0].code);
});
});
it('invokes the router when loaded', () => {
spyOn(learnjs, 'showView');
learnjs.appOnReady();
expect(learnjs.showView).toHaveBeenCalledWith(window.location.hash);
});
it('subscribes to the hash change event', () => {
learnjs.appOnReady();
spyOn(learnjs, 'showView');
$(window).trigger('hashchange');
expect(learnjs.showView).toHaveBeenCalledWith(window.location.hash);
});
describe('answer section', () => {
let view = undefined;
beforeEach(() => {
view = learnjs.problemView('1');
});
it('can check a correct answer by hitting a button', () => {
view.find('.answer').val('true');
view.find('.check-btn').click();
expect(view.find('.result').text().trim()).
toEqual('Correct! Next Problem');
});
it('rejects an incorrect answer', () => {
view.find('.answer').val('false');
view.find('.check-btn').click();
expect(view.find('.result').text().trim()).toEqual('Incorrect!');
});
});
describe('navigation', () => {
it('have a next problem', () => {
let correctFlash = learnjs.buildCorrectFlash(1);
expect(correctFlash.text().trim()).toEqual('Correct! Next Problem');
expect(correctFlash.find('a').attr('href')).toEqual('#problem-2');
});
it('finished', () => {
let correctFlash = learnjs.buildCorrectFlash(learnjs.problems.length);
expect(correctFlash.text().trim()).toEqual('Correct! You\'re Finished!');
expect(correctFlash.find('a').attr('href')).toEqual('');
});
});
describe('application shell', () => {
it('landing page link on toolbar', () => {
let link = $('.nav-container > ul a:contains("LearnJS")');
expect(link.attr('href')).toEqual('#');
});
it('start link on toolbar', () => {
let link = $('.nav-container > ul a:contains("Start")');
expect(link.attr('href')).toEqual('#problem-1');
});
it('not have skip link on toolbar in landing page', () => {
learnjs.showView('');
let link = $('.nav-container > ul a:contains("Skip This Problem")');
expect(link.length).toEqual(0);
});
it('have skip link on toolbar in problem view', () => {
learnjs.problemView('1');
let link = $('.nav-container > ul a:contains("Skip This Problem")');
expect(link.attr('href')).toEqual('#problem-2');
});
it('not have skip link on toolbar in final problem view', () => {
learnjs.problemView(learnjs.problems.length);
let link = $('.nav-container > ul a:contains("Skip This Problem")');
expect(link.length).toEqual(0);
});
});
});