mirror of
https://github.com/flarum/framework.git
synced 2025-01-19 07:42:48 +08:00
Hello world!
This commit is contained in:
commit
b5a246865d
6
framework/core/.gitignore
vendored
Normal file
6
framework/core/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
/vendor
|
||||
composer.phar
|
||||
composer.lock
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
public/*
|
12
framework/core/.travis.yml
Normal file
12
framework/core/.travis.yml
Normal file
|
@ -0,0 +1,12 @@
|
|||
language: php
|
||||
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
|
||||
before_script:
|
||||
- curl -s http://getcomposer.org/installer | php
|
||||
- php composer.phar install --dev
|
||||
|
||||
script: phpunit
|
21
framework/core/LICENSE.txt
Normal file
21
framework/core/LICENSE.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Toby Zerner
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
162
framework/core/README.md
Normal file
162
framework/core/README.md
Normal file
|
@ -0,0 +1,162 @@
|
|||
**Together, let's build amazing PHP forum software.**
|
||||
|
||||
I’m [Toby Zerner](http://tobyzerner.com), the developer of [esoTalk](http://esotalk.org). Years ago, I built esoTalk as a fresh, lightweight forum software alternative. esoTalk is nice but it is [not built on a sustainable foundation](http://esotalk.org/blog/faq.html).
|
||||
|
||||
**As it stands, there is a need for modern, well-architected, powerful forum software that is easy to use and self-host.** That’s what Flarum is. But I'm a full-time student and I don't have time to do this by myself. So I'm opening up Flarum to the world — let's build it together.
|
||||
|
||||
## Philosophy
|
||||
|
||||
I have a vision for Flarum — one that has grown over years of playing with forums, developing esoTalk, and learning from mistakes. It is captured by the following four points:
|
||||
|
||||
- **Modern design.** Beautiful, clean, customizable. Forum design hasn’t evolved with the rest of the web; let's finally bring it up to speed.
|
||||
- **Unopinionated feature-set.** Every community is different, and forum software should be able to adapt to — rather than define — how a community is run. As such, Flarum will have a lightweight core, and most features will be implemented as optional extensions.
|
||||
- **High performance.** Flarum should perform well on scales big and small, and on a large range of devices.
|
||||
- **Sustainable architecture.** Flarum should be built for the future, on a strong foundation which can evolve with the technologies that power it.
|
||||
|
||||
Flarum is open-source software released under the [MIT license](https://github.com/flarum/core/blob/master/LICENSE.txt).
|
||||
|
||||
## Technology
|
||||
|
||||
I've carefully considered which frameworks to use to build Flarum. Here's the reasoning:
|
||||
|
||||
### Laravel
|
||||
|
||||
PHP remains the most user-friendly language for deploying web scripts, especially on shared hosting. The Laravel framework will allow for rapid development of Flarum’s API, and has a large community which will encourage collaboration and evolution.
|
||||
|
||||
### Ember.js
|
||||
|
||||
Ember.js is a mature JavaScript framework which will power Flarum’s front-end. Use of a JavaScript framework allows us to build a fast, dynamic interface which feels more like an app than a simple web page.
|
||||
|
||||
> Don't like the fact that Flarum is an Ember.js app? Take a look at [FluxBB 2](https://github.com/fluxbb/fluxbb/tree/2.0), which is being developed in a more traditional manner with Laravel.
|
||||
|
||||
## Current State
|
||||
|
||||
I’ve been working on a prototype for some time in-between my studies. In addition to interface design, most of my time has been spent building out the architecture: making decisions about which frameworks to use, the most effective way to componentize everything, standardizing the API, etc.
|
||||
|
||||
### What’s Done
|
||||
|
||||
- [x] The basic technology stack (Laravel and Ember – see above)
|
||||
- [x] The [architectural foundation](https://github.com/flarum/core/wiki/Architecture) (core/API/web layers)
|
||||
- [x] Some of the API (discussion and post read + write)
|
||||
- [x] Discussion list view and basic search functionality
|
||||
- [x] Discussion viewing and scrolling
|
||||
|
||||
### What’s Next
|
||||
|
||||
The priority at the moment is to build out a lightweight core, and only start building [Extensions](https://github.com/flarum/core/wiki/Extensions) when it is relatively stable. Below is a list of the things to work on immediately, with links to the relevant discussion.
|
||||
|
||||
- [ ] Interface redesign (#1)
|
||||
- [ ] Upgrade to Laravel 5 (#2)
|
||||
- [ ] Set up testing frameworks in both Laravel (#3) and Ember (#4)
|
||||
- [ ] Further consolidation of Extension interfaces (see Extensions)
|
||||
- [ ] Develop user authentication strategy (#5)
|
||||
- [ ] Implement replying, post editing, discussion creation (#6)
|
||||
- [ ] Implement discussion title editing (#7)
|
||||
- [ ] Implement post deletion (#8)
|
||||
- [ ] Implement discussion deletion (#9)
|
||||
- [ ] Build Notifications system (#10)
|
||||
- [ ] Design user profile interface (#11)
|
||||
- [ ] Design admin interfaces (#12)
|
||||
|
||||
For a full list of planned features, see [Features](https://github.com/flarum/core/wiki/Features).
|
||||
|
||||
## Installation
|
||||
|
||||
Currently Flarum is in its very early stages, and it isn’t pretty. **It is far from usable.** Set it up only if you know what you’re doing, and expect it to break a lot.
|
||||
|
||||
1. Make sure you have [Composer](http://getcomposer.org) and [ember-cli](http://www.ember-cli.com) installed globally.
|
||||
2. Create a new [Laravel 4](http://laravel.com/docs/4.2/quick) project.
|
||||
3. Run the following command in your project directory:
|
||||
|
||||
composer require flarum/core
|
||||
|
||||
4. Create a new MySQL database and enter your details into `app/config/packages/flarum/core/config.php`.
|
||||
5. Run the Flarum migrations and database seeder to generate dummy data:
|
||||
|
||||
php artisan migrate
|
||||
php artisan db:seed --class="Flarum\Core\Support\DatabaseSeeder"
|
||||
|
||||
6. Run the following commands to compile the Ember app:
|
||||
|
||||
cd vendor/flarum/core/ember
|
||||
ember serve --output-path="../public"
|
||||
|
||||
7. Visit your Laravel application in a browser.
|
||||
|
||||
> Note: You must access the Laravel application so that it is at the top level (i.e. not under any sub-directories.) To do this, you can either make your web server's document root the `public` folder of your application, or you can [configure a virtual host](http://davidwalsh.name/create-virtual-host) pointing to the `public` folder.
|
||||
|
||||
If you’re having trouble, **do not** create a new issue — instead, get help on the [Flarum Development Forum](http://discuss.flarum.org).
|
||||
|
||||
## Contributing
|
||||
|
||||
Building Flarum is going to be a team effort, and we'd love for you to help! All contributions are welcomed.
|
||||
|
||||
### What Can I Do?
|
||||
|
||||
- **Contribute code.** Start by becoming familiar with Flarum's source code and its [Architecture](https://github.com/flarum/core/wiki/Architecture). Then have a look at what needs to be done in the list above, and see if there's anything you can help out with. See below for instructions on submitting a Pull Request.
|
||||
|
||||
- **Participate in discussion.** Review the [wiki](https://github.com/flarum/core/wiki) and [issues](https://github.com/flarum/core/issues) and contribute your constructive thoughts. We'd also love to hear general feedback on the [Flarum Development Forum](http://discuss.flarum.org).
|
||||
|
||||
- **Spread the word.** Know someone who could help out? Please share this project with them!
|
||||
|
||||
> In this early stage of development, bug reports won't be particularly helpful, because things will be constantly changing and breaking.
|
||||
|
||||
### Process
|
||||
|
||||
1. Review the [Flarum Contributor License Agreement](#contributor-license-agreement). ([Why?](https://julien.ponge.org/blog/in-defense-of-contributor-license-agreements/))
|
||||
|
||||
2. Install Flarum as detailed in the instructions above.
|
||||
|
||||
3. Create a new branch.
|
||||
|
||||
git checkout -b new-flarum-branch
|
||||
|
||||
> Please implement only one feature/bugfix per branch to keep pull requests clean and focused.
|
||||
|
||||
4. Code.
|
||||
- Follow the coding style: [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md).
|
||||
- Include tests and make sure they pass (subject to #3 and #4).
|
||||
|
||||
5. Commit.
|
||||
- Commit messages are **required**.
|
||||
- They should include a short description of the changes on the first line, then a blank line, then more details if necessary.
|
||||
|
||||
6. Clean up. Squash together minor commits.
|
||||
|
||||
git rebase -i
|
||||
|
||||
7. Update your branch so that it is based on top of the latest code from the Flarum repository.
|
||||
|
||||
git fetch origin
|
||||
git rebase origin/master
|
||||
|
||||
8. Fork your repository on GitHub and push to it.
|
||||
|
||||
git remote add mine git@github.com:<your user name>/flarum.git
|
||||
git push mine new-flarum-branch
|
||||
|
||||
9. Submit a pull request.
|
||||
- Go to the Flarum repository you just pushed to (e.g. https://github.com/your-user-name/flarum).
|
||||
- Click "Pull Request".
|
||||
- Write your branch name in the branch field.
|
||||
- Click "Update Commit Range".
|
||||
- Ensure that the correct commits and files changes are included.
|
||||
- Fill in a descriptive title and other details about your pull request.
|
||||
- Click "Send pull request".
|
||||
|
||||
10. Respond to feedback.
|
||||
- We may suggest changes to your code. Maintaining a high standard of code quality is important for the longevity of this project — use it as an opportunity to improve your own skills and learn something new!
|
||||
|
||||
### Core Team
|
||||
|
||||
Currently the only person on the core development team is Toby Zerner ([@tobscure](http://twitter.com/tobscure)). Over time, judged by display of commitment to the project, and quantity/quality of contributions, I will be looking for more people to join the core development team. Please do not email me asking to be on the core team; rather, demonstrate initiative and commitment to the project and I will notice!
|
||||
|
||||
### Contributor License Agreement
|
||||
|
||||
By contributing your code to Flarum you grant Toby Zerner a non-exclusive, irrevocable, worldwide, royalty-free, sublicenseable, transferable license under all of Your relevant intellectual property rights (including copyright, patent, and any other rights), to use, copy, prepare derivative works of, distribute and publicly perform and display the Contributions on any licensing terms, including without limitation: (a) open source licenses like the MIT license; and (b) binary, proprietary, or commercial licenses. Except for the licenses granted herein, You reserve all right, title, and interest in and to the Contribution.
|
||||
|
||||
You confirm that you are able to grant us these rights. You represent that You are legally entitled to grant the above license. If Your employer has rights to intellectual property that You create, You represent that You have received permission to make the Contributions on behalf of that employer, or that Your employer has waived such rights for the Contributions.
|
||||
|
||||
You represent that the Contributions are Your original works of authorship, and to Your knowledge, no other person claims, or has the right to claim, any right in any invention or patent related to the Contributions. You also represent that You are not legally obligated, whether by entering into an agreement or otherwise, in any way that conflicts with the terms of this license.
|
||||
|
||||
Toby Zerner acknowledges that, except as explicitly described in this Agreement, any Contribution which you provide is on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
|
33
framework/core/composer.json
Normal file
33
framework/core/composer.json
Normal file
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "flarum/core",
|
||||
"description": "",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Toby Zerner",
|
||||
"email": "toby@flarum.org"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.4.0",
|
||||
"illuminate/support": "4.2.*",
|
||||
"laracasts/commander": "1.1.*",
|
||||
"leafo/lessphp": "0.4.0",
|
||||
"fzaninotto/faker": "1.4.0"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"src/migrations"
|
||||
],
|
||||
"psr-0": {
|
||||
"Flarum\\Core": "src/",
|
||||
"Flarum\\Api": "src/",
|
||||
"Flarum\\Web": "src/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"post-install-cmd": [
|
||||
"php artisan config:publish flarum/core"
|
||||
]
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
3
framework/core/ember/.bowerrc
Normal file
3
framework/core/ember/.bowerrc
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"directory": "vendor"
|
||||
}
|
17
framework/core/ember/.gitignore
vendored
Normal file
17
framework/core/ember/.gitignore
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/tmp
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/vendor/*
|
||||
|
||||
# misc
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage/*
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
testem.log
|
32
framework/core/ember/.jshintrc
Normal file
32
framework/core/ember/.jshintrc
Normal file
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"predef": {
|
||||
"document": true,
|
||||
"window": true,
|
||||
"FlarumENV": true
|
||||
},
|
||||
"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
|
||||
}
|
20
framework/core/ember/Brocfile.js
Normal file
20
framework/core/ember/Brocfile.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
/* global require, module */
|
||||
|
||||
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
|
||||
|
||||
var app = new EmberApp();
|
||||
|
||||
app.import('vendor/bootstrap/dist/js/bootstrap.js');
|
||||
app.import('vendor/spin.js/spin.js');
|
||||
app.import('vendor/spin.js/jquery.spin.js');
|
||||
app.import('vendor/moment/moment.js');
|
||||
app.import('vendor/jquery-scrollparent/jquery.scrollparent.js');
|
||||
app.import('vendor/json-api.js');
|
||||
|
||||
app.import('vendor/font-awesome/fonts/fontawesome-webfont.eot');
|
||||
app.import('vendor/font-awesome/fonts/fontawesome-webfont.svg');
|
||||
app.import('vendor/font-awesome/fonts/fontawesome-webfont.ttf');
|
||||
app.import('vendor/font-awesome/fonts/fontawesome-webfont.woff');
|
||||
app.import('vendor/font-awesome/fonts/FontAwesome.otf');
|
||||
|
||||
module.exports = app.toTree();
|
25
framework/core/ember/README.md
Normal file
25
framework/core/ember/README.md
Normal file
|
@ -0,0 +1,25 @@
|
|||
# Flarum
|
||||
|
||||
This README outlines the details of collaborating on this Ember application.
|
||||
|
||||
## Installation
|
||||
|
||||
* `git clone` this repository
|
||||
* `npm install`
|
||||
* `bower install`
|
||||
|
||||
## Running
|
||||
|
||||
* `ember server`
|
||||
* Visit your app at http://localhost:4200.
|
||||
|
||||
## Running Tests
|
||||
|
||||
* `ember test`
|
||||
* `ember test --server`
|
||||
|
||||
## Building
|
||||
|
||||
* `ember build`
|
||||
|
||||
For more information on using ember-cli, visit [http://iamstef.net/ember-cli/](http://iamstef.net/ember-cli/).
|
26
framework/core/ember/app/adapters/application.js
Normal file
26
framework/core/ember/app/adapters/application.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
import Ember from 'ember';
|
||||
import DS from 'ember-data';
|
||||
|
||||
export default DS.JsonApiAdapter.extend({
|
||||
host: '/api',
|
||||
|
||||
xhr: [],
|
||||
|
||||
ajax: function(url, type, hash) {
|
||||
var adapter = this;
|
||||
|
||||
return new Ember.RSVP.Promise(function(resolve, reject) {
|
||||
hash = adapter.ajaxOptions(url, type, hash);
|
||||
|
||||
hash.success = function(json) {
|
||||
Ember.run(null, resolve, json);
|
||||
};
|
||||
|
||||
hash.error = function(jqXHR, textStatus, errorThrown) {
|
||||
Ember.run(null, reject, adapter.ajaxError(jqXHR));
|
||||
};
|
||||
|
||||
adapter.xhr.push(Ember.$.ajax(hash));
|
||||
}, "DS: RestAdapter#ajax " + type + " to " + url);
|
||||
},
|
||||
});
|
81
framework/core/ember/app/app.js
Normal file
81
framework/core/ember/app/app.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
import Ember from 'ember';
|
||||
import Resolver from 'ember/resolver';
|
||||
import loadInitializers from 'ember/load-initializers';
|
||||
|
||||
Ember.MODEL_FACTORY_INJECTIONS = true;
|
||||
|
||||
var App = Ember.Application.extend({
|
||||
modulePrefix: 'flarum', // TODO: loaded via config
|
||||
Resolver: Resolver,
|
||||
|
||||
registerPlugin: function(plugin) {
|
||||
console.log('Plugin loaded: '+plugin.name);
|
||||
plugin.boot();
|
||||
}
|
||||
});
|
||||
|
||||
loadInitializers(App, 'flarum');
|
||||
|
||||
|
||||
//-----------------------------------------
|
||||
// TODO: Move all this to an initializer
|
||||
|
||||
/*
|
||||
import User from 'flarum/models/user';
|
||||
|
||||
// Authentication
|
||||
|
||||
import BaseAuthenticator from 'simple-auth/authenticators/base';
|
||||
|
||||
var FlarumAuthenticator = BaseAuthenticator.extend({
|
||||
restore: function(data) {
|
||||
// return Ember.RSVP.Promise.resolve(data);
|
||||
},
|
||||
authenticate: function(credentials) {
|
||||
return new Ember.RSVP.Promise(function(resolve, reject) {
|
||||
Ember.$.ajax({
|
||||
url: 'http://localhost/public/Flarum/flarum/public/api/auth',
|
||||
type: 'POST',
|
||||
data: { type: 'password', identification: credentials.identification, password: credentials.password }
|
||||
}).then(function(response) {
|
||||
resolve({ token: response.token, userId: response.user.id });
|
||||
}, function(xhr, status, error) {
|
||||
reject(xhr.responseText);
|
||||
});
|
||||
});
|
||||
},
|
||||
// invalidate: function() {
|
||||
// return Ember.RSVP.Promise.resolve();
|
||||
// }
|
||||
});
|
||||
|
||||
import BaseAuthorizer from 'simple-auth/authorizers/base';
|
||||
|
||||
var FlarumAuthorizer = BaseAuthorizer.extend({
|
||||
|
||||
});
|
||||
|
||||
App.initializer({
|
||||
name: 'authentication',
|
||||
initialize: function(container, application) {
|
||||
container.register('authenticator:flarum', FlarumAuthenticator);
|
||||
container.register('authorizer:flarum', FlarumAuthorizer);
|
||||
|
||||
// customize the session so that it allows access to the account object
|
||||
Ember.SimpleAuth.Session.reopen({
|
||||
user: function() {
|
||||
var userId = this.get('userId');
|
||||
if (!userId) return;
|
||||
return container.lookup('store:main').find('user', userId);
|
||||
}.property('userId')
|
||||
});
|
||||
|
||||
Ember.SimpleAuth.setup(container, application, {
|
||||
authorizerFactory: 'authorizer:flarum',
|
||||
routeAfterAuthentication: 'discussions'
|
||||
});
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
export default App;
|
0
framework/core/ember/app/components/.gitkeep
Normal file
0
framework/core/ember/app/components/.gitkeep
Normal file
18
framework/core/ember/app/components/button-item.js
Normal file
18
framework/core/ember/app/components/button-item.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.View.extend({
|
||||
title: '',
|
||||
icon: '',
|
||||
class: '',
|
||||
action: null,
|
||||
|
||||
tagName: 'a',
|
||||
classNames: ['btn'],
|
||||
classNameBindings: ['class', 'disabled'],
|
||||
|
||||
layout: Ember.Handlebars.compile('{{#if view.icon}}{{fa-icon view.icon class="fa-fw"}} {{/if}}<span>{{view.title}}</span>'),
|
||||
|
||||
click: function() {
|
||||
this.action();
|
||||
}
|
||||
});
|
6
framework/core/ember/app/components/item-collection.js
Normal file
6
framework/core/ember/app/components/item-collection.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
tagName: 'ul',
|
||||
layoutName: 'components/item-collection',
|
||||
});
|
13
framework/core/ember/app/components/loading-indicator.js
Normal file
13
framework/core/ember/app/components/loading-indicator.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
|
||||
classNames: ['loading'],
|
||||
|
||||
layout: Ember.Handlebars.compile(' '),
|
||||
|
||||
didInsertElement: function() {
|
||||
this.$().spin(this.get('size'));
|
||||
}
|
||||
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
liClass: 'divider'
|
||||
});
|
31
framework/core/ember/app/components/menu-item.js
Normal file
31
framework/core/ember/app/components/menu-item.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
var MenuItem = Ember.Component.extend({
|
||||
title: '',
|
||||
icon: '',
|
||||
className: '',
|
||||
action: null,
|
||||
divider: false,
|
||||
|
||||
tagName: 'a',
|
||||
attributeBindings: ['href'],
|
||||
classNameBindings: ['className'],
|
||||
href: '#',
|
||||
layout: Ember.Handlebars.compile('{{#if icon}}{{fa-icon icon class="fa-fw"}} {{/if}}<span>{{title}}</span>'),
|
||||
|
||||
click: function(e) {
|
||||
e.preventDefault();
|
||||
// this.sendAction('action');
|
||||
this.get('action')();
|
||||
}
|
||||
});
|
||||
|
||||
MenuItem.reopenClass({
|
||||
separator: function() {
|
||||
return this.create({
|
||||
divider: true
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
export default MenuItem;
|
6
framework/core/ember/app/components/menu-list.js
Normal file
6
framework/core/ember/app/components/menu-list.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
tagName: 'ul',
|
||||
layoutName: 'components/menu-list',
|
||||
});
|
15
framework/core/ember/app/components/menu-split.js
Normal file
15
framework/core/ember/app/components/menu-split.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
items: null, // NamedContainerView/Menu
|
||||
layoutName: 'components/menu-split',
|
||||
show: 1,
|
||||
|
||||
visibleItems: function() {
|
||||
return this.get('items').slice(0, this.get('show'));
|
||||
}.property('items'),
|
||||
|
||||
hiddenItems: function() {
|
||||
return this.get('items').slice(this.get('show'));
|
||||
}.property('items'),
|
||||
});
|
34
framework/core/ember/app/components/nav-item.js
Normal file
34
framework/core/ember/app/components/nav-item.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
icon: '',
|
||||
title: '',
|
||||
action: null,
|
||||
badge: '',
|
||||
badgeAction: null,
|
||||
// active: false,
|
||||
|
||||
tagName: 'li',
|
||||
classNameBindings: ['active'],
|
||||
active: function() {
|
||||
return this.get('childViews').anyBy('active');
|
||||
}.property('childViews.@each.active'),
|
||||
|
||||
layout: function() {
|
||||
return Ember.Handlebars.compile('<a href="#" class="count" {{action "badge"}}>{{badge}}</a>\
|
||||
{{#link-to '+this.get('linkTo')+'}}'+this.get('iconTemplate')+'{{title}}{{/link-to}}');
|
||||
}.property('linkTo', 'iconTemplate'),
|
||||
|
||||
iconTemplate: function() {
|
||||
return '{{fa-icon icon}}';
|
||||
}.property(),
|
||||
|
||||
actions: {
|
||||
main: function() {
|
||||
this.get('action')();
|
||||
},
|
||||
badge: function() {
|
||||
this.get('badgeAction')();
|
||||
}
|
||||
}
|
||||
});
|
9
framework/core/ember/app/components/notification-message.js
Executable file
9
framework/core/ember/app/components/notification-message.js
Executable file
|
@ -0,0 +1,9 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
|
||||
close: function() {
|
||||
this.sendAction('closeAction');
|
||||
}
|
||||
|
||||
});
|
38
framework/core/ember/app/components/search-input.js
Normal file
38
framework/core/ember/app/components/search-input.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['search-input'],
|
||||
classNameBindings: ['active', 'value:clearable'],
|
||||
|
||||
didInsertElement: function() {
|
||||
var self = this;
|
||||
this.$().find('input').on('keydown', function(e) {
|
||||
if (e.which == 27) {
|
||||
self.clear();
|
||||
}
|
||||
});
|
||||
this.$().find('.clear').on('mousedown', function(e) {
|
||||
e.preventDefault();
|
||||
}).on('click', function(e) {
|
||||
e.preventDefault();
|
||||
self.clear();
|
||||
})
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
this.set('value', '');
|
||||
this.sendAction('action', '');
|
||||
this.$().find('input').focus();
|
||||
},
|
||||
|
||||
willDestroyElement: function() {
|
||||
this.$().find('input').off('keydown');
|
||||
this.$().find('.clear').off('mousedown click');
|
||||
},
|
||||
|
||||
actions: {
|
||||
search: function() {
|
||||
this.sendAction('action', this.get('value'));
|
||||
}
|
||||
}
|
||||
});
|
9
framework/core/ember/app/components/select-input.js
Normal file
9
framework/core/ember/app/components/select-input.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.View.extend({
|
||||
|
||||
tagName: 'span',
|
||||
classNames: ['select'],
|
||||
layout: Ember.Handlebars.compile('{{view Ember.Select content=view.content optionValuePath=view.optionValuePath optionLabelPath=view.optionLabelPath value=view.value}} {{fa-icon "sort"}}')
|
||||
|
||||
});
|
0
framework/core/ember/app/controllers/.gitkeep
Normal file
0
framework/core/ember/app/controllers/.gitkeep
Normal file
48
framework/core/ember/app/controllers/application.js
Normal file
48
framework/core/ember/app/controllers/application.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
// import NotificationMessage from '../models/notification-message';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
|
||||
needs: ['discussions'],
|
||||
|
||||
// The title of the forum.
|
||||
// TODO: Preload this value in the index.html payload from Laravel config.
|
||||
forumTitle: 'Ninetech Support Forum',
|
||||
// forumTitle: '<img src="tv.png" height="24" style="vertical-align: baseline; margin-right: 5px"> TV Addicts',
|
||||
// forumTitle: '<img src="gametoaid.png" height="50">',
|
||||
// forumTitle: '<i class="fa fa-stethoscope" style="font-size: 140%"></i> Med Students Forum',
|
||||
pageTitle: '',
|
||||
documentTitle: function() {
|
||||
return this.get('pageTitle') || this.get('forumTitle');
|
||||
}.property('pageTitle', 'forumTitle'),
|
||||
|
||||
_updateTitle: function() {
|
||||
var parts = [this.get('forumTitle')];
|
||||
var pageTitle = this.get('pageTitle');
|
||||
if (pageTitle) parts.unshift(pageTitle);
|
||||
document.title = parts.join(' - ');
|
||||
}.observes('pageTitle', 'forumTitle'),
|
||||
|
||||
searchQuery: '',
|
||||
searchActive: false,
|
||||
|
||||
showDiscussionStream: false,
|
||||
|
||||
// notificationMessage: NotificationMessage.create({text: 'Sorry, you do not have permission to do that!', class: 'message-warning'}), // currently displaying notification message object
|
||||
|
||||
currentUser: null,
|
||||
|
||||
actions: {
|
||||
|
||||
hideMessage: function() {
|
||||
this.set('notificationMessage', null);
|
||||
},
|
||||
|
||||
search: function(query) {
|
||||
this.transitionToRoute('discussions', {queryParams: {searchQuery: query, sort: query ? 'relevance' : 'recent'}});
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
});
|
17
framework/core/ember/app/controllers/composer.js
Normal file
17
framework/core/ember/app/controllers/composer.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
|
||||
needs: ['discussions'],
|
||||
|
||||
showing: false,
|
||||
|
||||
title: 'Replying to <em>Some Discussion Title</em>',
|
||||
|
||||
actions: {
|
||||
close: function() {
|
||||
this.set('showing', false);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
103
framework/core/ember/app/controllers/discussion.js
Normal file
103
framework/core/ember/app/controllers/discussion.js
Normal file
|
@ -0,0 +1,103 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
import PostStream from '../models/post-stream';
|
||||
|
||||
export default Ember.ObjectController.extend(Ember.Evented, {
|
||||
|
||||
needs: ['application', 'composer'],
|
||||
|
||||
queryParams: ['start'],
|
||||
start: '1',
|
||||
searchQuery: '',
|
||||
|
||||
loaded: false,
|
||||
postStream: null,
|
||||
|
||||
setup: function(discussion) {
|
||||
this.set('model', discussion);
|
||||
|
||||
// Set up the post stream object. It needs to know about the discussion
|
||||
// its representing the posts for, and we also need to inject the Ember
|
||||
// data store.
|
||||
var postStream = PostStream.create();
|
||||
postStream.set('discussion', discussion);
|
||||
postStream.set('store', this.get('store'));
|
||||
this.set('postStream', postStream);
|
||||
|
||||
// Next, we need to load a list of the discussion's post IDs into the
|
||||
// post stream object. If we don't already have this information, we'll
|
||||
// need to reload the discussion model.
|
||||
var promise = discussion.get('posts') ? Ember.RSVP.resolve(discussion) : discussion.reload();
|
||||
|
||||
// When we know we have the post IDs, we can set up the post stream with
|
||||
// them. Then we're ready to load some posts!
|
||||
var controller = this;
|
||||
promise.then(function(discussion) {
|
||||
postStream.setup(discussion.get('postIds'));
|
||||
controller.set('loaded', true);
|
||||
controller.send('jumpToNumber', controller.get('start'));
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
||||
reply: function() {
|
||||
this.set('controllers.composer.showing', true);
|
||||
this.set('controllers.composer.title', 'Replying to <em>'+this.get('model.title')+'</em>');
|
||||
},
|
||||
|
||||
jumpToNumber: function(number) {
|
||||
// In some instances, we might be given a placeholder start index
|
||||
// value. We need to convert this into a numerical value.
|
||||
switch (number) {
|
||||
case 'last':
|
||||
number = this.get('model.lastPostNumber');
|
||||
break;
|
||||
|
||||
case 'unread':
|
||||
number = this.get('model.readNumber') + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
number = Math.max(number, 1);
|
||||
|
||||
// Let's start by telling our listeners that we're going to load
|
||||
// posts near this number. The discussion view will listen and
|
||||
// consequently scroll down to the appropriate position in the
|
||||
// discussion.
|
||||
this.trigger('loadingNumber', number);
|
||||
|
||||
// Now we have to actually make sure the posts around this new start
|
||||
// position are loaded. We will tell our listeners when they are.
|
||||
// Again, the view will scroll down to the appropriate post.
|
||||
var controller = this;
|
||||
this.get('postStream').loadNearNumber(number).then(function() {
|
||||
Ember.run.scheduleOnce('afterRender', function() {
|
||||
controller.trigger('loadedNumber', number);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
jumpToIndex: function(index) {
|
||||
// Let's start by telling our listeners that we're going to load
|
||||
// posts at this index. The discussion view will listen and
|
||||
// consequently scroll down to the appropriate position in the
|
||||
// discussion.
|
||||
this.trigger('loadingIndex', index);
|
||||
|
||||
// Now we have to actually make sure the posts around this index are
|
||||
// loaded. We will tell our listeners when they are. Again, the view
|
||||
// will scroll down to the appropriate post.
|
||||
var controller = this;
|
||||
this.get('postStream').loadNearIndex(index).then(function() {
|
||||
Ember.run.scheduleOnce('afterRender', function() {
|
||||
controller.trigger('loadedIndex', index);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
loadRange: function(start, end, backwards) {
|
||||
this.get('postStream').loadRange(start, end, backwards);
|
||||
}
|
||||
}
|
||||
});
|
175
framework/core/ember/app/controllers/discussions.js
Normal file
175
framework/core/ember/app/controllers/discussions.js
Normal file
|
@ -0,0 +1,175 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
import DiscussionResult from '../models/discussion-result';
|
||||
import PostResult from '../models/post-result';
|
||||
|
||||
export default Ember.ArrayController.extend(Ember.Evented, {
|
||||
|
||||
needs: ['application', 'composer'],
|
||||
|
||||
paned: false,
|
||||
paneShowing: false,
|
||||
paneTimeout: null,
|
||||
panePinned: false,
|
||||
|
||||
current: null,
|
||||
|
||||
index: function() {
|
||||
var index = '?';
|
||||
var id = this.get('current.id');
|
||||
this.get('model').some(function(result, i) {
|
||||
if (result.get('id') == id) {
|
||||
index = i + 1;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return index;
|
||||
}.property('current', 'model.@each'),
|
||||
|
||||
count: function() {
|
||||
return this.get('model.length');
|
||||
}.property('model.@each'),
|
||||
|
||||
previous: function() {
|
||||
var result = this.get('model').objectAt(this.get('index') - 2);
|
||||
return result && result.get('content');
|
||||
}.property('index'),
|
||||
|
||||
next: function() {
|
||||
var result = this.get('model').objectAt(this.get('index'));
|
||||
return result && result.get('content');
|
||||
}.property('index'),
|
||||
|
||||
queryParams: ['sort', 'show', {searchQuery: 'q'}, 'filter'],
|
||||
sort: 'recent',
|
||||
show: 'discussions',
|
||||
filter: '',
|
||||
|
||||
searchQuery: '',
|
||||
loadingMore: false,
|
||||
|
||||
sortOptions: [
|
||||
{sort: 'recent', label: 'Recent'},
|
||||
{sort: 'replies', label: 'Replies'},
|
||||
{sort: 'newest', label: 'Newest'},
|
||||
{sort: 'oldest', label: 'Oldest'},
|
||||
],
|
||||
|
||||
displayStartUsers: function() {
|
||||
return ['newest', 'oldest'].indexOf(this.get('sort')) != -1;
|
||||
}.property('sort'),
|
||||
|
||||
discussionsCount: function() {
|
||||
return this.get('model.length');
|
||||
}.property('@each'),
|
||||
|
||||
resultsLoading: false,
|
||||
|
||||
start: 0,
|
||||
|
||||
moreResults: function() {
|
||||
return !! this.get('meta.moreUrl');
|
||||
}.property('meta.moreUrl'),
|
||||
|
||||
meta: null,
|
||||
|
||||
getResults: function(start) {
|
||||
var sort = this.get('sort');
|
||||
// var order = this.get('order');
|
||||
var order;
|
||||
var show = this.get('show');
|
||||
var searchQuery = this.get('searchQuery');
|
||||
|
||||
if (sort == 'newest') sort = 'created';
|
||||
else if (sort == 'oldest') {
|
||||
sort = 'created';
|
||||
order = 'asc';
|
||||
}
|
||||
else if (sort == 'recent') {
|
||||
sort = '';
|
||||
}
|
||||
else if (sort == 'replies') {
|
||||
order = 'desc';
|
||||
}
|
||||
|
||||
var params = {
|
||||
sort: (order == 'desc' ? '-' : '')+sort,
|
||||
q: searchQuery,
|
||||
start: start
|
||||
};
|
||||
|
||||
if (show == 'posts') {
|
||||
if (searchQuery) params.include = 'relevantPosts';
|
||||
else if (sort == 'created') params.include = 'startPost,startUser';
|
||||
else params.include = 'lastPost,lastUser';
|
||||
}
|
||||
|
||||
return this.store.find('discussion', params).then(function(discussions) {
|
||||
var results = Em.A();
|
||||
discussions.forEach(function(discussion) {
|
||||
var relevantPosts = Em.A();
|
||||
discussion.get('relevantPosts.content').forEach(function(post) {
|
||||
relevantPosts.pushObject(PostResult.create(post));
|
||||
});
|
||||
results.pushObject(DiscussionResult.create({
|
||||
content: discussion,
|
||||
relevantPosts: relevantPosts,
|
||||
lastPost: PostResult.create(discussion.get('lastPost')),
|
||||
startPost: PostResult.create(discussion.get('startPost'))
|
||||
}));
|
||||
results.set('meta', discussions.get('meta'));
|
||||
});
|
||||
return results;
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
showDiscussionPane: function() {
|
||||
this.set('paneShowing', true);
|
||||
},
|
||||
|
||||
hideDiscussionPane: function() {
|
||||
this.set('paneShowing', false);
|
||||
},
|
||||
|
||||
togglePinned: function() {
|
||||
this.set('panePinned', ! this.get('panePinned'));
|
||||
},
|
||||
|
||||
loadMore: function() {
|
||||
var self = this;
|
||||
this.set('start', this.get('length'));
|
||||
this.set('loadingMore', true);
|
||||
|
||||
this.getResults(this.get('start')).then(function(results) {
|
||||
self.get('model').addObjects(results);
|
||||
self.set('meta', results.get('meta'));
|
||||
// self.set('moreResults', !! results.get('meta.moreUrl'));
|
||||
self.set('loadingMore', false);
|
||||
});
|
||||
},
|
||||
|
||||
delete: function(discussion) {
|
||||
alert('are you sure you want to delete discusn: '+discussion.get('title'));
|
||||
}
|
||||
},
|
||||
|
||||
queryDidChange: function(q) {
|
||||
this.get('controllers.application').set('searchQuery', this.get('searchQuery'));
|
||||
this.get('controllers.application').set('searchActive', !! this.get('searchQuery'));
|
||||
|
||||
var sortOptions = this.get('sortOptions');
|
||||
|
||||
if (this.get('searchQuery') && sortOptions[0].sort != 'relevance') {
|
||||
sortOptions.unshiftObject({sort: 'relevance', label: 'Relevance'});
|
||||
}
|
||||
else if ( ! this.get('searchQuery') && sortOptions[0].sort == 'relevance') {
|
||||
sortOptions.shiftObject();
|
||||
}
|
||||
}.observes('searchQuery'),
|
||||
|
||||
paramsDidChange: function(show) {
|
||||
this.set('start', 0);
|
||||
}.observes('show', 'sort', 'searchQuery')
|
||||
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.ArrayController.extend({
|
||||
needs: ['application', 'composer']
|
||||
});
|
9
framework/core/ember/app/controllers/login.js
Normal file
9
framework/core/ember/app/controllers/login.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
// import NotificationMessage from '../models/notification-message';
|
||||
|
||||
export default Ember.Controller.extend(Ember.SimpleAuth.LoginControllerMixin, Ember.Evented, {
|
||||
|
||||
authenticatorFactory: 'authenticator:flarum'
|
||||
|
||||
});
|
0
framework/core/ember/app/helpers/.gitkeep
Normal file
0
framework/core/ember/app/helpers/.gitkeep
Normal file
6
framework/core/ember/app/helpers/abbreviate-number.js
Normal file
6
framework/core/ember/app/helpers/abbreviate-number.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Handlebars.makeBoundHelper(function(number, options) {
|
||||
return new Handlebars.SafeString(number);
|
||||
});
|
||||
|
31
framework/core/ember/app/helpers/abbreviate-time.js
Normal file
31
framework/core/ember/app/helpers/abbreviate-time.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Handlebars.makeBoundHelper(function(time) {
|
||||
var m = moment(time);
|
||||
var datetime = m.format(),
|
||||
full = m.format('LLLL');
|
||||
|
||||
var second = 1e3;
|
||||
var minute = 6e4;
|
||||
var hour = 36e5;
|
||||
var day = 864e5;
|
||||
var week = 6048e5;
|
||||
var ago = null;
|
||||
|
||||
var diff = Math.abs(m.diff(moment()));
|
||||
|
||||
if (diff < 60 * minute) {
|
||||
ago = moment.duration(diff).minutes()+'m';
|
||||
} else if (diff < 24 * hour) {
|
||||
ago = moment.duration(diff).hours()+'h';
|
||||
} else if (diff < 30 * day) {
|
||||
ago = moment.duration(diff).days()+'d';
|
||||
} else if (m.year() == moment().year()) {
|
||||
ago = m.format('D MMM');
|
||||
} else {
|
||||
ago = m.format('MMM \'YY');
|
||||
}
|
||||
|
||||
return new Handlebars.SafeString('<time pubdate datetime="'+datetime+'" title="'+full+'">'+ago+'</time>');
|
||||
});
|
||||
|
6
framework/core/ember/app/helpers/fa-icon.js
Normal file
6
framework/core/ember/app/helpers/fa-icon.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Handlebars.makeBoundHelper(function(icon, options) {
|
||||
return new Handlebars.SafeString('<i class="fa fa-icon fa-'+icon+' '+(options.hash.class || '')+'"></i>');
|
||||
});
|
||||
|
18
framework/core/ember/app/helpers/highlight-words.js
Normal file
18
framework/core/ember/app/helpers/highlight-words.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Handlebars.makeBoundHelper(function(text, phrase, options) {
|
||||
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 Handlebars.SafeString(text);
|
||||
});
|
||||
|
14
framework/core/ember/app/helpers/post-content.js
Normal file
14
framework/core/ember/app/helpers/post-content.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
// This helper takes a post as its argument and renders a certain component
|
||||
// corresponding to the post's type. The naming convention is 'post-type-[type]'
|
||||
// (for example, post-type-comment for a comment.) Other arguments added to the
|
||||
// helper are passed through to the component.
|
||||
|
||||
export default Ember.Handlebars.makeBoundHelper(function(post, options) {
|
||||
options.hash.post = post;
|
||||
var component = 'post-type-'+post.get('type');
|
||||
var helper = Ember.Handlebars.resolveHelper(options.data.view.container, component);
|
||||
|
||||
helper.call(this, options);
|
||||
});
|
6
framework/core/ember/app/helpers/render-hook.js
Normal file
6
framework/core/ember/app/helpers/render-hook.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Handlebars.makeBoundHelper(function(name, options) {
|
||||
return new Handlebars.SafeString('');
|
||||
});
|
||||
|
53
framework/core/ember/app/helpers/user-avatar.js
Normal file
53
framework/core/ember/app/helpers/user-avatar.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
function HSVtoRGB(h, s, v) {
|
||||
var r, g, b, i, f, p, q, t;
|
||||
if (h && s === undefined && v === undefined) {
|
||||
s = h.s, v = h.v, h = h.h;
|
||||
}
|
||||
i = Math.floor(h * 6);
|
||||
f = h * 6 - i;
|
||||
p = v * (1 - s);
|
||||
q = v * (1 - f * s);
|
||||
t = v * (1 - (1 - f) * s);
|
||||
switch (i % 6) {
|
||||
case 0: r = v, g = t, b = p; break;
|
||||
case 1: r = q, g = v, b = p; break;
|
||||
case 2: r = p, g = v, b = t; break;
|
||||
case 3: r = p, g = q, b = v; break;
|
||||
case 4: r = t, g = p, b = v; break;
|
||||
case 5: r = v, g = p, b = q; break;
|
||||
}
|
||||
return {
|
||||
r: Math.floor(r * 255),
|
||||
g: Math.floor(g * 255),
|
||||
b: Math.floor(b * 255)
|
||||
};
|
||||
}
|
||||
|
||||
export default Ember.Handlebars.makeBoundHelper(function(user, options) {
|
||||
if (!user) return;
|
||||
|
||||
var number;
|
||||
if (number = user.get('avatarNumber')) {
|
||||
number = number + '';
|
||||
var filename = number.length >= 3 ? number : new Array(3 - number.length + 1).join('0') + number;
|
||||
return new Handlebars.SafeString('<img src="/packages/flarum/core/avatars/'+filename+'.jpg" class="avatar '+options.hash.class+'">');
|
||||
}
|
||||
|
||||
var username = user.get('username');
|
||||
if (!username) username = '?';
|
||||
|
||||
var letter = username.charAt(0).toUpperCase();
|
||||
|
||||
var num = 0;
|
||||
for (var i = 0; i < username.length; i++) {
|
||||
num += username.charCodeAt(i) * 13;
|
||||
}
|
||||
|
||||
var hue = num % 360;
|
||||
var rgb = HSVtoRGB(hue / 360, 100 / 255, 200 / 255);
|
||||
var bg = ''+rgb.r.toString(16)+rgb.g.toString(16)+rgb.b.toString(16);
|
||||
return new Handlebars.SafeString('<span class="avatar '+options.hash.class+'" style="background:#'+bg+'" title="'+username+'">'+letter+'</span>');
|
||||
});
|
||||
|
26
framework/core/ember/app/index.html
Normal file
26
framework/core/ember/app/index.html
Normal file
|
@ -0,0 +1,26 @@
|
|||
<!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">
|
||||
|
||||
{{BASE_TAG}}
|
||||
|
||||
<link rel="stylesheet" href="assets/vendor.css">
|
||||
<link rel="stylesheet" href="assets/flarum.css">
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
window.FlarumENV = {{ENV}};
|
||||
window.EmberENV = window.FlarumENV.EmberENV;
|
||||
</script>
|
||||
<script src="assets/vendor.js"></script>
|
||||
<script src="assets/flarum.js"></script>
|
||||
<script>
|
||||
window.Flarum = require('flarum/app')['default'].create(FlarumENV.APP);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
33
framework/core/ember/app/mixins/post-stream.js
Normal file
33
framework/core/ember/app/mixins/post-stream.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
export default Ember.Mixin.create({
|
||||
// Find the DOM element of the item that is nearest to a post with a certain
|
||||
// number. This will either be another post (if the requested post doesn't
|
||||
// exist,) or a gap presumed to container the requested post.
|
||||
findNearestToNumber: function(number) {
|
||||
var nearestItem = $();
|
||||
$('.posts .item').each(function() {
|
||||
var $this = $(this),
|
||||
thisNumber = $this.data('number');
|
||||
if (thisNumber > number) {
|
||||
return false;
|
||||
}
|
||||
nearestItem = $this;
|
||||
});
|
||||
return nearestItem;
|
||||
},
|
||||
|
||||
findNearestToIndex: function(index) {
|
||||
var nearestItem = $('.posts .item[data-start='+index+'][data-end='+index+']');
|
||||
|
||||
if (! nearestItem.length) {
|
||||
$('.posts .item').each(function() {
|
||||
var $this = $(this);
|
||||
if ($this.data('start') <= index && $this.data('end') >= index) {
|
||||
nearestItem = $this;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return nearestItem;
|
||||
}
|
||||
});
|
0
framework/core/ember/app/models/.gitkeep
Normal file
0
framework/core/ember/app/models/.gitkeep
Normal file
12
framework/core/ember/app/models/discussion-result.js
Normal file
12
framework/core/ember/app/models/discussion-result.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
var DiscussionResult = Ember.ObjectProxy.extend({
|
||||
|
||||
relevantPosts: Em.A(),
|
||||
|
||||
startPost: null,
|
||||
lastPost: null
|
||||
|
||||
});
|
||||
|
||||
export default DiscussionResult;
|
7
framework/core/ember/app/models/discussion-state.js
Normal file
7
framework/core/ember/app/models/discussion-state.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import Ember from 'ember';
|
||||
import DS from 'ember-data';
|
||||
|
||||
export default DS.Model.extend({
|
||||
readTime: DS.attr('date'),
|
||||
readNumber: DS.attr('number')
|
||||
});
|
81
framework/core/ember/app/models/discussion.js
Normal file
81
framework/core/ember/app/models/discussion.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
import Ember from 'ember';
|
||||
import DS from 'ember-data';
|
||||
|
||||
var Discussion = DS.Model.extend({
|
||||
|
||||
title: DS.attr('string'),
|
||||
|
||||
slug: function() {
|
||||
return this.get('title').toLowerCase().replace(/[^a-z0-9]/gi, '-').replace(/-+/g, '-');
|
||||
}.property('title'),
|
||||
|
||||
canReply: DS.attr('boolean'),
|
||||
canEdit: DS.attr('boolean'),
|
||||
canDelete: DS.attr('boolean'),
|
||||
|
||||
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'),
|
||||
|
||||
relevantPosts: DS.hasMany('post'),
|
||||
|
||||
postsCount: DS.attr('number'),
|
||||
repliesCount: function() {
|
||||
return Math.max(0, this.get('postsCount') - 1);
|
||||
}.property('postsCount'),
|
||||
|
||||
posts: DS.attr('string'),
|
||||
postIds: function() {
|
||||
return this.get('posts').split(',');
|
||||
}.property('posts'),
|
||||
|
||||
readNumber: DS.attr('number'),
|
||||
unreadCount: function() {
|
||||
return this.get('lastPostNumber') - this.get('readNumber');
|
||||
}.property('lastPostNumber', 'readNumber'),
|
||||
|
||||
//--------------------------------
|
||||
// Prototype generated properties
|
||||
|
||||
// category: function() {
|
||||
// var categories = [null, 'Announcements', 'General', 'Support', 'Feedback', 'Core', 'Plugins', 'Themes'];
|
||||
// return categories[Math.floor(Math.random() * categories.length)];
|
||||
// }.property(),
|
||||
category: DS.attr('string'),
|
||||
|
||||
_recent: function() {
|
||||
var cutoff = new Date('September 19, 2014');
|
||||
return this.get('lastTime') > cutoff;
|
||||
}.property('lastTime'),
|
||||
|
||||
unread: function() {
|
||||
return Math.round(Math.random() * (this.get('_recent') ? 0.8 : 0) * this.get('postsCount'));
|
||||
}.property(),
|
||||
|
||||
// sticky: function() {
|
||||
// return Math.random() > (this.get('_recent') ? 0.95 : 0.99);
|
||||
// }.property(),
|
||||
sticky: DS.attr('boolean'),
|
||||
|
||||
excerpt: function() {
|
||||
// return 'I want to get your thoughts on this one TV Addicts: what new show have you been getting into this year, and why?';
|
||||
// return 'Here\'s the near-final game list, in no particular order. The list may be subject to amendments, as we\'re still chasing up copies of some games.';
|
||||
// return 'Nominating for the Annual General Meeting is easy. Read this to find out how.'
|
||||
return 'There are many apps made with Ninetech in the Mac App Store. If you\'d like, take a moment to share your Nintech-made apps in this thread.';
|
||||
}.property(),
|
||||
|
||||
locked: function() {
|
||||
return Math.random() > 0.95;
|
||||
}.property(),
|
||||
|
||||
following: function() {
|
||||
return Math.random() > 0.95;
|
||||
}.property()
|
||||
});
|
||||
|
||||
export default Discussion;
|
10
framework/core/ember/app/models/group.js
Normal file
10
framework/core/ember/app/models/group.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
import Ember from 'ember';
|
||||
import DS from 'ember-data';
|
||||
|
||||
export default DS.Model.extend({
|
||||
|
||||
name: DS.attr('string'),
|
||||
|
||||
users: DS.hasMany('group'),
|
||||
|
||||
});
|
20
framework/core/ember/app/models/post-result.js
Normal file
20
framework/core/ember/app/models/post-result.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
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;
|
201
framework/core/ember/app/models/post-stream.js
Normal file
201
framework/core/ember/app/models/post-stream.js
Normal file
|
@ -0,0 +1,201 @@
|
|||
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: Em.A(),
|
||||
content: Em.A(),
|
||||
store: null,
|
||||
discussion: null,
|
||||
|
||||
postLoadCount: 20,
|
||||
|
||||
_init: function() {
|
||||
this.clear();
|
||||
}.on('init'),
|
||||
|
||||
setup: function(ids) {
|
||||
this.set('ids', ids);
|
||||
this.clear();
|
||||
},
|
||||
|
||||
count: function() {
|
||||
return this.get('ids.length');
|
||||
}.property('ids'),
|
||||
|
||||
firstLoaded: function() {
|
||||
var first = this.objectAt(0);
|
||||
return first && ! first.gap;
|
||||
}.property('content.@each'),
|
||||
|
||||
lastLoaded: function() {
|
||||
var last = this.objectAt(this.get('length') - 1);
|
||||
return last && ! last.gap;
|
||||
}.property('content.@each'),
|
||||
|
||||
// Clear the contents of the post stream, resetting it to one big gap.
|
||||
clear: function() {
|
||||
var stream = this.get('content');
|
||||
stream.enumerableContentWillChange();
|
||||
stream.clear().pushObject(Em.Object.create({
|
||||
gap: true,
|
||||
indexStart: 0,
|
||||
indexEnd: this.get('count') - 1,
|
||||
loading: true
|
||||
}));
|
||||
stream.enumerableContentDidChange();
|
||||
},
|
||||
|
||||
loadRange: function(start, end, backwards) {
|
||||
var limit = this.get('postLoadCount');
|
||||
end = end || start + limit;
|
||||
|
||||
// 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.gap && (
|
||||
(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('post.number') == number) {
|
||||
return Ember.RSVP.resolve([item.get('post')]);
|
||||
} else if (item.gap) {
|
||||
item.set('direction', 'down').set('loading', true);
|
||||
}
|
||||
}
|
||||
|
||||
var stream = this;
|
||||
return this.store.find('post', {
|
||||
discussions: this.get('discussion.id'),
|
||||
near: number
|
||||
}).then(function(posts) {
|
||||
stream.addPosts(posts);
|
||||
});
|
||||
},
|
||||
|
||||
loadNearIndex: function(index) {
|
||||
// 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.gap) {
|
||||
return Ember.RSVP.resolve([item.get('post')]);
|
||||
} else {
|
||||
item.set('direction', 'down').set('loading', true);
|
||||
}
|
||||
return this.loadRange(Math.max(item.indexStart, index - 10), item.indexEnd);
|
||||
}
|
||||
|
||||
return Ember.RSVP.reject();
|
||||
},
|
||||
|
||||
addPosts: function(posts) {
|
||||
this.trigger('postsLoaded', posts);
|
||||
|
||||
var stream = this;
|
||||
posts.forEach(function(post) {
|
||||
stream.addPost(post);
|
||||
});
|
||||
|
||||
this.trigger('postsAdded');
|
||||
},
|
||||
|
||||
addPost: function(post) {
|
||||
var stream = this;
|
||||
var index = this.get('ids').indexOf(post.get('id'));
|
||||
var content = this.get('content');
|
||||
|
||||
// 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(Ember.Object.create({
|
||||
gap: true,
|
||||
indexStart: item.indexStart,
|
||||
indexEnd: index - 1
|
||||
}));
|
||||
}
|
||||
newItems.push(Ember.Object.create({
|
||||
indexStart: index,
|
||||
indexEnd: index,
|
||||
post: post
|
||||
}));
|
||||
if (item.indexEnd > index) {
|
||||
newItems.push(Ember.Object.create({
|
||||
gap: true,
|
||||
indexStart: index + 1,
|
||||
indexEnd: item.indexEnd
|
||||
}));
|
||||
}
|
||||
content.enumerableContentWillChange();
|
||||
content.replace(i, 1, newItems);
|
||||
content.enumerableContentDidChange();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
findNearestToNumber: function(number) {
|
||||
var nearestItem;
|
||||
this.get('content').some(function(item) {
|
||||
var thisNumber = item.get('post.number');
|
||||
if (thisNumber > number) {
|
||||
return true;
|
||||
}
|
||||
nearestItem = item;
|
||||
});
|
||||
return nearestItem;
|
||||
},
|
||||
|
||||
findNearestToIndex: function(index) {
|
||||
var nearestItem;
|
||||
this.get('content').some(function(item) {
|
||||
if (item.indexStart <= index && item.indexEnd >= index) {
|
||||
nearestItem = item;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return nearestItem;
|
||||
}
|
||||
|
||||
});
|
37
framework/core/ember/app/models/post.js
Normal file
37
framework/core/ember/app/models/post.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
import Ember from 'ember';
|
||||
import DS from 'ember-data';
|
||||
|
||||
export default DS.Model.extend({
|
||||
|
||||
discussion: DS.belongsTo('discussion', {inverse: 'actualPosts'}),
|
||||
number: DS.attr('number'),
|
||||
|
||||
time: DS.attr('string'),
|
||||
user: DS.belongsTo('user'),
|
||||
type: DS.attr('string'),
|
||||
content: DS.attr('string'),
|
||||
contentHtml: DS.attr('string'),
|
||||
|
||||
editTime: DS.attr('string'),
|
||||
editUser: DS.belongsTo('user'),
|
||||
edited: Ember.computed.notEmpty('editTime'),
|
||||
|
||||
deleteTime: DS.attr('string'),
|
||||
deleteUser: DS.belongsTo('user'),
|
||||
deleted: Ember.computed.notEmpty('deleteTime'),
|
||||
|
||||
replyTo: DS.belongsTo('post', {inverse: 'replies'}),
|
||||
replyToNumber: DS.attr('number'),
|
||||
replyToUser: DS.belongsTo('user'),
|
||||
|
||||
replies: DS.hasMany('post', {inverse: 'replyTo'}),
|
||||
repliesCount: DS.attr('number'),
|
||||
|
||||
canEdit: DS.attr('boolean'),
|
||||
canDelete: DS.attr('boolean'),
|
||||
|
||||
likes: function() {
|
||||
return Math.floor(Math.random() * (Math.random() < 0.3 ? 10 : 1));
|
||||
}.property()
|
||||
|
||||
});
|
44
framework/core/ember/app/models/result-stream.js
Normal file
44
framework/core/ember/app/models/result-stream.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
// Represents a collection of results (e.g. a list of discussions)
|
||||
|
||||
export default Ember.Object.extend({
|
||||
|
||||
// An array of the results.
|
||||
results: Em.A(),
|
||||
|
||||
// The currently-active result.
|
||||
currentResult: null,
|
||||
|
||||
sort: null,
|
||||
|
||||
// The index of the currently-active result (determined by ID.) Returns '?'
|
||||
// if the currently-active result is not in the results list.
|
||||
index: function() {
|
||||
var index = '?';
|
||||
var id = this.get('currentResult.id');
|
||||
this.get('results').some(function(result, i) {
|
||||
if (result.get('id') == id) {
|
||||
index = i + 1;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return index;
|
||||
}.property('currentResult', 'results'),
|
||||
|
||||
// The number of results.
|
||||
count: function() {
|
||||
return this.get('results.length');
|
||||
}.property('results'),
|
||||
|
||||
// The previous result.
|
||||
previous: function() {
|
||||
return this.get('results').objectAt(this.get('index') - 2);
|
||||
}.property('index'),
|
||||
|
||||
// The next result.
|
||||
next: function() {
|
||||
return this.get('results').objectAt(this.get('index'));
|
||||
}.property('index'),
|
||||
|
||||
});
|
21
framework/core/ember/app/models/user.js
Normal file
21
framework/core/ember/app/models/user.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
import Ember from 'ember';
|
||||
import DS from 'ember-data';
|
||||
|
||||
export default DS.Model.extend({
|
||||
|
||||
username: DS.attr('string'),
|
||||
avatarUrl: DS.attr('string'),
|
||||
joinTime: DS.attr('date'),
|
||||
lastSeenTime: DS.attr('date'),
|
||||
discussionsCount: DS.attr('number'),
|
||||
postsCount: DS.attr('number'),
|
||||
|
||||
canEdit: DS.attr('boolean'),
|
||||
canDelete: DS.attr('boolean'),
|
||||
|
||||
groups: DS.hasMany('group'),
|
||||
|
||||
avatarNumber: function() {
|
||||
return Math.random() > 0.3 ? Math.floor(Math.random() * 19) + 1 : null;
|
||||
}.property()
|
||||
});
|
24
framework/core/ember/app/router.js
Normal file
24
framework/core/ember/app/router.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
var Router = Ember.Router.extend({
|
||||
location: FlarumENV.locationType
|
||||
});
|
||||
|
||||
Router.map(function() {
|
||||
|
||||
this.resource('categories', { path: '/categories' });
|
||||
|
||||
this.resource('discussions', { path: '/' }, function() {
|
||||
this.resource('discussion', { path: '/:id/:slug' });
|
||||
});
|
||||
|
||||
this.resource('user', { path: '/user/:username' }, function() {
|
||||
this.route('activity');
|
||||
this.route('posts');
|
||||
this.route('discussions');
|
||||
this.route('preferences');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
export default Router;
|
0
framework/core/ember/app/routes/.gitkeep
Normal file
0
framework/core/ember/app/routes/.gitkeep
Normal file
28
framework/core/ember/app/routes/application.js
Normal file
28
framework/core/ember/app/routes/application.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
import Ember from 'ember';
|
||||
import ApplicationRouteMixin from 'simple-auth/mixins/application-route-mixin';
|
||||
|
||||
export default Ember.Route.extend(ApplicationRouteMixin, {
|
||||
|
||||
actions: {
|
||||
|
||||
login: function() {
|
||||
return this.render('login', {
|
||||
into: 'application',
|
||||
outlet: 'modal'
|
||||
});
|
||||
},
|
||||
|
||||
doLogin: function() {
|
||||
this.get('session').authenticate('authenticator:custom', {});
|
||||
},
|
||||
|
||||
closeModal: function() {
|
||||
return this.disconnectOutlet({
|
||||
outlet: 'modal',
|
||||
parentView: 'application'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
64
framework/core/ember/app/routes/discussion.js
Normal file
64
framework/core/ember/app/routes/discussion.js
Normal file
|
@ -0,0 +1,64 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
|
||||
queryParams: {
|
||||
start: {replace: true}
|
||||
},
|
||||
|
||||
renderTemplate: function() {
|
||||
this.render();
|
||||
this.render('discussion-sidebar', {
|
||||
into: 'application',
|
||||
outlet: 'sidebar'
|
||||
});
|
||||
},
|
||||
|
||||
model: function(params) {
|
||||
return this.store.find('discussion', params.id);
|
||||
},
|
||||
|
||||
resetController: function(controller) {
|
||||
// Whenever we exit the discussion view, or transition to a different
|
||||
// discussion, we want to reset the query params so that they don't stick.
|
||||
controller.set('start', '1');
|
||||
controller.set('searchQuery', '');
|
||||
controller.set('loaded', false);
|
||||
controller.set('postStream', null);
|
||||
},
|
||||
|
||||
setupController: function(controller, model) {
|
||||
controller.setup(model);
|
||||
|
||||
this.controllerFor('application').set('showDiscussionStream', true);
|
||||
this.controllerFor('discussions').set('paned', true);
|
||||
this.controllerFor('discussions').set('current', model);
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
||||
queryParamsDidChange: function(params) {
|
||||
// We're only interested in changes to the ?start param, and we're
|
||||
// not interested if nothing has actually changed. If the start
|
||||
// param has changed, we want to tell the controller to load posts
|
||||
// near it.
|
||||
if (! params.start || params.start == this.get('controller.start') || ! this.get('controller.loaded')) {
|
||||
return;
|
||||
}
|
||||
this.get('controller').send('jumpToNumber', params.start);
|
||||
},
|
||||
|
||||
willTransition: function(transition) {
|
||||
// If we're going to transition out, we need to abort any unfinished
|
||||
// AJAX requests. We need to do this because sometimes a transition
|
||||
// to another discussion will happen very rapidly (i.e. when using
|
||||
// the arrow buttons on the result stream.) If a previous
|
||||
// discussion's posts finish loading while displaying a new
|
||||
// discussion, strange things will happen.
|
||||
this.store.adapterFor('discussion').xhr.forEach(function(xhr) {
|
||||
xhr.abort();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
});
|
36
framework/core/ember/app/routes/discussions.js
Normal file
36
framework/core/ember/app/routes/discussions.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
import Ember from 'ember';
|
||||
import Discussion from '../models/discussion';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
|
||||
setupController: function(controller, model) {
|
||||
controller.set('model', model);
|
||||
|
||||
if ( ! model.get('length')) {
|
||||
controller.set('resultsLoading', true);
|
||||
|
||||
controller.getResults().then(function(results) {
|
||||
controller
|
||||
.set('resultsLoading', false)
|
||||
.set('meta', results.get('meta'))
|
||||
.set('model.content', results);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
model: function(params) {
|
||||
var model = Ember.ArrayProxy.create();
|
||||
|
||||
return Ember.RSVP.resolve(model);
|
||||
},
|
||||
|
||||
actions: {
|
||||
queryParamsDidChange: function(newParams, params) {
|
||||
var self = this;
|
||||
Ember.run.scheduleOnce('afterRender', function() {
|
||||
self.refresh();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
20
framework/core/ember/app/routes/discussions/index.js
Normal file
20
framework/core/ember/app/routes/discussions/index.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
|
||||
renderTemplate: function() {
|
||||
this.render();
|
||||
this.render('discussions-sidebar', {
|
||||
into: 'application',
|
||||
outlet: 'sidebar'
|
||||
});
|
||||
},
|
||||
|
||||
setupController: function(controller, model) {
|
||||
this.controllerFor('discussions').set('paneShowing', false);
|
||||
this.controllerFor('discussions').set('paned', false);
|
||||
this.controllerFor('application').set('showDiscussionStream', false);
|
||||
this._super(controller, model);
|
||||
}
|
||||
|
||||
});
|
14
framework/core/ember/app/serializers/application.js
Normal file
14
framework/core/ember/app/serializers/application.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
import Ember from 'ember';
|
||||
import DS from 'ember-data';
|
||||
|
||||
export default DS.JsonApiSerializer.extend({
|
||||
normalize: function(type, hash, property) {
|
||||
var json = {};
|
||||
|
||||
for (var prop in hash) {
|
||||
json[prop.camelize()] = hash[prop];
|
||||
}
|
||||
|
||||
return this._super(type, json, property);
|
||||
}
|
||||
});
|
0
framework/core/ember/app/styles/.gitkeep
Normal file
0
framework/core/ember/app/styles/.gitkeep
Normal file
14
framework/core/ember/app/styles/app.less
Normal file
14
framework/core/ember/app/styles/app.less
Normal file
|
@ -0,0 +1,14 @@
|
|||
@import "config.less";
|
||||
|
||||
@flarum-base: "flarum/";
|
||||
@bootstrap-base: "../../vendor/bootstrap/less/";
|
||||
@font-awesome-base: "../../vendor/font-awesome/less/";
|
||||
|
||||
@import "@{flarum-base}bootstrap/bootstrap.less";
|
||||
@import "@{font-awesome-base}font-awesome.less";
|
||||
|
||||
@fa-font-path: "../font-awesome/fonts";
|
||||
|
||||
@import "@{flarum-base}global.less";
|
||||
@import "@{flarum-base}discussions.less";
|
||||
@import "@{flarum-base}discussion.less";
|
32
framework/core/ember/app/styles/config.less
Normal file
32
framework/core/ember/app/styles/config.less
Normal file
|
@ -0,0 +1,32 @@
|
|||
// Default blue
|
||||
@flarum-hue: 210;
|
||||
@flarum-saturation: 30%;
|
||||
@flarum-lightness: 90%;
|
||||
@flarum-body-saturation: 30%;
|
||||
@flarum-body-lightness: 99.99%;
|
||||
|
||||
@flarum-primary-color: hsl(@flarum-hue, @flarum-saturation, 35%);
|
||||
|
||||
@flarum-background-color: hsl(@flarum-hue, @flarum-saturation, @flarum-lightness);
|
||||
@flarum-background-image: none;
|
||||
@flarum-background-repeat: no-repeat;
|
||||
|
||||
@flarum-bg-primary-color: contrast(@flarum-background-color, @flarum-primary-color, #fff);
|
||||
@flarum-bg-secondary-color: contrast(@flarum-background-color, rgba(255, 255, 255, 0.1), darken(@flarum-background-color, 7%));
|
||||
@flarum-bg-text-color: @flarum-bg-primary-color;
|
||||
@flarum-bg-link-color: @flarum-bg-primary-color;
|
||||
@flarum-bg-muted-color: contrast(@flarum-background-color, rgba(255, 255, 255, 0.25), desaturate(darken(@flarum-bg-secondary-color, 25%), 10%));
|
||||
|
||||
// styles for a background image
|
||||
// @flarum-bg-primary-color: rgba(255, 255, 255, 0.8);
|
||||
// @flarum-bg-secondary-color: rgba(255, 255, 255, 0.2);
|
||||
// @flarum-bg-text-color: @flarum-bg-primary-color;
|
||||
// @flarum-bg-link-color: @flarum-bg-primary-color;
|
||||
// @flarum-bg-muted-color: contrast(@flarum-background-color, lighten(@flarum-bg-secondary-color, 20%), darken(@flarum-bg-secondary-color, 20%));
|
||||
|
||||
@flarum-body-background-color: hsl(@flarum-hue, @flarum-body-saturation, @flarum-body-lightness);
|
||||
@flarum-body-primary-color: contrast(@flarum-body-background-color, @flarum-primary-color, #fff);
|
||||
@flarum-body-secondary-color: contrast(@flarum-body-background-color, lighten(@flarum-body-background-color, 13%), desaturate(darken(@flarum-body-background-color, 10%), 10%));
|
||||
@flarum-body-text-color: contrast(@flarum-body-background-color, #555, #fff);
|
||||
@flarum-body-link-color: @flarum-body-primary-color;
|
||||
@flarum-body-muted-color: contrast(@flarum-body-background-color, lighten(@flarum-body-secondary-color, 20%), darken(@flarum-body-secondary-color, 25%));
|
49
framework/core/ember/app/styles/flarum/bootstrap/bootstrap.less
vendored
Normal file
49
framework/core/ember/app/styles/flarum/bootstrap/bootstrap.less
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Core variables and mixins
|
||||
@import "variables.less";
|
||||
@import "@{bootstrap-base}mixins.less";
|
||||
|
||||
// Reset
|
||||
@import "@{bootstrap-base}normalize.less";
|
||||
@import "@{bootstrap-base}print.less";
|
||||
|
||||
// Core CSS
|
||||
@import "@{bootstrap-base}scaffolding.less";
|
||||
@import "@{bootstrap-base}type.less";
|
||||
@import "@{bootstrap-base}code.less";
|
||||
@import "@{bootstrap-base}grid.less";
|
||||
@import "@{bootstrap-base}tables.less";
|
||||
@import "@{bootstrap-base}forms.less";
|
||||
@import "@{bootstrap-base}buttons.less";
|
||||
|
||||
// Components
|
||||
@import "@{bootstrap-base}component-animations.less";
|
||||
// @import "@{bootstrap-base}glyphicons.less";
|
||||
@import "@{bootstrap-base}dropdowns.less";
|
||||
@import "@{bootstrap-base}button-groups.less";
|
||||
@import "@{bootstrap-base}input-groups.less";
|
||||
// @import "@{bootstrap-base}navs.less";
|
||||
// @import "@{bootstrap-base}navbar.less";
|
||||
// @import "@{bootstrap-base}breadcrumbs.less";
|
||||
@import "@{bootstrap-base}pagination.less";
|
||||
// @import "@{bootstrap-base}pager.less";
|
||||
// @import "@{bootstrap-base}labels.less";
|
||||
// @import "@{bootstrap-base}badges.less";
|
||||
// @import "@{bootstrap-base}jumbotron.less";
|
||||
// @import "@{bootstrap-base}thumbnails.less";
|
||||
// @import "@{bootstrap-base}alerts.less";
|
||||
@import "@{bootstrap-base}progress-bars.less";
|
||||
// @import "@{bootstrap-base}media.less";
|
||||
// @import "@{bootstrap-base}list-group.less";
|
||||
// @import "@{bootstrap-base}panels.less";
|
||||
// @import "@{bootstrap-base}wells.less";
|
||||
@import "@{bootstrap-base}close.less";
|
||||
|
||||
// Components w/ JavaScript
|
||||
@import "@{bootstrap-base}modals.less";
|
||||
@import "@{bootstrap-base}tooltip.less";
|
||||
@import "@{bootstrap-base}popovers.less";
|
||||
// @import "@{bootstrap-base}carousel.less";
|
||||
|
||||
// Utility classes
|
||||
@import "@{bootstrap-base}utilities.less";
|
||||
@import "@{bootstrap-base}responsive-utilities.less";
|
832
framework/core/ember/app/styles/flarum/bootstrap/variables.less
Normal file
832
framework/core/ember/app/styles/flarum/bootstrap/variables.less
Normal file
|
@ -0,0 +1,832 @@
|
|||
//
|
||||
// Variables
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
//== Colors
|
||||
//
|
||||
//## Gray and brand colors for use across Bootstrap.
|
||||
|
||||
@gray-darker: lighten(#000, 13.5%); // #222
|
||||
@gray-dark: lighten(#000, 20%); // #333
|
||||
@gray: lighten(#000, 33.5%); // #555
|
||||
@gray-light: lighten(#000, 60%); // #999
|
||||
@gray-lighter: lighten(#000, 93.5%); // #eee
|
||||
|
||||
@brand-primary: @flarum-body-primary-color; // CHANGED
|
||||
@brand-success: #5cb85c;
|
||||
@brand-info: #5bc0de;
|
||||
@brand-warning: #f0ad4e;
|
||||
@brand-danger: #d9534f;
|
||||
|
||||
|
||||
//== Scaffolding
|
||||
//
|
||||
// ## Settings for some of the most global styles.
|
||||
|
||||
//** Background color for `<body>`.
|
||||
@body-bg: @flarum-background-color; // CHANGED
|
||||
//** Global text color on `<body>`.
|
||||
@text-color: @flarum-bg-text-color; // CHANGED
|
||||
|
||||
//** Global textual link color.
|
||||
@link-color: @brand-primary;
|
||||
//** Link hover color set via `darken()` function.
|
||||
@link-hover-color: darken(@link-color, 15%);
|
||||
|
||||
|
||||
//== Typography
|
||||
//
|
||||
//## Font, line-height, and color for body text, headings, and more.
|
||||
|
||||
@font-family-sans-serif: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; // CHANGED
|
||||
@font-family-serif: Georgia, "Times New Roman", Times, serif;
|
||||
//** Default monospace fonts for `<code>`, `<kbd>`, and `<pre>`.
|
||||
@font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
@font-family-base: @font-family-sans-serif;
|
||||
|
||||
@font-size-base: 13px; // CHANGED
|
||||
@font-size-large: ceil((@font-size-base * 1.25)); // ~18px
|
||||
@font-size-small: ceil((@font-size-base * 0.85)); // ~12px
|
||||
|
||||
@font-size-h1: floor((@font-size-base * 2.6)); // ~36px
|
||||
@font-size-h2: floor((@font-size-base * 2.15)); // ~30px
|
||||
@font-size-h3: ceil((@font-size-base * 1.7)); // ~24px
|
||||
@font-size-h4: ceil((@font-size-base * 1.25)); // ~18px
|
||||
@font-size-h5: @font-size-base;
|
||||
@font-size-h6: ceil((@font-size-base * 0.85)); // ~12px
|
||||
|
||||
//** Unit-less `line-height` for use in components like buttons.
|
||||
@line-height-base: 1.538461538; // 20/13 CHANGED
|
||||
//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
|
||||
@line-height-computed: floor((@font-size-base * @line-height-base)); // ~20px
|
||||
|
||||
//** By default, this inherits from the `<body>`.
|
||||
@headings-font-family: inherit;
|
||||
@headings-font-weight: 500;
|
||||
@headings-line-height: 1.1;
|
||||
@headings-color: inherit;
|
||||
|
||||
|
||||
//-- Iconography
|
||||
//
|
||||
//## Specify custom locations of the include Glyphicons icon font. Useful for those including Bootstrap via Bower.
|
||||
|
||||
@icon-font-path: "../fonts/";
|
||||
@icon-font-name: "glyphicons-halflings-regular";
|
||||
@icon-font-svg-id: "glyphicons_halflingsregular";
|
||||
|
||||
//== Components
|
||||
//
|
||||
//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
|
||||
|
||||
@padding-base-vertical: 9px; // CHANGED
|
||||
@padding-base-horizontal: 15px; // CHANGED
|
||||
|
||||
@padding-large-vertical: 10px;
|
||||
@padding-large-horizontal: 16px;
|
||||
|
||||
@padding-small-vertical: 5px;
|
||||
@padding-small-horizontal: 10px;
|
||||
|
||||
@padding-xs-vertical: 1px;
|
||||
@padding-xs-horizontal: 5px;
|
||||
|
||||
@line-height-large: 1.33;
|
||||
@line-height-small: 1.5;
|
||||
|
||||
@border-radius-base: 4px;
|
||||
@border-radius-large: 6px;
|
||||
@border-radius-small: 3px;
|
||||
|
||||
//** Global color for active items (e.g., navs or dropdowns).
|
||||
@component-active-color: #fff;
|
||||
//** Global background color for active items (e.g., navs or dropdowns).
|
||||
@component-active-bg: @brand-primary;
|
||||
|
||||
//** Width of the `border` for generating carets that indicator dropdowns.
|
||||
@caret-width-base: 4px;
|
||||
//** Carets increase slightly in size for larger components.
|
||||
@caret-width-large: 5px;
|
||||
|
||||
|
||||
//== Tables
|
||||
//
|
||||
//## Customizes the `.table` component with basic values, each used across all table variations.
|
||||
|
||||
//** Padding for `<th>`s and `<td>`s.
|
||||
@table-cell-padding: 8px;
|
||||
//** Padding for cells in `.table-condensed`.
|
||||
@table-condensed-cell-padding: 5px;
|
||||
|
||||
//** Default background color used for all tables.
|
||||
@table-bg: transparent;
|
||||
//** Background color used for `.table-striped`.
|
||||
@table-bg-accent: #f9f9f9;
|
||||
//** Background color used for `.table-hover`.
|
||||
@table-bg-hover: #f5f5f5;
|
||||
@table-bg-active: @table-bg-hover;
|
||||
|
||||
//** Border color for table and cell borders.
|
||||
@table-border-color: #ddd;
|
||||
|
||||
|
||||
//== Buttons
|
||||
//
|
||||
//## For each of Bootstrap's buttons, define text, background and border color.
|
||||
|
||||
@btn-font-weight: normal;
|
||||
|
||||
@btn-default-color: @flarum-body-primary-color; // CHANGED
|
||||
@btn-default-bg: darken(@flarum-body-secondary-color, 7%); // CHANGED
|
||||
@btn-default-border: @btn-default-bg; // CHANGED
|
||||
|
||||
@btn-primary-color: contrast(@brand-primary, @flarum-body-secondary-color, #fff); // CHANGED
|
||||
@btn-primary-bg: @brand-primary;
|
||||
@btn-primary-border: @btn-primary-bg; // CHANGED
|
||||
|
||||
@btn-success-color: #fff;
|
||||
@btn-success-bg: @brand-success;
|
||||
@btn-success-border: @btn-success-bg; // CHANGED
|
||||
|
||||
@btn-info-color: #fff;
|
||||
@btn-info-bg: @brand-info;
|
||||
@btn-info-border: @btn-info-bg; // CHANGED
|
||||
|
||||
@btn-warning-color: #fff;
|
||||
@btn-warning-bg: @brand-warning;
|
||||
@btn-warning-border: @btn-warning-bg; // CHANGED
|
||||
|
||||
@btn-danger-color: #fff;
|
||||
@btn-danger-bg: @brand-danger;
|
||||
@btn-danger-border: @btn-danger-bg; // CHANGED
|
||||
|
||||
@btn-link-disabled-color: @gray-light;
|
||||
|
||||
|
||||
//== Forms
|
||||
//
|
||||
//##
|
||||
|
||||
//** `<input>` background color
|
||||
@input-bg: #fff;
|
||||
//** `<input disabled>` background color
|
||||
@input-bg-disabled: @gray-lighter;
|
||||
|
||||
//** Text color for `<input>`s
|
||||
@input-color: @gray;
|
||||
//** `<input>` border color
|
||||
@input-border: #ccc;
|
||||
//** `<input>` border radius
|
||||
@input-border-radius: @border-radius-base;
|
||||
//** Border color for inputs on focus
|
||||
@input-border-focus: #66afe9;
|
||||
|
||||
//** Placeholder text color
|
||||
@input-color-placeholder: @gray-light;
|
||||
|
||||
//** Default `.form-control` height
|
||||
@input-height-base: (@line-height-computed + (@padding-base-vertical * 2) + 2);
|
||||
//** Large `.form-control` height
|
||||
@input-height-large: (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2);
|
||||
//** Small `.form-control` height
|
||||
@input-height-small: (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2);
|
||||
|
||||
@legend-color: @gray-dark;
|
||||
@legend-border-color: #e5e5e5;
|
||||
|
||||
//** Background color for textual input addons
|
||||
@input-group-addon-bg: @gray-lighter;
|
||||
//** Border color for textual input addons
|
||||
@input-group-addon-border-color: @input-border;
|
||||
|
||||
|
||||
//== Dropdowns
|
||||
//
|
||||
//## Dropdown menu container and contents.
|
||||
|
||||
//** Background for the dropdown menu.
|
||||
@dropdown-bg: #fff;
|
||||
//** Dropdown menu `border-color`.
|
||||
@dropdown-border: rgba(0,0,0,.15);
|
||||
//** Dropdown menu `border-color` **for IE8**.
|
||||
@dropdown-fallback-border: #ccc;
|
||||
//** Divider color for between dropdown items.
|
||||
@dropdown-divider-bg: #e5e5e5;
|
||||
|
||||
//** Dropdown link text color.
|
||||
@dropdown-link-color: @gray-dark;
|
||||
//** Hover color for dropdown links.
|
||||
@dropdown-link-hover-color: darken(@gray-dark, 5%);
|
||||
//** Hover background for dropdown links.
|
||||
@dropdown-link-hover-bg: #f5f5f5;
|
||||
|
||||
//** Active dropdown menu item text color.
|
||||
@dropdown-link-active-color: @component-active-color;
|
||||
//** Active dropdown menu item background color.
|
||||
@dropdown-link-active-bg: @component-active-bg;
|
||||
|
||||
//** Disabled dropdown menu item background color.
|
||||
@dropdown-link-disabled-color: @gray-light;
|
||||
|
||||
//** Text color for headers within dropdown menus.
|
||||
@dropdown-header-color: @gray-light;
|
||||
|
||||
// Note: Deprecated @dropdown-caret-color as of v3.1.0
|
||||
@dropdown-caret-color: #000;
|
||||
|
||||
|
||||
//-- Z-index master list
|
||||
//
|
||||
// Warning: Avoid customizing these values. They're used for a bird's eye view
|
||||
// of components dependent on the z-axis and are designed to all work together.
|
||||
//
|
||||
// Note: These variables are not generated into the Customizer.
|
||||
|
||||
@zindex-navbar: 1000;
|
||||
@zindex-dropdown: 1000;
|
||||
@zindex-popover: 1010;
|
||||
@zindex-tooltip: 1030;
|
||||
@zindex-navbar-fixed: 1030;
|
||||
@zindex-modal-background: 1040;
|
||||
@zindex-modal: 1050;
|
||||
|
||||
|
||||
//== Media queries breakpoints
|
||||
//
|
||||
//## Define the breakpoints at which your layout will change, adapting to different screen sizes.
|
||||
|
||||
// Extra small screen / phone
|
||||
// Note: Deprecated @screen-xs and @screen-phone as of v3.0.1
|
||||
@screen-xs: 480px;
|
||||
@screen-xs-min: @screen-xs;
|
||||
@screen-phone: @screen-xs-min;
|
||||
|
||||
// Small screen / tablet
|
||||
// Note: Deprecated @screen-sm and @screen-tablet as of v3.0.1
|
||||
@screen-sm: 768px;
|
||||
@screen-sm-min: @screen-sm;
|
||||
@screen-tablet: @screen-sm-min;
|
||||
|
||||
// Medium screen / desktop
|
||||
// Note: Deprecated @screen-md and @screen-desktop as of v3.0.1
|
||||
@screen-md: 992px;
|
||||
@screen-md-min: @screen-md;
|
||||
@screen-desktop: @screen-md-min;
|
||||
|
||||
// Large screen / wide desktop
|
||||
// Note: Deprecated @screen-lg and @screen-lg-desktop as of v3.0.1
|
||||
@screen-lg: 1200px;
|
||||
@screen-lg-min: @screen-lg;
|
||||
@screen-lg-desktop: @screen-lg-min;
|
||||
|
||||
// So media queries don't overlap when required, provide a maximum
|
||||
@screen-xs-max: (@screen-sm-min - 1);
|
||||
@screen-sm-max: (@screen-md-min - 1);
|
||||
@screen-md-max: (@screen-lg-min - 1);
|
||||
|
||||
|
||||
//== Grid system
|
||||
//
|
||||
//## Define your custom responsive grid.
|
||||
|
||||
//** Number of columns in the grid.
|
||||
@grid-columns: 12;
|
||||
//** Padding between columns. Gets divided in half for the left and right.
|
||||
@grid-gutter-width: 15px;
|
||||
// Navbar collapse
|
||||
//** Point at which the navbar becomes uncollapsed.
|
||||
@grid-float-breakpoint: @screen-sm-min;
|
||||
//** Point at which the navbar begins collapsing.
|
||||
@grid-float-breakpoint-max: (@grid-float-breakpoint - 1);
|
||||
|
||||
|
||||
//== Navbar
|
||||
//
|
||||
//##
|
||||
|
||||
// Basics of a navbar
|
||||
@navbar-height: 50px;
|
||||
@navbar-margin-bottom: @line-height-computed;
|
||||
@navbar-border-radius: @border-radius-base;
|
||||
@navbar-padding-horizontal: floor((@grid-gutter-width / 2));
|
||||
@navbar-padding-vertical: ((@navbar-height - @line-height-computed) / 2);
|
||||
@navbar-collapse-max-height: 340px;
|
||||
|
||||
@navbar-default-color: #777;
|
||||
@navbar-default-bg: #f8f8f8;
|
||||
@navbar-default-border: darken(@navbar-default-bg, 6.5%);
|
||||
|
||||
// Navbar links
|
||||
@navbar-default-link-color: #777;
|
||||
@navbar-default-link-hover-color: #333;
|
||||
@navbar-default-link-hover-bg: transparent;
|
||||
@navbar-default-link-active-color: #555;
|
||||
@navbar-default-link-active-bg: darken(@navbar-default-bg, 6.5%);
|
||||
@navbar-default-link-disabled-color: #ccc;
|
||||
@navbar-default-link-disabled-bg: transparent;
|
||||
|
||||
// Navbar brand label
|
||||
@navbar-default-brand-color: @navbar-default-link-color;
|
||||
@navbar-default-brand-hover-color: darken(@navbar-default-brand-color, 10%);
|
||||
@navbar-default-brand-hover-bg: transparent;
|
||||
|
||||
// Navbar toggle
|
||||
@navbar-default-toggle-hover-bg: #ddd;
|
||||
@navbar-default-toggle-icon-bar-bg: #888;
|
||||
@navbar-default-toggle-border-color: #ddd;
|
||||
|
||||
|
||||
// Inverted navbar
|
||||
// Reset inverted navbar basics
|
||||
@navbar-inverse-color: @gray-light;
|
||||
@navbar-inverse-bg: #222;
|
||||
@navbar-inverse-border: darken(@navbar-inverse-bg, 10%);
|
||||
|
||||
// Inverted navbar links
|
||||
@navbar-inverse-link-color: @gray-light;
|
||||
@navbar-inverse-link-hover-color: #fff;
|
||||
@navbar-inverse-link-hover-bg: transparent;
|
||||
@navbar-inverse-link-active-color: @navbar-inverse-link-hover-color;
|
||||
@navbar-inverse-link-active-bg: darken(@navbar-inverse-bg, 10%);
|
||||
@navbar-inverse-link-disabled-color: #444;
|
||||
@navbar-inverse-link-disabled-bg: transparent;
|
||||
|
||||
// Inverted navbar brand label
|
||||
@navbar-inverse-brand-color: @navbar-inverse-link-color;
|
||||
@navbar-inverse-brand-hover-color: #fff;
|
||||
@navbar-inverse-brand-hover-bg: transparent;
|
||||
|
||||
// Inverted navbar toggle
|
||||
@navbar-inverse-toggle-hover-bg: #333;
|
||||
@navbar-inverse-toggle-icon-bar-bg: #fff;
|
||||
@navbar-inverse-toggle-border-color: #333;
|
||||
|
||||
|
||||
//== Navs
|
||||
//
|
||||
//##
|
||||
|
||||
//=== Shared nav styles
|
||||
@nav-link-padding: 10px 15px;
|
||||
@nav-link-hover-bg: @gray-lighter;
|
||||
|
||||
@nav-disabled-link-color: @gray-light;
|
||||
@nav-disabled-link-hover-color: @gray-light;
|
||||
|
||||
@nav-open-link-hover-color: #fff;
|
||||
|
||||
//== Tabs
|
||||
@nav-tabs-border-color: #ddd;
|
||||
|
||||
@nav-tabs-link-hover-border-color: @gray-lighter;
|
||||
|
||||
@nav-tabs-active-link-hover-bg: @body-bg;
|
||||
@nav-tabs-active-link-hover-color: @gray;
|
||||
@nav-tabs-active-link-hover-border-color: #ddd;
|
||||
|
||||
@nav-tabs-justified-link-border-color: #ddd;
|
||||
@nav-tabs-justified-active-link-border-color: @body-bg;
|
||||
|
||||
//== Pills
|
||||
@nav-pills-border-radius: @border-radius-base;
|
||||
@nav-pills-active-link-hover-bg: @component-active-bg;
|
||||
@nav-pills-active-link-hover-color: @component-active-color;
|
||||
|
||||
|
||||
//== Pagination
|
||||
//
|
||||
//##
|
||||
|
||||
@pagination-color: @link-color;
|
||||
@pagination-bg: #fff;
|
||||
@pagination-border: #ddd;
|
||||
|
||||
@pagination-hover-color: @link-hover-color;
|
||||
@pagination-hover-bg: @gray-lighter;
|
||||
@pagination-hover-border: #ddd;
|
||||
|
||||
@pagination-active-color: #fff;
|
||||
@pagination-active-bg: @brand-primary;
|
||||
@pagination-active-border: @brand-primary;
|
||||
|
||||
@pagination-disabled-color: @gray-light;
|
||||
@pagination-disabled-bg: #fff;
|
||||
@pagination-disabled-border: #ddd;
|
||||
|
||||
|
||||
//== Pager
|
||||
//
|
||||
//##
|
||||
|
||||
@pager-bg: @pagination-bg;
|
||||
@pager-border: @pagination-border;
|
||||
@pager-border-radius: 15px;
|
||||
|
||||
@pager-hover-bg: @pagination-hover-bg;
|
||||
|
||||
@pager-active-bg: @pagination-active-bg;
|
||||
@pager-active-color: @pagination-active-color;
|
||||
|
||||
@pager-disabled-color: @pagination-disabled-color;
|
||||
|
||||
|
||||
//== Jumbotron
|
||||
//
|
||||
//##
|
||||
|
||||
@jumbotron-padding: 30px;
|
||||
@jumbotron-color: inherit;
|
||||
@jumbotron-bg: @gray-lighter;
|
||||
@jumbotron-heading-color: inherit;
|
||||
@jumbotron-font-size: ceil((@font-size-base * 1.5));
|
||||
|
||||
|
||||
//== Form states and alerts
|
||||
//
|
||||
//## Define colors for form feedback states and, by default, alerts.
|
||||
|
||||
@state-success-text: #3c763d;
|
||||
@state-success-bg: #dff0d8;
|
||||
@state-success-border: darken(spin(@state-success-bg, -10), 5%);
|
||||
|
||||
@state-info-text: #31708f;
|
||||
@state-info-bg: #d9edf7;
|
||||
@state-info-border: darken(spin(@state-info-bg, -10), 7%);
|
||||
|
||||
@state-warning-text: #8a6d3b;
|
||||
@state-warning-bg: #fcf8e3;
|
||||
@state-warning-border: darken(spin(@state-warning-bg, -10), 5%);
|
||||
|
||||
@state-danger-text: #a94442;
|
||||
@state-danger-bg: #f2dede;
|
||||
@state-danger-border: darken(spin(@state-danger-bg, -10), 5%);
|
||||
|
||||
|
||||
//== Tooltips
|
||||
//
|
||||
//##
|
||||
|
||||
//** Tooltip max width
|
||||
@tooltip-max-width: 200px;
|
||||
//** Tooltip text color
|
||||
@tooltip-color: #fff;
|
||||
//** Tooltip background color
|
||||
@tooltip-bg: #000;
|
||||
@tooltip-opacity: .9;
|
||||
|
||||
//** Tooltip arrow width
|
||||
@tooltip-arrow-width: 5px;
|
||||
//** Tooltip arrow color
|
||||
@tooltip-arrow-color: @tooltip-bg;
|
||||
|
||||
|
||||
//== Popovers
|
||||
//
|
||||
//##
|
||||
|
||||
//** Popover body background color
|
||||
@popover-bg: #fff;
|
||||
//** Popover maximum width
|
||||
@popover-max-width: 276px;
|
||||
//** Popover border color
|
||||
@popover-border-color: rgba(0,0,0,.2);
|
||||
//** Popover fallback border color
|
||||
@popover-fallback-border-color: #ccc;
|
||||
|
||||
//** Popover title background color
|
||||
@popover-title-bg: darken(@popover-bg, 3%);
|
||||
|
||||
//** Popover arrow width
|
||||
@popover-arrow-width: 10px;
|
||||
//** Popover arrow color
|
||||
@popover-arrow-color: #fff;
|
||||
|
||||
//** Popover outer arrow width
|
||||
@popover-arrow-outer-width: (@popover-arrow-width + 1);
|
||||
//** Popover outer arrow color
|
||||
@popover-arrow-outer-color: rgba(0,0,0,.25);
|
||||
//** Popover outer arrow fallback color
|
||||
@popover-arrow-outer-fallback-color: #999;
|
||||
|
||||
|
||||
//== Labels
|
||||
//
|
||||
//##
|
||||
|
||||
//** Default label background color
|
||||
@label-default-bg: @gray-light;
|
||||
//** Primary label background color
|
||||
@label-primary-bg: @brand-primary;
|
||||
//** Success label background color
|
||||
@label-success-bg: @brand-success;
|
||||
//** Info label background color
|
||||
@label-info-bg: @brand-info;
|
||||
//** Warning label background color
|
||||
@label-warning-bg: @brand-warning;
|
||||
//** Danger label background color
|
||||
@label-danger-bg: @brand-danger;
|
||||
|
||||
//** Default label text color
|
||||
@label-color: #fff;
|
||||
//** Default text color of a linked label
|
||||
@label-link-hover-color: #fff;
|
||||
|
||||
|
||||
//== Modals
|
||||
//
|
||||
//##
|
||||
|
||||
//** Padding applied to the modal body
|
||||
@modal-inner-padding: 20px;
|
||||
|
||||
//** Padding applied to the modal title
|
||||
@modal-title-padding: 15px;
|
||||
//** Modal title line-height
|
||||
@modal-title-line-height: @line-height-base;
|
||||
|
||||
//** Background color of modal content area
|
||||
@modal-content-bg: #fff;
|
||||
//** Modal content border color
|
||||
@modal-content-border-color: rgba(0,0,0,.2);
|
||||
//** Modal content border color **for IE8**
|
||||
@modal-content-fallback-border-color: #999;
|
||||
|
||||
//** Modal backdrop background color
|
||||
@modal-backdrop-bg: #000;
|
||||
//** Modal backdrop opacity
|
||||
@modal-backdrop-opacity: .5;
|
||||
//** Modal header border color
|
||||
@modal-header-border-color: #e5e5e5;
|
||||
//** Modal footer border color
|
||||
@modal-footer-border-color: @modal-header-border-color;
|
||||
|
||||
@modal-lg: 900px;
|
||||
@modal-md: 600px;
|
||||
@modal-sm: 300px;
|
||||
|
||||
|
||||
//== Alerts
|
||||
//
|
||||
//## Define alert colors, border radius, and padding.
|
||||
|
||||
@alert-padding: 15px;
|
||||
@alert-border-radius: @border-radius-base;
|
||||
@alert-link-font-weight: bold;
|
||||
|
||||
@alert-success-bg: @state-success-bg;
|
||||
@alert-success-text: @state-success-text;
|
||||
@alert-success-border: @state-success-border;
|
||||
|
||||
@alert-info-bg: @state-info-bg;
|
||||
@alert-info-text: @state-info-text;
|
||||
@alert-info-border: @state-info-border;
|
||||
|
||||
@alert-warning-bg: @state-warning-bg;
|
||||
@alert-warning-text: @state-warning-text;
|
||||
@alert-warning-border: @state-warning-border;
|
||||
|
||||
@alert-danger-bg: @state-danger-bg;
|
||||
@alert-danger-text: @state-danger-text;
|
||||
@alert-danger-border: @state-danger-border;
|
||||
|
||||
|
||||
//== Progress bars
|
||||
//
|
||||
//##
|
||||
|
||||
//** Background color of the whole progress component
|
||||
@progress-bg: #f5f5f5;
|
||||
//** Progress bar text color
|
||||
@progress-bar-color: #fff;
|
||||
|
||||
//** Default progress bar color
|
||||
@progress-bar-bg: @brand-primary;
|
||||
//** Success progress bar color
|
||||
@progress-bar-success-bg: @brand-success;
|
||||
//** Warning progress bar color
|
||||
@progress-bar-warning-bg: @brand-warning;
|
||||
//** Danger progress bar color
|
||||
@progress-bar-danger-bg: @brand-danger;
|
||||
//** Info progress bar color
|
||||
@progress-bar-info-bg: @brand-info;
|
||||
|
||||
|
||||
//== List group
|
||||
//
|
||||
//##
|
||||
|
||||
//** Background color on `.list-group-item`
|
||||
@list-group-bg: #fff;
|
||||
//** `.list-group-item` border color
|
||||
@list-group-border: #ddd;
|
||||
//** List group border radius
|
||||
@list-group-border-radius: @border-radius-base;
|
||||
|
||||
//** Background color of single list elements on hover
|
||||
@list-group-hover-bg: #f5f5f5;
|
||||
//** Text color of active list elements
|
||||
@list-group-active-color: @component-active-color;
|
||||
//** Background color of active list elements
|
||||
@list-group-active-bg: @component-active-bg;
|
||||
//** Border color of active list elements
|
||||
@list-group-active-border: @list-group-active-bg;
|
||||
@list-group-active-text-color: lighten(@list-group-active-bg, 40%);
|
||||
|
||||
@list-group-link-color: #555;
|
||||
@list-group-link-heading-color: #333;
|
||||
|
||||
|
||||
//== Panels
|
||||
//
|
||||
//##
|
||||
|
||||
@panel-bg: #fff;
|
||||
@panel-body-padding: 15px;
|
||||
@panel-border-radius: @border-radius-base;
|
||||
|
||||
//** Border color for elements within panels
|
||||
@panel-inner-border: #ddd;
|
||||
@panel-footer-bg: #f5f5f5;
|
||||
|
||||
@panel-default-text: @gray-dark;
|
||||
@panel-default-border: #ddd;
|
||||
@panel-default-heading-bg: #f5f5f5;
|
||||
|
||||
@panel-primary-text: #fff;
|
||||
@panel-primary-border: @brand-primary;
|
||||
@panel-primary-heading-bg: @brand-primary;
|
||||
|
||||
@panel-success-text: @state-success-text;
|
||||
@panel-success-border: @state-success-border;
|
||||
@panel-success-heading-bg: @state-success-bg;
|
||||
|
||||
@panel-info-text: @state-info-text;
|
||||
@panel-info-border: @state-info-border;
|
||||
@panel-info-heading-bg: @state-info-bg;
|
||||
|
||||
@panel-warning-text: @state-warning-text;
|
||||
@panel-warning-border: @state-warning-border;
|
||||
@panel-warning-heading-bg: @state-warning-bg;
|
||||
|
||||
@panel-danger-text: @state-danger-text;
|
||||
@panel-danger-border: @state-danger-border;
|
||||
@panel-danger-heading-bg: @state-danger-bg;
|
||||
|
||||
|
||||
//== Thumbnails
|
||||
//
|
||||
//##
|
||||
|
||||
//** Padding around the thumbnail image
|
||||
@thumbnail-padding: 4px;
|
||||
//** Thumbnail background color
|
||||
@thumbnail-bg: @body-bg;
|
||||
//** Thumbnail border color
|
||||
@thumbnail-border: #ddd;
|
||||
//** Thumbnail border radius
|
||||
@thumbnail-border-radius: @border-radius-base;
|
||||
|
||||
//** Custom text color for thumbnail captions
|
||||
@thumbnail-caption-color: @text-color;
|
||||
//** Padding around the thumbnail caption
|
||||
@thumbnail-caption-padding: 9px;
|
||||
|
||||
|
||||
//== Wells
|
||||
//
|
||||
//##
|
||||
|
||||
@well-bg: #f5f5f5;
|
||||
@well-border: darken(@well-bg, 7%);
|
||||
|
||||
|
||||
//== Badges
|
||||
//
|
||||
//##
|
||||
|
||||
@badge-color: #fff;
|
||||
//** Linked badge text color on hover
|
||||
@badge-link-hover-color: #fff;
|
||||
@badge-bg: @gray-light;
|
||||
|
||||
//** Badge text color in active nav link
|
||||
@badge-active-color: @link-color;
|
||||
//** Badge background color in active nav link
|
||||
@badge-active-bg: #fff;
|
||||
|
||||
@badge-font-weight: bold;
|
||||
@badge-line-height: 1;
|
||||
@badge-border-radius: 10px;
|
||||
|
||||
|
||||
//== Breadcrumbs
|
||||
//
|
||||
//##
|
||||
|
||||
@breadcrumb-padding-vertical: 8px;
|
||||
@breadcrumb-padding-horizontal: 15px;
|
||||
//** Breadcrumb background color
|
||||
@breadcrumb-bg: #f5f5f5;
|
||||
//** Breadcrumb text color
|
||||
@breadcrumb-color: #ccc;
|
||||
//** Text color of current page in the breadcrumb
|
||||
@breadcrumb-active-color: @gray-light;
|
||||
//** Textual separator for between breadcrumb elements
|
||||
@breadcrumb-separator: "/";
|
||||
|
||||
|
||||
//== Carousel
|
||||
//
|
||||
//##
|
||||
|
||||
@carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6);
|
||||
|
||||
@carousel-control-color: #fff;
|
||||
@carousel-control-width: 15%;
|
||||
@carousel-control-opacity: .5;
|
||||
@carousel-control-font-size: 20px;
|
||||
|
||||
@carousel-indicator-active-bg: #fff;
|
||||
@carousel-indicator-border-color: #fff;
|
||||
|
||||
@carousel-caption-color: #fff;
|
||||
|
||||
|
||||
//== Close
|
||||
//
|
||||
//##
|
||||
|
||||
@close-font-weight: bold;
|
||||
@close-color: #000;
|
||||
@close-text-shadow: 0 1px 0 #fff;
|
||||
|
||||
|
||||
//== Code
|
||||
//
|
||||
//##
|
||||
|
||||
@code-color: #c7254e;
|
||||
@code-bg: #f9f2f4;
|
||||
|
||||
@kbd-color: #fff;
|
||||
@kbd-bg: #333;
|
||||
|
||||
@pre-bg: #f5f5f5;
|
||||
@pre-color: @gray-dark;
|
||||
@pre-border-color: #ccc;
|
||||
@pre-scrollable-max-height: 340px;
|
||||
|
||||
|
||||
//== Type
|
||||
//
|
||||
//##
|
||||
|
||||
//** Text muted color
|
||||
@text-muted: @gray-light;
|
||||
//** Abbreviations and acronyms border color
|
||||
@abbr-border-color: @gray-light;
|
||||
//** Headings small color
|
||||
@headings-small-color: @gray-light;
|
||||
//** Blockquote small color
|
||||
@blockquote-small-color: @gray-light;
|
||||
//** Blockquote font size
|
||||
@blockquote-font-size: (@font-size-base * 1.25);
|
||||
//** Blockquote border color
|
||||
@blockquote-border-color: @gray-lighter;
|
||||
//** Page header border color
|
||||
@page-header-border-color: @gray-lighter;
|
||||
//** Width of horizontal description list titles
|
||||
@dl-horizontal-offset: @component-offset-horizontal;
|
||||
//** Horizontal line color.
|
||||
@hr-border: @gray-lighter;
|
||||
|
||||
//== Miscellaneous
|
||||
//
|
||||
//##
|
||||
|
||||
//** Horizontal line color.
|
||||
@hr-border: @gray-lighter;
|
||||
|
||||
//** Horizontal offset for forms and lists.
|
||||
@component-offset-horizontal: 180px;
|
||||
|
||||
|
||||
//== Container sizes
|
||||
//
|
||||
//## Define the maximum width of `.container` for different screen sizes.
|
||||
|
||||
// Small screen / tablet
|
||||
@container-tablet: ((720px + @grid-gutter-width));
|
||||
//** For `@screen-sm-min` and up.
|
||||
@container-sm: @container-tablet;
|
||||
|
||||
// Medium screen / desktop
|
||||
@container-desktop: ((940px + @grid-gutter-width));
|
||||
//** For `@screen-md-min` and up.
|
||||
@container-md: @container-desktop;
|
||||
|
||||
// Large screen / wide desktop
|
||||
@container-large-desktop: ((1140px + @grid-gutter-width));
|
||||
//** For `@screen-lg-min` and up.
|
||||
@container-lg: @container-large-desktop;
|
357
framework/core/ember/app/styles/flarum/discussion.less
Normal file
357
framework/core/ember/app/styles/flarum/discussion.less
Normal file
|
@ -0,0 +1,357 @@
|
|||
.discussion-header, .post, .items div.gap:first-of-type:last-of-type {
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.posts {
|
||||
// margin-bottom: 15px;
|
||||
}
|
||||
.posts .post {
|
||||
padding-top: 25px;
|
||||
padding-bottom: 25px;
|
||||
border-bottom: 1px solid @flarum-body-secondary-color;
|
||||
}
|
||||
.item.highlight .post {
|
||||
box-shadow: inset 0 -5px 0 rgba(255, 255, 0, 0.2), inset 0 5px 0 rgba(255, 255, 0, 0.2);
|
||||
}
|
||||
.items .item:first-of-type {
|
||||
border-top: 0;
|
||||
}
|
||||
.items .item:last-of-type {
|
||||
border-bottom: 0;
|
||||
}
|
||||
.post {
|
||||
padding-left: 65px;
|
||||
transition: 0.2s box-shadow;
|
||||
}
|
||||
.gap {
|
||||
padding: 20px 0;
|
||||
text-align: center;
|
||||
color: @flarum-bg-muted-color;
|
||||
margin: -1px -30px 0;
|
||||
cursor: pointer;
|
||||
border-top: 1px dashed @flarum-body-background-color !important;
|
||||
border-bottom: 1px dashed @flarum-body-background-color !important;
|
||||
transition: color 0.2s, padding 0.2s;
|
||||
background: lighten(@flarum-background-color, 0%);
|
||||
text-transform: uppercase;
|
||||
font-size: 12px;
|
||||
letter-spacing: 1px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
.items div.gap:first-of-type {
|
||||
margin-top: -30px;
|
||||
position: relative;
|
||||
}
|
||||
.items div.gap:last-of-type {
|
||||
margin-bottom: -30px;
|
||||
border-bottom: 0;
|
||||
}
|
||||
.items div.gap:first-of-type:last-of-type {
|
||||
margin: 0;
|
||||
background: @flarum-body-background-color;
|
||||
color: @flarum-body-muted-color;
|
||||
}
|
||||
.items div.gap:first-of-type:last-of-type:after {
|
||||
display: none;
|
||||
}
|
||||
.gap.active, .gap:hover, .gap.loading {
|
||||
padding: 50px 0;
|
||||
}
|
||||
.gap.loading {
|
||||
color: @flarum-bg-muted-color;
|
||||
transition: none;
|
||||
}
|
||||
.gap.down:after {
|
||||
content: '\f078';
|
||||
font-family: 'FontAwesome';
|
||||
display: block;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
margin-bottom: -25px;
|
||||
margin-top: 10px;
|
||||
height: 15px;
|
||||
color: @flarum-body-muted-color;
|
||||
}
|
||||
.gap.up:before {
|
||||
content: '\f077';
|
||||
font-family: 'FontAwesome';
|
||||
display: block;
|
||||
opacity: 0;
|
||||
transition: opacity 0.4s;
|
||||
margin-top: -25px;
|
||||
margin-bottom: 10px;
|
||||
height: 15px;
|
||||
}
|
||||
.gap:hover:before, .gap:hover:after, .gap.loading:before, .gap.loading:after, .gap.active:before, .gap.active:after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.post {
|
||||
line-height: 1.75em;
|
||||
position: relative;
|
||||
}
|
||||
.item .controls {
|
||||
// position: absolute;
|
||||
// right: 10px;
|
||||
// top: -2px;
|
||||
// display: none;
|
||||
float: right;
|
||||
margin: 0 0 0 10px;
|
||||
}
|
||||
.item:hover .controls {
|
||||
display: block;
|
||||
}
|
||||
.post .controls .btn {
|
||||
margin-top: -2px;
|
||||
}
|
||||
.post header {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.post header, .post header a {
|
||||
color: @flarum-body-muted-color;
|
||||
}
|
||||
.post .user {
|
||||
margin: 0;
|
||||
display: inline;
|
||||
}
|
||||
.post .user, .post .user a {
|
||||
color: @flarum-body-primary-color;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
.post .time {
|
||||
font-size: 12px;
|
||||
float: right;
|
||||
&, & a {
|
||||
color: @flarum-body-muted-color;
|
||||
}
|
||||
}
|
||||
.post .reply-to {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.post {
|
||||
// float: left;
|
||||
}
|
||||
.post-meta {
|
||||
// float: left;
|
||||
position: absolute;
|
||||
left: 100%;
|
||||
top: 25px;
|
||||
margin-left: 30px;
|
||||
width: 100px;
|
||||
color: @flarum-body-muted-color;
|
||||
// opacity: 0;
|
||||
// transition: opacity 0s;
|
||||
}
|
||||
.post-icon {
|
||||
float: left;
|
||||
|
||||
}
|
||||
|
||||
.post.deleted {
|
||||
|
||||
& .post-body,
|
||||
& .post-meta,
|
||||
& .avatar,
|
||||
& .reply-to {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& header {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&, & header, & header a {
|
||||
color: fadeout(@flarum-body-text-color, 50%);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1300px) {
|
||||
.post-meta {
|
||||
position: static;
|
||||
margin: 0;
|
||||
width: auto;
|
||||
margin-bottom: -15px;
|
||||
}
|
||||
.post-meta li {
|
||||
display: inline;
|
||||
margin-right: 15px;
|
||||
}
|
||||
.post-meta .reveal {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
}
|
||||
.post-meta ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: 2em;
|
||||
}
|
||||
.post-meta a {
|
||||
color: @flarum-body-muted-color;
|
||||
}
|
||||
.post-meta a:hover {
|
||||
color: @flarum-body-primary-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
.post-meta .reveal {
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
.item:hover .post-meta .reveal {
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
.post header .avatar, .post .post-icon {
|
||||
margin-left: -65px;
|
||||
float: left;
|
||||
}
|
||||
.activity {
|
||||
font-size: 16px;
|
||||
// padding-top: 20px;
|
||||
// padding-bottom: 20px;
|
||||
}
|
||||
.activity, .activity a {
|
||||
color: @flarum-body-text-color;
|
||||
}
|
||||
.post .post-icon {
|
||||
width: 48px;
|
||||
text-align: right;
|
||||
font-size: 22px;
|
||||
}
|
||||
.activity a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.discussion-header h4 {
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
margin: 0 0 15px 0;
|
||||
}
|
||||
.discussion-header h4, .discussion-header h4 a {
|
||||
color: @flarum-body-muted-color;
|
||||
}
|
||||
.discussion-header .category {
|
||||
padding: 3px 7px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.discussion-header h4 a {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
|
||||
.discussion-footer {
|
||||
margin-top: 15px;
|
||||
}
|
||||
.discussion-footer .list-inline {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.discussion-controls {
|
||||
margin: 0 auto;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.discussion-controls .btn {
|
||||
// margin-top: 10px;
|
||||
}
|
||||
.discussion-controls .btn-group {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn-group-suffix {
|
||||
display: table;
|
||||
border-spacing: 1px;
|
||||
}
|
||||
.btn-group-suffix .btn {
|
||||
display: table-cell;
|
||||
float: none;
|
||||
width: 100%;
|
||||
}
|
||||
// .btn-group-suffix .dropdown-toggle {
|
||||
// float: right;
|
||||
// margin-right: -40px;
|
||||
// width: 39px;
|
||||
// }
|
||||
|
||||
|
||||
@media (min-width: @screen-md-min) {
|
||||
|
||||
.discussion-scrubber {
|
||||
margin: 0 auto;
|
||||
text-align: right;
|
||||
margin-right: 3px;
|
||||
}
|
||||
.scrubber a {
|
||||
color: @flarum-bg-muted-color;
|
||||
}
|
||||
.scrubber a i {
|
||||
font-size: 14px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.scrubber a:hover {
|
||||
text-decoration: none;
|
||||
color: @flarum-bg-link-color;
|
||||
}
|
||||
.scrubber .scrollbar {
|
||||
margin: 10px 4px 10px 0;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
.scrubber .scrollbar-before, .scrubber .scrollbar-after {
|
||||
border-right: 1px solid @flarum-bg-secondary-color;
|
||||
}
|
||||
.scrubber .scrollbar-slider {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding: 5px 0;
|
||||
}
|
||||
.scrubber .handle {
|
||||
height: 100%;
|
||||
width: 9px;
|
||||
background: @flarum-bg-primary-color;
|
||||
border-radius: 9px;
|
||||
float: right;
|
||||
margin-right: -4px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.scrubber .disabled .handle {
|
||||
background: @flarum-bg-secondary-color;
|
||||
}
|
||||
.scrubber .info {
|
||||
height: (2em * @line-height-base);
|
||||
margin-top: (-1em * @line-height-base);
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 100%;
|
||||
right: 15px;
|
||||
}
|
||||
.scrubber .info strong {
|
||||
display: block;
|
||||
}
|
||||
.scrubber .info .description {
|
||||
color: @flarum-bg-muted-color;
|
||||
}
|
||||
.scrollbar-highlights {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
.scrollbar-highlights li {
|
||||
position: absolute;
|
||||
right: -6px;
|
||||
background: #fc0;
|
||||
height: 8px;
|
||||
width: 13px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15), inset 0 0 0 1px rgba(255, 255, 255, 0.5);
|
||||
opacity: 0.99;
|
||||
}
|
||||
}
|
||||
|
||||
|
454
framework/core/ember/app/styles/flarum/discussions.less
Normal file
454
framework/core/ember/app/styles/flarum/discussions.less
Normal file
|
@ -0,0 +1,454 @@
|
|||
.discussions-header, .discussions {
|
||||
max-width: 1200px;
|
||||
}
|
||||
|
||||
.discussions {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.discussions > li {
|
||||
.clearfix();
|
||||
border-bottom: 1px solid @flarum-body-secondary-color;
|
||||
position: relative;
|
||||
line-height: 20px;
|
||||
padding-left: 45px;
|
||||
padding-right: 30px;
|
||||
}
|
||||
.discussions > li.highlight, .discussions > li.active {
|
||||
background: lighten(@flarum-body-secondary-color, 8%);
|
||||
}
|
||||
.discussions, .discussions a {
|
||||
color: @flarum-body-muted-color;
|
||||
}
|
||||
.discussions .action {
|
||||
float: left;
|
||||
width: 45px;
|
||||
padding-right: 12px;
|
||||
margin-left: -45px;
|
||||
text-align: right;
|
||||
// visibility: hidden;
|
||||
// opacity: 0.5;
|
||||
min-height: 1px;
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
font-size: 12px;
|
||||
color: @flarum-body-muted-color;
|
||||
}
|
||||
.discussions .action .unread {
|
||||
color: #fff;
|
||||
background: @flarum-body-primary-color;
|
||||
padding: 0 4px;
|
||||
border-radius: 4px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.discussions li:hover .action {
|
||||
visibility: visible;
|
||||
}
|
||||
.discussions .action:hover {
|
||||
text-decoration: none;
|
||||
// opacity: 0.75;
|
||||
}
|
||||
.discussions .action:active {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.discussions .info {
|
||||
float: left;
|
||||
width: 74%;
|
||||
margin-right: 2%;
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
display: block;
|
||||
}
|
||||
.discussions .info .title {
|
||||
font-weight: 300;
|
||||
font-size: 16px;
|
||||
color: @flarum-body-link-color;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.discussions .icon {
|
||||
margin-right: 5px;
|
||||
font-size: 14px;
|
||||
color: @flarum-body-link-color;
|
||||
margin-left: 0;
|
||||
}
|
||||
.discussions .info:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.discussions .info:hover .title {
|
||||
text-decoration: underline;
|
||||
}
|
||||
// .discussions .info > span {
|
||||
// font-size: 12px;
|
||||
// margin-left: 5px;
|
||||
// }
|
||||
.discussions .info .draft {
|
||||
color:#4ea11b;
|
||||
}
|
||||
.discussions .info .excerpt {
|
||||
display: block;
|
||||
white-space: normal;
|
||||
color: @flarum-body-muted-color;
|
||||
line-height: 1.5em;
|
||||
margin: 10px 0 5px;
|
||||
}
|
||||
.discussions .category {
|
||||
float: right;
|
||||
margin: -1px 0 -1px 10px;
|
||||
}
|
||||
.discussions .terminal-post {
|
||||
float: left;
|
||||
width: 15%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
font-size: 90%;
|
||||
}
|
||||
.discussions .terminal-post .avatar {
|
||||
margin: -1px 5px -1px 0;
|
||||
}
|
||||
.discussions .replies {
|
||||
float: left;
|
||||
width: 8%;
|
||||
font-size: 20px;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
.discussions .unread .title {
|
||||
font-weight: 600;
|
||||
}
|
||||
.discussions .locked .title, .discussions .icon-locked {
|
||||
color: #777;
|
||||
}
|
||||
.discussions .locked .unread {
|
||||
background-color: #777;
|
||||
}
|
||||
.discussions .sticky .title, .discussions .icon-sticky {
|
||||
color: #D03202;
|
||||
}
|
||||
.discussions .sticky .unread {
|
||||
background-color: #D03202;
|
||||
}
|
||||
.discussions .following .title {
|
||||
color: #F5A623 !important;
|
||||
}
|
||||
.discussions .icon-following {
|
||||
color: #F5A623;
|
||||
}
|
||||
.discussions .following .unread {
|
||||
background-color: #F5A623 !important;
|
||||
}
|
||||
.discussions .controls {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 11px;
|
||||
display: none;
|
||||
}
|
||||
.discussions li:hover .controls {
|
||||
display: block;
|
||||
}
|
||||
.discussions .relevant-posts {
|
||||
clear: both;
|
||||
// border: solid lighten(@flarum-body-text-color, 62%);
|
||||
// border-width: 1px;
|
||||
border-radius: 3px;
|
||||
margin-bottom: 30px;
|
||||
// padding: 0 10px;
|
||||
}
|
||||
.discussions .relevant-posts .post {
|
||||
padding: 5px 0 5px 35px;
|
||||
display: block;
|
||||
// border-color: lighten(@flarum-body-text-color, 62%);
|
||||
border: 0;
|
||||
// margin-top: -1px;
|
||||
color: @flarum-body-muted-color;
|
||||
}
|
||||
.discussions .relevant-posts .post:hover {
|
||||
text-decoration: none;
|
||||
// background: lighten(@flarum-body-secondary-color, 8%);
|
||||
color: @flarum-body-text-color;
|
||||
// padding-left: 45px;
|
||||
// padding-right: 10px;
|
||||
// margin-left: -10px;
|
||||
// margin-right: -10px;
|
||||
}
|
||||
.discussions .relevant-posts .avatar {
|
||||
margin-left: -35px;
|
||||
opacity: 0.25;
|
||||
float: left;
|
||||
}
|
||||
.discussions .relevant-posts .post:hover .avatar {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.load-more {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.load-more .loading {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.discussions-pane {
|
||||
left: 300px - 375px;
|
||||
width: 100%;
|
||||
}
|
||||
.discussions-pane.paned {
|
||||
position: fixed;
|
||||
z-index: 10;
|
||||
overflow: auto;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 375px;
|
||||
padding: 2.5vh 0;
|
||||
background: #fff;
|
||||
border-right: 5px solid @flarum-background-color;
|
||||
transition: left 0.2s;
|
||||
|
||||
&.showing {
|
||||
left: 300px;
|
||||
}
|
||||
|
||||
& .page-header .pull-right {
|
||||
right: 20px;
|
||||
}
|
||||
|
||||
& .discussions > li {
|
||||
padding-right: 15px;
|
||||
padding-left: 20px;
|
||||
|
||||
& .action {
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
& .info {
|
||||
padding: 15px 0;
|
||||
width: 85%;
|
||||
min-height: 70px;
|
||||
& .category {
|
||||
padding: 1px 4px;
|
||||
font-size: 11px;
|
||||
margin-top: 0;
|
||||
}
|
||||
& .name {
|
||||
display: block;
|
||||
}
|
||||
& .title {
|
||||
font-size: 14px;
|
||||
}
|
||||
& .excerpt {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
& .controls {
|
||||
display: none;
|
||||
}
|
||||
& .terminal-post {
|
||||
width: 13%;
|
||||
text-align: right;
|
||||
float: right;
|
||||
padding: 15px 0 5px;
|
||||
& .avatar {
|
||||
display: none;
|
||||
}
|
||||
& a {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
// & .replies {
|
||||
// width: auto;
|
||||
// float: right;
|
||||
// clear: right;
|
||||
// padding: 0 5px;
|
||||
// border-radius: 4px;
|
||||
// background: @flarum-body-secondary-color;
|
||||
// color: #fff;
|
||||
// font-size: 12px;
|
||||
// font-weight: 600;
|
||||
// margin-bottom: 15px;
|
||||
// }
|
||||
& .replies, & .action .unread {
|
||||
width: auto;
|
||||
float: none;
|
||||
position: absolute;
|
||||
top: 37px;
|
||||
right: 15px;
|
||||
padding: 0 5px;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
pointer-events: none;
|
||||
}
|
||||
& .replies {
|
||||
background: @flarum-body-secondary-color;
|
||||
}
|
||||
&.unread .replies {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pinned {
|
||||
& .discussions-pane {
|
||||
left: 300px;
|
||||
transition: left 0.2s, width 0.2s;
|
||||
}
|
||||
& .discussion-pane {
|
||||
margin-left: 375px;
|
||||
}
|
||||
}
|
||||
|
||||
.discussions-header .select {
|
||||
vertical-align: -1px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@media (max-width: @screen-sm-max) {
|
||||
.discussions-pane.paned {
|
||||
display: none;
|
||||
}
|
||||
.discussions-header {
|
||||
display: none;
|
||||
}
|
||||
.discussions {
|
||||
& > li {
|
||||
margin-left: 20px;
|
||||
padding: 0;
|
||||
line-height: inherit;
|
||||
}
|
||||
& .discussion {
|
||||
position: relative;
|
||||
background: #fff;
|
||||
}
|
||||
& .controls {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
display: block;
|
||||
|
||||
& .dropdown-toggle {
|
||||
display: none;
|
||||
}
|
||||
& .dropdown-menu {
|
||||
float: none;
|
||||
position: static;
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
background: none;
|
||||
height: 100%;
|
||||
|
||||
& li {
|
||||
float: left;
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
|
||||
& a {
|
||||
height: 100%;
|
||||
padding: 25px 10px;
|
||||
background: @flarum-body-primary-color;
|
||||
color: #fff;
|
||||
&.delete {
|
||||
background: #e74135;
|
||||
}
|
||||
}
|
||||
& .fa {
|
||||
font-size: 22px;
|
||||
}
|
||||
& span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
& .info {
|
||||
display: block;
|
||||
padding: 15px 75px 15px 20px;
|
||||
width: auto;
|
||||
float: none;
|
||||
min-height: 70px;
|
||||
margin: 0 0 0 -20px;
|
||||
&.pressed {
|
||||
background: @flarum-body-secondary-color;
|
||||
}
|
||||
& .category {
|
||||
padding: 1px 4px;
|
||||
font-size: 11px;
|
||||
margin-top: 0;
|
||||
}
|
||||
& .name {
|
||||
display: block;
|
||||
}
|
||||
& .excerpt {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
& .info:hover .title {
|
||||
text-decoration: none;
|
||||
}
|
||||
& .info:after {
|
||||
content: '\f054';
|
||||
font-family: FontAwesome;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 15px;
|
||||
color: @flarum-body-secondary-color;
|
||||
}
|
||||
& .info .title {
|
||||
font-weight: 400;
|
||||
font-size: 13px;
|
||||
}
|
||||
& .unread .title {
|
||||
font-weight: 600;
|
||||
}
|
||||
& .terminal-post {
|
||||
width: auto;
|
||||
float: none;
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 25px;
|
||||
padding: 0;
|
||||
pointer-events: none;
|
||||
& .avatar {
|
||||
display: none;
|
||||
}
|
||||
& a {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
& .replies, & .action .unread {
|
||||
width: auto;
|
||||
float: none;
|
||||
position: absolute;
|
||||
top: 37px;
|
||||
right: 25px;
|
||||
padding: 0 5px;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
pointer-events: none;
|
||||
}
|
||||
& .replies {
|
||||
background: @flarum-body-secondary-color;
|
||||
}
|
||||
& .unread .replies {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
858
framework/core/ember/app/styles/flarum/global.less
Normal file
858
framework/core/ember/app/styles/flarum/global.less
Normal file
|
@ -0,0 +1,858 @@
|
|||
@import url(http://fonts.googleapis.com/css?family=Open+Sans:400,300,600);
|
||||
|
||||
@import url(http://fonts.googleapis.com/css?family=Fugaz+One);
|
||||
|
||||
// .pace .pace-progress {
|
||||
// background: @flarum-bg-muted-color;
|
||||
// position: fixed;
|
||||
// z-index: 2000;
|
||||
// top: 0;
|
||||
// left: 0;
|
||||
// height: 2px;
|
||||
|
||||
// -webkit-transition: width 1s;
|
||||
// -moz-transition: width 1s;
|
||||
// -o-transition: width 1s;
|
||||
// transition: width 1s;
|
||||
// }
|
||||
|
||||
// .pace-inactive {
|
||||
// display: none;
|
||||
// }
|
||||
|
||||
body {
|
||||
background-color: @flarum-background-color;
|
||||
background-image: @flarum-background-image;
|
||||
color: @flarum-bg-text-color;
|
||||
// -webkit-font-smoothing: antialiased;
|
||||
}
|
||||
body, input, button, select, textarea {
|
||||
font-size: 13px;
|
||||
}
|
||||
body, h1, h2, h3, h4, h5, h6 {
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
}
|
||||
a {
|
||||
color: @flarum-bg-link-color;
|
||||
}
|
||||
a:hover {
|
||||
color: @flarum-bg-link-color;
|
||||
}
|
||||
#wrapper {
|
||||
padding-top: 2.5vh;
|
||||
padding-bottom: 2.5vh;
|
||||
}
|
||||
@media (min-width: @screen-md-min) {
|
||||
#sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
width: 295px;
|
||||
padding: 2.5vh 25px 2.5vh 0;
|
||||
display: table;
|
||||
z-index: 20;
|
||||
}
|
||||
#sidebar-body {
|
||||
display: table-row;
|
||||
height: 100%;
|
||||
}
|
||||
#sidebar-content {
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
box-sizing: content-box;
|
||||
padding: 10px 25px;
|
||||
margin: 0 -25px;
|
||||
}
|
||||
#sidebar-content .toolbar, #sidebar-content .body {
|
||||
list-style-type: none;
|
||||
margin: 0 0 10px;
|
||||
padding: 0;
|
||||
}
|
||||
#sidebar-footer {
|
||||
display: table-row;
|
||||
}
|
||||
#sidebar footer {
|
||||
border-top: 1px solid @flarum-bg-secondary-color;
|
||||
padding-top: 15px;
|
||||
}
|
||||
#sidebar-footer .statistics {
|
||||
border-bottom: 1px solid @flarum-bg-secondary-color;
|
||||
padding: 10px 0;
|
||||
}
|
||||
#sidebar-footer .statistics i {
|
||||
margin-right: 10px;
|
||||
}
|
||||
#sidebar-footer .statistics a {
|
||||
margin-right: 10px;
|
||||
}
|
||||
#sidebar-footer .meta {
|
||||
padding-top: 10px;
|
||||
.clearfix();
|
||||
}
|
||||
#sidebar-footer .meta > a, #sidebar-footer .meta > .dropdown > a {
|
||||
color: @flarum-bg-muted-color;
|
||||
}
|
||||
#sidebar-footer .language {
|
||||
float: left;
|
||||
}
|
||||
#sidebar-footer .powered-by {
|
||||
float: right;
|
||||
}
|
||||
#sidebar-header {
|
||||
display: table-row;
|
||||
}
|
||||
#sidebar header {
|
||||
}
|
||||
#sidebar header h1 {
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
margin: 0 0 15px;
|
||||
line-height: 1.5em;
|
||||
text-align: center;
|
||||
|
||||
// font-family: Fugaz One;
|
||||
// font-weight: normal;
|
||||
// font-size: 22px;
|
||||
}
|
||||
#sidebar header h1 .logo {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
}
|
||||
#sidebar .search {
|
||||
white-space: nowrap;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
#sidebar .search input, #sidebar .search .search-nav {
|
||||
border-radius: 20px;
|
||||
background: transparent;
|
||||
border: 1px solid @flarum-bg-secondary-color;
|
||||
box-shadow: none;
|
||||
padding: @padding-base-vertical @padding-base-horizontal;
|
||||
}
|
||||
#sidebar .search input {
|
||||
padding-right: 30px;
|
||||
}
|
||||
#sidebar .search input::-webkit-input-placeholder {
|
||||
color: @flarum-bg-muted-color;
|
||||
}
|
||||
#sidebar .search input::-moz-placeholder {
|
||||
color: @flarum-bg-muted-color;
|
||||
}
|
||||
.search-input {
|
||||
position: relative;
|
||||
}
|
||||
.search-input input[type=text] {
|
||||
padding-right: 30px;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
.search-input .clear {
|
||||
position: absolute;
|
||||
right: 13px;
|
||||
top: @padding-base-vertical;
|
||||
line-height: 18px;
|
||||
color: @flarum-bg-muted-color;
|
||||
font-size: 18px;
|
||||
transition: transform 0.1s;
|
||||
transform: scale(0.01);
|
||||
}
|
||||
.search-input.clearable .clear {
|
||||
transform: scale(1);
|
||||
}
|
||||
#sidebar .search input:focus, #sidebar .search .search-nav, #sidebar .search-input.active input {
|
||||
background: @flarum-body-background-color;
|
||||
color: @flarum-body-text-color;
|
||||
border-color: transparent;
|
||||
}
|
||||
#sidebar .search-nav {
|
||||
text-align: center;
|
||||
}
|
||||
#sidebar .search-nav, #sidebar .search-nav a {
|
||||
color: @flarum-body-muted-color;
|
||||
}
|
||||
#sidebar .search-nav a {
|
||||
padding: 5px 10px;
|
||||
margin: 0 -5px;
|
||||
}
|
||||
#sidebar .search-nav a:hover {
|
||||
color: @flarum-body-primary-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
#sidebar .search-nav a.disabled {
|
||||
color: @flarum-body-secondary-color;
|
||||
cursor: default;
|
||||
}
|
||||
.nav-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.nav-list li a {
|
||||
display: block;
|
||||
padding: (@padding-base-vertical + 1) @padding-base-horizontal;
|
||||
color: @flarum-bg-link-color;
|
||||
border-top: 1px solid @flarum-bg-secondary-color;
|
||||
}
|
||||
.nav-list li a i {
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
margin-top: 3px;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.nav-list li.active + li a, .nav-list:not(.categories) li:first-of-type > a {
|
||||
border-top: 0;
|
||||
}
|
||||
.nav-list li.active a {
|
||||
color: contrast(@flarum-bg-primary-color, #000, #fff) !important;
|
||||
background: @flarum-bg-primary-color;
|
||||
border-radius: @border-radius-base;
|
||||
font-weight: 600;
|
||||
border-top: 0;
|
||||
}
|
||||
.nav-list li.active a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.nav-list li a.count {
|
||||
float: right;
|
||||
border: 0;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.nav-list .header {
|
||||
display: block;
|
||||
color: @flarum-bg-muted-color;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
border-top: 0;
|
||||
padding: 8px 0;
|
||||
margin-top: 15px;
|
||||
}
|
||||
.nav-list-small li a {
|
||||
padding: 7px 12px;
|
||||
}
|
||||
|
||||
.nav-list.categories {
|
||||
margin-left: 35px;
|
||||
}
|
||||
.nav-list ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.nav-list ul ul {
|
||||
padding-left: 20px;
|
||||
font-size: 12px;
|
||||
display: none;
|
||||
}
|
||||
.nav-list.categories .color {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: inline-block;
|
||||
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.15);
|
||||
border-radius: 4px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.category {
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
display: inline-block;
|
||||
line-height: @line-height-base;
|
||||
color: @flarum-body-muted-color;
|
||||
border: 1px solid @flarum-body-secondary-color;
|
||||
}
|
||||
// .category-announcements {
|
||||
// background-color: #2D4053;
|
||||
// color: #fff !important;
|
||||
// }
|
||||
// .category-general {
|
||||
// background-color: #D9D9D9;
|
||||
// color: #555 !important;
|
||||
// }
|
||||
// .category-support {
|
||||
// background-color: #5A69DB;
|
||||
// color: #fff !important;
|
||||
// }
|
||||
// .category-feedback {
|
||||
// background-color: #529E3C;
|
||||
// color: #fff !important;
|
||||
// }
|
||||
// .category-core {
|
||||
// background-color: #F7D64E;
|
||||
// color: #7E6500 !important;
|
||||
// }
|
||||
// .category-plugins {
|
||||
// background-color: #EC9A3D;
|
||||
// color: #fff !important;
|
||||
// }
|
||||
// .category-themes {
|
||||
// background-color: #DB5A5A;
|
||||
// color: #fff !important;
|
||||
// }
|
||||
|
||||
#body {
|
||||
background: @flarum-body-background-color;
|
||||
margin-left: 295px;
|
||||
padding: 2.5vh 30px;
|
||||
border-radius: @border-radius-base;
|
||||
// min-height: 94vh;
|
||||
color: @flarum-body-text-color;
|
||||
}
|
||||
.page-header {
|
||||
border-bottom: 1px solid @flarum-body-secondary-color;
|
||||
position: relative;
|
||||
padding-bottom: 40px;
|
||||
margin: 0;
|
||||
}
|
||||
.page-header h2 {
|
||||
text-align: center;
|
||||
font-size: 22px;
|
||||
font-weight: 300;
|
||||
color: @flarum-body-primary-color;
|
||||
margin: 0;
|
||||
}
|
||||
.page-header h2 i {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.page-header:before {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 15px solid transparent;
|
||||
border-bottom-color: @flarum-body-secondary-color;
|
||||
border-top-width: 0;
|
||||
left: 50%;
|
||||
bottom: -1px;
|
||||
margin-left: -15px;
|
||||
}
|
||||
.page-header:after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 14px solid transparent;
|
||||
border-bottom-color: @flarum-body-background-color;
|
||||
border-top-width: 0;
|
||||
left: 50%;
|
||||
bottom: -2px;
|
||||
margin-left: -14px;
|
||||
}
|
||||
.page-header .pull-right {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
.page-header .pull-left {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: @padding-base-vertical @padding-base-horizontal;
|
||||
font-weight: 600;
|
||||
}
|
||||
.btn-following {
|
||||
.button-variant(#d80; #ffd; #ffd);
|
||||
}
|
||||
.btn i {
|
||||
font-size: 14px;
|
||||
}
|
||||
.btn .icon-caret-down {
|
||||
margin: 0 2px;
|
||||
}
|
||||
.btn:active,
|
||||
.btn.active,
|
||||
.btn-group.open .dropdown-toggle {
|
||||
.box-shadow(inset 0 1px 4px rgba(0,0,0,.05));
|
||||
}
|
||||
|
||||
.btn-default {
|
||||
&,
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active,
|
||||
&.active,
|
||||
.open .dropdown-toggle& {
|
||||
background: transparent;
|
||||
border-color: @flarum-body-secondary-color;
|
||||
color: @flarum-body-muted-color;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
#sidebar .btn-default {
|
||||
.button-variant(@flarum-bg-primary-color; @flarum-bg-secondary-color; @flarum-bg-secondary-color);
|
||||
border: 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
padding: @padding-small-vertical @padding-small-horizontal;
|
||||
border-radius: @border-radius-base;
|
||||
font-size: 13px;
|
||||
}
|
||||
.btn-xs {
|
||||
padding: @padding-xs-vertical @padding-xs-horizontal;
|
||||
border-radius: @border-radius-base;
|
||||
}
|
||||
|
||||
.select {
|
||||
// margin-right: @padding-small-horizontal;
|
||||
}
|
||||
.select select {
|
||||
-webkit-appearance: none;
|
||||
background: transparent;
|
||||
border-color: @flarum-body-secondary-color;
|
||||
color: @flarum-body-muted-color;
|
||||
padding: @padding-small-vertical @padding-small-horizontal;
|
||||
padding-right: @padding-small-horizontal + 16;
|
||||
outline: none;
|
||||
border-radius: @border-radius-base;
|
||||
cursor: pointer;
|
||||
margin-right: -3px;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
.select i {
|
||||
margin-left: -@padding-small-horizontal - 12;
|
||||
pointer-events: none;
|
||||
font-size: 12px;
|
||||
color: @flarum-body-muted-color;
|
||||
}
|
||||
|
||||
|
||||
.controls.open {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
|
||||
.tooltip-inner {
|
||||
padding: 5px 8px;
|
||||
}
|
||||
|
||||
#message-container {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 2vh;
|
||||
text-align: center;
|
||||
z-index: 100;
|
||||
pointer-events: none;
|
||||
}
|
||||
.message {
|
||||
background: fade(#feb, 95%);
|
||||
box-shadow: 0 0 0 1px fade(#c2991a, 40%), 0 3px 3px rgba(0, 0, 0, 0.1);
|
||||
padding: @padding-base-vertical @padding-base-horizontal;
|
||||
display: inline-block;
|
||||
border-radius: @border-radius-base;
|
||||
pointer-events: auto;
|
||||
text-align: left;
|
||||
&, & a {
|
||||
color: #a61;
|
||||
}
|
||||
}
|
||||
.message a {
|
||||
font-weight: bold;
|
||||
margin: 0 -10px 0 0;
|
||||
padding: 10px;
|
||||
}
|
||||
.message-warning {
|
||||
background: fade(#c21a1a, 95%);
|
||||
box-shadow: 0 0 0 1px fade(#911, 80%), 0 3px 3px rgba(0, 0, 0, 0.1);
|
||||
&, & a {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.message-text {
|
||||
display: inline-block;
|
||||
max-width: 400px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.message-actions {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.avatar, .avatar img {
|
||||
display: inline-block;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 24px;
|
||||
color: #fff;
|
||||
font-size: 26px;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
line-height: 48px;
|
||||
vertical-align: top;
|
||||
}
|
||||
.avatar-thumb, .avatar-thumb img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
font-size: 13px;
|
||||
line-height: 24px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#session {
|
||||
// position: fixed;
|
||||
// top: 10px;
|
||||
// right: 10px;
|
||||
// background: darken(@flarum-body-background-color, 3%);
|
||||
// box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
|
||||
// border-radius: 24px;
|
||||
// padding: 5px;
|
||||
z-index: 100;
|
||||
text-align: center;
|
||||
}
|
||||
#session > ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
.clearfix();
|
||||
}
|
||||
#session > ul > li {
|
||||
display: inline-block;
|
||||
// border-right: 1px solid @flarum-bg-secondary-color;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#session > ul > li:last-of-type {
|
||||
border-right: 0;
|
||||
}
|
||||
#session > ul > li > a, #session > ul > li > .dropdown > a {
|
||||
display: block;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
#session .avatar-thumb {
|
||||
margin-right: 10px;
|
||||
margin-left: -10px;
|
||||
vertical-align: 0;
|
||||
}
|
||||
#session .notifications-dropdown .dropdown-toggle i {
|
||||
font-size: 14px;
|
||||
// color: @flarum-bg-muted-color;
|
||||
}
|
||||
#session .badge {
|
||||
background: #e00;
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
border-radius: 3px;
|
||||
padding: 2px 3px;
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
right: 8px;
|
||||
line-height: 10px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.btn-icon {
|
||||
color: @flarum-body-muted-color;
|
||||
font-size: 14px;
|
||||
padding-left: 7px;
|
||||
padding-right: 7px;
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
}
|
||||
.btn-icon:hover {
|
||||
// color: @flarum-body-muted-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
||||
.dropdown-menu .fa {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
||||
.loading {
|
||||
color: @flarum-body-muted-color;
|
||||
padding: 50px 0;
|
||||
}
|
||||
|
||||
/* Full width styles */
|
||||
@media (min-width: @screen-md-min) {
|
||||
.container {
|
||||
max-width: none;
|
||||
width: auto;
|
||||
padding: 0;
|
||||
}
|
||||
#sidebar {
|
||||
width: 325px;
|
||||
padding-left: 25px;
|
||||
background: @flarum-background-color;
|
||||
background-image: @flarum-background-image;
|
||||
}
|
||||
#body {
|
||||
margin-left: 325px;
|
||||
border-radius: 0;
|
||||
// max-width: 900px;
|
||||
}
|
||||
#wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background: @flarum-body-background-color;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* New post */
|
||||
.composer {
|
||||
position: fixed;
|
||||
left: 330px;
|
||||
right: 30px;
|
||||
bottom: -100%;
|
||||
// right: 0;
|
||||
z-index: 9;
|
||||
max-width: 800px;
|
||||
// transition: bottom 0.5s;
|
||||
}
|
||||
#body {
|
||||
// transition: padding-bottom 0.5s;
|
||||
}
|
||||
.composer-handle {
|
||||
// background: @flarum-background-color;
|
||||
cursor: row-resize;
|
||||
// height: 5px;
|
||||
}
|
||||
.composer-body {
|
||||
padding: 15px 15px 0;
|
||||
border: 1px solid @flarum-body-primary-color;
|
||||
border-radius: 4px;
|
||||
background: @flarum-body-background-color;
|
||||
// background: @flarum-body-background-color;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.composer-controls {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 15px;
|
||||
}
|
||||
.composer-controls a {
|
||||
color: @flarum-body-muted-color;
|
||||
font-size: 14px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.composer-controls a:hover {
|
||||
color: @flarum-body-primary-color;
|
||||
font-size: 14px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.composer h3 {
|
||||
font-size: 13px;
|
||||
color: @flarum-body-muted-color;
|
||||
font-weight: 400;
|
||||
margin: 0 0 15px;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 1px solid @flarum-body-secondary-color;
|
||||
}
|
||||
|
||||
.composer-editor {
|
||||
|
||||
}
|
||||
.composer-editor textarea {
|
||||
.box-shadow(none);
|
||||
min-height: 200px;
|
||||
background: @flarum-body-background-color;
|
||||
border: 0;
|
||||
resize: none;
|
||||
padding: 0;
|
||||
// padding-bottom: 0;
|
||||
&:focus {
|
||||
.box-shadow(none);
|
||||
}
|
||||
}
|
||||
.composer-editor-controls {
|
||||
padding: 15px 0;
|
||||
.clearfix();
|
||||
}
|
||||
.composer-editor-controls .btn {
|
||||
}
|
||||
.composer-editor-controls .pull-left > .btn:first-child {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.composer-editor-controls .pull-right > .btn {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
|
||||
.composer.collapsed .composer-handle {
|
||||
height: 5px;
|
||||
}
|
||||
.composer.collapsed .composer-body {
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
.composer.collapsed .composer-editor {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.highlight-keyword {
|
||||
background: #fff28e;
|
||||
padding: 1px 4px;
|
||||
border-radius: 3px;
|
||||
color: @flarum-body-text-color;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Small devices (tablets, 768px and up) */
|
||||
@media (min-width: @screen-sm-min) {
|
||||
#sidebar-header .mobile-header {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Medium devices (desktops, 992px and up) */
|
||||
@media (min-width: @screen-md-min) {
|
||||
|
||||
}
|
||||
|
||||
/* Large devices (large desktops, 1200px and up) */
|
||||
@media (min-width: @screen-lg-min) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: @screen-sm-max) {
|
||||
body {
|
||||
padding-top: 45px;
|
||||
}
|
||||
#sidebar-header header {
|
||||
display: none;
|
||||
}
|
||||
#sidebar-header .mobile-header {
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: @flarum-background-color;
|
||||
padding: 12px 10px;
|
||||
color: @flarum-bg-primary-color;
|
||||
text-align: center;
|
||||
z-index: 20;
|
||||
height: 45px;
|
||||
}
|
||||
#sidebar-header .mobile-header h1 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
display: inline-block;
|
||||
width: 80%;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
#sidebar-header .mobile-header .back {
|
||||
position: absolute;
|
||||
left: 15px;
|
||||
top: 10px;
|
||||
font-size: 18px;
|
||||
}
|
||||
#sidebar-header .search {
|
||||
padding: 10px 15px;
|
||||
margin: 0;
|
||||
background: lighten(@flarum-body-secondary-color, 5%);
|
||||
& .search-input input {
|
||||
border: 0;
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
#sidebar-footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#sidebar-content {
|
||||
overflow: hidden;
|
||||
height: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: block;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: auto;
|
||||
background: #fff;
|
||||
box-shadow: 0 -1px 0 rgba(0, 0, 0, 0.15);
|
||||
height: 45px;
|
||||
width: auto;
|
||||
z-index: 20;
|
||||
}
|
||||
#sidebar-content .toolbar {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: table;
|
||||
list-style-type: none;
|
||||
width: 100%;
|
||||
|
||||
& li {
|
||||
display: table-cell;
|
||||
text-align: center;
|
||||
}
|
||||
& li:first-of-type {
|
||||
text-align: left;
|
||||
}
|
||||
& li:last-of-type {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
& .btn {
|
||||
padding: 13px;
|
||||
transition: opacity 0.3s;
|
||||
&:active {
|
||||
opacity: 0.25;
|
||||
}
|
||||
}
|
||||
& .btn, & .btn-default, & .btn-group {
|
||||
display: inline-block;
|
||||
background: none !important;
|
||||
border: 0 !important;
|
||||
box-shadow: none !important;
|
||||
width: auto;
|
||||
|
||||
& span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
#sidebar-content .body {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.discussion-scrubber {
|
||||
& .scrubber-first, & .scrollbar-before, & .handle, & .description, & .scrollbar-after, & .scrubber-last {
|
||||
display: none;
|
||||
}
|
||||
& .info {
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#body {
|
||||
margin-left: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
0
framework/core/ember/app/templates/.gitkeep
Normal file
0
framework/core/ember/app/templates/.gitkeep
Normal file
56
framework/core/ember/app/templates/application.hbs
Normal file
56
framework/core/ember/app/templates/application.hbs
Normal file
|
@ -0,0 +1,56 @@
|
|||
{{outlet "modal"}}
|
||||
|
||||
{{#if notificationMessage}}
|
||||
<div id="message-container">
|
||||
{{notification-message message=notificationMessage closeAction="hideMessage"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div id="wrapper" class="sidebar-open">
|
||||
|
||||
<div id="sidebar">
|
||||
|
||||
<div id="sidebar-header">
|
||||
<header>
|
||||
<h1>
|
||||
{{#link-to "discussions" (query-params searchQuery="" sort="recent" show="discussions")}}
|
||||
{{{view.title}}}
|
||||
{{/link-to}}
|
||||
</h1>
|
||||
</header>
|
||||
|
||||
<header class="mobile-header">
|
||||
<a href="javascript:window.history.back()" class="back">{{fa-icon "chevron-left"}}</a>
|
||||
<h1>{{documentTitle}}</h1>
|
||||
</header>
|
||||
|
||||
<div class="search">
|
||||
{{#if showDiscussionStream}}
|
||||
{{render "discussions-nav" controller="discussions"}}
|
||||
{{else}}
|
||||
{{search-input placeholder="Search forum..." value=searchQuery active=searchActive action="search"}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="sidebar-body">
|
||||
<div id="sidebar-content">
|
||||
{{liquid-outlet "sidebar" enableGrowth=false}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="sidebar-footer">
|
||||
<footer>
|
||||
{{view "session"}}
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="body">
|
||||
{{outlet}}
|
||||
|
||||
{{render "composer"}}
|
||||
</div>
|
||||
|
||||
</div>
|
|
@ -0,0 +1,3 @@
|
|||
{{#each item in items}}
|
||||
{{view item}}
|
||||
{{/each}}
|
|
@ -0,0 +1,3 @@
|
|||
{{#each item in items}}
|
||||
<li {{bind-attr class="item.liClass"}}>{{view item}}</li>
|
||||
{{/each}}
|
13
framework/core/ember/app/templates/components/menu-split.hbs
Normal file
13
framework/core/ember/app/templates/components/menu-split.hbs
Normal file
|
@ -0,0 +1,13 @@
|
|||
{{#if items}}
|
||||
<div class="dropdown">
|
||||
<div class="btn-group btn-group-suffix">
|
||||
{{#each item in visibleItems}}
|
||||
{{view item class="btn btn-default"}}
|
||||
{{/each}}
|
||||
{{#if hiddenItems}}
|
||||
<button data-toggle="dropdown" class="dropdown-toggle btn btn-default">{{fa-icon "caret-down"}}</button>
|
||||
{{menu-list items=hiddenItems class="dropdown-menu pull-right"}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
|
@ -0,0 +1,9 @@
|
|||
<div {{bind-attr class=":message message.class"}}>
|
||||
<span class="message-text">{{message.text}}</span>
|
||||
<span class="message-actions">
|
||||
<a href="#">Undo</a> <!-- menu -->
|
||||
{{#if message.dismissable}}
|
||||
<a href="#" {{action "close"}}><i class="fa fa-times"></i></a>
|
||||
{{/if}}
|
||||
</span>
|
||||
</div>
|
|
@ -0,0 +1,26 @@
|
|||
{{#if post.deleteTime}}
|
||||
<i class="post-icon fa fa-trash-o"></i>
|
||||
{{/if}}
|
||||
|
||||
<header>
|
||||
<h3 class="user">
|
||||
{{#link-to "user" post.user}}{{user-avatar post.user}} {{post.user.username}}{{/link-to}}
|
||||
</h3>
|
||||
</header>
|
||||
|
||||
<div class="post-body">
|
||||
{{{post.contentHtml}}}
|
||||
</div>
|
||||
|
||||
<aside class="post-meta">
|
||||
<ul>
|
||||
{{!-- {{menu-list items=meta}} --}}
|
||||
{{#if post.likes}}
|
||||
<li>
|
||||
<a href="">{{fa-icon "thumbs-o-up"}} {{post.likes}}</a>
|
||||
·
|
||||
<a href="">Like</a>
|
||||
</li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
</aside>
|
|
@ -0,0 +1,4 @@
|
|||
<div class="activity">
|
||||
<i class="post-icon fa fa-pencil"></i>
|
||||
{{#link-to "user" post.user}}{{post.user.username}}{{/link-to}} named the discussion: <strong>{{post.content}}</strong>.
|
||||
</div>
|
|
@ -0,0 +1,2 @@
|
|||
{{input type="text" placeholder="Search forum..." class="form-control" value=value action="search"}}
|
||||
<a href="#" class="clear">{{fa-icon "times-circle"}}</a>
|
|
@ -0,0 +1,9 @@
|
|||
<textarea class="form-control" {{bind-attr placeholder=placeholder}}></textarea>
|
||||
|
||||
<div class="composer-editor-controls">
|
||||
<div class="pull-left">
|
||||
<button class="btn btn-primary">Submit Post</button>
|
||||
<button class="btn btn-default btn-icon btn-sm"><i class="fa fa-fw fa-image"></i></button>
|
||||
<button class="btn btn-default btn-icon btn-sm"><i class="fa fa-fw fa-paperclip"></i></button>
|
||||
</div>
|
||||
</div>
|
17
framework/core/ember/app/templates/composer.hbs
Normal file
17
framework/core/ember/app/templates/composer.hbs
Normal file
|
@ -0,0 +1,17 @@
|
|||
<div class="composer-handle"></div>
|
||||
|
||||
<div class="composer-body">
|
||||
|
||||
<div class="composer-controls">
|
||||
<a href="#" {{action "fullScreen"}}><i class="fa fa-fw fa-expand"></i></a>
|
||||
<a href="#" {{action "hide"}}><i class="fa fa-fw fa-caret-square-o-down"></i></a>
|
||||
<a href="#" {{action "close"}}><i class="fa fa-fw fa-times"></i></a>
|
||||
</div>
|
||||
|
||||
<h3>{{{title}}}</h3>
|
||||
|
||||
<div class="composer-editor">
|
||||
{{text-editor placeholder=""}}
|
||||
</div>
|
||||
|
||||
</div>
|
9
framework/core/ember/app/templates/discussion-header.hbs
Normal file
9
framework/core/ember/app/templates/discussion-header.hbs
Normal file
|
@ -0,0 +1,9 @@
|
|||
{{#if category}}
|
||||
<h4>
|
||||
<span {{bind-attr class=":category view.categoryClass"}}>{{category}}</span>
|
||||
</h4>
|
||||
{{/if}}
|
||||
|
||||
<h2>
|
||||
{{title}}
|
||||
</h2>
|
13
framework/core/ember/app/templates/discussion-post.hbs
Normal file
13
framework/core/ember/app/templates/discussion-post.hbs
Normal file
|
@ -0,0 +1,13 @@
|
|||
{{#if view.controls}}
|
||||
<div class="controls btn-group">
|
||||
<button data-toggle="dropdown" class="dropdown-toggle btn btn-default btn-icon btn-xs">{{fa-icon "caret-down"}}</button>
|
||||
{{menu-list items=view.controls class="dropdown-menu pull-right"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#link-to "discussion" view.post.discussion (query-params start=view.post.number) class="time"}}
|
||||
{{abbreviate-time view.post.time}}
|
||||
{{!-- #{{view.post.number}} (ID: {{view.post.id}}) --}}
|
||||
{{/link-to}}
|
||||
|
||||
{{post-content view.post}}
|
20
framework/core/ember/app/templates/discussion-scrollbar.hbs
Normal file
20
framework/core/ember/app/templates/discussion-scrollbar.hbs
Normal file
|
@ -0,0 +1,20 @@
|
|||
<a href="#" class="scrubber-first" {{action "firstPost" target="view"}}>Original Post <i class="fa fa-angle-double-up"></i></a>
|
||||
<div class="scrollbar disabled">
|
||||
<div class="scrollbar-before"></div>
|
||||
<div class="scrollbar-slider">
|
||||
<div class="handle"></div>
|
||||
<div class="info">
|
||||
<strong><span class="index">0</span> of <span class="count">{{postStream.count}}</span> posts</strong>
|
||||
<span class="description"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scrollbar-after"></div>
|
||||
{{#if relevantPostRanges}}
|
||||
<ul class="scrollbar-highlights">
|
||||
{{#each range in relevantPostRanges}}
|
||||
<li {{bind-attr style=range}}></li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
</div>
|
||||
<a href="#" class="scrubber-last" {{action "lastPost" target="view"}}>Now <i class="fa fa-angle-double-down"></i></a>
|
19
framework/core/ember/app/templates/discussion.hbs
Normal file
19
framework/core/ember/app/templates/discussion.hbs
Normal file
|
@ -0,0 +1,19 @@
|
|||
<header class="page-header discussion-header">
|
||||
{{partial "discussion-header"}}
|
||||
</header>
|
||||
|
||||
<section class="items posts">
|
||||
{{#each item in postStream}}
|
||||
|
||||
{{#view "discussion-item" item=item}}
|
||||
{{#if item.post}}{{view "discussion-post" post=item.post}}{{/if}}
|
||||
{{/view}}
|
||||
|
||||
{{/each}}
|
||||
</section>
|
||||
|
||||
{{#if postStream.lastLoaded}}
|
||||
<footer class="page-footer discussion-footer">
|
||||
{{menu-list items=view.footerControls class="list-inline"}}
|
||||
</footer>
|
||||
{{/if}}
|
|
@ -0,0 +1,7 @@
|
|||
<h2>
|
||||
{{#if searchQuery}}
|
||||
{{fa-icon "search"}} {{searchQuery}}
|
||||
{{else}}
|
||||
All Discussions
|
||||
{{/if}}
|
||||
</h2>
|
4
framework/core/ember/app/templates/discussions-nav.hbs
Normal file
4
framework/core/ember/app/templates/discussions-nav.hbs
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
{{#link-to "discussions"}}{{index}} of {{count}} {{view.type}}{{/link-to}}
|
||||
{{#link-to "discussion" previous classNameBindings="previous::disabled"}}{{fa-icon "chevron-up"}}{{/link-to}}
|
||||
{{#link-to "discussion" next classNameBindings="next::disabled"}}{{fa-icon "chevron-down"}}{{/link-to}}
|
60
framework/core/ember/app/templates/discussions-result.hbs
Normal file
60
framework/core/ember/app/templates/discussions-result.hbs
Normal file
|
@ -0,0 +1,60 @@
|
|||
<div class="controls btn-group">
|
||||
<button data-toggle="dropdown" class="dropdown-toggle btn btn-default btn-xs btn-icon">{{fa-icon "caret-down"}}</button>
|
||||
{{menu-list items=view.controls class="dropdown-menu pull-right"}}
|
||||
</div>
|
||||
|
||||
<div class="discussion">
|
||||
|
||||
{{!-- {{#if view.action}}
|
||||
<a href="#" class="action" {{action "action" target="view"}}>
|
||||
{{partial view.action}}
|
||||
</a>
|
||||
{{/if}} --}}
|
||||
|
||||
<a href="#" class="action" {{action "action" target="view"}}>
|
||||
{{#if discussion.unread}}
|
||||
<span class="unread">{{discussion.unread}}</span>
|
||||
{{/if}}
|
||||
</a>
|
||||
|
||||
{{#link-to "discussion" discussion.content (query-params searchQuery=searchQuery) class="info"}}
|
||||
{{#if discussion.category}}
|
||||
<span {{bind-attr class=":category view.categoryClass"}}>{{discussion.category}}</span>
|
||||
{{/if}}
|
||||
<span class="name">
|
||||
{{!-- {{#if discussion.following}}{{fa-icon "star" class="icon icon-following"}}{{/if}} --}}
|
||||
{{#if discussion.sticky}}{{fa-icon "thumb-tack" class="icon icon-sticky"}}{{/if}}
|
||||
{{#if discussion.locked}}{{fa-icon "lock" class="icon icon-locked"}}{{/if}}
|
||||
<strong class="title">{{highlight-words discussion.title searchQuery}}</strong>
|
||||
</span>
|
||||
{{#if discussion.sticky}}
|
||||
<span class="excerpt">{{discussion.excerpt}}</span>
|
||||
{{/if}}
|
||||
{{/link-to}}
|
||||
|
||||
<span class="terminal-post">
|
||||
{{#if displayStartPosts}}
|
||||
{{#link-to "user" discussion.startUser}}{{user-avatar discussion.startUser class="avatar-thumb"}}{{/link-to}}
|
||||
{{#link-to "discussion" discussion.content}}{{abbreviate-time discussion.startTime}}{{/link-to}}
|
||||
{{else}}
|
||||
{{#link-to "user" discussion.lastUser}}{{user-avatar discussion.lastUser class="avatar-thumb"}}{{/link-to}}
|
||||
{{#link-to "discussion" discussion.content (query-params start="last")}}{{abbreviate-time discussion.lastTime}}{{/link-to}}
|
||||
{{/if}}
|
||||
</span>
|
||||
|
||||
<span class="replies">{{abbreviate-number discussion.repliesCount}}</span>
|
||||
|
||||
{{#if view.relevantPosts}}
|
||||
<div class="relevant-posts">
|
||||
{{#each post in view.relevantPosts}}
|
||||
{{#link-to "discussion" discussion.content (query-params start=post.number) class="post item"}}
|
||||
{{user-avatar post.user class="avatar-thumb"}}
|
||||
<span class="post-body">{{highlight-words post.relevantContent searchQuery}}</span>
|
||||
{{/link-to}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{render-hook "discussions-result"}}
|
||||
|
||||
</div>
|
46
framework/core/ember/app/templates/discussions.hbs
Normal file
46
framework/core/ember/app/templates/discussions.hbs
Normal file
|
@ -0,0 +1,46 @@
|
|||
<div {{bind-attr class=":discussions-pane paned paneShowing:showing"}}>
|
||||
<header class="page-header discussions-header">
|
||||
<div class="pull-right">
|
||||
{{#if paned}}
|
||||
<a href="" {{bind-attr class=":btn :btn-default :btn-sm panePinned:active"}} {{action "togglePinned"}}>{{fa-icon "thumb-tack"}}</a>
|
||||
{{else}}
|
||||
<span class="btn-group">
|
||||
{{#link-to (query-params show="discussions") class="btn btn-default btn-sm"}}{{fa-icon "bars"}}{{/link-to}}
|
||||
{{#link-to (query-params show="posts") class="btn btn-default btn-sm"}}{{fa-icon "square-o"}}{{/link-to}}
|
||||
</span>
|
||||
|
||||
{{select-input
|
||||
content=sortOptions
|
||||
optionValuePath="content.sort"
|
||||
optionLabelPath="content.label"
|
||||
value=sort}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{partial "discussions-header"}}
|
||||
</header>
|
||||
|
||||
{{#if resultsLoading}}
|
||||
{{loading-indicator size="small"}}
|
||||
{{else}}
|
||||
<ul class="discussions">
|
||||
{{#each discussion in content}}
|
||||
{{view "discussions-result" discussion=discussion}}
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
||||
{{#if moreResults}}
|
||||
<div class="load-more">
|
||||
{{#if loadingMore}}
|
||||
{{loading-indicator size="small"}}
|
||||
{{else}}
|
||||
<a href="#" class="btn btn-default" {{action loadMore}}>Load More</a>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="discussion-pane">
|
||||
{{liquid-outlet}}
|
||||
</div>
|
5
framework/core/ember/app/templates/error.hbs
Normal file
5
framework/core/ember/app/templates/error.hbs
Normal file
|
@ -0,0 +1,5 @@
|
|||
<h1>Oops! Something went wrong.</h1>
|
||||
|
||||
<p>{{message}}</p>
|
||||
|
||||
<pre>{{stack}}</pre>
|
1
framework/core/ember/app/templates/loading.hbs
Normal file
1
framework/core/ember/app/templates/loading.hbs
Normal file
|
@ -0,0 +1 @@
|
|||
{{loading-indicator size="large"}}
|
27
framework/core/ember/app/templates/login.hbs
Normal file
27
framework/core/ember/app/templates/login.hbs
Normal file
|
@ -0,0 +1,27 @@
|
|||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content">
|
||||
<form {{action 'authenticate' on='submit'}}>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" {{action close target="view"}}>×</button>
|
||||
<h3 style="margin:0">Log In</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
{{input value=identification type="text" class="form-control" placeholder="Username or Email"}}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{{input value=password type="password" class="form-control" placeholder="Password"}}
|
||||
</div>
|
||||
<div class="checkbox" style="margin:0">
|
||||
<label>
|
||||
{{input checked=remember type="checkbox"}} Remember me
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer" style="margin:0">
|
||||
<a href="#" class="btn btn-default" {{action close target="view"}}>Close</a>
|
||||
{{input class="btn btn-primary" type="submit" value="Log In"}}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
36
framework/core/ember/app/templates/session.hbs
Normal file
36
framework/core/ember/app/templates/session.hbs
Normal file
|
@ -0,0 +1,36 @@
|
|||
<div id="session">
|
||||
{{#if session.user}}
|
||||
<ul>
|
||||
<li>
|
||||
<div class="dropdown dropup">
|
||||
<a href="#" data-toggle="dropdown" class="dropdown-toggle">{{avatar session.user class="avatar-thumb"}}{{session.user.username}} <i class="fa fa-fw fa-caret-down"></i></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#">Action</a></li>
|
||||
<li><a href="#">Another action</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="dropdown dropup notifications-dropdown">
|
||||
<a href="#" data-toggle="dropdown" class="dropdown-toggle" style="position:relative">
|
||||
<i class="fa fa-fw fa-envelope"></i>
|
||||
<span class="badge">1</span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#">Action</a></li>
|
||||
<li><a href="#">Another action</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
{{else}}
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<a href="#" class="btn btn-default btn-block" {{action "login"}}>Log In</a>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<a href="#" class="btn btn-default btn-block" {{action "signup"}}>Sign Up</a>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
12
framework/core/ember/app/transitions.js
Normal file
12
framework/core/ember/app/transitions.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
export default function() {
|
||||
this.transition(
|
||||
this.fromRoute('discussions-sidebar'),
|
||||
this.toRoute('discussion-sidebar'),
|
||||
this.use('slideLeft')
|
||||
);
|
||||
this.transition(
|
||||
this.fromRoute('discussions'),
|
||||
this.toRoute('discussion'),
|
||||
this.use('slideLeft')
|
||||
);
|
||||
}
|
2
framework/core/ember/app/transitions/slide-left.js
Normal file
2
framework/core/ember/app/transitions/slide-left.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import { curryTransition } from "vendor/liquid-fire";
|
||||
export default curryTransition('slide', 'x', -1, { duration: 300 });
|
2
framework/core/ember/app/transitions/slide-right.js
Normal file
2
framework/core/ember/app/transitions/slide-right.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import { curryTransition } from "vendor/liquid-fire";
|
||||
export default curryTransition('slide', 'x', 1, { duration: 300 });
|
48
framework/core/ember/app/transitions/slide.js
Normal file
48
framework/core/ember/app/transitions/slide.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
import { stop, animate, Promise, isAnimating, finish } from "vendor/liquid-fire";
|
||||
|
||||
export default function slide(oldView, insertNewView, dimension, direction, opts) {
|
||||
var oldParams = {},
|
||||
newParams = {},
|
||||
firstStep,
|
||||
property,
|
||||
measure;
|
||||
|
||||
if (dimension.toLowerCase() === 'x') {
|
||||
property = 'translateX';
|
||||
measure = 'width';
|
||||
} else {
|
||||
property = 'translateY';
|
||||
measure = 'height';
|
||||
}
|
||||
|
||||
if (isAnimating(oldView, 'moving-in')) {
|
||||
firstStep = finish(oldView, 'moving-in');
|
||||
} else {
|
||||
stop(oldView);
|
||||
firstStep = Promise.cast();
|
||||
}
|
||||
|
||||
|
||||
return firstStep.then(insertNewView).then(function(newView){
|
||||
// if (newView && newView.$() && oldView && oldView.$()) {
|
||||
// var sizes = [parseInt(newView.$().css(measure), 10),
|
||||
// parseInt(oldView.$().css(measure), 10)];
|
||||
// var bigger = Math.max.apply(null, sizes);
|
||||
var bigger = 20;
|
||||
oldParams[property] = (bigger * direction) + 'px';
|
||||
newParams[property] = ["0px", (-1 * bigger * direction) + 'px'];
|
||||
// }
|
||||
// else {
|
||||
// oldParams[property] = (100 * direction) + '%';
|
||||
// newParams[property] = ["0%", (-100 * direction) + '%'];
|
||||
// }
|
||||
|
||||
oldParams['opacity'] = [0, 1];
|
||||
newParams['opacity'] = [1, 0];
|
||||
|
||||
return Promise.all([
|
||||
animate(oldView, oldParams, opts),
|
||||
animate(newView, newParams, opts, 'moving-in')
|
||||
]);
|
||||
});
|
||||
}
|
0
framework/core/ember/app/utils/.gitkeep
Normal file
0
framework/core/ember/app/utils/.gitkeep
Normal file
33
framework/core/ember/app/utils/menu.js
Normal file
33
framework/core/ember/app/utils/menu.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
import NamedContainerView from './named-container-view';
|
||||
import MenuItemSeparator from '../components/menu-item-separator';
|
||||
|
||||
export default NamedContainerView.extend({
|
||||
|
||||
tagName: 'ul',
|
||||
|
||||
active: null,
|
||||
|
||||
i: 1,
|
||||
addSeparator: function(index) {
|
||||
var item = MenuItemSeparator;
|
||||
this.addItem('separator'+(this.i++), item, index);
|
||||
},
|
||||
|
||||
activeChanged: function() {
|
||||
var active = this.get('active');
|
||||
if (typeof active != 'array') {
|
||||
active = [active];
|
||||
}
|
||||
|
||||
var namedViews = this.get('namedViews');
|
||||
var view;
|
||||
for (var name in namedViews) {
|
||||
if (namedViews.hasOwnProperty(name) && (view = namedViews.get(name))) {
|
||||
view.set('active', active.indexOf(name) !== -1);
|
||||
}
|
||||
}
|
||||
}.observes('active')
|
||||
|
||||
});
|
65
framework/core/ember/app/utils/named-container-view.js
Normal file
65
framework/core/ember/app/utils/named-container-view.js
Normal file
|
@ -0,0 +1,65 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.ArrayProxy.extend({
|
||||
|
||||
content: null,
|
||||
|
||||
namedViews: null,
|
||||
|
||||
init: function() {
|
||||
this.set('content', Ember.A());
|
||||
this.set('namedViews', Ember.Object.create());
|
||||
this._super();
|
||||
},
|
||||
|
||||
// Add an item to the container.
|
||||
addItem: function(name, viewClass, index) {
|
||||
// view = this.createChildView(view);
|
||||
|
||||
if (typeof index == 'undefined') {
|
||||
index = this.get('length');
|
||||
}
|
||||
this.replace(index, 0, [viewClass]);
|
||||
this.get('namedViews').set(name, viewClass);
|
||||
},
|
||||
|
||||
// Remove an item from the container.
|
||||
removeItem: function(name) {
|
||||
this.removeObject(this.get('namedViews').get(name));
|
||||
this.get('namedViews').set(name, null);
|
||||
},
|
||||
|
||||
// Replace an item in the container with another one.
|
||||
replaceItem: function(name, viewClass) {
|
||||
// view = this.createChildView(view);
|
||||
|
||||
var oldView = this.get('namedViews').get(name);
|
||||
var index = this.indexOf(oldView);
|
||||
this.replace(index, 1, [viewClass])
|
||||
this.get('namedViews').set(name, viewClass);
|
||||
},
|
||||
|
||||
// Move an item in the container to a new position.
|
||||
moveItem: function(name, index) {
|
||||
var view = this.get('namedViews').get(name);
|
||||
this.removeItem(name);
|
||||
this.addItem(name, view, index);
|
||||
},
|
||||
|
||||
firstItem: function() {
|
||||
return this.objectAt(0);
|
||||
}.property(),
|
||||
|
||||
secondItem: function() {
|
||||
return this.objectAt(1);
|
||||
}.property(),
|
||||
|
||||
remainingItems: function() {
|
||||
return this.slice(2);
|
||||
}.property(),
|
||||
|
||||
getItem: function(name) {
|
||||
return this.get('namedViews').get(name);
|
||||
}
|
||||
|
||||
});
|
5
framework/core/ember/app/utils/plugin.js
Normal file
5
framework/core/ember/app/utils/plugin.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Object.extend({
|
||||
|
||||
});
|
62
framework/core/ember/app/utils/scrollbar.js
Normal file
62
framework/core/ember/app/utils/scrollbar.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
// TODO probably change this into an Ember object/merge it into discussion-scrollbar
|
||||
|
||||
var Scrollbar = function(element) {
|
||||
this.$ = $(element);
|
||||
this.count = 1;
|
||||
this.index = 0;
|
||||
this.visible = 1;
|
||||
this.disabled = false;
|
||||
};
|
||||
|
||||
Scrollbar.prototype = {
|
||||
|
||||
setIndex: function(index) {
|
||||
this.index = index;
|
||||
},
|
||||
|
||||
setVisible: function(visible) {
|
||||
this.visible = visible;
|
||||
},
|
||||
|
||||
setCount: function(count) {
|
||||
this.count = count;
|
||||
},
|
||||
|
||||
setDisabled: function(disabled) {
|
||||
this.disabled = disabled;
|
||||
},
|
||||
|
||||
percentPerPost: function() {
|
||||
// To stop the slider of the scrollbar from getting too small when there
|
||||
// are many posts, we define a minimum percentage height for the slider
|
||||
// calculated from a 50 pixel limit. Subsequently, we can calculate the
|
||||
// minimum percentage per visible post. If this is greater than the
|
||||
// actual percentage per post, then we need to adjust the 'before'
|
||||
// percentage to account for it.
|
||||
var minPercentVisible = 50 / this.$.outerHeight() * 100;
|
||||
var percentPerVisiblePost = Math.max(100 / this.count, minPercentVisible / this.visible);
|
||||
var percentPerPost = this.count == this.visible ? 0 : (100 - percentPerVisiblePost * this.visible) / (this.count - this.visible);
|
||||
|
||||
return {
|
||||
index: percentPerPost,
|
||||
visible: percentPerVisiblePost
|
||||
};
|
||||
},
|
||||
|
||||
update: function(animate) {
|
||||
var percentPerPost = this.percentPerPost();
|
||||
|
||||
var before = percentPerPost.index * this.index,
|
||||
slider = Math.min(100 - before, percentPerPost.visible * this.visible),
|
||||
func = animate ? 'animate' : 'css';
|
||||
|
||||
this.$.find('.scrollbar-before').stop(true)[func]({height: before+'%'}).css('overflow', 'visible');
|
||||
this.$.find('.scrollbar-slider').stop(true)[func]({height: slider+'%'}).css('overflow', 'visible');
|
||||
this.$.find('.scrollbar-after').stop(true)[func]({height: (100 - before - slider)+'%'}).css('overflow', 'visible');
|
||||
|
||||
this.$.toggleClass('disabled', this.disabled || slider >= 100);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export default Scrollbar;
|
0
framework/core/ember/app/views/.gitkeep
Normal file
0
framework/core/ember/app/views/.gitkeep
Normal file
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user