mirror of
https://github.com/flarum/framework.git
synced 2024-12-02 15:03:44 +08:00
Replace Ember app with Mithril app
This commit is contained in:
parent
cceb2b1249
commit
b5c95a222f
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"directory": "bower_components",
|
||||
"analytics": false
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.js]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.hbs]
|
||||
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
|
17
framework/core/ember/admin/.gitignore
vendored
17
framework/core/ember/admin/.gitignore
vendored
|
@ -1,17 +0,0 @@
|
|||
# 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,33 +0,0 @@
|
|||
{
|
||||
"predef": [
|
||||
"document",
|
||||
"window",
|
||||
"-Promise",
|
||||
"moment"
|
||||
],
|
||||
"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
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
---
|
||||
language: node_js
|
||||
|
||||
sudo: false
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
|
||||
before_install:
|
||||
- "npm config set spin false"
|
||||
- "npm install -g npm@^2"
|
||||
|
||||
install:
|
||||
- npm install -g bower
|
||||
- npm install
|
||||
- bower install
|
||||
|
||||
script:
|
||||
- npm test
|
|
@ -1,20 +0,0 @@
|
|||
/* global require, module */
|
||||
|
||||
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
|
||||
|
||||
var app = new EmberApp();
|
||||
|
||||
app.import('bower_components/bootstrap/dist/js/bootstrap.js');
|
||||
app.import('bower_components/spin.js/spin.js');
|
||||
app.import('bower_components/spin.js/jquery.spin.js');
|
||||
app.import('bower_components/moment/moment.js');
|
||||
app.import('bower_components/jquery.hotkeys/jquery.hotkeys.js');
|
||||
app.import('bower_components/blurjs/dist/jquery.blur.js');
|
||||
|
||||
app.import('bower_components/font-awesome/fonts/fontawesome-webfont.eot');
|
||||
app.import('bower_components/font-awesome/fonts/fontawesome-webfont.svg');
|
||||
app.import('bower_components/font-awesome/fonts/fontawesome-webfont.ttf');
|
||||
app.import('bower_components/font-awesome/fonts/fontawesome-webfont.woff');
|
||||
app.import('bower_components/font-awesome/fonts/FontAwesome.otf');
|
||||
|
||||
module.exports = app.toTree();
|
|
@ -1,18 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
import Resolver from 'ember/resolver';
|
||||
import loadInitializers from 'ember/load-initializers';
|
||||
import config from './config/environment';
|
||||
|
||||
Ember.MODEL_FACTORY_INJECTIONS = true;
|
||||
|
||||
var App = Ember.Application.extend({
|
||||
modulePrefix: config.modulePrefix,
|
||||
podModulePrefix: config.podModulePrefix,
|
||||
Resolver: Resolver
|
||||
});
|
||||
|
||||
loadInitializers(App, config.modulePrefix);
|
||||
|
||||
Ember.$('#assets-loading').remove();
|
||||
|
||||
export default App;
|
|
@ -1,8 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
import NavItem from './nav-item';
|
||||
|
||||
var precompileTemplate = Ember.Handlebars.compile;
|
||||
|
||||
export default NavItem.extend({
|
||||
layout: precompileTemplate('{{#link-to routeName}}{{fa-icon icon class="icon"}} <span class="label">{{label}}</span> <div class="description">{{description}}</div>{{/link-to}}')
|
||||
});
|
|
@ -1,23 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
import HasItemLists from '../mixins/has-item-lists';
|
||||
import DropdownButton from './ui/dropdown-button';
|
||||
|
||||
var precompileTemplate = Ember.Handlebars.compile;
|
||||
|
||||
export default DropdownButton.extend(HasItemLists, {
|
||||
layoutName: 'components/application/user-dropdown',
|
||||
itemLists: ['items'],
|
||||
|
||||
buttonClass: 'btn btn-default btn-naked btn-rounded btn-user',
|
||||
menuClass: 'pull-right',
|
||||
label: Ember.computed.alias('user.username'),
|
||||
|
||||
populateItems: function(items) {
|
||||
var self = this;
|
||||
|
||||
this.addActionItem(items, 'logout', 'Log Out', 'sign-out', null, function() {
|
||||
self.get('parentController').send('invalidateSession');
|
||||
});
|
||||
}
|
||||
})
|
|
@ -1,9 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
actions: {
|
||||
toggleDrawer: function() {
|
||||
this.toggleProperty('drawerShowing');
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,25 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Flarum</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/flarum.css">
|
||||
|
||||
{{content-for 'head-footer'}}
|
||||
</head>
|
||||
<body>
|
||||
{{content-for 'body'}}
|
||||
|
||||
<script src="assets/vendor.js"></script>
|
||||
<script src="assets/flarum.js"></script>
|
||||
|
||||
{{content-for 'body-footer'}}
|
||||
</body>
|
||||
</html>
|
|
@ -1,16 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
import config from './config/environment';
|
||||
|
||||
var Router = Ember.Router.extend({
|
||||
location: config.locationType
|
||||
});
|
||||
|
||||
Router.map(function() {
|
||||
this.resource('dashboard', {path: '/'});
|
||||
this.resource('basics');
|
||||
this.resource('permissions');
|
||||
this.resource('appearance');
|
||||
this.resource('extensions');
|
||||
});
|
||||
|
||||
export default Router;
|
|
@ -1,2 +0,0 @@
|
|||
// Flarum styles are stored in the top-level `less` directory. This remains
|
||||
// here as a placeholder file to prevent ember-cli from crashing.
|
|
@ -1 +0,0 @@
|
|||
Appearance
|
|
@ -1,43 +0,0 @@
|
|||
<div id="page" class="global-page with-pane">
|
||||
|
||||
{{application/back-button className="back-control" toggleDrawer="toggleDrawer" goBack="goBack" canGoBack=false}}
|
||||
|
||||
<div id="drawer" class="global-drawer">
|
||||
<header id="header" class="global-header">
|
||||
{{application/back-button goBack="goBack" canGoBack=true}}
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div class="header-primary">
|
||||
<h1 class="header-title">
|
||||
Administration
|
||||
</h1>
|
||||
{{ui/item-list items=view.headerPrimary class="header-controls"}}
|
||||
</div>
|
||||
|
||||
<div class="header-secondary">
|
||||
{{ui/item-list items=view.headerSecondary class="header-controls"}}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<main id="content" class="global-content">
|
||||
<div class="container">
|
||||
<div class="side-nav admin-nav title-control">
|
||||
{{ui/dropdown-select items=view.adminNav}}
|
||||
</div>
|
||||
<div class="admin-content">
|
||||
{{outlet}}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="modal" class="modal fade">
|
||||
{{outlet "modal"}}
|
||||
</div>
|
||||
|
||||
{{render "alerts"}}
|
|
@ -1 +0,0 @@
|
|||
Basics
|
|
@ -1 +0,0 @@
|
|||
Dashboard
|
|
@ -1 +0,0 @@
|
|||
Extensions
|
|
@ -1 +0,0 @@
|
|||
{{ui/loading-indicator class="loading-indicator-block"}}
|
|
@ -1 +0,0 @@
|
|||
Permissions
|
|
@ -1,79 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
import HasItemLists from '../mixins/has-item-lists';
|
||||
import AdminNavItem from '../components/ui/admin-nav-item';
|
||||
import SearchInput from '../components/ui/search-input';
|
||||
import UserDropdown from '../components/user-dropdown';
|
||||
|
||||
export default Ember.View.extend(HasItemLists, {
|
||||
itemLists: ['headerPrimary', 'headerSecondary', 'adminNav'],
|
||||
|
||||
drawerShowingChanged: Ember.observer('controller.drawerShowing', function() {
|
||||
Ember.run.scheduleOnce('afterRender', this, function() {
|
||||
$('body').toggleClass('drawer-open', this.get('controller.drawerShowing'));
|
||||
});
|
||||
}),
|
||||
|
||||
didInsertElement: function() {
|
||||
this.$('.global-content').click(function(e) {
|
||||
if (view.get('controller.drawerShowing')) {
|
||||
e.preventDefault();
|
||||
view.set('controller.drawerShowing', false);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
populateHeaderSecondary: function(items) {
|
||||
var controller = this.get('controller');
|
||||
|
||||
items.pushObjectWithTag(SearchInput.extend({
|
||||
placeholder: 'Search Forum',
|
||||
controller: controller,
|
||||
valueBinding: Ember.Binding.oneWay('controller.searchQuery'),
|
||||
activeBinding: Ember.Binding.oneWay('controller.searchActive'),
|
||||
action: function(value) { controller.send('search', value); }
|
||||
}), 'search');
|
||||
|
||||
items.pushObjectWithTag(UserDropdown.extend({
|
||||
user: this.get('controller.session.user'),
|
||||
parentController: controller
|
||||
}), 'user');
|
||||
},
|
||||
|
||||
populateAdminNav: function(items) {
|
||||
items.pushObjectWithTag(AdminNavItem.extend({
|
||||
routeName: 'dashboard',
|
||||
icon: 'bar-chart',
|
||||
label: 'Dashboard',
|
||||
description: 'Your forum at a glance.'
|
||||
}), 'dashboard');
|
||||
|
||||
items.pushObjectWithTag(AdminNavItem.extend({
|
||||
routeName: 'basics',
|
||||
icon: 'pencil',
|
||||
label: 'Basics',
|
||||
description: 'Set your forum title, language, and other basic settings.'
|
||||
}), 'basics');
|
||||
|
||||
items.pushObjectWithTag(AdminNavItem.extend({
|
||||
routeName: 'permissions',
|
||||
icon: 'key',
|
||||
label: 'Permissions',
|
||||
description: 'Configure who can see and do what.'
|
||||
}), 'permissions');
|
||||
|
||||
items.pushObjectWithTag(AdminNavItem.extend({
|
||||
routeName: 'appearance',
|
||||
icon: 'paint-brush',
|
||||
label: 'Appearance',
|
||||
description: 'Customize your forum\'s colors, logos, and other variables.'
|
||||
}), 'appearance');
|
||||
|
||||
items.pushObjectWithTag(AdminNavItem.extend({
|
||||
routeName: 'extensions',
|
||||
icon: 'puzzle-piece',
|
||||
label: 'Extensions',
|
||||
description: 'Add extra functionality to your forum and make it your own.'
|
||||
}), 'extensions');
|
||||
}
|
||||
});
|
|
@ -1,28 +0,0 @@
|
|||
{
|
||||
"name": "flarum-admin",
|
||||
"dependencies": {
|
||||
"jquery": "2.1.3",
|
||||
"ember": "1.11.0-beta.3",
|
||||
"ember-data": "1.0.0-beta.16.1",
|
||||
"ember-resolver": "~0.1.11",
|
||||
"loader.js": "ember-cli/loader.js#1.0.1",
|
||||
"ember-cli-shims": "ember-cli/ember-cli-shims#0.0.3",
|
||||
"ember-cli-test-loader": "0.1.3",
|
||||
"ember-load-initializers": "ember-cli/ember-load-initializers#0.0.2",
|
||||
"ember-qunit": "0.2.8",
|
||||
"ember-qunit-notifications": "0.0.7",
|
||||
"qunit": "~1.17.1",
|
||||
"bootstrap": "~3.3.2",
|
||||
"font-awesome": "~4",
|
||||
"spin.js": "~2.0.1",
|
||||
"moment": "~2.8.4",
|
||||
"ember-simple-auth": "0.7.2",
|
||||
"jquery.hotkeys": "jeresig/jquery.hotkeys#0.2.0",
|
||||
"blurjs": ""
|
||||
},
|
||||
"resolutions": {
|
||||
"ember-cli-test-loader": "0.1.3",
|
||||
"ember-qunit": "0.2.8",
|
||||
"ember-qunit-notifications": "0.0.7"
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
/* jshint node: true */
|
||||
|
||||
module.exports = function(environment) {
|
||||
var ENV = {
|
||||
modulePrefix: 'flarum-admin',
|
||||
environment: environment,
|
||||
baseURL: '/',
|
||||
apiURL: '/api',
|
||||
locationType: 'hash',
|
||||
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
|
||||
}
|
||||
};
|
||||
|
||||
ENV['simple-auth'] = {
|
||||
authorizer: 'authorizer:flarum'
|
||||
};
|
||||
|
||||
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';
|
||||
ENV.apiURL = 'http://flarum.dev/api',
|
||||
|
||||
// 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;
|
||||
};
|
|
@ -1,41 +0,0 @@
|
|||
{
|
||||
"name": "flarum-admin",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"directories": {
|
||||
"doc": "doc",
|
||||
"test": "tests"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "ember server",
|
||||
"build": "ember build",
|
||||
"test": "ember test",
|
||||
"preinstall": "sudo npm link ../common"
|
||||
},
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"ember-cli": "^0.2.0-beta.1",
|
||||
"ember-cli-app-version": "0.3.1",
|
||||
"ember-cli-babel": "^4.1.0",
|
||||
"ember-cli-content-security-policy": "0.3.0",
|
||||
"ember-cli-dependency-checker": "0.0.7",
|
||||
"ember-cli-htmlbars": "^0.7.4",
|
||||
"ember-cli-ic-ajax": "0.1.1",
|
||||
"ember-cli-inject-live-reload": "^1.3.0",
|
||||
"ember-cli-qunit": "^0.3.8",
|
||||
"ember-cli-simple-auth": "^0.7.2",
|
||||
"ember-cli-uglify": "1.0.1",
|
||||
"ember-data": "1.0.0-beta.16.1",
|
||||
"ember-export-application-global": "^1.0.2",
|
||||
"ember-json-api": "eneuhauser/ember-json-api",
|
||||
"broccoli-ember-inline-template-compiler": "tobscure/broccoli-ember-inline-template-compiler#f884d11",
|
||||
"express": "^4.8.5",
|
||||
"glob": "^4.0.5",
|
||||
"flarum-common": "*"
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"framework": "qunit",
|
||||
"test_page": "tests/index.html?hidepassed",
|
||||
"launch_in_ci": [
|
||||
"PhantomJS"
|
||||
],
|
||||
"launch_in_dev": [
|
||||
"PhantomJS",
|
||||
"Chrome"
|
||||
]
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
{
|
||||
"predef": [
|
||||
"document",
|
||||
"window",
|
||||
"location",
|
||||
"setTimeout",
|
||||
"$",
|
||||
"-Promise",
|
||||
"QUnit",
|
||||
"define",
|
||||
"console",
|
||||
"equal",
|
||||
"notEqual",
|
||||
"notStrictEqual",
|
||||
"test",
|
||||
"asyncTest",
|
||||
"testBoth",
|
||||
"testWithDefault",
|
||||
"raises",
|
||||
"throws",
|
||||
"deepEqual",
|
||||
"start",
|
||||
"stop",
|
||||
"ok",
|
||||
"strictEqual",
|
||||
"module",
|
||||
"moduleFor",
|
||||
"moduleForComponent",
|
||||
"moduleForModel",
|
||||
"process",
|
||||
"expect",
|
||||
"visit",
|
||||
"exists",
|
||||
"fillIn",
|
||||
"click",
|
||||
"keyEvent",
|
||||
"triggerEvent",
|
||||
"find",
|
||||
"findWithAssert",
|
||||
"wait",
|
||||
"DS",
|
||||
"isolatedContainer",
|
||||
"startApp",
|
||||
"andThen",
|
||||
"currentURL",
|
||||
"currentPath",
|
||||
"currentRouteName"
|
||||
],
|
||||
"node": false,
|
||||
"browser": false,
|
||||
"boss": true,
|
||||
"curly": false,
|
||||
"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
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
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;
|
|
@ -1,19 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
import Application from '../../app';
|
||||
import Router from '../../router';
|
||||
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;
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Flarum 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/flarum-admin.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/flarum-admin.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>
|
|
@ -1,48 +0,0 @@
|
|||
import Ember from "ember";
|
||||
import { test } from 'ember-qunit';
|
||||
import startApp from '../helpers/start-app';
|
||||
var App;
|
||||
|
||||
module('Index', {
|
||||
setup: function() {
|
||||
App = startApp();
|
||||
},
|
||||
teardown: function() {
|
||||
Ember.run(App, App.destroy);
|
||||
}
|
||||
});
|
||||
|
||||
test('Discussion list loading', function() {
|
||||
expect(3);
|
||||
visit('/').then(function() {
|
||||
equal(find('.discussions-list').length, 1, 'Page contains list of discussions');
|
||||
equal(find('.discussions-list li').length, 20, 'There are 20 discussions in the list');
|
||||
|
||||
click('.control-loadMore').then(function() {
|
||||
equal(find('.discussions-list li').length, 40, 'There are 40 discussions in the list');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Discussion list sorting', function() {
|
||||
expect(1);
|
||||
visit('/').then(function() {
|
||||
fillIn('.control-sort select', 'replies').then(function() {
|
||||
var discussions = find('.discussions-list li');
|
||||
var good = true;
|
||||
var getCount = function(item) {
|
||||
return parseInt(item.find('.count strong').text());
|
||||
};
|
||||
var previousCount = getCount(discussions.eq(0));
|
||||
for (var i = 1; i < discussions.length; i++) {
|
||||
var count = getCount(discussions.eq(i));
|
||||
if (count > previousCount) {
|
||||
good = false;
|
||||
break;
|
||||
}
|
||||
previousCount = count;
|
||||
}
|
||||
ok(good, 'Discussions are listed in order of reply count');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,6 +0,0 @@
|
|||
import resolver from './helpers/resolver';
|
||||
import {
|
||||
setResolver
|
||||
} from 'ember-qunit';
|
||||
|
||||
setResolver(resolver);
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"directory": "bower_components",
|
||||
"analytics": false
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.js]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.hbs]
|
||||
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
|
17
framework/core/ember/common/.gitignore
vendored
17
framework/core/ember/common/.gitignore
vendored
|
@ -1,17 +0,0 @@
|
|||
# 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,32 +0,0 @@
|
|||
{
|
||||
"predef": [
|
||||
"document",
|
||||
"window",
|
||||
"-Promise"
|
||||
],
|
||||
"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
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
bower_components/
|
||||
tests/
|
||||
|
||||
.bowerrc
|
||||
.editorconfig
|
||||
.ember-cli
|
||||
.travis.yml
|
||||
.npmignore
|
||||
**/.gitkeep
|
||||
bower.json
|
||||
Brocfile.js
|
||||
testem.json
|
|
@ -1,20 +0,0 @@
|
|||
---
|
||||
language: node_js
|
||||
|
||||
sudo: false
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
|
||||
before_install:
|
||||
- "npm config set spin false"
|
||||
- "npm install -g npm@^2"
|
||||
|
||||
install:
|
||||
- npm install -g bower
|
||||
- npm install
|
||||
- bower install
|
||||
|
||||
script:
|
||||
- npm test
|
|
@ -1,21 +0,0 @@
|
|||
/* jshint node: true */
|
||||
/* global require, module */
|
||||
|
||||
var EmberAddon = require('ember-cli/lib/broccoli/ember-addon');
|
||||
|
||||
var app = new EmberAddon();
|
||||
|
||||
// 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();
|
|
@ -1,50 +0,0 @@
|
|||
import DS from 'ember-data';
|
||||
import JsonApiAdapter from 'ember-json-api/json-api-adapter';
|
||||
|
||||
import config from '../config/environment';
|
||||
import AlertMessage from '../components/ui/alert-message';
|
||||
|
||||
export default JsonApiAdapter.extend({
|
||||
host: config.apiURL,
|
||||
|
||||
pathForType: function(type) {
|
||||
if (type == 'activity') {
|
||||
return type;
|
||||
}
|
||||
return this._super(type);
|
||||
},
|
||||
|
||||
ajaxError: function(jqXHR) {
|
||||
var errors = this._super(jqXHR);
|
||||
|
||||
// Reparse the errors in accordance with the JSON-API spec to fit with
|
||||
// Ember Data style. Hopefully something like this will eventually be a
|
||||
// part of the JsonApiAdapter.
|
||||
if (errors instanceof DS.InvalidError) {
|
||||
var newErrors = {};
|
||||
for (var i in errors.errors) {
|
||||
var error = errors.errors[i];
|
||||
newErrors[error.path] = error.detail;
|
||||
}
|
||||
return new DS.InvalidError(newErrors);
|
||||
}
|
||||
|
||||
// If it's a server error, show an alert message. The alerts controller
|
||||
// has been injected into this adapter.
|
||||
if (errors instanceof JsonApiAdapter.ServerError) {
|
||||
var message;
|
||||
if (errors.status === 401) {
|
||||
message = 'You don\'t have permission to do this.';
|
||||
} else {
|
||||
message = errors.message;
|
||||
}
|
||||
var alert = AlertMessage.extend({
|
||||
type: 'warning',
|
||||
message: message
|
||||
});
|
||||
this.get('alerts').send('alert', alert);
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
});
|
|
@ -1,28 +0,0 @@
|
|||
import Base from 'simple-auth/authenticators/base';
|
||||
import config from '../config/environment';
|
||||
|
||||
export default Base.extend({
|
||||
|
||||
authenticate: function(credentials) {
|
||||
var container = this.container;
|
||||
return new Ember.RSVP.Promise(function(resolve, reject) {
|
||||
Ember.$.ajax({
|
||||
url: config.baseURL+'login',
|
||||
type: 'POST',
|
||||
data: { identification: credentials.identification, password: credentials.password }
|
||||
}).then(function(response) {
|
||||
container.lookup('store:main').find('user', response.userId).then(function(user) {
|
||||
resolve({ token: response.token, userId: response.userId, user: user });
|
||||
});
|
||||
}, function(xhr, status, error) {
|
||||
reject(xhr.responseJSON.errors);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
invalidate: function(data) {
|
||||
return new Ember.RSVP.Promise(function() {
|
||||
window.location = config.baseURL+'logout';
|
||||
});
|
||||
}
|
||||
});
|
|
@ -1,10 +0,0 @@
|
|||
import Base from 'simple-auth/authorizers/base';
|
||||
|
||||
export default Base.extend({
|
||||
authorize: function(jqXHR, requestOptions) {
|
||||
var token = this.get('session.token');
|
||||
if (this.get('session.isAuthenticated') && !Ember.isEmpty(token)) {
|
||||
jqXHR.setRequestHeader('Authorization', 'Token ' + token);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,43 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
/**
|
||||
The back/pin button group in the top-left corner of Flarum's interface.
|
||||
*/
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['back-button'],
|
||||
classNameBindings: ['active', 'className'],
|
||||
|
||||
active: Ember.computed.or('target.paneIsShowing', 'target.paneIsPinned'),
|
||||
|
||||
mouseEnter: function() {
|
||||
var target = this.get('target');
|
||||
if (target) {
|
||||
target.send('showPane');
|
||||
}
|
||||
},
|
||||
|
||||
mouseLeave: function() {
|
||||
var target = this.get('target');
|
||||
if (target) {
|
||||
target.send('hidePane');
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
// WE HAVE TO GO BACK. WAAAAAALLLLLLTTTTT
|
||||
back: function() {
|
||||
this.sendAction('goBack');
|
||||
},
|
||||
|
||||
togglePinned: function() {
|
||||
var target = this.get('target');
|
||||
if (target) {
|
||||
target.send('togglePinned');
|
||||
}
|
||||
},
|
||||
|
||||
toggleDrawer: function() {
|
||||
this.sendAction('toggleDrawer');
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,29 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
var precompileTemplate = Ember.Handlebars.compile;
|
||||
|
||||
/**
|
||||
Button which sends an action when clicked.
|
||||
*/
|
||||
export default Ember.Component.extend({
|
||||
tagName: 'a',
|
||||
attributeBindings: ['href', 'title'],
|
||||
classNameBindings: ['className'],
|
||||
href: '#',
|
||||
layout: precompileTemplate('{{#if icon}}{{fa-icon icon class="fa-fw icon-glyph"}} {{/if}}<span class="label">{{label}}</span>'),
|
||||
|
||||
label: '',
|
||||
icon: '',
|
||||
className: '',
|
||||
action: null,
|
||||
|
||||
click: function(e) {
|
||||
e.preventDefault();
|
||||
var action = this.get('action');
|
||||
if (typeof action === 'string') {
|
||||
this.sendAction('action');
|
||||
} else if (typeof action === 'function') {
|
||||
action.call(this);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,53 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
import HasItemLists from '../../mixins/has-item-lists';
|
||||
import ActionButton from './action-button';
|
||||
|
||||
/**
|
||||
An alert message. Has a message, a `controls` item list, and a dismiss
|
||||
button.
|
||||
*/
|
||||
export default Ember.Component.extend(HasItemLists, {
|
||||
layoutName: 'components/ui/alert-message',
|
||||
classNames: ['alert'],
|
||||
classNameBindings: ['classForType'],
|
||||
itemLists: ['controls'],
|
||||
|
||||
message: '',
|
||||
type: '',
|
||||
dismissable: true,
|
||||
buttons: [],
|
||||
|
||||
classForType: Ember.computed('type', function() {
|
||||
return 'alert-'+this.get('type');
|
||||
}),
|
||||
|
||||
populateControls: function(controls) {
|
||||
var component = this;
|
||||
|
||||
this.get('buttons').forEach(function(button) {
|
||||
controls.pushObject(ActionButton.extend({
|
||||
label: button.label,
|
||||
action: function() {
|
||||
component.send('dismiss');
|
||||
button.action();
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
if (this.get('dismissable')) {
|
||||
var dismiss = ActionButton.extend({
|
||||
icon: 'times',
|
||||
className: 'btn btn-icon btn-link',
|
||||
action: function() { component.send('dismiss'); }
|
||||
});
|
||||
controls.pushObjectWithTag(dismiss, 'dismiss');
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
dismiss: function() {
|
||||
this.sendAction('dismiss', this);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,11 +0,0 @@
|
|||
import ActionButton from './action-button';
|
||||
|
||||
export default ActionButton.extend({
|
||||
tagName: 'span',
|
||||
classNames: ['badge'],
|
||||
title: Ember.computed.alias('label'),
|
||||
|
||||
didInsertElement: function() {
|
||||
this.$().tooltip();
|
||||
}
|
||||
});
|
|
@ -1,31 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
/**
|
||||
Button which has an attached dropdown menu containing an item list.
|
||||
*/
|
||||
export default Ember.Component.extend({
|
||||
layoutName: 'components/ui/dropdown-button',
|
||||
classNames: ['dropdown', 'btn-group'],
|
||||
classNameBindings: ['itemCountClass', 'class'],
|
||||
|
||||
label: 'Controls',
|
||||
icon: 'ellipsis-v',
|
||||
buttonClass: 'btn btn-default',
|
||||
menuClass: '',
|
||||
items: null,
|
||||
|
||||
dropdownMenuClass: Ember.computed('menuClass', function() {
|
||||
return 'dropdown-menu '+this.get('menuClass');
|
||||
}),
|
||||
|
||||
itemCountClass: Ember.computed('items.length', function() {
|
||||
var count = this.get('items.length');
|
||||
return count ? 'item-count-'+this.get('items.length') : '';
|
||||
}),
|
||||
|
||||
actions: {
|
||||
buttonClick: function() {
|
||||
this.sendAction('buttonClick');
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,32 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
/**
|
||||
Button which has an attached dropdown menu containing an item list. The
|
||||
currently-active item's label is displayed as the label of the button.
|
||||
*/
|
||||
export default Ember.Component.extend({
|
||||
layoutName: 'components/ui/dropdown-select',
|
||||
classNames: ['dropdown', 'dropdown-select', 'btn-group'],
|
||||
classNameBindings: ['itemCountClass', 'className'],
|
||||
|
||||
buttonClass: 'btn btn-default',
|
||||
menuClass: '',
|
||||
icon: 'ellipsis-v',
|
||||
items: [],
|
||||
|
||||
mainButtonClass: Ember.computed('buttonClass', function() {
|
||||
return 'btn '+this.get('buttonClass');
|
||||
}),
|
||||
|
||||
dropdownMenuClass: Ember.computed('menuClass', function() {
|
||||
return 'dropdown-menu '+this.get('menuClass');
|
||||
}),
|
||||
|
||||
itemCountClass: Ember.computed('items.length', function() {
|
||||
return 'item-count-'+this.get('items.length');
|
||||
}),
|
||||
|
||||
activeItem: Ember.computed('menu.childViews.@each.active', function() {
|
||||
return this.get('menu.childViews').findBy('active');
|
||||
})
|
||||
});
|
|
@ -1,22 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
import DropdownButton from './dropdown-button';
|
||||
|
||||
/**
|
||||
Given a list of items, this component displays a split button: the left side
|
||||
is the first item in the list, while the right side is a dropdown-toggle
|
||||
which shows a dropdown menu containing all of the items.
|
||||
*/
|
||||
export default DropdownButton.extend({
|
||||
layoutName: 'components/ui/dropdown-split',
|
||||
classNames: ['dropdown', 'dropdown-split', 'btn-group'],
|
||||
menuClass: 'pull-right',
|
||||
|
||||
mainButtonClass: Ember.computed('buttonClass', function() {
|
||||
return 'btn '+this.get('buttonClass');
|
||||
}),
|
||||
|
||||
firstItem: Ember.computed('items.[]', function() {
|
||||
return this.get('items').objectAt(0);
|
||||
})
|
||||
});
|
|
@ -1,13 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
/**
|
||||
A set of fields with a heading.
|
||||
*/
|
||||
export default Ember.Component.extend({
|
||||
layoutName: 'components/ui/field-set',
|
||||
tagName: 'fieldset',
|
||||
classNameBindings: ['className'],
|
||||
|
||||
label: '',
|
||||
fields: []
|
||||
});
|
|
@ -1,24 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
/**
|
||||
Output a list of components within a <ul>, making sure each one is contained
|
||||
in an <li> element.
|
||||
*/
|
||||
export default Ember.Component.extend({
|
||||
layoutName: 'components/ui/item-list',
|
||||
tagName: 'ul',
|
||||
|
||||
listItems: Ember.computed('items.[]', function() {
|
||||
var items = this.get('items');
|
||||
if (!Ember.isArray(items)) {
|
||||
return [];
|
||||
}
|
||||
var instances = [];
|
||||
items.forEach(function(item) {
|
||||
item = item.create();
|
||||
item.set('isListItem', item.constructor.proto().tagName === 'li');
|
||||
instances.pushObject(item);
|
||||
});
|
||||
return instances;
|
||||
})
|
||||
});
|
|
@ -1,19 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
var precompileTemplate = Ember.Handlebars.compile;
|
||||
|
||||
/**
|
||||
Loading spinner.
|
||||
*/
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['loading-indicator'],
|
||||
|
||||
layout: precompileTemplate(' '),
|
||||
size: 'small',
|
||||
|
||||
didInsertElement: function() {
|
||||
var size = this.get('size');
|
||||
Ember.$.fn.spin.presets[size].zIndex = 'auto';
|
||||
this.$().spin(size);
|
||||
}
|
||||
});
|
|
@ -1,22 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
var precompileTemplate = Ember.Handlebars.compile;
|
||||
|
||||
/**
|
||||
A list item which contains a navigation link. The list item's `active`
|
||||
property reflects whether or not the link is active.
|
||||
*/
|
||||
export default Ember.Component.extend({
|
||||
layout: precompileTemplate('{{#link-to routeName}}{{fa-icon icon}} {{label}} <span class="count">{{badge}}</span>{{/link-to}}'),
|
||||
tagName: 'li',
|
||||
classNameBindings: ['active'],
|
||||
|
||||
icon: '',
|
||||
label: '',
|
||||
badge: '',
|
||||
routeName: '',
|
||||
|
||||
active: Ember.computed('childViews.@each.active', function() {
|
||||
return !!this.get('childViews').anyBy('active');
|
||||
})
|
||||
});
|
|
@ -1,36 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
/**
|
||||
A basic search input. Comes with the ability to be cleared by pressing
|
||||
escape or with a button. Sends an action when enter is pressed.
|
||||
*/
|
||||
export default Ember.Component.extend({
|
||||
layoutName: 'components/ui/search-input',
|
||||
classNames: ['search-input'],
|
||||
classNameBindings: ['active', 'value:clearable'],
|
||||
|
||||
didInsertElement: function() {
|
||||
this.$('input').on('keydown', 'esc', function(e) {
|
||||
self.clear();
|
||||
});
|
||||
|
||||
var self = this;
|
||||
this.$('.clear').on('mousedown click', function(e) {
|
||||
e.preventDefault();
|
||||
}).on('click', function(e) {
|
||||
self.clear();
|
||||
});
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
this.set('value', '');
|
||||
this.send('search');
|
||||
this.$().find('input').focus();
|
||||
},
|
||||
|
||||
actions: {
|
||||
search: function() {
|
||||
this.get('action')(this.get('value'));
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,16 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
var precompileTemplate = Ember.Handlebars.compile;
|
||||
|
||||
/**
|
||||
A basic select input. Wraps Ember's select component with a span/icon so
|
||||
that we can style it more fancily.
|
||||
*/
|
||||
export default Ember.Component.extend({
|
||||
layout: precompileTemplate('{{view "select" content=view.content optionValuePath=view.optionValuePath optionLabelPath=view.optionLabelPath value=view.value class="form-control"}} {{fa-icon "sort"}}'),
|
||||
tagName: 'span',
|
||||
classNames: ['select-input'],
|
||||
|
||||
optionValuePath: 'content',
|
||||
optionLabelPath: 'content'
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
/**
|
||||
A simple separator list item for use in menus.
|
||||
*/
|
||||
export default Ember.Component.extend({
|
||||
tagName: 'li',
|
||||
classNames: ['divider']
|
||||
});
|
|
@ -1,19 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
/**
|
||||
A toggle switch.
|
||||
*/
|
||||
export default Ember.Component.extend({
|
||||
layoutName: 'components/ui/switch-input',
|
||||
classNames: ['checkbox', 'checkbox-switch'],
|
||||
|
||||
label: '',
|
||||
toggleState: true,
|
||||
|
||||
didInsertElement: function() {
|
||||
var component = this;
|
||||
this.$('input').on('change', function() {
|
||||
component.get('changed')($(this).prop('checked'), component);
|
||||
});
|
||||
}
|
||||
});
|
|
@ -1,33 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
import HasItemLists from '../../mixins/has-item-lists';
|
||||
import ActionButton from './action-button';
|
||||
|
||||
/**
|
||||
A text editor. Contains a textarea and an item list of `controls`, including
|
||||
a submit button.
|
||||
*/
|
||||
export default Ember.Component.extend(HasItemLists, {
|
||||
classNames: ['text-editor'],
|
||||
itemLists: ['controls'],
|
||||
|
||||
value: '',
|
||||
disabled: false,
|
||||
|
||||
didInsertElement: function() {
|
||||
var component = this;
|
||||
this.$('textarea').bind('keydown', 'meta+return', function() {
|
||||
component.send('submit');
|
||||
});
|
||||
},
|
||||
|
||||
populateControls: function(items) {
|
||||
this.addActionItem(items, 'submit', this.get('submitLabel'), 'check').reopen({className: 'btn btn-primary', listItemClass: 'primary-control'});
|
||||
},
|
||||
|
||||
actions: {
|
||||
submit: function() {
|
||||
this.sendAction('submit', this.get('value'));
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,26 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
/**
|
||||
An extension of Ember's text field with an option to set up an auto-growing
|
||||
text input.
|
||||
*/
|
||||
export default Ember.TextField.extend({
|
||||
autoGrow: false,
|
||||
|
||||
didInsertElement: function() {
|
||||
if (this.get('autoGrow')) {
|
||||
var component = this;
|
||||
this.$().on('input', function() {
|
||||
var empty = !$(this).val();
|
||||
if (empty) {
|
||||
$(this).val(component.get('placeholder'));
|
||||
}
|
||||
$(this).css('width', 0);
|
||||
$(this).width($(this)[0].scrollWidth);
|
||||
if (empty) {
|
||||
$(this).val('');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,20 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
/**
|
||||
A toggle switch.
|
||||
*/
|
||||
export default Ember.Component.extend({
|
||||
layoutName: 'components/ui/yesno-input',
|
||||
tagName: 'label',
|
||||
classNames: ['yesno-control'],
|
||||
|
||||
toggleState: true,
|
||||
disabled: false,
|
||||
|
||||
didInsertElement: function() {
|
||||
var component = this;
|
||||
this.$('input').on('change', function() {
|
||||
component.get('changed')($(this).prop('checked'), component);
|
||||
});
|
||||
}
|
||||
});
|
|
@ -1,17 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
alerts: [],
|
||||
|
||||
actions: {
|
||||
alert: function(message) {
|
||||
this.get('alerts').pushObject(message);
|
||||
},
|
||||
dismissAlert: function(message) {
|
||||
this.get('alerts').removeObject(message.constructor);
|
||||
},
|
||||
clearAlerts: function() {
|
||||
this.get('alerts').clear();
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,6 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Handlebars.makeBoundHelper(function(number) {
|
||||
return new Ember.Handlebars.SafeString(''+number);
|
||||
});
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Handlebars.makeBoundHelper(function(icon, options) {
|
||||
return new Ember.Handlebars.SafeString('<i class="fa fa-fw fa-'+icon+' '+(options.hash.class || '')+'"></i>');
|
||||
});
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Handlebars.makeBoundHelper(function(time) {
|
||||
var m = moment(time);
|
||||
var datetime = m.format();
|
||||
var full = m.format('LLLL');
|
||||
|
||||
return new Ember.Handlebars.SafeString('<time pubdate datetime="'+datetime+'">'+full+'</time>');
|
||||
});
|
|
@ -1,17 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Handlebars.makeBoundHelper(function(text, phrase) {
|
||||
if (phrase) {
|
||||
var words = phrase.split(' ');
|
||||
var replacement = function(matched) {
|
||||
return '<span class="highlight-keyword">'+matched+'</span>';
|
||||
};
|
||||
words.forEach(function(word) {
|
||||
text = text.replace(
|
||||
new RegExp("\\b"+word+"\\b", 'gi'),
|
||||
replacement
|
||||
);
|
||||
});
|
||||
}
|
||||
return new Ember.Handlebars.SafeString(text);
|
||||
});
|
|
@ -1,13 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
import humanTime from '../utils/human-time';
|
||||
|
||||
export default Ember.Handlebars.makeBoundHelper(function(time) {
|
||||
var m = moment(time);
|
||||
var datetime = m.format();
|
||||
var full = m.format('LLLL');
|
||||
|
||||
var ago = humanTime(m);
|
||||
|
||||
return new Ember.Handlebars.SafeString('<time pubdate datetime="'+datetime+'" title="'+full+'" data-humantime>'+ago+'</time>');
|
||||
});
|
|
@ -1,26 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Handlebars.makeBoundHelper(function(user, options) {
|
||||
var attributes = 'class="avatar '+(options.hash.class || '')+'"';
|
||||
var content = '';
|
||||
|
||||
if (user) {
|
||||
var username = user.get('username') || '?';
|
||||
|
||||
if (typeof options.hash.title === 'undefined') {
|
||||
options.hash.title = Ember.Handlebars.Utils.escapeExpression(username);
|
||||
}
|
||||
attributes += ' title="'+options.hash.title+'"';
|
||||
|
||||
var avatarUrl = user.get('avatarUrl');
|
||||
if (avatarUrl) {
|
||||
return new Ember.Handlebars.SafeString('<img src="'+avatarUrl+'" '+attributes+'>');
|
||||
}
|
||||
|
||||
content = username.charAt(0).toUpperCase();
|
||||
attributes += ' style="background:'+user.get('color')+'"';
|
||||
}
|
||||
|
||||
return new Ember.Handlebars.SafeString('<span '+attributes+'>'+content+'</span>');
|
||||
}, 'avatarUrl', 'username', 'color');
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Handlebars.makeBoundHelper(function(user, options) {
|
||||
var username;
|
||||
if (user) {
|
||||
username = user.get('username');
|
||||
}
|
||||
username = username || '[deleted]';
|
||||
|
||||
return new Ember.Handlebars.SafeString('<span class="username">'+Ember.Handlebars.Utils.escapeExpression(username)+'</span>');
|
||||
});
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
import FlarumAuthorizer from '../authorizers/flarum';
|
||||
import Config from '../config/environment';
|
||||
|
||||
export default {
|
||||
name: 'authentication',
|
||||
before: 'simple-auth',
|
||||
initialize: function(container) {
|
||||
container.register('authorizer:flarum', FlarumAuthorizer);
|
||||
Config['simple-auth'] = {authorizer: 'authorizer:flarum'};
|
||||
}
|
||||
};
|
|
@ -1,25 +0,0 @@
|
|||
import DS from 'ember-data';
|
||||
|
||||
// This can be removed when
|
||||
// https://github.com/emberjs/data/pull/2584 is implemented.
|
||||
|
||||
export default {
|
||||
name: 'find-query-one',
|
||||
initialize: function(container) {
|
||||
DS.Store.reopen({
|
||||
findQueryOne: function(type, id, query) {
|
||||
var store = this;
|
||||
var typeClass = store.modelFor(type);
|
||||
var adapter = store.adapterFor(typeClass);
|
||||
var serializer = store.serializerFor(typeClass);
|
||||
var url = adapter.buildURL(type, id);
|
||||
var ajaxPromise = adapter.ajax(url, 'GET', { data: query });
|
||||
|
||||
return ajaxPromise.then(function(rawPayload) {
|
||||
var extractedPayload = serializer.extract(store, typeClass, rawPayload, id, 'find');
|
||||
return store.push(typeClass, extractedPayload);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
|
@ -1,147 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
import humanTime from '../utils/human-time';
|
||||
|
||||
var $ = Ember.$;
|
||||
|
||||
export default {
|
||||
name: 'human-time-updater',
|
||||
initialize: function(container) {
|
||||
|
||||
// Livestamp.js / v1.1.2 / (c) 2012 Matt Bradley / MIT License
|
||||
// @todo rewrite this to be simpler and cleaner
|
||||
(function($, moment) {
|
||||
var updateInterval = 1e3,
|
||||
paused = false,
|
||||
$livestamps = $([]),
|
||||
|
||||
init = function() {
|
||||
livestampGlobal.resume();
|
||||
},
|
||||
|
||||
prep = function($el, timestamp) {
|
||||
var oldData = $el.data('livestampdata');
|
||||
if (typeof timestamp == 'number')
|
||||
timestamp *= 1e3;
|
||||
|
||||
$el.removeAttr('data-livestamp')
|
||||
.removeData('livestamp');
|
||||
|
||||
timestamp = moment(timestamp);
|
||||
if (timestamp.diff(moment(new Date())) < 60 * 60) {
|
||||
return;
|
||||
}
|
||||
if (moment.isMoment(timestamp) && !isNaN(+timestamp)) {
|
||||
var newData = $.extend({ }, { 'original': $el.contents() }, oldData);
|
||||
newData.moment = moment(timestamp);
|
||||
|
||||
$el.data('livestampdata', newData).empty();
|
||||
$livestamps.push($el[0]);
|
||||
}
|
||||
},
|
||||
|
||||
run = function() {
|
||||
if (paused) return;
|
||||
livestampGlobal.update();
|
||||
setTimeout(run, updateInterval);
|
||||
},
|
||||
|
||||
livestampGlobal = {
|
||||
update: function() {
|
||||
$('[data-humantime]').each(function() {
|
||||
var $this = $(this);
|
||||
prep($this, $this.attr('datetime'));
|
||||
});
|
||||
|
||||
var toRemove = [];
|
||||
$livestamps.each(function() {
|
||||
var $this = $(this),
|
||||
data = $this.data('livestampdata');
|
||||
|
||||
if (data === undefined)
|
||||
toRemove.push(this);
|
||||
else if (moment.isMoment(data.moment)) {
|
||||
var from = $this.html(),
|
||||
to = humanTime(data.moment);
|
||||
// to = data.moment.fromNow();
|
||||
|
||||
if (from != to) {
|
||||
var e = $.Event('change.livestamp');
|
||||
$this.trigger(e, [from, to]);
|
||||
if (!e.isDefaultPrevented())
|
||||
$this.html(to);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$livestamps = $livestamps.not(toRemove);
|
||||
},
|
||||
|
||||
pause: function() {
|
||||
paused = true;
|
||||
},
|
||||
|
||||
resume: function() {
|
||||
paused = false;
|
||||
run();
|
||||
},
|
||||
|
||||
interval: function(interval) {
|
||||
if (interval === undefined)
|
||||
return updateInterval;
|
||||
updateInterval = interval;
|
||||
}
|
||||
},
|
||||
|
||||
livestampLocal = {
|
||||
add: function($el, timestamp) {
|
||||
if (typeof timestamp == 'number')
|
||||
timestamp *= 1e3;
|
||||
timestamp = moment(timestamp);
|
||||
|
||||
if (moment.isMoment(timestamp) && !isNaN(+timestamp)) {
|
||||
$el.each(function() {
|
||||
prep($(this), timestamp);
|
||||
});
|
||||
livestampGlobal.update();
|
||||
}
|
||||
|
||||
return $el;
|
||||
},
|
||||
|
||||
destroy: function($el) {
|
||||
$livestamps = $livestamps.not($el);
|
||||
$el.each(function() {
|
||||
var $this = $(this),
|
||||
data = $this.data('livestampdata');
|
||||
|
||||
if (data === undefined)
|
||||
return $el;
|
||||
|
||||
$this
|
||||
.html(data.original ? data.original : '')
|
||||
.removeData('livestampdata');
|
||||
});
|
||||
|
||||
return $el;
|
||||
},
|
||||
|
||||
isLivestamp: function($el) {
|
||||
return $el.data('livestampdata') !== undefined;
|
||||
}
|
||||
};
|
||||
|
||||
$.livestamp = livestampGlobal;
|
||||
$(init);
|
||||
$.fn.livestamp = function(method, options) {
|
||||
if (!livestampLocal[method]) {
|
||||
options = method;
|
||||
method = 'add';
|
||||
}
|
||||
|
||||
return livestampLocal[method](this, options);
|
||||
};
|
||||
})(jQuery, moment);
|
||||
|
||||
}
|
||||
};
|
|
@ -1,10 +0,0 @@
|
|||
export default {
|
||||
name: 'inject-components',
|
||||
initialize: function(container, application) {
|
||||
application.inject('adapter', 'alerts', 'controller:alerts')
|
||||
application.inject('component', 'alerts', 'controller:alerts')
|
||||
application.inject('model', 'session', 'simple-auth-session:main')
|
||||
application.inject('component', 'session', 'simple-auth-session:main')
|
||||
application.inject('component', 'store', 'store:main')
|
||||
}
|
||||
};
|
|
@ -1,20 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default {
|
||||
name: 'preload-data',
|
||||
after: 'ember-data',
|
||||
initialize: function(container) {
|
||||
var store = container.lookup('store:main');
|
||||
if (!Ember.isEmpty(FLARUM_DATA)) {
|
||||
store.pushPayload({included: FLARUM_DATA});
|
||||
}
|
||||
if (!Ember.isEmpty(FLARUM_SESSION)) {
|
||||
FLARUM_SESSION.user = store.getById('user', FLARUM_SESSION.userId);
|
||||
container.lookup('simple-auth-session:main').setProperties({
|
||||
isAuthenticated: true,
|
||||
authenticator: 'authenticator:flarum',
|
||||
content: FLARUM_SESSION
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
|
@ -1,16 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Mixin.create({
|
||||
activate: function() {
|
||||
var cssClass = this.toCssClass();
|
||||
Ember.$('body').addClass(cssClass);
|
||||
},
|
||||
|
||||
deactivate: function() {
|
||||
Ember.$('body').removeClass(this.toCssClass());
|
||||
},
|
||||
|
||||
toCssClass: function() {
|
||||
return this.routeName.replace(/\./g, '-').dasherize();
|
||||
}
|
||||
});
|
|
@ -1,14 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Mixin.create({
|
||||
fadeIn: Ember.on('didInsertElement', function() {
|
||||
var $this = this.$();
|
||||
var targetOpacity = $this.css('opacity');
|
||||
$this.css('opacity', 0);
|
||||
setTimeout(function() {
|
||||
$this.animate({opacity: targetOpacity}, 'fast', function() {
|
||||
$this.css('opacity', '');
|
||||
});
|
||||
}, 100);
|
||||
})
|
||||
});
|
|
@ -1,62 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
import TaggedArray from '../utils/tagged-array';
|
||||
import ActionButton from '../components/ui/action-button';
|
||||
import SeparatorItem from '../components/ui/separator-item';
|
||||
|
||||
export default Ember.Mixin.create({
|
||||
itemLists: [],
|
||||
|
||||
initItemLists: Ember.on('init', function() {
|
||||
var self = this;
|
||||
this.get('itemLists').forEach(function(name) {
|
||||
self.initItemList(name);
|
||||
});
|
||||
}),
|
||||
|
||||
initItemList: function(name) {
|
||||
this.set(name, this.populateItemList(name));
|
||||
},
|
||||
|
||||
populateItemList: function(name) {
|
||||
var items = TaggedArray.create();
|
||||
this.trigger('populate'+name.charAt(0).toUpperCase()+name.slice(1), items);
|
||||
this.removeUnneededSeparatorItems(items);
|
||||
return items;
|
||||
},
|
||||
|
||||
addActionItem: function(items, tag, label, icon, conditionProperty, action) {
|
||||
if (conditionProperty && !this.get(conditionProperty)) { return; }
|
||||
|
||||
var self = this;
|
||||
var item = ActionButton.extend({
|
||||
label: label,
|
||||
icon: icon,
|
||||
action: action || function() {
|
||||
self.get('controller').send(tag);
|
||||
}
|
||||
});
|
||||
|
||||
items.pushObjectWithTag(item, tag);
|
||||
|
||||
return item;
|
||||
},
|
||||
|
||||
addSeparatorItem: function(items) {
|
||||
items.pushObject(SeparatorItem);
|
||||
},
|
||||
|
||||
removeUnneededSeparatorItems: function(items) {
|
||||
var prevItem = null;
|
||||
items.forEach(function(item) {
|
||||
if (prevItem === SeparatorItem && item === SeparatorItem) {
|
||||
items.removeObject(item);
|
||||
return;
|
||||
}
|
||||
prevItem = item;
|
||||
});
|
||||
if (prevItem === SeparatorItem) {
|
||||
items.removeObject(prevItem);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Mixin.create(Ember.Evented, {
|
||||
actions: {
|
||||
focus: function() {
|
||||
this.trigger('focus');
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Mixin.create({
|
||||
focusEventOn: Ember.on('didInsertElement', function() {
|
||||
this.get('controller').on('focus', this, this.focus);
|
||||
}),
|
||||
|
||||
focusEventOff: Ember.on('willDestroyElement', function() {
|
||||
this.get('controller').off('focus', this, this.focus);
|
||||
}),
|
||||
|
||||
focus: Ember.on('didInsertElement', function() {
|
||||
this.$('input:first:visible:enabled').focus();
|
||||
})
|
||||
});
|
|
@ -1,11 +0,0 @@
|
|||
import DS from 'ember-data';
|
||||
|
||||
export default DS.Model.extend({
|
||||
contentType: DS.attr('string'),
|
||||
content: DS.attr(),
|
||||
time: DS.attr('date'),
|
||||
|
||||
user: DS.belongsTo('user'),
|
||||
sender: DS.belongsTo('user'),
|
||||
post: DS.belongsTo('post')
|
||||
});
|
|
@ -1,7 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.ObjectProxy.extend({
|
||||
relevantPosts: null,
|
||||
startPost: null,
|
||||
lastPost: null
|
||||
});
|
|
@ -1,73 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
import DS from 'ember-data';
|
||||
|
||||
import HasItemLists from '../mixins/has-item-lists';
|
||||
import Subject from './subject';
|
||||
|
||||
export default Subject.extend(HasItemLists, {
|
||||
/**
|
||||
Define a "badges" item list. Example usage:
|
||||
```
|
||||
populateBadges: function(items) {
|
||||
items.pushObjectWithTag(BadgeButton.extend({
|
||||
label: 'Sticky',
|
||||
icon: 'thumb-tack',
|
||||
className: 'badge-sticky',
|
||||
discussion: this,
|
||||
isHiddenInList: Ember.computed.not('discussion.sticky')
|
||||
}), 'sticky');
|
||||
}
|
||||
```
|
||||
*/
|
||||
itemLists: ['badges'],
|
||||
|
||||
title: DS.attr('string'),
|
||||
slug: Ember.computed('title', function() {
|
||||
return this.get('title').toLowerCase().replace(/[^a-z0-9]/gi, '-').replace(/-+/g, '-').replace(/-$|^-/g, '');
|
||||
}),
|
||||
|
||||
startTime: DS.attr('date'),
|
||||
startUser: DS.belongsTo('user'),
|
||||
startPost: DS.belongsTo('post'),
|
||||
|
||||
lastTime: DS.attr('date'),
|
||||
lastUser: DS.belongsTo('user'),
|
||||
lastPost: DS.belongsTo('post'),
|
||||
lastPostNumber: DS.attr('number'),
|
||||
|
||||
canReply: DS.attr('boolean'),
|
||||
canEdit: DS.attr('boolean'),
|
||||
canDelete: DS.attr('boolean'),
|
||||
|
||||
commentsCount: DS.attr('number'),
|
||||
repliesCount: Ember.computed('commentsCount', function() {
|
||||
return Math.max(0, this.get('commentsCount') - 1);
|
||||
}),
|
||||
|
||||
// The API returns the `posts` relationship as a list of IDs. To hydrate a
|
||||
// post-stream object, we're only interested in obtaining a list of IDs, so
|
||||
// we make it a string and then split it by comma. Instead, we'll put a
|
||||
// relationship on `loadedPosts`.
|
||||
// posts: DS.attr('string'),
|
||||
posts: DS.hasMany('post', {async: true}),
|
||||
postIds: Ember.computed(function() {
|
||||
var ids = [];
|
||||
this.get('data.posts').forEach(function(post) {
|
||||
ids.push(post.id);
|
||||
});
|
||||
return ids;
|
||||
}),
|
||||
loadedPosts: DS.hasMany('post'),
|
||||
relevantPosts: DS.hasMany('post'),
|
||||
addedPosts: DS.hasMany('post'),
|
||||
|
||||
readTime: DS.attr('date'),
|
||||
readNumber: DS.attr('number'),
|
||||
unreadCount: Ember.computed('lastPostNumber', 'readNumber', 'session.user.readTime', function() {
|
||||
return this.get('session.user.readTime') < this.get('lastTime') ? Math.max(0, this.get('lastPostNumber') - (this.get('readNumber') || 0)) : 0;
|
||||
}),
|
||||
isUnread: Ember.computed.bool('unreadCount'),
|
||||
|
||||
// Only used to save a new discussion
|
||||
content: DS.attr('string')
|
||||
});
|
|
@ -1,6 +0,0 @@
|
|||
import DS from 'ember-data';
|
||||
|
||||
export default DS.Model.extend({
|
||||
name: DS.attr('string'),
|
||||
users: DS.hasMany('group'),
|
||||
});
|
|
@ -1,21 +0,0 @@
|
|||
import DS from 'ember-data';
|
||||
|
||||
export default DS.Model.extend({
|
||||
contentType: DS.attr('string'),
|
||||
subjectId: DS.attr('number'),
|
||||
content: DS.attr(),
|
||||
time: DS.attr('date'),
|
||||
isRead: DS.attr('boolean'),
|
||||
unreadCount: DS.attr('number'),
|
||||
additionalUnreadCount: Ember.computed('unreadCount', function() {
|
||||
return Math.max(0, this.get('unreadCount') - 1);
|
||||
}),
|
||||
|
||||
decodedContent: Ember.computed('content', function() {
|
||||
return JSON.parse(this.get('content'));
|
||||
}),
|
||||
|
||||
user: DS.belongsTo('user'),
|
||||
sender: DS.belongsTo('user'),
|
||||
subject: DS.belongsTo('subject', {polymorphic: true})
|
||||
});
|
|
@ -1,22 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
var PostResult = Ember.ObjectProxy.extend({
|
||||
|
||||
relevantContent: ''
|
||||
|
||||
});
|
||||
|
||||
PostResult.reopenClass({
|
||||
create: function(post) {
|
||||
if (!post) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = this._super();
|
||||
result.set('content', post);
|
||||
result.set('relevantContent', post.get('content'));
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
||||
export default PostResult;
|
|
@ -1,209 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
/**
|
||||
The post stream is an object which represents the posts in a discussion as
|
||||
they are displayed on the discussion page, from top to bottom. ...
|
||||
*/
|
||||
export default Ember.ArrayProxy.extend(Ember.Evented, {
|
||||
|
||||
// An array of all of the post IDs, in chronological order, in the discussion.
|
||||
ids: null,
|
||||
|
||||
content: null,
|
||||
|
||||
store: null,
|
||||
discussion: null,
|
||||
|
||||
postLoadCount: 20,
|
||||
|
||||
count: Ember.computed.alias('ids.length'),
|
||||
|
||||
loadedCount: Ember.computed('content.@each', function() {
|
||||
return this.get('content').filterBy('content').length;
|
||||
}),
|
||||
|
||||
firstLoaded: Ember.computed('content.@each', function() {
|
||||
var first = this.objectAt(0);
|
||||
return first && first.content;
|
||||
}),
|
||||
|
||||
lastLoaded: Ember.computed('content.@each', function() {
|
||||
var last = this.objectAt(this.get('length') - 1);
|
||||
return last && last.content;
|
||||
}),
|
||||
|
||||
init: function() {
|
||||
this._super();
|
||||
this.set('ids', Ember.A());
|
||||
this.clear();
|
||||
},
|
||||
|
||||
setup: function(ids) {
|
||||
// Set our ids to the array provided and reset the content of the
|
||||
// stream to a big gap that covers the amount of posts we now have.
|
||||
this.set('ids', ids);
|
||||
this.clear();
|
||||
},
|
||||
|
||||
// Clear the contents of the post stream, resetting it to one big gap.
|
||||
clear: function() {
|
||||
var content = Ember.A();
|
||||
content.clear().pushObject(this.makeItem(0, this.get('count') - 1).set('loading', true));
|
||||
this.set('content', content);
|
||||
},
|
||||
|
||||
loadRange: function(start, end, backwards) {
|
||||
var limit = this.get('postLoadCount');
|
||||
|
||||
// Find the appropriate gap objects in the post stream. When we find
|
||||
// one, we will turn on its loading flag.
|
||||
this.get('content').forEach(function(item) {
|
||||
if (!item.content && ((item.indexStart >= start && item.indexStart <= end) || (item.indexEnd >= start && item.indexEnd <= end))) {
|
||||
item.set('loading', true);
|
||||
item.set('direction', backwards ? 'up' : 'down');
|
||||
}
|
||||
});
|
||||
|
||||
// Get a list of post numbers that we'll want to retrieve. If there are
|
||||
// more post IDs than the number of posts we want to load, then take a
|
||||
// slice of the array in the appropriate direction.
|
||||
var ids = this.get('ids').slice(start, end + 1);
|
||||
ids = backwards ? ids.slice(-limit) : ids.slice(0, limit);
|
||||
|
||||
return this.loadPosts(ids);
|
||||
},
|
||||
|
||||
loadPosts: function(ids) {
|
||||
if (!ids.length) {
|
||||
return Ember.RSVP.resolve();
|
||||
}
|
||||
|
||||
var stream = this;
|
||||
return this.store.find('post', {ids: ids}).then(function(posts) {
|
||||
stream.addPosts(posts);
|
||||
});
|
||||
},
|
||||
|
||||
loadNearNumber: function(number) {
|
||||
// Find the item in the post stream which is nearest to this number. If
|
||||
// it turns out the be the actual post we're trying to load, then we can
|
||||
// return a resolved promise (i.e. we don't need to make an API
|
||||
// request.) Or, if it's a gap, we'll switch on its loading flag.
|
||||
var item = this.findNearestToNumber(number);
|
||||
if (item) {
|
||||
if (item.get('content.number') == number) {
|
||||
return Ember.RSVP.resolve([item.get('content')]);
|
||||
} else if (! item.content) {
|
||||
item.set('direction', 'down').set('loading', true);
|
||||
}
|
||||
}
|
||||
|
||||
var stream = this;
|
||||
return this.store.find('post', {
|
||||
discussions: this.get('discussion.id'),
|
||||
near: number,
|
||||
count: this.get('postLoadCount')
|
||||
}).then(function(posts) {
|
||||
stream.addPosts(posts);
|
||||
});
|
||||
},
|
||||
|
||||
loadNearIndex: function(index, backwards) {
|
||||
// Find the item in the post stream which is nearest to this index. If
|
||||
// it turns out the be the actual post we're trying to load, then we can
|
||||
// return a resolved promise (i.e. we don't need to make an API
|
||||
// request.) Or, if it's a gap, we'll switch on its loading flag.
|
||||
var item = this.findNearestToIndex(index);
|
||||
if (item) {
|
||||
if (item.content) {
|
||||
return Ember.RSVP.resolve([item.get('content')]);
|
||||
}
|
||||
return this.loadRange(Math.max(item.indexStart, index - this.get('postLoadCount') / 2), item.indexEnd, backwards);
|
||||
}
|
||||
|
||||
return Ember.RSVP.reject();
|
||||
},
|
||||
|
||||
addPosts: function(posts) {
|
||||
this.trigger('postsLoaded', posts);
|
||||
|
||||
var stream = this;
|
||||
var content = this.get('content');
|
||||
content.beginPropertyChanges();
|
||||
posts.forEach(function(post) {
|
||||
stream.addPost(post);
|
||||
});
|
||||
content.endPropertyChanges();
|
||||
|
||||
this.trigger('postsAdded');
|
||||
},
|
||||
|
||||
addPost: function(post) {
|
||||
var index = this.get('ids').indexOf(post.get('id'));
|
||||
var content = this.get('content');
|
||||
var makeItem = this.makeItem;
|
||||
|
||||
// Here we loop through each item in the post stream, and find the gap
|
||||
// in which this post should be situated. When we find it, we can replace
|
||||
// it with the post, and new gaps either side if appropriate.
|
||||
content.some(function(item, i) {
|
||||
if (item.indexStart <= index && item.indexEnd >= index) {
|
||||
var newItems = [];
|
||||
if (item.indexStart < index) {
|
||||
newItems.push(makeItem(item.indexStart, index - 1));
|
||||
}
|
||||
newItems.push(makeItem(index, index, post));
|
||||
if (item.indexEnd > index) {
|
||||
newItems.push(makeItem(index + 1, item.indexEnd));
|
||||
}
|
||||
content.replace(i, 1, newItems);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
addPostToEnd: function(post) {
|
||||
this.get('ids').pushObject(post.get('id'));
|
||||
var index = this.get('count') - 1;
|
||||
this.get('content').pushObject(this.makeItem(index, index, post));
|
||||
},
|
||||
|
||||
removePost: function(post) {
|
||||
this.get('ids').removeObject(post.get('id'));
|
||||
var content = this.get('content');
|
||||
content.removeObject(content.findBy('content', post));
|
||||
},
|
||||
|
||||
makeItem: function(indexStart, indexEnd, post) {
|
||||
var item = Ember.Object.create({
|
||||
indexStart: indexStart,
|
||||
indexEnd: indexEnd
|
||||
});
|
||||
if (post) {
|
||||
item.setProperties({
|
||||
content: post,
|
||||
component: 'discussion/post-'+post.get('contentType')
|
||||
});
|
||||
}
|
||||
return item;
|
||||
},
|
||||
|
||||
findNearestTo: function(index, property) {
|
||||
var nearestItem;
|
||||
this.get('content').some(function(item) {
|
||||
if (item.get(property) > index) {
|
||||
return true;
|
||||
}
|
||||
nearestItem = item;
|
||||
});
|
||||
return nearestItem;
|
||||
},
|
||||
|
||||
findNearestToNumber: function(number) {
|
||||
return this.findNearestTo(number, 'content.number');
|
||||
},
|
||||
|
||||
findNearestToIndex: function(index) {
|
||||
return this.findNearestTo(index, 'indexStart');
|
||||
}
|
||||
});
|
|
@ -1,25 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
import DS from 'ember-data';
|
||||
import Subject from './subject';
|
||||
|
||||
export default Subject.extend({
|
||||
discussion: DS.belongsTo('discussion', {inverse: 'loadedPosts'}),
|
||||
number: DS.attr('number'),
|
||||
|
||||
time: DS.attr('date'),
|
||||
user: DS.belongsTo('user'),
|
||||
contentType: DS.attr('string'),
|
||||
content: DS.attr(),
|
||||
contentHtml: DS.attr('string'),
|
||||
|
||||
editTime: DS.attr('date'),
|
||||
editUser: DS.belongsTo('user'),
|
||||
isEdited: Ember.computed.notEmpty('editTime'),
|
||||
|
||||
hideTime: DS.attr('date'),
|
||||
hideUser: DS.belongsTo('user'),
|
||||
isHidden: DS.attr('boolean'),
|
||||
|
||||
canEdit: DS.attr('boolean'),
|
||||
canDelete: DS.attr('boolean')
|
||||
});
|
|
@ -1,5 +0,0 @@
|
|||
import DS from 'ember-data';
|
||||
|
||||
export default DS.Model.extend({
|
||||
notification: DS.belongsTo('notification')
|
||||
});
|
|
@ -1,36 +0,0 @@
|
|||
import DS from 'ember-data';
|
||||
|
||||
import HasItemLists from '../mixins/has-item-lists';
|
||||
import stringToColor from '../utils/string-to-color';
|
||||
|
||||
export default DS.Model.extend(HasItemLists, {
|
||||
itemLists: ['badges'],
|
||||
|
||||
username: DS.attr('string'),
|
||||
email: DS.attr('string'),
|
||||
password: DS.attr('string'),
|
||||
avatarUrl: DS.attr('string'),
|
||||
bio: DS.attr('string'),
|
||||
bioHtml: DS.attr('string'),
|
||||
preferences: DS.attr(),
|
||||
|
||||
groups: DS.hasMany('group'),
|
||||
|
||||
joinTime: DS.attr('date'),
|
||||
lastSeenTime: DS.attr('date'),
|
||||
online: Ember.computed('lastSeenTime', function() {
|
||||
return this.get('lastSeenTime') > moment().subtract(5, 'minutes').toDate();
|
||||
}),
|
||||
readTime: DS.attr('date'),
|
||||
unreadNotificationsCount: DS.attr('number'),
|
||||
|
||||
discussionsCount: DS.attr('number'),
|
||||
commentsCount: DS.attr('number'),
|
||||
|
||||
canEdit: DS.attr('boolean'),
|
||||
canDelete: DS.attr('boolean'),
|
||||
|
||||
color: Ember.computed('username', function() {
|
||||
return '#'+stringToColor(this.get('username'));
|
||||
})
|
||||
});
|
|
@ -1,5 +0,0 @@
|
|||
import JsonApiSerializer from 'ember-json-api/json-api-serializer';
|
||||
|
||||
export default JsonApiSerializer.extend({
|
||||
store: Ember.inject.service()
|
||||
});
|
|
@ -1,16 +0,0 @@
|
|||
import ApplicationSerializer from '../serializers/application';
|
||||
|
||||
export default ApplicationSerializer.extend({
|
||||
attrs: {
|
||||
number: {serialize: false},
|
||||
time: {serialize: false},
|
||||
type: {serialize: false},
|
||||
contentHtml: {serialize: false},
|
||||
editTime: {serialize: false},
|
||||
editUser: {serialize: false},
|
||||
hideTime: {serialize: false},
|
||||
hideUser: {serialize: false},
|
||||
canEdit: {serialize: false},
|
||||
canDelete: {serialize: false}
|
||||
}
|
||||
});
|
|
@ -1,7 +0,0 @@
|
|||
<div class="alerts">
|
||||
{{#each alert in alerts}}
|
||||
<div class="alert-wrapper">
|
||||
{{view alert dismiss="dismissAlert"}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user