This commit is contained in:
Brittany Storoz 2015-08-06 00:36:02 -04:00
Родитель 73d7e3a778 7a83db2460
Коммит baffdf72a1
86 изменённых файлов: 6080 добавлений и 46 удалений

Просмотреть файл

@ -1,3 +1,4 @@
{
"directory": "app/bower_components"
"directory": "bower_components",
"analytics": false
}

Просмотреть файл

@ -6,16 +6,29 @@ root = true
[*]
# Change these settings to your own preference
indent_style = space
indent_size = 4
# We recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
[*.md]
[*.js]
indent_style = space
indent_size = 2
[*.hbs]
insert_final_newline = false
indent_style = space
indent_size = 2
[*.css]
indent_style = space
indent_size = 2
[*.html]
indent_style = space
indent_size = 2
[*.{diff,md}]
trim_trailing_whitespace = false

9
.ember-cli Normal file
Просмотреть файл

@ -0,0 +1,9 @@
{
/**
Ember CLI sends analytics information by default. The data is completely
anonymous, but there are times when you might want to disable this behavior.
Setting `disableAnalytics` to true will prevent any data from being sent.
*/
"disableAnalytics": false
}

25
.gitignore поставляемый
Просмотреть файл

@ -1,8 +1,17 @@
node_modules
temp
dist
.sass-cache
.tmp
app/bower_components
.DS_Store
.grunt
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
# dependencies
/node_modules
/bower_components
# misc
/.sass-cache
/connect.lock
/coverage/*
/libpeerconnection.log
npm-debug.log
testem.log

Просмотреть файл

@ -1,28 +1,34 @@
{
"node": true,
"browser": true,
"esnext": true,
"bitwise": false,
"curly": false,
"eqeqeq": true,
"eqnull": true,
"immed": true,
"latedef": true,
"newcap": true,
"noarg": true,
"undef": true,
"strict": false,
"trailing": false,
"smarttabs": true,
"globals": {
"HighFidelity": true,
"$": true,
"jQuery": true,
"Ember": true,
"Handlebars": true,
"DS": true,
"Promise": true,
"I18n": true,
"localforage": true
}
"predef": [
"document",
"window",
"-Promise",
"$",
"EmberHifi"
],
"browser": true,
"boss": true,
"curly": true,
"debug": false,
"devel": true,
"eqeqeq": true,
"evil": true,
"forin": false,
"immed": false,
"laxbreak": false,
"newcap": true,
"noarg": true,
"noempty": false,
"nonew": false,
"nomen": false,
"onevar": false,
"plusplus": false,
"regexp": false,
"undef": true,
"sub": true,
"strict": false,
"white": false,
"eqnull": true,
"esnext": true,
"unused": true
}

56
.tmp/index.html Normal file
Просмотреть файл

@ -0,0 +1,56 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<!-- TODO: Localize title -->
<title>Podcasts</title>
<meta name="description" content="Podcasts in your browser and your phone.">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<!-- iOS-specific meta information; for homescreen web app capabilities -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<!-- iPhone -->
<link rel="apple-touch-icon-precomposed" sizes="57x57" href="apple-touch-icon-57x57-precomposed.png">
<link rel="apple-touch-startup-image" media="(device-width: 320px)" href="img/default.png">
<!-- Retina iPhone -->
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="apple-touch-icon-114x114-precomposed.png">
<link rel="apple-touch-startup-image" media="(device-width: 320px) and (-webkit-device-pixel-ratio: 2)" href="img/default@2x.png">
<!-- Enable Cleartype for MS Browsers -->
<meta http-equiv="cleartype" content="on">
<!-- Responsive and mobile friendly stuff -->
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<!-- build:css(app) styles/vendor.css -->
<link rel="stylesheet" href="bower_components/normalize.css/normalize.css">
<link rel="stylesheet" href="bower_components/brick/dist/brick.css">
<link rel="stylesheet" href="bower_components/fontawesome/css/font-awesome.css">
<!-- endbuild -->
<!-- build:css(.tmp) styles/app.css -->
<link rel="stylesheet" href="styles/compiled-stylus.css">
<!-- endbuild -->
</head>
<body>
<!-- build:js(app) scripts/vendor.js -->
<script src="bower_components/jquery/jquery.js"></script>
<script src="bower_components/handlebars/handlebars.runtime.js"></script>
<script src="bower_components/ember/ember.prod.js"></script>
<script src="bower_components/ember-data/ember-data.prod.js"></script>
<script src="bower_components/brick/dist/brick.js"></script>
<script src="bower_components/localforage/dist/localforage.js"></script>
<script src="bower_components/i18n-js/app/assets/javascripts/i18n.js"></script>
<script src="bower_components/ember-indexeddb-adapter/dist/ember_indexeddb_adapter.js"></script>
<!-- endbuild -->
<!-- build:js(.tmp) scripts/app.js -->
<script src="scripts/compiled-templates.js"></script>
<script src="scripts/combined-scripts.js"></script>
<!-- endbuild -->
</body>
</html>
<!-- I can still hear you saying you would never break the chain -->

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,627 @@
Ember.TEMPLATES["application"] = Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) {
this.compilerInfo = [4,'>= 1.0.0'];
helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {};
var buffer = '', stack1, stack2, hashTypes, hashContexts, options, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, self=this;
function program1(depth0,data) {
var stack1, hashTypes, hashContexts, options;
hashTypes = {};
hashContexts = {};
options = {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['t'] || depth0['t']),stack1 ? stack1.call(depth0, "tabs.myPodcasts", options) : helperMissing.call(depth0, "t", "tabs.myPodcasts", options))));
}
function program3(depth0,data) {
var buffer = '', stack1, stack2, hashTypes, hashContexts, options;
data.buffer.push("\n ");
data.buffer.push("\n <x-tabbar-tab>\n ");
hashTypes = {};
hashContexts = {};
options = {hash:{},inverse:self.noop,fn:self.program(4, program4, data),contexts:[depth0],types:["STRING"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
stack2 = ((stack1 = helpers['link-to'] || depth0['link-to']),stack1 ? stack1.call(depth0, "search", options) : helperMissing.call(depth0, "link-to", "search", options));
if(stack2 || stack2 === 0) { data.buffer.push(stack2); }
data.buffer.push("\n </x-tabbar-tab>\n ");
return buffer;
}
function program4(depth0,data) {
var stack1, hashTypes, hashContexts, options;
hashTypes = {};
hashContexts = {};
options = {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['t'] || depth0['t']),stack1 ? stack1.call(depth0, "tabs.search", options) : helperMissing.call(depth0, "t", "tabs.search", options))));
}
data.buffer.push("<x-layout>\n <header>\n <x-appbar>\n <h1>");
hashTypes = {};
hashContexts = {};
options = {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['t'] || depth0['t']),stack1 ? stack1.call(depth0, "app.title", options) : helperMissing.call(depth0, "t", "app.title", options))));
data.buffer.push("</h1>\n ");
hashTypes = {};
hashContexts = {};
options = {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers.outlet || depth0.outlet),stack1 ? stack1.call(depth0, "headerAction", options) : helperMissing.call(depth0, "outlet", "headerAction", options))));
data.buffer.push("\n </x-appbar>\n </header>\n <section>\n ");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers._triageMustache.call(depth0, "outlet", {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push("\n </section>\n <footer>\n ");
data.buffer.push("\n <audio id=\"audio-player\" preload=\"auto\" mozaudiochannel=\"content\"></audio>\n\n ");
hashTypes = {};
hashContexts = {};
options = {hash:{},contexts:[depth0],types:["STRING"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers.render || depth0.render),stack1 ? stack1.call(depth0, "player", options) : helperMissing.call(depth0, "render", "player", options))));
data.buffer.push("\n\n <x-tabbar>\n <x-tabbar-tab selected=\"true\">\n ");
hashTypes = {};
hashContexts = {};
options = {hash:{},inverse:self.noop,fn:self.program(1, program1, data),contexts:[depth0],types:["STRING"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
stack2 = ((stack1 = helpers['link-to'] || depth0['link-to']),stack1 ? stack1.call(depth0, "podcasts", options) : helperMissing.call(depth0, "link-to", "podcasts", options));
if(stack2 || stack2 === 0) { data.buffer.push(stack2); }
data.buffer.push("\n </x-tabbar-tab>\n\n ");
data.buffer.push("\n ");
hashTypes = {};
hashContexts = {};
stack2 = helpers['if'].call(depth0, "isPackaged", {hash:{},inverse:self.noop,fn:self.program(3, program3, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data});
if(stack2 || stack2 === 0) { data.buffer.push(stack2); }
data.buffer.push("\n </x-tabbar>\n </footer>\n</x-layout>\n");
return buffer;
});
Ember.TEMPLATES["episodes"] = Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) {
this.compilerInfo = [4,'>= 1.0.0'];
helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {};
var buffer = '', stack1, hashContexts, hashTypes, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, self=this;
function program1(depth0,data) {
var buffer = '', stack1, stack2, hashTypes, hashContexts, options;
data.buffer.push("\n <div ");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers.action.call(depth0, "setEpisode", "", {hash:{},contexts:[depth0,depth0],types:["ID","ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push(" ");
hashContexts = {'class': depth0};
hashTypes = {'class': "STRING"};
options = {hash:{
'class': (":episode isNew")
},contexts:[],types:[],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['bind-attr'] || depth0['bind-attr']),stack1 ? stack1.call(depth0, options) : helperMissing.call(depth0, "bind-attr", options))));
data.buffer.push(">\n <div class=\"name\">\n ");
hashTypes = {};
hashContexts = {};
stack2 = helpers['if'].call(depth0, "isNew", {hash:{},inverse:self.noop,fn:self.program(2, program2, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data});
if(stack2 || stack2 === 0) { data.buffer.push(stack2); }
data.buffer.push("\n ");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers._triageMustache.call(depth0, "name", {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push("\n </div>\n\n ");
data.buffer.push("\n </div>\n ");
return buffer;
}
function program2(depth0,data) {
var buffer = '', stack1, hashTypes, hashContexts, options;
data.buffer.push("\n <strong>");
hashTypes = {};
hashContexts = {};
options = {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['t'] || depth0['t']),stack1 ? stack1.call(depth0, "episode.new", options) : helperMissing.call(depth0, "t", "episode.new", options))));
data.buffer.push("</strong>\n ");
return buffer;
}
function program4(depth0,data) {
var buffer = '', stack1, hashTypes, hashContexts, options;
data.buffer.push("\n ");
hashTypes = {};
hashContexts = {};
options = {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers.render || depth0.render),stack1 ? stack1.call(depth0, "spinner", options) : helperMissing.call(depth0, "render", "spinner", options))));
data.buffer.push("\n ");
return buffer;
}
data.buffer.push("<div class=\"episodes\">\n ");
hashContexts = {'content': depth0};
hashTypes = {'content': "ID"};
stack1 = helpers.each.call(depth0, {hash:{
'content': ("controller")
},inverse:self.program(4, program4, data),fn:self.program(1, program1, data),contexts:[],types:[],hashContexts:hashContexts,hashTypes:hashTypes,data:data});
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
data.buffer.push("\n</div>\n");
return buffer;
});
Ember.TEMPLATES["header-actions/podcast"] = Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) {
this.compilerInfo = [4,'>= 1.0.0'];
helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {};
var buffer = '', hashTypes, hashContexts, escapeExpression=this.escapeExpression;
data.buffer.push("<h1>");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers._triageMustache.call(depth0, "title", {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push("</h1>\n<button class=\"update\" ");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers.action.call(depth0, "update", {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push("><i class=\"fa fa-refresh\"></i></button>\n");
return buffer;
});
Ember.TEMPLATES["header-actions/podcasts"] = Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) {
this.compilerInfo = [4,'>= 1.0.0'];
helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {};
var buffer = '', stack1, stack2, hashTypes, hashContexts, options, self=this, helperMissing=helpers.helperMissing;
function program1(depth0,data) {
data.buffer.push("<i class=\"fa fa-plus\"></i>");
}
hashTypes = {};
hashContexts = {};
options = {hash:{},inverse:self.noop,fn:self.program(1, program1, data),contexts:[depth0],types:["STRING"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
stack2 = ((stack1 = helpers['link-to'] || depth0['link-to']),stack1 ? stack1.call(depth0, "podcast.new", options) : helperMissing.call(depth0, "link-to", "podcast.new", options));
if(stack2 || stack2 === 0) { data.buffer.push(stack2); }
data.buffer.push("\n");
return buffer;
});
Ember.TEMPLATES["player"] = Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) {
this.compilerInfo = [4,'>= 1.0.0'];
helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {};
var buffer = '', stack1, stack2, hashContexts, hashTypes, options, escapeExpression=this.escapeExpression, helperMissing=helpers.helperMissing, self=this;
function program1(depth0,data) {
var buffer = '', stack1, stack2, hashContexts, hashTypes, options;
data.buffer.push("\n <div id=\"time-info\">\n <progress id=\"audio-progress\" ");
hashContexts = {'max': depth0};
hashTypes = {'max': "ID"};
options = {hash:{
'max': ("progressBar.max")
},contexts:[],types:[],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['bind-attr'] || depth0['bind-attr']),stack1 ? stack1.call(depth0, options) : helperMissing.call(depth0, "bind-attr", options))));
data.buffer.push("\n ");
hashContexts = {'value': depth0};
hashTypes = {'value': "ID"};
options = {hash:{
'value': ("progressBar.value")
},contexts:[],types:[],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['bind-attr'] || depth0['bind-attr']),stack1 ? stack1.call(depth0, options) : helperMissing.call(depth0, "bind-attr", options))));
data.buffer.push(">\n </progress>\n \n <span id=\"time-elapsed\">");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers._triageMustache.call(depth0, "timeElapsed", {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push("</span>\n <span id=\"time-remaining\">");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers._triageMustache.call(depth0, "timeTotal", {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push("</span>\n </div>\n \n <div class=\"episode-name\">");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers._triageMustache.call(depth0, "name", {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push("</div>\n \n <div class=\"player-controls\">\n <a ");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers.action.call(depth0, "rewind", "model", {hash:{},contexts:[depth0,depth0],types:["ID","ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push(" class=\"backward\">\n <i class=\"fa fa-backward\"></i>\n </a>\n \n ");
hashTypes = {};
hashContexts = {};
stack2 = helpers['if'].call(depth0, "isPlaying", {hash:{},inverse:self.program(4, program4, data),fn:self.program(2, program2, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data});
if(stack2 || stack2 === 0) { data.buffer.push(stack2); }
data.buffer.push("\n \n <a ");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers.action.call(depth0, "forward", "model", {hash:{},contexts:[depth0,depth0],types:["ID","ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push(" class=\"forward\">\n <i class=\"fa fa-forward\"></i>\n </a>\n </div>\n ");
return buffer;
}
function program2(depth0,data) {
var buffer = '', hashTypes, hashContexts;
data.buffer.push("\n <a ");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers.action.call(depth0, "pause", "model", {hash:{},contexts:[depth0,depth0],types:["ID","ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push(" class=\"play-pause\">\n <i class=\"fa fa-pause\"></i>\n </a>\n ");
return buffer;
}
function program4(depth0,data) {
var buffer = '', hashTypes, hashContexts;
data.buffer.push("\n <a ");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers.action.call(depth0, "play", "model", {hash:{},contexts:[depth0,depth0],types:["ID","ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push(" class=\"play-pause\">\n <i class=\"fa fa-play\"></i>\n </a>\n ");
return buffer;
}
data.buffer.push("<div id=\"player\" ");
hashContexts = {'class': depth0};
hashTypes = {'class': "STRING"};
options = {hash:{
'class': ("isPopulated")
},contexts:[],types:[],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['bind-attr'] || depth0['bind-attr']),stack1 ? stack1.call(depth0, options) : helperMissing.call(depth0, "bind-attr", options))));
data.buffer.push(">\n ");
hashTypes = {};
hashContexts = {};
stack2 = helpers['if'].call(depth0, "audio", {hash:{},inverse:self.noop,fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data});
if(stack2 || stack2 === 0) { data.buffer.push(stack2); }
data.buffer.push("\n</div>\n");
return buffer;
});
Ember.TEMPLATES["podcast"] = Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) {
this.compilerInfo = [4,'>= 1.0.0'];
helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {};
var buffer = '', stack1, hashContexts, hashTypes, options, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
data.buffer.push("<div class=\"podcast\">\n <img class=\"cover-image\" ");
hashContexts = {'src': depth0};
hashTypes = {'src': "ID"};
options = {hash:{
'src': ("coverImageURL")
},contexts:[],types:[],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['bind-attr'] || depth0['bind-attr']),stack1 ? stack1.call(depth0, options) : helperMissing.call(depth0, "bind-attr", options))));
data.buffer.push("\n ");
hashContexts = {'alt': depth0};
hashTypes = {'alt': "ID"};
options = {hash:{
'alt': ("title")
},contexts:[],types:[],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['bind-attr'] || depth0['bind-attr']),stack1 ? stack1.call(depth0, options) : helperMissing.call(depth0, "bind-attr", options))));
data.buffer.push(">\n\n <p>");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers._triageMustache.call(depth0, "description", {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push("</p>\n\n <button ");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers.action.call(depth0, "delete", {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push(">Unsubscribe</button>\n ");
data.buffer.push("\n</div>\n\n<hr/>\n\n");
hashTypes = {};
hashContexts = {};
options = {hash:{},contexts:[depth0,depth0],types:["ID","ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers.render || depth0.render),stack1 ? stack1.call(depth0, "episodes", "episodes", options) : helperMissing.call(depth0, "render", "episodes", "episodes", options))));
data.buffer.push("\n\n");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers._triageMustache.call(depth0, "outlet", {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push("\n");
return buffer;
});
Ember.TEMPLATES["podcast/new"] = Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) {
this.compilerInfo = [4,'>= 1.0.0'];
helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {};
var buffer = '', stack1, stack2, hashTypes, hashContexts, options, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, self=this;
function program1(depth0,data) {
data.buffer.push("\n <div class=\"error\">\n <p>Encountered an error while adding Podcast.<p>\n <p>Is the Podcast URL valid?</p>\n </div>\n");
}
function program3(depth0,data) {
var buffer = '', stack1, hashTypes, hashContexts, options;
data.buffer.push("\n <div class=\"full-screen\">\n ");
hashTypes = {};
hashContexts = {};
options = {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers.render || depth0.render),stack1 ? stack1.call(depth0, "spinner", options) : helperMissing.call(depth0, "render", "spinner", options))));
data.buffer.push("\n </div>\n");
return buffer;
}
data.buffer.push("<h2>");
hashTypes = {};
hashContexts = {};
options = {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['t'] || depth0['t']),stack1 ? stack1.call(depth0, "podcast.addRSSFeed", options) : helperMissing.call(depth0, "t", "podcast.addRSSFeed", options))));
data.buffer.push("</h2>\n\n");
hashTypes = {};
hashContexts = {};
stack2 = helpers['if'].call(depth0, "isInErrorState", {hash:{},inverse:self.noop,fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data});
if(stack2 || stack2 === 0) { data.buffer.push(stack2); }
data.buffer.push("\n\n");
hashContexts = {'type': depth0,'value': depth0,'placeholder': depth0};
hashTypes = {'type': "STRING",'value': "ID",'placeholder': "STRING"};
options = {hash:{
'type': ("url"),
'value': ("rssURL"),
'placeholder': ("Podcast URL")
},contexts:[],types:[],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers.input || depth0.input),stack1 ? stack1.call(depth0, options) : helperMissing.call(depth0, "input", options))));
data.buffer.push("\n<button ");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers.action.call(depth0, "create", {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push(">");
hashTypes = {};
hashContexts = {};
options = {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['t'] || depth0['t']),stack1 ? stack1.call(depth0, "podcast.add", options) : helperMissing.call(depth0, "t", "podcast.add", options))));
data.buffer.push("</button>\n\n<h2>");
hashTypes = {};
hashContexts = {};
options = {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['t'] || depth0['t']),stack1 ? stack1.call(depth0, "podcast.newToPodcasts", options) : helperMissing.call(depth0, "t", "podcast.newToPodcasts", options))));
data.buffer.push("</h2>\n<h3>");
hashTypes = {};
hashContexts = {};
options = {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['t'] || depth0['t']),stack1 ? stack1.call(depth0, "podcast.recommendations", options) : helperMissing.call(depth0, "t", "podcast.recommendations", options))));
data.buffer.push("</h3>\n\n<h4>The Cracked Podcast</h4>\n<img alt=\"The Cracked Podcast\" class=\"cover cover-image\"\n ");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers.action.call(depth0, "create", "http://rss.earwolf.com/the-cracked-podcast", {hash:{},contexts:[depth0,depth0],types:["ID","STRING"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push("\n src=\"http://cdn.earwolf.com/wp-content/uploads/2013/08/EAR_CrackedPodcast_1600x1600_Cover_Final.jpg\">\n\n<h4>Hello Internet</h4>\n<img alt=\"Hello Internet\" class=\"cover cover-image\"\n ");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers.action.call(depth0, "create", "http://feeds.podtrac.com/m2lTaLRx8AWb", {hash:{},contexts:[depth0,depth0],types:["ID","STRING"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push("\n src=\"http://static.squarespace.com/static/52d66949e4b0a8cec3bcdd46/t/52ebf67fe4b0f4af2a4502d8/1391195777839/1500w/Hello%20Internet.003.png\">\n\n");
hashTypes = {};
hashContexts = {};
stack2 = helpers['if'].call(depth0, "isAdding", {hash:{},inverse:self.noop,fn:self.program(3, program3, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data});
if(stack2 || stack2 === 0) { data.buffer.push(stack2); }
data.buffer.push("\n");
return buffer;
});
Ember.TEMPLATES["podcasts"] = Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) {
this.compilerInfo = [4,'>= 1.0.0'];
helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {};
var buffer = '', stack1, hashTypes, hashContexts, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, self=this;
function program1(depth0,data) {
var buffer = '', stack1, hashContexts, hashTypes;
data.buffer.push("\n <ul class=\"my-podcasts\">\n ");
hashContexts = {'content': depth0,'tagName': depth0,'className': depth0};
hashTypes = {'content': "ID",'tagName': "ID",'className': "STRING"};
stack1 = helpers.each.call(depth0, {hash:{
'content': ("controller"),
'tagName': ("ul"),
'className': ("my-podcasts")
},inverse:self.noop,fn:self.program(2, program2, data),contexts:[],types:[],hashContexts:hashContexts,hashTypes:hashTypes,data:data});
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
data.buffer.push("\n </ul>\n");
return buffer;
}
function program2(depth0,data) {
var buffer = '', stack1, stack2, hashContexts, hashTypes, options;
data.buffer.push("\n <li>\n ");
hashContexts = {'class': depth0};
hashTypes = {'class': "STRING"};
options = {hash:{
'class': ("cover")
},inverse:self.noop,fn:self.program(3, program3, data),contexts:[depth0,depth0],types:["STRING","ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
stack2 = ((stack1 = helpers['link-to'] || depth0['link-to']),stack1 ? stack1.call(depth0, "podcast", "", options) : helperMissing.call(depth0, "link-to", "podcast", "", options));
if(stack2 || stack2 === 0) { data.buffer.push(stack2); }
data.buffer.push("\n </li>\n ");
return buffer;
}
function program3(depth0,data) {
var buffer = '', stack1, hashTypes, hashContexts;
data.buffer.push("\n ");
hashTypes = {};
hashContexts = {};
stack1 = helpers['if'].call(depth0, "coverImage", {hash:{},inverse:self.program(6, program6, data),fn:self.program(4, program4, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data});
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
data.buffer.push("\n ");
return buffer;
}
function program4(depth0,data) {
var buffer = '', stack1, hashContexts, hashTypes, options;
data.buffer.push("\n <img ");
hashContexts = {'src': depth0};
hashTypes = {'src': "ID"};
options = {hash:{
'src': ("coverImage")
},contexts:[],types:[],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['bind-attr'] || depth0['bind-attr']),stack1 ? stack1.call(depth0, options) : helperMissing.call(depth0, "bind-attr", options))));
data.buffer.push(" ");
hashContexts = {'alt': depth0};
hashTypes = {'alt': "ID"};
options = {hash:{
'alt': ("title")
},contexts:[],types:[],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['bind-attr'] || depth0['bind-attr']),stack1 ? stack1.call(depth0, options) : helperMissing.call(depth0, "bind-attr", options))));
data.buffer.push(">\n ");
return buffer;
}
function program6(depth0,data) {
var buffer = '', hashTypes, hashContexts;
data.buffer.push("\n <span class=\"no-cover\">");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers._triageMustache.call(depth0, "title", {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push("</span>\n ");
return buffer;
}
function program8(depth0,data) {
var buffer = '', stack1, stack2, hashTypes, hashContexts, options;
data.buffer.push("\n <div class=\"center\">\n <h2>");
hashTypes = {};
hashContexts = {};
options = {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['t'] || depth0['t']),stack1 ? stack1.call(depth0, "podcast.noneFound", options) : helperMissing.call(depth0, "t", "podcast.noneFound", options))));
data.buffer.push("</h2>\n <p>\n ");
hashTypes = {};
hashContexts = {};
options = {hash:{},inverse:self.noop,fn:self.program(9, program9, data),contexts:[depth0],types:["STRING"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
stack2 = ((stack1 = helpers['link-to'] || depth0['link-to']),stack1 ? stack1.call(depth0, "podcast.new", options) : helperMissing.call(depth0, "link-to", "podcast.new", options));
if(stack2 || stack2 === 0) { data.buffer.push(stack2); }
data.buffer.push("\n </p>\n </div>\n");
return buffer;
}
function program9(depth0,data) {
var stack1, hashTypes, hashContexts, options;
hashTypes = {};
hashContexts = {};
options = {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['t'] || depth0['t']),stack1 ? stack1.call(depth0, "podcast.addOne", options) : helperMissing.call(depth0, "t", "podcast.addOne", options))));
}
hashTypes = {};
hashContexts = {};
stack1 = helpers['if'].call(depth0, "controller", {hash:{},inverse:self.program(8, program8, data),fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data});
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
data.buffer.push("\n\n");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers._triageMustache.call(depth0, "outlet", {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push("\n");
return buffer;
});
Ember.TEMPLATES["search"] = Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) {
this.compilerInfo = [4,'>= 1.0.0'];
helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {};
var buffer = '', stack1, stack2, hashTypes, hashContexts, options, escapeExpression=this.escapeExpression, helperMissing=helpers.helperMissing, self=this;
function program1(depth0,data) {
var buffer = '', stack1, hashTypes, hashContexts;
data.buffer.push("\n <div class=\"results\">\n ");
hashTypes = {};
hashContexts = {};
stack1 = helpers.each.call(depth0, "results", {hash:{},inverse:self.program(4, program4, data),fn:self.program(2, program2, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data});
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
data.buffer.push("\n </div>\n");
return buffer;
}
function program2(depth0,data) {
var buffer = '', stack1, hashTypes, hashContexts, options;
data.buffer.push("\n <div class=\"podcast\" ");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers.action.call(depth0, "subscribe", "feedUrl", "trackName", {hash:{},contexts:[depth0,depth0,depth0],types:["ID","ID","ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push(">\n <div class=\"name\" ");
hashContexts = {'data-collection-url': depth0};
hashTypes = {'data-collection-url': "ID"};
options = {hash:{
'data-collection-url': ("feedUrl")
},contexts:[],types:[],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['bind-attr'] || depth0['bind-attr']),stack1 ? stack1.call(depth0, options) : helperMissing.call(depth0, "bind-attr", options))));
data.buffer.push(">\n <img ");
hashContexts = {'src': depth0};
hashTypes = {'src': "ID"};
options = {hash:{
'src': ("artworkUrl100")
},contexts:[],types:[],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['bind-attr'] || depth0['bind-attr']),stack1 ? stack1.call(depth0, options) : helperMissing.call(depth0, "bind-attr", options))));
data.buffer.push(" ");
hashContexts = {'alt': depth0};
hashTypes = {'alt': "ID"};
options = {hash:{
'alt': ("trackName")
},contexts:[],types:[],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['bind-attr'] || depth0['bind-attr']),stack1 ? stack1.call(depth0, options) : helperMissing.call(depth0, "bind-attr", options))));
data.buffer.push(">\n <br>\n ");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers._triageMustache.call(depth0, "trackName", {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push("\n </div>\n </div>\n ");
return buffer;
}
function program4(depth0,data) {
var buffer = '', stack1, hashTypes, hashContexts, options;
data.buffer.push("\n <p>");
hashTypes = {};
hashContexts = {};
options = {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['t'] || depth0['t']),stack1 ? stack1.call(depth0, "search.noResults", options) : helperMissing.call(depth0, "t", "search.noResults", options))));
data.buffer.push("</p>\n ");
return buffer;
}
data.buffer.push("<h2>");
hashTypes = {};
hashContexts = {};
options = {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['t'] || depth0['t']),stack1 ? stack1.call(depth0, "search.searchForAPodcast", options) : helperMissing.call(depth0, "t", "search.searchForAPodcast", options))));
data.buffer.push("</h2>\n\n");
hashContexts = {'type': depth0,'value': depth0,'placeholder': depth0,'id': depth0};
hashTypes = {'type': "STRING",'value': "ID",'placeholder': "ID",'id': "STRING"};
options = {hash:{
'type': ("search"),
'value': ("query"),
'placeholder': ("searchPlaceholder"),
'id': ("search-input")
},contexts:[],types:[],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers.input || depth0.input),stack1 ? stack1.call(depth0, options) : helperMissing.call(depth0, "input", options))));
data.buffer.push("\n<button ");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers.action.call(depth0, "search", {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push(">");
hashTypes = {};
hashContexts = {};
options = {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['t'] || depth0['t']),stack1 ? stack1.call(depth0, "search.find", options) : helperMissing.call(depth0, "t", "search.find", options))));
data.buffer.push("</button>\n\n<div ");
hashContexts = {'class': depth0};
hashTypes = {'class': "STRING"};
options = {hash:{
'class': ("isSearching::hide")
},contexts:[],types:[],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers['bind-attr'] || depth0['bind-attr']),stack1 ? stack1.call(depth0, options) : helperMissing.call(depth0, "bind-attr", options))));
data.buffer.push(">\n ");
hashTypes = {};
hashContexts = {};
options = {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data};
data.buffer.push(escapeExpression(((stack1 = helpers.render || depth0.render),stack1 ? stack1.call(depth0, "spinner", options) : helperMissing.call(depth0, "render", "spinner", options))));
data.buffer.push("\n</div>\n\n");
hashTypes = {};
hashContexts = {};
stack2 = helpers['if'].call(depth0, "resultCount", {hash:{},inverse:self.noop,fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data});
if(stack2 || stack2 === 0) { data.buffer.push(stack2); }
data.buffer.push("\n");
return buffer;
});
Ember.TEMPLATES["spinner"] = Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) {
this.compilerInfo = [4,'>= 1.0.0'];
helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {};
data.buffer.push("<div class=\"spinner\">\n <div class=\"rect1\"></div>\n <div class=\"rect2\"></div>\n <div class=\"rect3\"></div>\n <div class=\"rect4\"></div>\n <div class=\"rect5\"></div>\n</div>\n");
});

Просмотреть файл

@ -0,0 +1,306 @@
.full-screen {
background: rgba(0,0,0,0.35);
color: #f2f2f2;
height: 100%;
left: 0;
position: fixed;
text-align: center;
top: 0;
width: 100%;
z-index: 10000;
}
.full-screen .spinner {
color: #f2f2f2;
margin: 100px auto;
}
hr {
margin: 0;
padding: 0;
}
ul,
dl,
ol {
margin: 0;
padding: 0;
}
.center {
text-align: center;
}
.hide {
display: none;
}
.fxos11 x-layout {
min-height: 460px;
}
.fxos11 x-layout header a {
float: right;
}
x-layout > header,
x-layout > footer {
position: fixed;
width: 100%;
}
x-layout > header {
top: 0;
}
x-layout > section {
padding: 45px 0 75px;
}
x-layout > footer {
bottom: 0;
}
x-appbar h1 {
display: none;
}
x-appbar h1:last-of-type {
display: block;
}
x-appbar > a {
cursor: pointer;
padding-left: 2px;
padding-right: 2px;
min-width: 45px;
background: transparent;
}
x-appbar > a:hover {
background-color: #00405d;
}
x-appbar > a::-moz-focus-inner {
border: 0;
padding: 0;
}
x-tabbar-tab a {
color: #fff;
display: block;
text-decoration: none;
}
x-tabbar-tab[selected] {
border-bottom: 0.3rem solid #0ac;
}
x-tabbar-tab[selected] a {
color: #0ac;
}
.my-podcasts {
margin: 5px 0;
}
.my-podcasts li {
display: inline-block;
float: left;
height: 150px;
margin: 0 2.5% 20px;
width: 45%;
}
.my-podcasts li img {
display: inline-block;
width: 100%;
}
.my-podcasts li .no-cover {
background: #dedede;
border: 1px solid #000;
color: #000;
display: inline-block;
font-size: 1.2em;
min-height: 100px;
padding: 30px 0 0;
text-align: center;
}
.episodes .episode {
box-sizing: border-box;
border-bottom: 1px solid #bbb;
cursor: pointer;
display: block;
height: 100%;
height: 60px;
padding: 5px;
}
.episodes .episode:active {
background: #dedede;
}
.episodes .episode.is-new {
background: rgba(49,49,49,0.1);
}
.episodes .name {
display: inline-block;
float: left;
line-height: 1.3em;
padding: 10px 0 0 5px;
width: 80%;
}
.episodes .actions {
float: right;
font-size: 20px;
margin: 15px 20px 0 0;
text-align: center;
}
#player {
background: #ddd;
border-top: #010101 1px solid;
font-size: 18px;
opacity: 0;
transition: 300ms linear all;
}
#player progress {
width: 100%;
border: 0;
background: #0ac;
height: 25px;
}
#player progress::-moz-progress-bar {
background: #00202f;
}
#player #time-elapsed,
#player #time-remaining {
position: absolute;
top: 5px;
color: #fff;
}
#player #time-elapsed {
left: 5px;
}
#player #time-remaining {
right: 5px;
}
#player .episode-name {
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
padding: 5px 10px;
}
#player .player-controls {
padding-bottom: 5px;
text-align: center;
}
#player .player-controls .forward,
#player .player-controls .backward {
cursor: pointer;
font-size: 30px;
padding: 0 15px;
color: #777;
}
#player .player-controls .play-pause {
cursor: pointer;
font-size: 30px;
color: #444;
}
#player.is-populated {
opacity: 1;
transition: 300ms linear all;
}
.cover-image {
display: block;
max-height: 150px;
}
.cover-image::after {
clear: both;
content: "";
display: block;
}
.podcast {
padding: 5px;
}
.error {
color: #ff4500;
}
#search-input {
width: 75%;
}
.results {
box-sizing: border-box;
}
.results .podcast {
box-sizing: border-box;
float: left;
height: 150px;
overflow: hidden;
padding: 2.5%;
text-align: center;
width: 45%;
}
.results .podcast:hover {
cursor: pointer;
}
.spinner {
margin: auto auto;
width: 50px;
height: 30px;
text-align: center;
font-size: 10px;
}
.spinner > div {
background-color: #333;
height: 100%;
width: 6px;
display: inline-block;
-webkit-animation: stretchdelay 1.2s infinite ease-in-out;
animation: stretchdelay 1.2s infinite ease-in-out;
}
.spinner .rect2 {
-webkit-animation-delay: -1.1s;
animation-delay: -1.1s;
}
.spinner .rect3 {
-webkit-animation-delay: -1s;
animation-delay: -1s;
}
.spinner .rect4 {
-webkit-animation-delay: -0.9s;
animation-delay: -0.9s;
}
.spinner .rect5 {
-webkit-animation-delay: -0.8s;
animation-delay: -0.8s;
}
@-webkit-keyframes stretchdelay {
0%, 40%, 100% {
-webkit-transform: scaleY(0.4);
}
20% {
-webkit-transform: scaleY(1);
}
}
@-moz-keyframes stretchdelay {
0%, 40%, 100% {
transform: scaleY(0.4);
-webkit-transform: scaleY(0.4);
}
20% {
transform: scaleY(1);
-webkit-transform: scaleY(1);
}
}
@-webkit-keyframes stretchdelay {
0%, 40%, 100% {
transform: scaleY(0.4);
-webkit-transform: scaleY(0.4);
}
20% {
transform: scaleY(1);
-webkit-transform: scaleY(1);
}
}
@-o-keyframes stretchdelay {
0%, 40%, 100% {
transform: scaleY(0.4);
-webkit-transform: scaleY(0.4);
}
20% {
transform: scaleY(1);
-webkit-transform: scaleY(1);
}
}
@keyframes stretchdelay {
0%, 40%, 100% {
transform: scaleY(0.4);
-webkit-transform: scaleY(0.4);
}
20% {
transform: scaleY(1);
-webkit-transform: scaleY(1);
}
}

Просмотреть файл

@ -1,6 +1,23 @@
before_install:
- npm install -g bower grunt-cli
- bower install
---
language: node_js
node_js:
- '0.10'
- "0.12"
sudo: false
cache:
directories:
- node_modules
before_install:
- export PATH=/usr/local/phantomjs-2.0.0/bin:$PATH
- "npm config set spin false"
- "npm install -g npm@^2"
install:
- npm install -g bower
- npm install
- bower install
script:
- npm test

3
.watchmanconfig Normal file
Просмотреть файл

@ -0,0 +1,3 @@
{
"ignore_dirs": ["tmp"]
}

24
Brocfile.js Normal file
Просмотреть файл

@ -0,0 +1,24 @@
/* global require, module */
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
var app = new EmberApp({
wrapInEval: false
});
app.import('bower_components/i18n-js/app/assets/javascripts/i18n.js');
// Use `app.import` to add additional libraries to the generated
// output files.
//
// If you need to use different assets in different
// environments, specify an object as the first parameter. That
// object's keys should be the environment name and the values
// should be the asset to use in that environment.
//
// If the library that you are including contains AMD or ES6
// modules that you would like to import into your application
// please specify an object with the list of modules as keys
// along with the exports of each module as its value.
module.exports = app.toTree();

53
README.md Normal file
Просмотреть файл

@ -0,0 +1,53 @@
# Ember-hifi
This README outlines the details of collaborating on this Ember application.
A short introduction of this app could easily go here.
## Prerequisites
You will need the following things properly installed on your computer.
* [Git](http://git-scm.com/)
* [Node.js](http://nodejs.org/) (with NPM)
* [Bower](http://bower.io/)
* [Ember CLI](http://www.ember-cli.com/)
* [PhantomJS](http://phantomjs.org/)
## Installation
* `git clone <repository-url>` this repository
* change into the new directory
* `npm install`
* `bower install`
## Running / Development
* `ember server`
* Visit your app at [http://localhost:4200](http://localhost:4200).
### Code Generators
Make use of the many generators for code, try `ember help generate` for more details
### Running Tests
* `ember test`
* `ember test --server`
### Building
* `ember build` (development)
* `ember build --environment production` (production)
### Deploying
Specify what it takes to deploy your app.
## Further Reading / Useful Links
* [ember.js](http://emberjs.com/)
* [ember-cli](http://www.ember-cli.com/)
* Development Browser Extensions
* [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi)
* [ember inspector for firefox](https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/)

Просмотреть файл

@ -0,0 +1,10 @@
import DS from 'ember-data';
export default DS.LSAdapter.extend({
databaseName: 'hifi',
version: 2,
migrations: function() {
this.addModel('podcast');
this.addModel('episode');
}
});

18
app/app.js Normal file
Просмотреть файл

@ -0,0 +1,18 @@
import Ember from 'ember';
import Resolver from 'ember/resolver';
import loadInitializers from 'ember/load-initializers';
import config from './config/environment';
var App;
Ember.MODEL_FACTORY_INJECTIONS = true;
App = Ember.Application.extend({
modulePrefix: config.modulePrefix,
podModulePrefix: config.podModulePrefix,
Resolver: Resolver
});
loadInitializers(App, config.modulePrefix);
export default App;

0
app/components/.gitkeep Normal file
Просмотреть файл

Просмотреть файл

@ -0,0 +1,4 @@
import Ember from 'ember';
export default Ember.Component.extend({
});

0
app/controllers/.gitkeep Normal file
Просмотреть файл

Просмотреть файл

@ -0,0 +1,7 @@
import Ember from 'ember';
export default Ember.Controller.extend({
isPackaged: function() {
return EmberHifi.get('isPackaged');
}.property('application.isPackaged')
});

Просмотреть файл

@ -0,0 +1,24 @@
import Ember from 'ember';
export default Ember.ArrayController.extend({
needs: ['player'],
sortAscending: false,
sortProperties: ['datePublished'],
selectedEpisode: null,
actions: {
download: function(episode) {
episode.download();
},
setEpisode: function(episode) {
this.set('selectedEpisode', episode);
if (!episode.get('isDownloaded')) {
this.get('controllers.player').send('setEpisode', episode);
} else {
this.get('controllers.player').send('setEpisode', episode);
}
}
}
});

113
app/controllers/player.js Normal file
Просмотреть файл

@ -0,0 +1,113 @@
import Ember from 'ember';
import timeStamper from 'ember-hifi/lib/timestamp';
export default Ember.Controller.extend({
init: function() {
this._super.apply(this, arguments);
},
_hasAudio: function() {
this.set('isPopulated', !!this.get('model').get('id'));
}.observes('model'),
isPopulated: false,
progressBar: {
max: 0,
value: 0
},
setToPlayAfterLoaded: false,
skipTime: 15, // Time, in seconds, to skip backward/forward.
timeElapsed: '--:--',
timeRemaining: '--:--',
timeTotal: '--:--',
actions: {
pause: function(episode) {
clearTimeout(this._timeUpdateTimeout);
clearTimeout(this._saveInBackgroundTimeout);
Ember.$('#audio-player')[0].pause();
episode.set('playbackPosition', Ember.$('#audio-player')[0].currentTime);
episode.set('isPlaying', false);
episode.save();
},
play: function(episode) {
Ember.$('#audio-player')[0].play();
episode.set('isPlaying', true);
},
rewind: function(episode) {
Ember.$('#audio-player')[0].currentTime -= this.get('skipTime');
episode.set('playbackPosition', Ember.$('#audio-player')[0].currentTime);
},
forward: function(episode) {
Ember.$('#audio-player')[0].currentTime += this.get('skipTime');
episode.set('playbackPosition', Ember.$('#audio-player')[0].currentTime);
},
setEpisode: function(episode) {
var _this = this;
this.set('model', episode);
if (this.get('model').get('isDownloaded')) {
episode.blobURL().then(function(url) {
_this.set('audio', url);
_this.playAfterSet();
});
} else {
this.set('audio', this.get('model').get('audioURL'));
this.playAfterSet();
}
}
},
playAfterSet: function() {
var audio = Ember.$('#audio-player')[0];
var _this = this;
Ember.$(audio).attr('src', this.get('audio'));
Ember.$(audio).bind('canplay', function() {
Ember.$(this).unbind('canplay');
if (_this.get('model').get('playbackPosition')) {
audio.currentTime = _this.get('model').get('playbackPosition');
}
_this.updateTime();
_this.send('play', _this.get('model'));
});
// this.updateTime();
this._saveInBackground();
},
updateTime: function() {
var audio = Ember.$('#audio-player')[0];
var _this = this;
this.set('progressBar.max', audio.duration);
this.set('progressBar.value', audio.currentTime);
this.set('timeElapsed', timeStamper.formatTime(audio.currentTime));
this.set('timeRemaining', timeStamper.formatTime(audio.duration - audio.currentTime));
this._timeUpdateTimeout = setTimeout(function() {
_this.updateTime();
}, 1000);
},
_saveInBackground: function() {
var audio = Ember.$('#audio-player')[0];
var _this = this;
this.get('model').set('playbackPosition', audio.currentTime);
this.get('model').save();
this._saveInBackgroundTimeout = setTimeout(function() {
_this._saveInBackground();
}, 10000);
}
});

Просмотреть файл

@ -0,0 +1,13 @@
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
delete: function() {
this.get('model').destroyRecord();
this.transitionToRoute('podcasts');
},
update: function() {
this.get('model').update();
}
}
});

Просмотреть файл

@ -0,0 +1,5 @@
import Ember from 'ember';
export default Ember.ArrayController.extend({
sortProperties: ['title']
});

Просмотреть файл

@ -0,0 +1,54 @@
import Ember from 'ember';
export default Ember.Controller.extend({
isAdding: false,
isInErrorState: false,
rssURL: '',
actions: {
create: function(url) {
var self = this;
if (url) {
self.set('rssURL', url);
}
if (!self.get('rssURL') || !self.get('rssURL').length) {
console.log('No rssUrl specified!');
return;
}
// If the URL entered doesn't have a protocol attached, make
// sure one is added so we don't get an error (#43).
if (!self.get('rssURL').match(/^http[s]?:\/\//i)) {
self.set('rssURL', 'http://' + self.get('rssURL'));
}
self.set('isAdding', true);
// var existingPodcast = self.store.find('podcast', {
// rssURL: self.get('rssURL')
// }).then(function(podcast) {
// console.log('Podcast: ', podcast);
// console.info('Podcast already exists', podcast.objectAt(0));
// self.set('isAdding', false);
// self.set('rssURL', '');
// self.transitionToRoute('podcast', podcast.objectAt(0));
// }, function() {
var podcast = self.store.createRecord('podcast', {
rssURL: self.get('rssURL')
});
podcast.update().then(function() {
self.set('isAdding', false);
self.set('rssURL', '');
self.transitionToRoute('podcast', podcast);
}, function() {
self.set('isAdding', false);
self.set('isInErrorState', true);
});
// });
}
}
});

0
app/helpers/.gitkeep Normal file
Просмотреть файл

Просмотреть файл

@ -0,0 +1,16 @@
// import Ember from 'ember';
// import I18n from 'I18n';
// export function t(params, hash) {
// console.log('Params: ', params);
// var _this = this;
// // Support variable interpolation for this string.
// Object.keys(params).forEach(function(key) {
// params[key] = Ember.Handlebars.get(_this, params[key], hash);
// });
// return I18n.t(params, hash);
// }
// export default Ember.HTMLBars.makeBoundHelper(t);

25
app/index.html Normal file
Просмотреть файл

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>EmberHifi</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
{{content-for 'head'}}
<link rel="stylesheet" href="assets/vendor.css">
<link rel="stylesheet" href="assets/ember-hifi.css">
{{content-for 'head-footer'}}
</head>
<body>
{{content-for 'body'}}
<script src="assets/vendor.js"></script>
<script src="assets/ember-hifi.js"></script>
{{content-for 'body-footer'}}
</body>
</html>

40
app/lib/rss.js Normal file
Просмотреть файл

@ -0,0 +1,40 @@
'use strict';
import Ember from 'ember';
const NUMBER_OF_PODCASTS_TO_GET = 1000;
export default function getRSS(url, callback) {
return new Ember.RSVP.Promise(function(resolve, reject) {
var jsonpCallback = EmberHifi.isPackaged ? '' : '&callback=?';
$.ajax({
url: 'https://ajax.googleapis.com/ajax/' +
'services/feed/load?v=1.0' + jsonpCallback +
// TODO: Actually paginate results; for now we just get
// "all" podcasts, presuming most podcasts have fewer
// than 1,000 episodes.
'&num=' + NUMBER_OF_PODCASTS_TO_GET + '&output=json_xml' +
'&q=' + encodeURIComponent(url),
dataType: 'json',
success: function(response, xhr) {
if (!response || !response.responseData) {
console.error('Bad response', response);
return reject(xhr);
}
var xml = new DOMParser();
var xmlString = response.responseData.xmlString;
var xmlDoc = xml.parseFromString(xmlString, 'text/xml');
if (callback) {
callback(xmlDoc);
}
resolve(xmlDoc);
},
error: function(error) {
reject(error);
}
});
});
}

31
app/lib/timestamp.js Normal file
Просмотреть файл

@ -0,0 +1,31 @@
'use strict';
export default {
formatTime: function(secs) {
if (isNaN(secs)) {
return '--:--';
}
var hours = parseInt(secs / 3600, 10) % 24;
var minutes = parseInt(secs / 60, 10) % 60;
var seconds = parseInt(secs % 60, 10);
var time = (hours !== 0 ? hours + ':' : '') +
(minutes < 10 ? '0' + minutes : minutes) + ':' +
(seconds < 10 ? '0' + seconds : seconds);
return time;
},
timestamp: function(date) {
if (!date) {
date = new Date();
}
if (date instanceof Date !== true) {
date = new Date(date);
}
return Math.round(date.getTime() / 1000);
}
};

0
app/models/.gitkeep Normal file
Просмотреть файл

164
app/models/episode.js Normal file
Просмотреть файл

@ -0,0 +1,164 @@
import DS from 'ember-data';
import Ember from 'ember';
export default DS.Model.extend({
podcast: DS.belongsTo('podcast', {async: true}),
name: DS.attr('string'),
audioURL: DS.attr('string'),
audioLength: DS.attr('number'),
playbackPosition: DS.attr('number'),
playCount: DS.attr('number'),
// audioFile: DS.attr('object'),
guid: DS.attr('string'),
// Episode metadata from RSS.
datePublished: DS.attr('number'),
// Episode download data; unavailable in hosted version for the time being.
isDownloaded: DS.attr('boolean'),
_chunkCount: DS.attr('number'),
_chunkCountSaved: DS.attr('number'),
_loadComplete: false,
isDownloading: false,
isPlaying: false,
isNew: function() {
return !this.get('playbackPosition') && !this.get('playCount');
}.property('playbackPosition', 'playCount'),
blobURL: function() {
var _this = this;
return new Ember.RSVP.Promise(function(resolve) {
_this._assembleChunkData().then(function(blob) {
resolve(window.URL.createObjectURL(blob));
});
});
},
// Download the episode for local playback.
download: function(model) {
this.set('isDownloading', true);
this.set('_chunkCount', 0);
this.set('_chunkCountSaved', 0);
var _this = this;
var request = new XMLHttpRequest({mozSystem: true});
request.open('GET', this.get('audioURL'), true);
request.responseType = 'moz-chunked-arraybuffer';
// request.addEventListener('load', );
request.addEventListener('load', function() {
console.log(request.readyState, request);
if (request.readyState === 4) {
// _this._setAudioType(_this.get('audioURL'));
_this._loadComplete = true;
}
});
request.addEventListener('progress', function() {
console.info('eventProgress', _this.get('_chunkCount'));
localforage.setItem('ep:' + _this.get('id') +
_this.get('_chunkCount'), request.response)
.then(function() {
// Increment our internal data chunk count.
_this.incrementProperty('_chunkCountSaved');
if (_this.get('_chunkCount') ===
_this.get('_chunkCountSaved') &&
_this.get('_loadComplete')) {
console.log('chunk data assembled');
_this.set('isDownloading', false);
_this.set('isDownloaded', true);
// try {
// setTimeout(function() {
// _this.save();
// }, 20);
// } catch (e) {
// console.error(e);
// }
}
});
_this.incrementProperty('_chunkCount');
});
request.addEventListener('error', function() {
window.alert('Error downloading this episode. Please try again.');
// _this.trigger('download:cancel');
});
request.send(null);
},
// Because of limitations/platform bugs in b2g18 (what ships on
// first-gen Firefox OS devices), we can't move around huge Blob
// objects (particularly, we can't save them anywhere) without crashing
// the app on a device from out of memory errors.
//
// This retrieves the smaller, more manageable chunks of data from
// IndexedDB and assembles them into a Blob object we hand off to a
// callback function. For some reason, B2G seems to cope just fine with
// this very odd solution because it handles Blobs full of arrayBuffers
// better than it saves large Blobs.
//
// This is a HACK and should be fixed by platform in the next release,
// I hope.
//
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=873274
// and: https://bugzilla.mozilla.org/show_bug.cgi?id=869812
_assembleChunkData: function() {
var audioBlobs = [];
var chunkCount = this.get('_chunkCount');
var _this = this;
return new Ember.RSVP.Promise(function(resolve) {
function _walkChunks(chunkID) {
if (chunkID === undefined) {
chunkID = 0;
}
if (chunkID < chunkCount) {
localforage.getItem('ep:' + _this.get('id') + chunkID)
.then(function(blob) {
console.info('ep:' + _this.get('id') + chunkID, blob);
audioBlobs.push(blob);
_walkChunks(chunkID + 1);
});
} else {
var blob = new Blob(audioBlobs, {type: 'mp3'});
resolve(blob);
}
}
_walkChunks();
});
},
// Set the audio type based on the responseType (or filename) of this
// episode's enclosure file/URL.
_setAudioType: function(audioURL, event) {
// TODO: Make this better.
var type;
try {
type = event.target.response.type.split('/')[1];
} catch (e) {
// Try to extract the type of this file from its filename.
var enclosureArray = audioURL.split('.');
type = enclosureArray[enclosureArray.length - 1];
}
// Assume "mpeg" = MP3, for now. Kinda hacky.
if (type === 'mpeg') {
type = 'mp3';
}
this.set('type', type);
// this.save();
}
});

160
app/models/podcast.js Normal file
Просмотреть файл

@ -0,0 +1,160 @@
import DS from 'ember-data';
import Ember from 'ember';
import timeStamper from 'ember-hifi/lib/timestamp';
import getRSS from 'ember-hifi/lib/rss';
export default DS.Model.extend({
episodes: DS.hasMany('episode', {async: true}),
title: DS.attr('string'),
description: DS.attr('string'),
rssURL: DS.attr('string'),
lastUpdated: DS.attr('number'),
lastPlayed: DS.attr('number'),
coverImageBlob: DS.attr('string'),
coverImageURL: DS.attr('string'),
coverImage: function() {
if (this.get('coverImageURL')) {
return this.get('coverImageURL');
} else {
return null;
}
}.property('coverImageBlob', 'coverImageURL'),
destroyRecord: function() {
this.get('episodes').forEach(function(episode) {
episode.destroyRecord();
});
return this._super();
},
getCoverImage: function() {
if (!this.get('coverImageURL')) {
console.debug('No coverImageURL found; skipping.');
return;
}
var _this = this;
var request = new XMLHttpRequest({mozSystem: true});
request.open('GET', this.get('coverImageURL'), true);
request.responseType = 'arraybuffer';
request.addEventListener('load', function() {
_this.set('coverImageBlob', request.response);
_this.save();
});
try {
request.send(null);
} catch (err) {
console.error(err);
}
},
// Update this podcast from the RSS feed, but don't download any
// new episodes by default.
update: function() {
var _this = this;
console.info('Updating podcast:' + this.get('id'));
return new Ember.RSVP.Promise(function(resolve, reject) {
// _this.getCoverImage();
// Update last updated time so we aren't constantly looking
// for new episodes ;-)
_this.set('lastUpdated', timeStamper.timestamp());
getRSS(_this.get('rssURL')).then(function(result) {
var $xml = $(result);
var $channel = $xml.find('channel');
var $items = $xml.find('item');
if (!$xml.length || !$xml.find('item').length) {
// If we can't make sense of this podcast's feed, we delete
// it and inform the user of the error.
window.alert('Error downloading podcast feed.');
return;
}
_this.set('title', $channel.find('title').eq(0).text());
_this.set('description', $channel.find('description')
.eq(0).text());
_this.set('coverImageURL', $channel.find('itunes\\:image')
.attr('href'));
console.log('Podcast record after setting data: ', _this);
_this.get('episodes').then(function(episodes) {
var itemsSaved = 0;
$items.each(function(i, episode) {
var guid = $(episode).find('guid').text();
if (episodes.filterBy('guid', guid).length) {
return;
}
var oldImageURL = _this.get('coverImageURL');
// Use the latest artwork for the cover image.
var episodeImageURL = $(episode).find('itunes\\:image')
.attr('href');
if (i === 0 && episodeImageURL !== oldImageURL) {
_this.set('coverImageURL', episodeImageURL);
}
// If the cover image has changed (or this podcast is
// new) we update the cover image.
if (!oldImageURL ||
oldImageURL !== _this.get('coverImageURL')) {
// _this.getCoverImage();
}
var e = _this.store.createRecord('episode', {
guid: guid,
audioURL: $(episode).find('enclosure').attr('url'),
datePublished: timeStamper.timestamp(
$(episode).find('pubDate').text()
),
name: $(episode).find('title').text(),
podcast: _this
});
// Add this episode to the list of objects; this will
// cause it to appear in any existing lists.
e.save();
episodes.pushObject(e);
itemsSaved++;
if ($items.length === i + 1) {
console.info('Updated podcast:' + _this.get('id'),
itemsSaved + ' new episodes.');
var saved = true;
_this.save().then(resolve);
}
});
if (!itemsSaved && !$items.length && !saved) {
console.info('Updated podcast:' + _this.get('id'),
itemsSaved + ' new episodes.');
_this.save().then(resolve);
}
});
}, function(error) {
console.error('Could not download podcast', error);
_this.destroyRecord().then(reject);
});
});
},
_autoUpdate: function() {
if (this.get('lastUpdated') + 3600 < timeStamper.timestamp()) {
console.debug('Auto update for:' + this.get('title'));
this.update();
}
}.observes('lastUpdated').on('init')
});

18
app/router.js Normal file
Просмотреть файл

@ -0,0 +1,18 @@
import Ember from 'ember';
import config from './config/environment';
var Router = Ember.Router.extend({
location: config.locationType
});
Router.map(function() {
this.route('search');
this.route('podcasts', { path: '/podcasts' }, function() {
this.route('new');
});
this.route('podcast', { path: '/podcast/:podcast_id'});
});
export default Router;

0
app/routes/.gitkeep Normal file
Просмотреть файл

Просмотреть файл

@ -0,0 +1,7 @@
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store;
}
});

7
app/routes/index.js Normal file
Просмотреть файл

@ -0,0 +1,7 @@
import Ember from 'ember';
export default Ember.Route.extend({
redirect: function() {
this.transitionTo('podcasts');
}
});

8
app/routes/podcast.js Normal file
Просмотреть файл

@ -0,0 +1,8 @@
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params) {
return this.store.find('podcast', params.podcast_id);
}
});

7
app/routes/podcasts.js Normal file
Просмотреть файл

@ -0,0 +1,7 @@
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.get('store').findAll('podcast');
}
});

Просмотреть файл

@ -0,0 +1,16 @@
import DS from 'ember-data';
export default DS.LSSerializer.extend({
serializeHasMany: function(snapshot, json, relationship) {
var key = relationship.key;
var payloadKey = this.keyForRelationship ? this.keyForRelationship(key, "hasMany") : key;
var relationshipType = snapshot.type.determineRelationshipType(relationship, this.store);
if (relationshipType === 'manyToNone' ||
relationshipType === 'manyToMany' ||
relationshipType === 'manyToOne') {
json[payloadKey] = snapshot.hasMany(key, { ids: true });
// TODO support for polymorphic manyToNone and manyToMany relationships
}
}
});

1940
app/styles/app.css Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

0
app/styles/fa.css Normal file
Просмотреть файл

Просмотреть файл

@ -0,0 +1,3 @@
<h1><a href="/podcasts">High Fidelity</a></h1>
{{outlet}}

Просмотреть файл

@ -0,0 +1,37 @@
<div class="episodes">
<audio id="audio-player" preload="auto" mozaudiochannel="content"></audio>
{{render "player"}}
{{#each episode in model}}
<div {{action "setEpisode" episode}} {{bind-attr class=":episode isNew"}}>
<div class="name">
{{#if isNew}}
<strong>{{episode.new}}</strong>
{{/if}}
{{model.name}} {{name}} {{episode.name}}
</div>
{{!-- Commented out while we are streaming-only. This code exposes
download/downloading/play/stream UI, but for now we're streaming
only so we don't show anything.
{{#if HighFidelity.isPackaged}}
<div class="actions">
{{#if isDownloaded}}
<i class="fa fa-play-circle"></i>
{{else}}
{{#if isDownloading}}
<i class="fa fa-spinner fa-spin"></i>
{{else}}
<i class="fa fa-cloud-download" {{action download this}}></i>
{{/if}}
{{/if}}
</div>
{{/if}}
--}}
</div>
{{else}}
{{render "spinner"}}
{{/each}}
</div>

33
app/templates/player.hbs Normal file
Просмотреть файл

@ -0,0 +1,33 @@
<div id="player" {{bind-attr class="isPopulated"}}>
<div id="time-info">
<progress id="audio-progress" {{bind-attr max=progressBar.max}}
{{bind-attr value=progressBar.value}}>
</progress>
<span id="time-elapsed">{{timeElapsed}}</span>
<span id="time-remaining">{{timeRemaining}}</span>
</div>
<div class="episode-name">{{model.name}}</div>
<div class="player-controls">
<a {{action "rewind" model}} class="backward">
<i class="fa fa-backward"></i>
{{fa-icon "backward"}}
</a>
{{#if model.isPlaying}}
<a {{action "pause" model}} class="play-pause">
{{fa-icon "pause"}}
</a>
{{else}}
<a {{action "play" model}} class="play-pause">
{{fa-icon "play"}}
</a>
{{/if}}
<a {{action "forward" model}} class="forward">
{{fa-icon "forward"}}
</a>
</div>
</div>

12
app/templates/podcast.hbs Normal file
Просмотреть файл

@ -0,0 +1,12 @@
<div class="podcast">
<img class="cover-image" {{bind-attr src=model.coverImageURL}}
{{bind-attr alt=model.title}}>
<p>{{model.description}} {{model.lastUpdated}}</p>
<button {{action "delete"}}>Unsubscribe</button>
</div>
<hr/>
{{render "episodes" model.episodes}}

Просмотреть файл

@ -0,0 +1,16 @@
{{#link-to 'podcasts.new'}}Add New Podcast{{/link-to}}
<h3>My Podcasts</h3>
<ul class="my-podcasts">
{{#each podcast in model tagName=ul className="my-podcasts"}}
<li>
{{#link-to 'podcast' podcast class="cover"}}
{{#if coverImage}}
<img {{bind-attr src=podcast.coverImage}} {{bind-attr alt=podcast.title}}>
{{else}}
<span class="no-cover">{{podcast.title}}</span>
{{/if}}
{{/link-to}}
</li>
{{/each}}
</ul>
{{outlet}}

Просмотреть файл

@ -0,0 +1,30 @@
<h2>{{podcast.addRSSFeed}}</h2>
{{#if isInErrorState}}
<div class="error">
<p>Encountered an error while adding Podcast.</p>
<p>Is the Podcast URL valid?</p>
</div>
{{/if}}
{{input type="url" value=rssURL placeholder="Podcast URL"}}
<button {{action 'create'}}>{{podcast.add}}</button>
<h2>{{podcast.newToPodcasts}}</h2>
<h3>{{podcast.recommendations}}</h3>
<h4>The Cracked Podcast</h4>
<img alt="The Cracked Podcast" class="cover cover-image"
{{action 'create' "http://rss.earwolf.com/the-cracked-podcast"}}
src="http://cdn.earwolf.com/wp-content/uploads/2013/08/EAR_CrackedPodcast_1600x1600_Cover_Final.jpg">
<h4>Hello Internet</h4>
<img alt="Hello Internet" class="cover cover-image"
{{action 'create' "http://feeds.podtrac.com/m2lTaLRx8AWb"}}
src="http://static.squarespace.com/static/52d66949e4b0a8cec3bcdd46/t/52ebf67fe4b0f4af2a4502d8/1391195777839/1500w/Hello%20Internet.003.png">
{{#if isAdding}}
<div class="full-screen">
{{render "spinner"}}
</div>
{{/if}}

Просмотреть файл

@ -0,0 +1,7 @@
<div class="spinner">
<div class="rect1"></div>
<div class="rect2"></div>
<div class="rect3"></div>
<div class="rect4"></div>
<div class="rect5"></div>
</div>

221
app/translations.js Normal file
Просмотреть файл

@ -0,0 +1,221 @@
import I18n from 'I18n';
I18n.defaultLocale = "en-US";
I18n.locale = window.navigator.language || 'en-US';
I18n.fallbacks = true;
I18n.translations = {
// English by @tofumatt
en: {
app: {
title: 'Podcasts',
},
episode: {
episodes: 'Episodes',
new: 'New:',
},
podcast: {
add: 'Add',
addOne: 'Want to add one?',
addRSSFeed: 'Add RSS Feed',
newToPodcasts: 'New to Podcasts?',
noneFound: 'No podcasts found.',
recommendations: 'Check out our recommendations:',
},
search: {
find: 'Find',
noResults: 'No results.',
placeholder: 'Podcast Name or Keywords',
searchForAPodcast: 'Search for a Podcast',
subscribe: 'Subscribe to {{podcast}}?',
},
tabs: {
myPodcasts: 'My Podcasts',
popular: 'Popular',
search: 'Search',
},
},
// Esperanto by @Airon90
eo: {
app: {
title: 'Podkastoj',
},
episode: {
episodes: 'Epizodoj',
new: 'Nova:',
},
podcast: {
add: 'Aldoni',
addOne: 'Ĉu vi volas aldoni novan?',
addRSSFeed: 'Aldoni RSS-fluon',
newToPodcasts: 'Ĉu malkutimas podkastojn?',
noneFound: 'Neniu podkasto trovata.',
recommendations: 'Kontrolu niajn rekomendojn:',
},
search: {
find: 'Trovi',
noResults: 'Neniu rezulto.',
placeholder: 'Nomo de podkasto aŭ ŝlosilvorto',
searchForAPodcast: 'Serĉi podkaston',
subscribe: 'Ĉu aliĝi al {{podcast}}?',
},
tabs: {
myPodcasts: 'Miaj podkastoj',
popular: 'Popularaj',
search: 'Serĉi',
},
},
// French by @AntoineTurmel
fr: {
app: {
title: 'Podcasts',
},
episode: {
episodes: "Episodes",
new: 'nouveau:',
},
podcast: {
add: 'Ajouter',
addOne: 'Voulez-vous en ajouter un?',
addRSSFeed: 'Ajouter un flux RSS',
newToPodcasts: 'Vous ne connaissez pas de Podcasts?',
noneFound: 'Aucun podcast trouvé.',
recommendations: 'Voici nos recommandations:',
},
search: {
find: 'chercher',
noResults: 'Aucun résultat.',
placeholder: 'Nom du podcast ou mots-clés',
searchForAPodcast: 'Trouver un podcast',
subscribe: 'S\'abonner à {{podcast}}?',
},
tabs: {
myPodcasts: 'mes podcasts',
popular: 'populaire',
search: 'recherche',
},
},
// Italian by @Airon90
it: {
app: {
title: 'Podcast',
},
episode: {
episodes: 'Episodi',
new: 'Nuovo:',
},
podcast: {
add: 'Aggiungi',
addOne: 'Aggiungi uno nuovo?',
addRSSFeed: 'Aggiungi feed RSS',
newToPodcasts: 'Nuovo ai podcast?',
noneFound: 'Nessun podcast trovato.',
recommendations: 'Controlla i nostri consigli:',
},
search: {
find: 'Trova',
noResults: 'Nessun risultato.',
placeholder: 'Nome del podcast o parola chiave',
searchForAPodcast: 'Cerca un podcast',
subscribe: 'Vuoi abbonarti a {{podcast}}?',
},
tabs: {
myPodcasts: 'I miei podcast',
popular: 'Popolari',
search: 'Cerca',
},
},
// Polish by @MaciejCzyzewski
pl: {
app: {
title: 'Podcasty', // Transmisje
},
episode: {
episodes: 'Epizody', // Fragmenty
new: 'Nowe:',
},
podcast: {
add: 'Dodaj',
addOne: 'Chcesz jedno dodać?',
addRSSFeed: 'Dodaj źródło RSS',
newToPodcasts: 'Nowe do podcastu?',
noneFound: 'Nie znaleziono podcastów.',
recommendations: 'Sprawdź nasze rekomendacje:',
},
search: {
find: 'Szukaj',
noResults: 'Brak wyników.',
placeholder: 'Podcast tytuł albo słowo kluczowe',
searchForAPodcast: 'Szukaj w podcastach',
subscribe: 'Subskrybuj {{podcast}}?',
},
tabs: {
myPodcasts: 'Moje Podcasty',
popular: 'Popularne',
search: 'Szukaj',
},
},
// galego by @xmgz
gl: {
app: {
title: 'Podcasts',
},
episode: {
episodes: 'Episodios',
new: 'Novo:',
},
podcast: {
add: 'Engadir',
addOne: 'Quere engadir un?',
addRSSFeed: 'Engdir fonte RSS',
newToPodcasts: 'Novo nos podcast?',
noneFound: 'Non se atoparon podcast.',
recommendations: 'Mire as nosas recomendacións:',
},
search: {
find: 'Buscar',
noResults: 'Sen resultados.',
placeholder: 'Nome do podcast ou palabras chave',
searchForAPodcast: 'Buscar un podcast',
subscribe: 'Suscribirse a {{podcast}}?',
},
tabs: {
myPodcasts: 'Os meus podcast',
popular: 'Popular',
search: 'Buscar',
},
},
// spanish by @xmgz
es: {
app: {
title: 'Podcasts',
},
episode: {
episodes: 'Episodios',
new: 'Nuevo:',
},
podcast: {
add: 'Añadir',
addOne: 'Desea añadir uno',
addRSSFeed: 'Añadir fuente RSS',
newToPodcasts: 'Nuevo en los podcast?',
noneFound: 'No se encontraron podcast.',
recommendations: 'Estas son nuestras recomendaciones:',
},
search: {
find: 'Buscar',
noResults: 'Sin resultados.',
placeholder: 'Nombre del podcast o palabras clave',
searchForAPodcast: 'Buscar un podcast',
subscribe: 'Suscribirse a {{podcast}}?',
},
tabs: {
myPodcasts: 'Mis podcast',
popular: 'Popular',
search: 'Buscar',
},
},
};

17
bower.json Normal file
Просмотреть файл

@ -0,0 +1,17 @@
{
"name": "ember-hifi",
"dependencies": {
"ember": "1.13.3",
"ember-cli-shims": "ember-cli/ember-cli-shims#0.0.3",
"ember-cli-test-loader": "ember-cli-test-loader#0.1.3",
"ember-data": "1.13.5",
"ember-load-initializers": "ember-cli/ember-load-initializers#0.1.5",
"ember-qunit": "0.4.1",
"ember-qunit-notifications": "0.0.7",
"ember-resolver": "~0.1.18",
"jquery": "^1.11.1",
"loader.js": "ember-cli/loader.js#3.2.0",
"qunit": "~1.17.1",
"font-awesome": "~4.3.0"
}
}

56
config/environment.js Normal file
Просмотреть файл

@ -0,0 +1,56 @@
/* jshint node: true */
module.exports = function(environment) {
var ENV = {
modulePrefix: 'ember-hifi',
environment: environment,
baseURL: '/',
locationType: 'auto',
EmberENV: {
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. 'with-controller': true
}
},
APP: {
// Here you can pass flags/options to your application instance
// when it is created
},
contentSecurityPolicy: {
'default-src': "'none'",
'script-src': "'self' 'unsafe-eval' https://ajax.googleapis.com/",
'font-src': "'self'",
'connect-src': "'self'",
'img-src': "'self'",
'style-src': "'self'",
'media-src': "'self'"
}
};
if (environment === 'development') {
// ENV.APP.LOG_RESOLVER = true;
// ENV.APP.LOG_ACTIVE_GENERATION = true;
// ENV.APP.LOG_TRANSITIONS = true;
// ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
// ENV.APP.LOG_VIEW_LOOKUPS = true;
}
if (environment === 'test') {
// Testem prefers this...
ENV.baseURL = '/';
ENV.locationType = 'none';
// keep test console output quieter
ENV.APP.LOG_ACTIVE_GENERATION = false;
ENV.APP.LOG_VIEW_LOOKUPS = false;
ENV.APP.rootElement = '#ember-testing';
}
if (environment === 'production') {
}
return ENV;
};

23
ember-cli-build.js Normal file
Просмотреть файл

@ -0,0 +1,23 @@
/* global require, module */
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
var app = new EmberApp(defaults, {
// Add options here
});
// Use `app.import` to add additional libraries to the generated
// output files.
//
// If you need to use different assets in different
// environments, specify an object as the first parameter. That
// object's keys should be the environment name and the values
// should be the asset to use in that environment.
//
// If the library that you are including contains AMD or ES6
// modules that you would like to import into your application
// please specify an object with the list of modules as keys
// along with the exports of each module as its value.
return app.toTree();
};

42
package.json Normal file
Просмотреть файл

@ -0,0 +1,42 @@
{
"name": "ember-hifi",
"version": "0.0.0",
"description": "Small description for ember-hifi goes here",
"private": true,
"directories": {
"doc": "doc",
"test": "tests"
},
"scripts": {
"start": "ember server",
"build": "ember build",
"test": "ember test"
},
"repository": "",
"engines": {
"node": ">= 0.10.0"
},
"author": "",
"license": "MIT",
"devDependencies": {
"broccoli-asset-rev": "^2.0.2",
"ember-browserify": "^1.0.1",
"ember-cli": "1.13.1",
"ember-cli-app-version": "0.4.0",
"ember-cli-babel": "^5.0.0",
"ember-cli-content-security-policy": "0.4.0",
"ember-cli-dependency-checker": "^1.0.0",
"ember-cli-font-awesome": "0.1.0",
"ember-cli-htmlbars": "0.7.9",
"ember-cli-htmlbars-inline-precompile": "^0.1.1",
"ember-cli-ic-ajax": "0.2.1",
"ember-cli-inject-live-reload": "^1.3.0",
"ember-cli-qunit": "0.3.15",
"ember-cli-release": "0.2.3",
"ember-cli-uglify": "^1.0.1",
"ember-data": "1.13.5",
"ember-disable-proxy-controllers": "^1.0.0",
"ember-export-application-global": "^1.0.2",
"ember-localstorage-adapter": "0.5.4"
}
}

15
public/crossdomain.xml Normal file
Просмотреть файл

@ -0,0 +1,15 @@
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<!-- Read this: www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html -->
<!-- Most restrictive policy: -->
<site-control permitted-cross-domain-policies="none"/>
<!-- Least restrictive policy: -->
<!--
<site-control permitted-cross-domain-policies="all"/>
<allow-access-from domain="*" to-ports="*" secure="false"/>
<allow-http-request-headers-from domain="*" headers="*" secure="false"/>
-->
</cross-domain-policy>

Двоичные данные
public/images/icon-120.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 16 KiB

Двоичные данные
public/images/icon-128.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 16 KiB

Двоичные данные
public/images/icon-256.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 37 KiB

Двоичные данные
public/images/icon-32.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.2 KiB

Двоичные данные
public/images/icon-512.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 88 KiB

Двоичные данные
public/images/icon-60.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 6.6 KiB

Двоичные данные
public/images/icon-64.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 7.1 KiB

Двоичные данные
public/images/icon-90.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 11 KiB

23
public/manifest.webapp Normal file
Просмотреть файл

@ -0,0 +1,23 @@
{
"version": "@@versionNumber",
"name": "@@appName",
"description": "@@appDescription",
"icons": {
"32": "/assets/images/icons/icon-32.png",
"60": "/assets/images/icons/icon-60.png",
"64": "/assets/images/icons/icon-64.png",
"90": "/assets/images/icons/icon-90.png",
"120": "/assets/images/icons/icon-120.png",
"128": "/assets/images/icons/icon-128.png",
"256": "/assets/images/icons/icon-256.png",
"512": "/assets/images/icons/icon-512.png"
},
"developer": {
"name": "@@developerName",
"url": "@@developerUrl"
},
"launch_path": "/index.html",
"installs_allowed_from": ["https://marketplace.firefox.com", "*"],
"permissions": {
}
}

3
public/robots.txt Normal file
Просмотреть файл

@ -0,0 +1,3 @@
# http://www.robotstxt.org
User-agent: *
Disallow:

12
testem.json Normal file
Просмотреть файл

@ -0,0 +1,12 @@
{
"framework": "qunit",
"test_page": "tests/index.html?hidepassed",
"disable_watching": true,
"launch_in_ci": [
"PhantomJS"
],
"launch_in_dev": [
"PhantomJS",
"Chrome"
]
}

52
tests/.jshintrc Normal file
Просмотреть файл

@ -0,0 +1,52 @@
{
"predef": [
"document",
"window",
"location",
"setTimeout",
"$",
"-Promise",
"define",
"console",
"visit",
"exists",
"fillIn",
"click",
"keyEvent",
"triggerEvent",
"find",
"findWithAssert",
"wait",
"DS",
"andThen",
"currentURL",
"currentPath",
"currentRouteName"
],
"node": false,
"browser": false,
"boss": true,
"curly": true,
"debug": false,
"devel": false,
"eqeqeq": true,
"evil": true,
"forin": false,
"immed": false,
"laxbreak": false,
"newcap": true,
"noarg": true,
"noempty": false,
"nonew": false,
"nomen": false,
"onevar": false,
"plusplus": false,
"regexp": false,
"undef": true,
"sub": true,
"strict": false,
"white": false,
"eqnull": true,
"esnext": true,
"unused": true
}

11
tests/helpers/resolver.js Normal file
Просмотреть файл

@ -0,0 +1,11 @@
import Resolver from 'ember/resolver';
import config from '../../config/environment';
var resolver = Resolver.create();
resolver.namespace = {
modulePrefix: config.modulePrefix,
podModulePrefix: config.podModulePrefix
};
export default resolver;

Просмотреть файл

@ -0,0 +1,18 @@
import Ember from 'ember';
import Application from '../../app';
import config from '../../config/environment';
export default function startApp(attrs) {
var application;
var attributes = Ember.merge({}, config.APP);
attributes = Ember.merge(attributes, attrs); // use defaults, but you can override;
Ember.run(function() {
application = Application.create(attributes);
application.setupForTesting();
application.injectTestHelpers();
});
return application;
}

33
tests/index.html Normal file
Просмотреть файл

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>EmberHifi Tests</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
{{content-for 'head'}}
{{content-for 'test-head'}}
<link rel="stylesheet" href="assets/vendor.css">
<link rel="stylesheet" href="assets/ember-hifi.css">
<link rel="stylesheet" href="assets/test-support.css">
{{content-for 'head-footer'}}
{{content-for 'test-head-footer'}}
</head>
<body>
{{content-for 'body'}}
{{content-for 'test-body'}}
<script src="assets/vendor.js"></script>
<script src="assets/test-support.js"></script>
<script src="assets/ember-hifi.js"></script>
<script src="testem.js"></script>
<script src="assets/test-loader.js"></script>
{{content-for 'body-footer'}}
{{content-for 'test-body-footer'}}
</body>
</html>

6
tests/test-helper.js Normal file
Просмотреть файл

@ -0,0 +1,6 @@
import resolver from './helpers/resolver';
import {
setResolver
} from 'ember-qunit';
setResolver(resolver);

0
tests/unit/.gitkeep Normal file
Просмотреть файл

Просмотреть файл

@ -0,0 +1,12 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('adapter:application', 'Unit | Adapter | application', {
// Specify the other units that are required for this test.
// needs: ['serializer:foo']
});
// Replace this with your real tests.
test('it exists', function(assert) {
var adapter = this.subject();
assert.ok(adapter);
});

Просмотреть файл

@ -0,0 +1,19 @@
import { moduleForComponent, test } from 'ember-qunit';
moduleForComponent('audio-player', 'Unit | Component | audio player', {
// Specify the other units that are required for this test
// needs: ['component:foo', 'helper:bar'],
unit: true
});
test('it renders', function(assert) {
assert.expect(2);
// Creates the component instance
var component = this.subject();
assert.equal(component._state, 'preRender');
// Renders the component to the page
this.render();
assert.equal(component._state, 'inDOM');
});

Просмотреть файл

@ -0,0 +1,12 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('controller:application', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
// Replace this with your real tests.
test('it exists', function(assert) {
var controller = this.subject();
assert.ok(controller);
});

Просмотреть файл

@ -0,0 +1,12 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('controller:episodes', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
// Replace this with your real tests.
test('it exists', function(assert) {
var controller = this.subject();
assert.ok(controller);
});

Просмотреть файл

@ -0,0 +1,12 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('controller:player', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
// Replace this with your real tests.
test('it exists', function(assert) {
var controller = this.subject();
assert.ok(controller);
});

Просмотреть файл

@ -0,0 +1,12 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('controller:podcast', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
// Replace this with your real tests.
test('it exists', function(assert) {
var controller = this.subject();
assert.ok(controller);
});

Просмотреть файл

@ -0,0 +1,12 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('controller:podcasts', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
// Replace this with your real tests.
test('it exists', function(assert) {
var controller = this.subject();
assert.ok(controller);
});

Просмотреть файл

@ -0,0 +1,10 @@
import { t } from '../../../helpers/t';
import { module, test } from 'qunit';
module('Unit | Helper | t');
// Replace this with your real tests.
test('it works', function(assert) {
var result = t(42);
assert.ok(result);
});

Просмотреть файл

@ -0,0 +1,12 @@
import { moduleForModel, test } from 'ember-qunit';
moduleForModel('episode', 'Unit | Model | episode', {
// Specify the other units that are required for this test.
needs: []
});
test('it exists', function(assert) {
var model = this.subject();
// var store = this.store();
assert.ok(!!model);
});

Просмотреть файл

@ -0,0 +1,12 @@
import { moduleForModel, test } from 'ember-qunit';
moduleForModel('podcast', 'Unit | Model | podcast', {
// Specify the other units that are required for this test.
needs: []
});
test('it exists', function(assert) {
var model = this.subject();
// var store = this.store();
assert.ok(!!model);
});

Просмотреть файл

@ -0,0 +1,11 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('route:application', 'Unit | Route | application', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
test('it exists', function(assert) {
var route = this.subject();
assert.ok(route);
});

Просмотреть файл

@ -0,0 +1,11 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('route:podcast', 'Unit | Route | podcast', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
test('it exists', function(assert) {
var route = this.subject();
assert.ok(route);
});

Просмотреть файл

@ -0,0 +1,11 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('route:podcasts', 'Unit | Route | podcasts', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
test('it exists', function(assert) {
var route = this.subject();
assert.ok(route);
});

Просмотреть файл

@ -0,0 +1,15 @@
import { moduleForModel, test } from 'ember-qunit';
moduleForModel('application', 'Unit | Serializer | application', {
// Specify the other units that are required for this test.
needs: ['serializer:application']
});
// Replace this with your real tests.
test('it serializes records', function(assert) {
var record = this.subject();
var serializedRecord = record.serialize();
assert.ok(serializedRecord);
});

0
vendor/.gitkeep поставляемый Normal file
Просмотреть файл