mirror of
https://github.com/itorquey/tn-community-browser.git
synced 2026-01-19 12:14:45 +00:00
Initial commit.
This commit is contained in:
commit
a7afd37268
4
.bowerrc
Normal file
4
.bowerrc
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"directory": "bower_components",
|
||||
"json": "bower.json"
|
||||
}
|
||||
21
.editorconfig
Normal file
21
.editorconfig
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
|
||||
[*]
|
||||
|
||||
# Change these settings to your own preference
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# We recommend you to keep these unchanged
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
* text=auto
|
||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
/node_modules
|
||||
/dist
|
||||
/.tmp
|
||||
/.sass-cache
|
||||
/bower_components
|
||||
6
.jscsrc
Normal file
6
.jscsrc
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"requireCamelCaseOrUpperCaseIdentifiers": true,
|
||||
"requireCapitalizedConstructors": true,
|
||||
"requireParenthesesAroundIIFE": true,
|
||||
"validateQuoteMarks": "'"
|
||||
}
|
||||
16
.jshintrc
Normal file
16
.jshintrc
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"bitwise": true,
|
||||
"browser": true,
|
||||
"curly": true,
|
||||
"eqeqeq": true,
|
||||
"esnext": true,
|
||||
"latedef": true,
|
||||
"noarg": true,
|
||||
"node": true,
|
||||
"strict": true,
|
||||
"undef": true,
|
||||
"unused": true,
|
||||
"globals": {
|
||||
"angular": false
|
||||
}
|
||||
}
|
||||
9
.travis.yml
Normal file
9
.travis.yml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- 'iojs'
|
||||
- '0.12'
|
||||
- '0.10'
|
||||
before_script:
|
||||
- 'npm install -g bower grunt-cli'
|
||||
- 'bower install'
|
||||
1
.yo-rc.json
Normal file
1
.yo-rc.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
445
Gruntfile.js
Normal file
445
Gruntfile.js
Normal file
|
|
@ -0,0 +1,445 @@
|
|||
// Generated on 2016-01-05 using generator-angular 0.15.1
|
||||
'use strict';
|
||||
|
||||
|
||||
module.exports = function (grunt) {
|
||||
|
||||
// Time how long tasks take. Can help when optimizing build times
|
||||
require('time-grunt')(grunt);
|
||||
|
||||
// Automatically load required Grunt tasks
|
||||
require('jit-grunt')(grunt, {
|
||||
useminPrepare: 'grunt-usemin',
|
||||
ngtemplates: 'grunt-angular-templates',
|
||||
cdnify: 'grunt-google-cdn'
|
||||
});
|
||||
|
||||
// Configurable paths for the application
|
||||
var appConfig = {
|
||||
app: require('./bower.json').appPath || 'app',
|
||||
dist: 'dist'
|
||||
};
|
||||
|
||||
// Define the configuration for all the tasks
|
||||
grunt.initConfig({
|
||||
|
||||
// Project settings
|
||||
yeoman: appConfig,
|
||||
|
||||
// Watches files for changes and runs tasks based on the changed files
|
||||
watch: {
|
||||
bower: {
|
||||
files: ['bower.json'],
|
||||
tasks: ['wiredep']
|
||||
},
|
||||
js: {
|
||||
files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
|
||||
tasks: ['newer:jshint:all', 'newer:jscs:all'],
|
||||
options: {
|
||||
livereload: '<%= connect.options.livereload %>'
|
||||
}
|
||||
},
|
||||
compass: {
|
||||
files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
|
||||
tasks: ['compass:server', 'postcss:server']
|
||||
},
|
||||
gruntfile: {
|
||||
files: ['Gruntfile.js']
|
||||
},
|
||||
livereload: {
|
||||
options: {
|
||||
livereload: '<%= connect.options.livereload %>'
|
||||
},
|
||||
files: [
|
||||
'<%= yeoman.app %>/{,*/}*.html',
|
||||
'.tmp/styles/{,*/}*.css',
|
||||
'<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// The actual grunt server settings
|
||||
connect: {
|
||||
options: {
|
||||
port: 9000,
|
||||
// Change this to '0.0.0.0' to access the server from outside.
|
||||
hostname: 'localhost',
|
||||
livereload: 35729
|
||||
},
|
||||
livereload: {
|
||||
options: {
|
||||
open: true,
|
||||
middleware: function (connect) {
|
||||
return [
|
||||
connect.static('.tmp'),
|
||||
connect().use(
|
||||
'/bower_components',
|
||||
connect.static('./bower_components')
|
||||
),
|
||||
connect().use(
|
||||
'/app/styles',
|
||||
connect.static('./app/styles')
|
||||
),
|
||||
connect.static(appConfig.app)
|
||||
];
|
||||
}
|
||||
}
|
||||
},
|
||||
dist: {
|
||||
options: {
|
||||
open: true,
|
||||
base: '<%= yeoman.dist %>'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Make sure there are no obvious mistakes
|
||||
jshint: {
|
||||
options: {
|
||||
jshintrc: '.jshintrc',
|
||||
reporter: require('jshint-stylish')
|
||||
},
|
||||
all: {
|
||||
src: [
|
||||
'Gruntfile.js',
|
||||
'<%= yeoman.app %>/scripts/{,*/}*.js'
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// Make sure code styles are up to par
|
||||
jscs: {
|
||||
options: {
|
||||
config: '.jscsrc',
|
||||
verbose: true
|
||||
},
|
||||
all: {
|
||||
src: [
|
||||
'Gruntfile.js',
|
||||
'<%= yeoman.app %>/scripts/{,*/}*.js'
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// Empties folders to start fresh
|
||||
clean: {
|
||||
dist: {
|
||||
files: [{
|
||||
dot: true,
|
||||
src: [
|
||||
'.tmp',
|
||||
'<%= yeoman.dist %>/{,*/}*',
|
||||
'!<%= yeoman.dist %>/.git{,*/}*'
|
||||
]
|
||||
}]
|
||||
},
|
||||
server: '.tmp'
|
||||
},
|
||||
|
||||
// Add vendor prefixed styles
|
||||
postcss: {
|
||||
options: {
|
||||
processors: [
|
||||
require('autoprefixer-core')({browsers: ['last 1 version']})
|
||||
]
|
||||
},
|
||||
server: {
|
||||
options: {
|
||||
map: true
|
||||
},
|
||||
files: [{
|
||||
expand: true,
|
||||
cwd: '.tmp/styles/',
|
||||
src: '{,*/}*.css',
|
||||
dest: '.tmp/styles/'
|
||||
}]
|
||||
},
|
||||
dist: {
|
||||
files: [{
|
||||
expand: true,
|
||||
cwd: '.tmp/styles/',
|
||||
src: '{,*/}*.css',
|
||||
dest: '.tmp/styles/'
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
// Automatically inject Bower components into the app
|
||||
wiredep: {
|
||||
app: {
|
||||
src: ['<%= yeoman.app %>/index.html'],
|
||||
ignorePath: /\.\.\//
|
||||
},
|
||||
sass: {
|
||||
src: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
|
||||
ignorePath: /(\.\.\/){1,2}bower_components\//
|
||||
}
|
||||
},
|
||||
|
||||
// Compiles Sass to CSS and generates necessary files if requested
|
||||
compass: {
|
||||
options: {
|
||||
sassDir: '<%= yeoman.app %>/styles',
|
||||
cssDir: '.tmp/styles',
|
||||
generatedImagesDir: '.tmp/images/generated',
|
||||
imagesDir: '<%= yeoman.app %>/images',
|
||||
javascriptsDir: '<%= yeoman.app %>/scripts',
|
||||
fontsDir: '<%= yeoman.app %>/styles/fonts',
|
||||
importPath: './bower_components',
|
||||
httpImagesPath: '/images',
|
||||
httpGeneratedImagesPath: '/images/generated',
|
||||
httpFontsPath: '/styles/fonts',
|
||||
relativeAssets: false,
|
||||
assetCacheBuster: false,
|
||||
raw: 'Sass::Script::Number.precision = 10\n'
|
||||
},
|
||||
dist: {
|
||||
options: {
|
||||
generatedImagesDir: '<%= yeoman.dist %>/images/generated'
|
||||
}
|
||||
},
|
||||
server: {
|
||||
options: {
|
||||
sourcemap: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Renames files for browser caching purposes
|
||||
filerev: {
|
||||
dist: {
|
||||
src: [
|
||||
'<%= yeoman.dist %>/scripts/{,*/}*.js',
|
||||
'<%= yeoman.dist %>/styles/{,*/}*.css',
|
||||
'<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
|
||||
'<%= yeoman.dist %>/styles/fonts/*'
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// Reads HTML for usemin blocks to enable smart builds that automatically
|
||||
// concat, minify and revision files. Creates configurations in memory so
|
||||
// additional tasks can operate on them
|
||||
useminPrepare: {
|
||||
html: '<%= yeoman.app %>/index.html',
|
||||
options: {
|
||||
dest: '<%= yeoman.dist %>',
|
||||
flow: {
|
||||
html: {
|
||||
steps: {
|
||||
js: ['concat', 'uglifyjs'],
|
||||
css: ['cssmin']
|
||||
},
|
||||
post: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Performs rewrites based on filerev and the useminPrepare configuration
|
||||
usemin: {
|
||||
html: ['<%= yeoman.dist %>/{,*/}*.html'],
|
||||
css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
|
||||
js: ['<%= yeoman.dist %>/scripts/{,*/}*.js'],
|
||||
options: {
|
||||
assetsDirs: [
|
||||
'<%= yeoman.dist %>',
|
||||
'<%= yeoman.dist %>/images',
|
||||
'<%= yeoman.dist %>/styles'
|
||||
],
|
||||
patterns: {
|
||||
js: [[/(images\/[^''""]*\.(png|jpg|jpeg|gif|webp|svg))/g, 'Replacing references to images']]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// The following *-min tasks will produce minified files in the dist folder
|
||||
// By default, your `index.html`'s <!-- Usemin block --> will take care of
|
||||
// minification. These next options are pre-configured if you do not wish
|
||||
// to use the Usemin blocks.
|
||||
// cssmin: {
|
||||
// dist: {
|
||||
// files: {
|
||||
// '<%= yeoman.dist %>/styles/main.css': [
|
||||
// '.tmp/styles/{,*/}*.css'
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// uglify: {
|
||||
// dist: {
|
||||
// files: {
|
||||
// '<%= yeoman.dist %>/scripts/scripts.js': [
|
||||
// '<%= yeoman.dist %>/scripts/scripts.js'
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// concat: {
|
||||
// dist: {}
|
||||
// },
|
||||
|
||||
imagemin: {
|
||||
dist: {
|
||||
files: [{
|
||||
expand: true,
|
||||
cwd: '<%= yeoman.app %>/images',
|
||||
src: '{,*/}*.{png,jpg,jpeg,gif}',
|
||||
dest: '<%= yeoman.dist %>/images'
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
svgmin: {
|
||||
dist: {
|
||||
files: [{
|
||||
expand: true,
|
||||
cwd: '<%= yeoman.app %>/images',
|
||||
src: '{,*/}*.svg',
|
||||
dest: '<%= yeoman.dist %>/images'
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
htmlmin: {
|
||||
dist: {
|
||||
options: {
|
||||
collapseWhitespace: true,
|
||||
conservativeCollapse: true,
|
||||
collapseBooleanAttributes: true,
|
||||
removeCommentsFromCDATA: true
|
||||
},
|
||||
files: [{
|
||||
expand: true,
|
||||
cwd: '<%= yeoman.dist %>',
|
||||
src: ['*.html'],
|
||||
dest: '<%= yeoman.dist %>'
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
ngtemplates: {
|
||||
dist: {
|
||||
options: {
|
||||
module: 'tnCommunityBrowserApp',
|
||||
htmlmin: '<%= htmlmin.dist.options %>',
|
||||
usemin: 'scripts/scripts.js'
|
||||
},
|
||||
cwd: '<%= yeoman.app %>',
|
||||
src: 'views/{,*/}*.html',
|
||||
dest: '.tmp/templateCache.js'
|
||||
}
|
||||
},
|
||||
|
||||
// ng-annotate tries to make the code safe for minification automatically
|
||||
// by using the Angular long form for dependency injection.
|
||||
ngAnnotate: {
|
||||
dist: {
|
||||
files: [{
|
||||
expand: true,
|
||||
cwd: '.tmp/concat/scripts',
|
||||
src: '*.js',
|
||||
dest: '.tmp/concat/scripts'
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
// Replace Google CDN references
|
||||
cdnify: {
|
||||
dist: {
|
||||
html: ['<%= yeoman.dist %>/*.html']
|
||||
}
|
||||
},
|
||||
|
||||
// Copies remaining files to places other tasks can use
|
||||
copy: {
|
||||
dist: {
|
||||
files: [{
|
||||
expand: true,
|
||||
dot: true,
|
||||
cwd: '<%= yeoman.app %>',
|
||||
dest: '<%= yeoman.dist %>',
|
||||
src: [
|
||||
'*.{ico,png,txt}',
|
||||
'*.html',
|
||||
'images/{,*/}*.{webp}',
|
||||
'styles/fonts/{,*/}*.*'
|
||||
]
|
||||
}, {
|
||||
expand: true,
|
||||
cwd: '.tmp/images',
|
||||
dest: '<%= yeoman.dist %>/images',
|
||||
src: ['generated/*']
|
||||
}, {
|
||||
expand: true,
|
||||
cwd: '.',
|
||||
src: 'bower_components/bootstrap-sass-official/assets/fonts/bootstrap/*',
|
||||
dest: '<%= yeoman.dist %>'
|
||||
}]
|
||||
},
|
||||
styles: {
|
||||
expand: true,
|
||||
cwd: '<%= yeoman.app %>/styles',
|
||||
dest: '.tmp/styles/',
|
||||
src: '{,*/}*.css'
|
||||
}
|
||||
},
|
||||
|
||||
// Run some tasks in parallel to speed up the build process
|
||||
concurrent: {
|
||||
server: [
|
||||
'compass:server'
|
||||
],
|
||||
dist: [
|
||||
'compass:dist',
|
||||
'imagemin',
|
||||
'svgmin'
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
grunt.registerTask('serve', 'Compile then start a connect web server', function (target) {
|
||||
if (target === 'dist') {
|
||||
return grunt.task.run(['build', 'connect:dist:keepalive']);
|
||||
}
|
||||
|
||||
grunt.task.run([
|
||||
'clean:server',
|
||||
'wiredep',
|
||||
'concurrent:server',
|
||||
'postcss:server',
|
||||
'connect:livereload',
|
||||
'watch'
|
||||
]);
|
||||
});
|
||||
|
||||
grunt.registerTask('server', 'DEPRECATED TASK. Use the "serve" task instead', function (target) {
|
||||
grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
|
||||
grunt.task.run(['serve:' + target]);
|
||||
});
|
||||
|
||||
grunt.registerTask('build', [
|
||||
'clean:dist',
|
||||
'wiredep',
|
||||
'useminPrepare',
|
||||
'concurrent:dist',
|
||||
'postcss',
|
||||
'ngtemplates',
|
||||
'concat',
|
||||
'ngAnnotate',
|
||||
'copy:dist',
|
||||
'cdnify',
|
||||
'cssmin',
|
||||
'uglify',
|
||||
'filerev',
|
||||
'usemin',
|
||||
'htmlmin'
|
||||
]);
|
||||
|
||||
grunt.registerTask('default', [
|
||||
'newer:jshint',
|
||||
'newer:jscs',
|
||||
'build'
|
||||
]);
|
||||
};
|
||||
4
README.md
Normal file
4
README.md
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
TribesNext Community Browser
|
||||
==========
|
||||
|
||||
A web-based user interface for interacting with the TribesNext community features.
|
||||
152
app/404.html
Normal file
152
app/404.html
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Page Not Found :(</title>
|
||||
<style>
|
||||
::-moz-selection {
|
||||
background: #b3d4fc;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: #b3d4fc;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
html {
|
||||
padding: 30px 10px;
|
||||
font-size: 20px;
|
||||
line-height: 1.4;
|
||||
color: #737373;
|
||||
background: #f0f0f0;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
html,
|
||||
input {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 500px;
|
||||
padding: 30px 20px 50px;
|
||||
border: 1px solid #b3b3b3;
|
||||
border-radius: 4px;
|
||||
margin: 0 auto;
|
||||
box-shadow: 0 1px 10px #a7a7a7, inset 0 1px 0 #fff;
|
||||
background: #fcfcfc;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0 10px;
|
||||
font-size: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 span {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 1.5em 0 0.5em;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding: 0 0 0 40px;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 380px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* google search */
|
||||
|
||||
#goog-fixurl ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#goog-fixurl form {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#goog-wm-qt,
|
||||
#goog-wm-sb {
|
||||
border: 1px solid #bbb;
|
||||
font-size: 16px;
|
||||
line-height: normal;
|
||||
vertical-align: top;
|
||||
color: #444;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
#goog-wm-qt {
|
||||
width: 220px;
|
||||
height: 20px;
|
||||
padding: 5px;
|
||||
margin: 5px 10px 0 0;
|
||||
box-shadow: inset 0 1px 1px #ccc;
|
||||
}
|
||||
|
||||
#goog-wm-sb {
|
||||
display: inline-block;
|
||||
height: 32px;
|
||||
padding: 0 10px;
|
||||
margin: 5px 0 0;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
background-color: #f5f5f5;
|
||||
background-image: -webkit-linear-gradient(rgba(255,255,255,0), #f1f1f1);
|
||||
background-image: -moz-linear-gradient(rgba(255,255,255,0), #f1f1f1);
|
||||
background-image: -ms-linear-gradient(rgba(255,255,255,0), #f1f1f1);
|
||||
background-image: -o-linear-gradient(rgba(255,255,255,0), #f1f1f1);
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
#goog-wm-sb:hover,
|
||||
#goog-wm-sb:focus {
|
||||
border-color: #aaa;
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
#goog-wm-qt:hover,
|
||||
#goog-wm-qt:focus {
|
||||
border-color: #105cb6;
|
||||
outline: 0;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
input::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Not found <span>:(</span></h1>
|
||||
<p>Sorry, but the page you were trying to view does not exist.</p>
|
||||
<p>It looks like this was the result of either:</p>
|
||||
<ul>
|
||||
<li>a mistyped address</li>
|
||||
<li>an out-of-date link</li>
|
||||
</ul>
|
||||
<script>
|
||||
var GOOG_FIXURL_LANG = (navigator.language || '').slice(0,2),GOOG_FIXURL_SITE = location.host;
|
||||
</script>
|
||||
<script src="//linkhelp.clients.google.com/tbproxy/lh/wm/fixurl.js"></script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
0
app/favicon.ico
Normal file
0
app/favicon.ico
Normal file
66
app/index.html
Normal file
66
app/index.html
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>TribesNext Community Browser</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<!-- build:css(.) styles/vendor.css -->
|
||||
<!-- bower:css -->
|
||||
<link rel="stylesheet" href="bower_components/angular-xeditable/dist/css/xeditable.css" />
|
||||
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css" />
|
||||
<link rel="stylesheet" href="bower_components/bootstrap-switch/dist/css/bootstrap3/bootstrap-switch.css" />
|
||||
<!-- endbower -->
|
||||
<!-- endbuild -->
|
||||
<!-- build:css(.tmp) styles/main.css -->
|
||||
<link rel="stylesheet" href="styles/main.css">
|
||||
<!-- endbuild -->
|
||||
</head>
|
||||
<body ng-app="tnCommunityBrowserApp">
|
||||
<!--[if lte IE 10]>
|
||||
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
|
||||
<![endif]-->
|
||||
<header>
|
||||
<nav class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="#">TribesNext Community Browser</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-3 col-md-2 sidebar">
|
||||
<ul class="nav nav-sidebar">
|
||||
<li ng-hide="isLoggedIn" ng-class="{active: isActive('/login')}"><a href="#/login">Login</a></li>
|
||||
<li ng-show="isLoggedIn" ng-class="{active: isActive('/profile')}"><a href="#/profile">My Profile</a></li>
|
||||
<li ng-show="isLoggedIn" ng-class="{active: isActive('/search')}"><a href="#/search">Search</a></li>
|
||||
<li ng-show="isLoggedIn" ng-class="{active: isActive('/create')}"><a href="#/create">Create Tribe</a></li>
|
||||
<li ng-show="isLoggedIn"><a href="#/logout" logout-button>Logout</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main" ng-view></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- build:js(.) scripts/vendor.js -->
|
||||
<!-- bower:js -->
|
||||
<script src="bower_components/jquery/dist/jquery.js"></script>
|
||||
<script src="bower_components/angular/angular.js"></script>
|
||||
<script src="bower_components/bootstrap-sass-official/assets/javascripts/bootstrap.js"></script>
|
||||
<script src="bower_components/angular-route/angular-route.js"></script>
|
||||
<script src="bower_components/underscore/underscore.js"></script>
|
||||
<script src="bower_components/underscore.string/dist/underscore.string.js"></script>
|
||||
<script src="bower_components/angular-xeditable/dist/js/xeditable.js"></script>
|
||||
<script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
|
||||
<script src="bower_components/bootstrap-switch/dist/js/bootstrap-switch.js"></script>
|
||||
<script src="bower_components/angular-bootstrap-switch/dist/angular-bootstrap-switch.js"></script>
|
||||
<!-- endbower -->
|
||||
<!-- endbuild -->
|
||||
<!-- build:js({.tmp,app}) scripts/scripts.js -->
|
||||
<script src="scripts/app.js"></script>
|
||||
<!-- endbuild -->
|
||||
</body>
|
||||
</html>
|
||||
4
app/robots.txt
Normal file
4
app/robots.txt
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# robotstxt.org
|
||||
|
||||
User-agent: *
|
||||
Disallow:
|
||||
527
app/scripts/app.js
Normal file
527
app/scripts/app.js
Normal file
|
|
@ -0,0 +1,527 @@
|
|||
'use strict';
|
||||
|
||||
var entityMap = { "&": "&", "<": "<", ">": ">", '"': '"', "'": ''', "/": '/' };
|
||||
function escapeHtml(string) { return String(string).replace(/[&<>"'\/]/g, function (s) { return entityMap[s]; }); }
|
||||
|
||||
angular
|
||||
.module('tnCommunityBrowserApp', ['ngRoute','frapontillo.bootstrap-switch','xeditable'])
|
||||
.run(['$rootScope', '$location', 'editableOptions', 'TNSession', function ($rootScope, $location, editableOptions, TNSession) {
|
||||
editableOptions.theme = 'bs3';
|
||||
_.mixin(s.exports());
|
||||
var openRoutes = ['/login'];
|
||||
var cleanRoute = function (route) {
|
||||
return _.find(openRoutes, function (r) { return _.startsWith(route, r)});
|
||||
};
|
||||
|
||||
$rootScope.$on('$routeChangeStart', function (event,next,current) {
|
||||
if (!cleanRoute($location.url()) && !TNSession.isLoggedIn())
|
||||
$location.path('/login');
|
||||
});
|
||||
|
||||
$rootScope.isActive = function (path) {
|
||||
return _.startsWith($location.path(), path);
|
||||
}
|
||||
|
||||
var updateSession = function (e) {
|
||||
$rootScope.isLoggedIn = TNSession.isLoggedIn();
|
||||
$rootScope.username = TNSession.getUsername();
|
||||
$rootScope.guid = TNSession.getGuid();
|
||||
};
|
||||
var handleLogout = function (e) {
|
||||
updateSession();
|
||||
if (!_.startsWith($location.url(), '/login'))
|
||||
$location.path('/login');
|
||||
};
|
||||
$rootScope.$on('TNSession.login', updateSession);
|
||||
$rootScope.$on('TNSession.logout', handleLogout);
|
||||
$rootScope.$on('TNSession.expired', handleLogout);
|
||||
}])
|
||||
.config(['$routeProvider', function ($routeProvider) {
|
||||
$routeProvider
|
||||
.when('/login', {templateUrl:'views/login.html', controller:'LoginCtrl', controllerAs:'loginCtrl'})
|
||||
.when('/profile', {templateUrl:'views/profile.html', controller:'ProfileCtrl', controllerAs:'profile'})
|
||||
.when('/player/:guid', {templateUrl:'views/player.html', controller:'PlayerCtrl', controllerAs:'player'})
|
||||
.when('/tribe/:tribeId', {templateUrl:'views/tribe.html', controller:'TribeCtrl', controllerAs:'tribe'})
|
||||
.when('/search', {templateUrl:'views/search.html', controller:'SearchCtrl', controllerAs:'search'})
|
||||
.when('/create', {templateUrl:'views/create.html', controller:'CreateTribeCtrl', controllerAs:'tribe'})
|
||||
.otherwise({ redirectTo: '/login' });
|
||||
}])
|
||||
.filter('preserve_space', function () { return function (input) {return (input||'').replace(/ /g, '\xa0')} })
|
||||
.filter('yes_no', function () { return function (input) {return (input=='1')?'yes':'no'}})
|
||||
.filter('titleize', function () {return function (input) {return _.titleize(input)}})
|
||||
.directive('tribeLink', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
id: '=',
|
||||
tag: '=',
|
||||
name: '=',
|
||||
append: '='
|
||||
},
|
||||
template: '<a href="#/tribe/{{id}}"><span class="tag" ng-show="append==0">{{tag|preserve_space}}</span>{{name|preserve_space}}<span class="tag" ng-show="append==1">{{tag|preserve_space}}</span></a>'
|
||||
};
|
||||
})
|
||||
.directive('playerLink', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
guid: '=',
|
||||
tag: '=',
|
||||
name: '=',
|
||||
append: '='
|
||||
},
|
||||
template: '<a href="#/player/{{guid}}"><span class="tag" ng-show="append==0">{{tag|preserve_space}}</span>{{name|preserve_space}}<span class="tag" ng-show="append==1">{{tag|preserve_space}}</span></a>'
|
||||
};
|
||||
})
|
||||
.directive('historyItem', function () {
|
||||
return {
|
||||
link: function (scope, element, attrs) {
|
||||
var template = (scope.item.template || '');
|
||||
// Populate with @payload components...
|
||||
var payload = scope.item.payload || '';
|
||||
var payloads = payload.split('\t');
|
||||
template = template.replace(/@payload(;(\d+))?[\^]?/gi, function (_,__,g) {
|
||||
if (g !== undefined) return payloads[g]; return payload;
|
||||
});
|
||||
// Escape HTML
|
||||
template = escapeHtml(template);
|
||||
// Populate with @clan and @user link(s)
|
||||
template = template.replace(/@(clan|player[12]?)/gi, function (w) {
|
||||
var result;
|
||||
switch (w) {
|
||||
case '@clan':
|
||||
var clan = scope.item.clan;
|
||||
var tag = '<span class="tag">' + escapeHtml((clan.tag || '').replace(/ /g, '\xa0')) + '</span>';
|
||||
var name = escapeHtml((clan.name || '').replace(/ /g, '\xa0'));
|
||||
if (clan.append == '0')
|
||||
result = tag + name;
|
||||
else
|
||||
result = name + tag;
|
||||
return '<em><a href="#/tribe/' + clan.id + '">' + result + '</a></em>';
|
||||
case '@player':
|
||||
var player = scope.item.player;
|
||||
var tag = '<span class="tag">' + escapeHtml((player.tag || '').replace(/ /g, '\xa0')) + '</span>';
|
||||
var name = escapeHtml((player.name || '').replace(/ /g, '\xa0'));
|
||||
if (player.append == '0')
|
||||
result = tag + name;
|
||||
else
|
||||
result = name + tag;
|
||||
return '<em><a href="#/player/' + player.guid + '">' + result + '</a></em>';
|
||||
case '@player1':
|
||||
var player = scope.item.user1;
|
||||
var tag = '<span class="tag">' + escapeHtml((player.tag || '').replace(/ /g, '\xa0')) + '</span>';
|
||||
var name = escapeHtml((player.name || '').replace(/ /g, '\xa0'));
|
||||
if (player.append == '0')
|
||||
result = tag + name;
|
||||
else
|
||||
result = name + tag;
|
||||
return '<em><a href="#/player/' + player.guid + '">' + result + '</a></em>';
|
||||
case '@player2':
|
||||
var player = scope.item.user2;
|
||||
var tag = '<span class="tag">' + escapeHtml((player.tag || '').replace(/ /g, '\xa0')) + '</span>';
|
||||
var name = escapeHtml((player.name || '').replace(/ /g, '\xa0'));
|
||||
if (player.append == '0')
|
||||
result = tag + name;
|
||||
else
|
||||
result = name + tag;
|
||||
return '<em><a href="#/player/' + player.guid + '">' + result + '</a></em>';
|
||||
}
|
||||
});
|
||||
// Output
|
||||
element.html(template);
|
||||
}
|
||||
}
|
||||
})
|
||||
.directive('logoutButton', ['TNSession', function (TNSession) {
|
||||
return { link: function (scope, element, attrs) {
|
||||
element.bind('click', function () { if (TNSession.isLoggedIn()) TNSession.logout()})}}
|
||||
}])
|
||||
.controller('LoginCtrl', ['$scope', '$timeout', '$location', 'Notice', 'TNSession', function ($scope, $timeout, $location, notice, TNSession) {
|
||||
$scope.messages = notice.messages;
|
||||
|
||||
$scope.login = function (username, password) {
|
||||
TNSession
|
||||
.login(username, password)
|
||||
.then(
|
||||
function (response) { $location.path('/profile'); },
|
||||
function (error) { notice.error(error) })
|
||||
.finally(function () { $scope.password = null; });
|
||||
};
|
||||
|
||||
$scope.logout = function () {
|
||||
TNSession
|
||||
.logout()
|
||||
.then(
|
||||
function () { notice.success('You have been logged out.'); },
|
||||
function (error) { notice.error(error) });
|
||||
};
|
||||
|
||||
/*
|
||||
$scope.$watch('TNSession.expired', function() {
|
||||
notice.info('You have been logged out due to inactivity.', 0);
|
||||
});
|
||||
*/
|
||||
}])
|
||||
.controller('ProfileCtrl', ['$scope', 'Notice', 'TNSession', function ($scope, notice, TNSession) {
|
||||
$scope.messages = notice.messages;
|
||||
|
||||
var getProfile = function () {
|
||||
TNSession.sendRequest('userview', {id: TNSession.getGuid()})
|
||||
.then(function (r) {$scope.info = r},
|
||||
function (e) {notice.error('An error was encountered while retrieving profile details.')});
|
||||
};
|
||||
var getHistory = function () {
|
||||
TNSession.sendRequest('userhistory', {id: TNSession.getGuid()})
|
||||
.then(function (r) {$scope.history = r},
|
||||
function (e) {notice.error('An error was encountered while retrieving profile history.')});
|
||||
};
|
||||
var getInvites = function () {
|
||||
TNSession.sendRequest('userinvites', {})
|
||||
.then(function (r) {$scope.invites = r},
|
||||
function (e) {notice.error('An error was encountered while retrieving tribe invitations.')});
|
||||
};
|
||||
|
||||
$scope.$watch('TNSession.login', function (e) {
|
||||
$scope.messages = [];
|
||||
getProfile();
|
||||
getHistory();
|
||||
getInvites();
|
||||
});
|
||||
$scope.$watch('TNSession.logout', function (e) {
|
||||
$scope.info = null;
|
||||
$scope.history = null;
|
||||
});
|
||||
|
||||
$scope.setDescription = function (description) { return TNSession.sendRequest('userinfo', {info:description}) };
|
||||
$scope.setWebsite = function (website) { return TNSession.sendRequest('usersite', {site:website}) };
|
||||
$scope.setName = function (name) { return TNSession.sendRequest('username', {name:name}) };
|
||||
|
||||
$scope.setActiveTribe = function (tribeId,tag,append) {
|
||||
TNSession.sendRequest('userclan', {id:tribeId})
|
||||
.then(function (r) {$scope.info.append=append; return true;},
|
||||
function (e) {getProfile(); notice.error('Unable to set active tribe: ' + e)});
|
||||
};
|
||||
|
||||
$scope.acceptInvite = function (invite) {
|
||||
return TNSession.sendRequest('useraccept', {id:invite.clan.id})
|
||||
.then(function () {
|
||||
notice.success('You have joined ' + invite.clan.name + '.')
|
||||
var index = $scope.invites.indexOf(invite);
|
||||
if (index !== -1) {
|
||||
$scope.invites.splice(index,1);
|
||||
}
|
||||
getProfile();
|
||||
},
|
||||
function (e) {notice.error('Error accepting invitation to ' + invite.clan.name + ': ' + e)});
|
||||
};
|
||||
|
||||
$scope.rejectInvite = function (invite) {
|
||||
return TNSession.sendRequest('userreject', {id:invite.clan.id})
|
||||
.then(function () {
|
||||
notice.success('Invitation to ' + invite.clan.name + ' rejected.');
|
||||
var index = $scope.invites.indexOf(invite);
|
||||
if (index !== -1) {
|
||||
$scope.invites.splice(index,1);
|
||||
}
|
||||
},
|
||||
function (e) {notice.error('Error rejecting invitation to ' + invite.clan.name + ': ' + e)});
|
||||
};
|
||||
|
||||
$scope.leaveTribe = function (tribe) {
|
||||
return TNSession.sendRequest('userleave', {id:tribe.id})
|
||||
.then(function () {
|
||||
notice.success('You have left ' + tribe.name);
|
||||
getProfile();
|
||||
return true;
|
||||
});
|
||||
};
|
||||
}])
|
||||
.controller('PlayerCtrl', ['$scope', '$routeParams', 'Notice', 'TNSession', function ($scope, $routeParams, notice, TNSession) {
|
||||
var guid = $routeParams.guid;
|
||||
TNSession.sendRequest('userview', {id: guid})
|
||||
.then(function (r) {$scope.info = r},
|
||||
function (e) {notice.error('An error was encountered while retrieving player details.\n' + e)});
|
||||
TNSession.sendRequest('userhistory', {id: guid})
|
||||
.then(function (r) {$scope.history = r},
|
||||
function (e) {notice.error('An error was encountered while retrieving player history.\n' + e)});
|
||||
}])
|
||||
.controller('TribeCtrl', ['$scope', '$routeParams', '$filter', 'Notice', 'TNSession', function ($scope,$routeParams,$filter,notice,TNSession) {
|
||||
$scope.messages = notice.messages;
|
||||
var tribeId = $routeParams.tribeId;
|
||||
$scope.canKick = function (member) { return $scope.isMember && $scope.canKickMembers && $scope.membership.rank > member.rank; };
|
||||
$scope.canSetRank = function (member) { return $scope.isMember && $scope.canSetMemberRanks && (member.guid == TNSession.getGuid() || $scope.membership.rank > member.rank); };
|
||||
|
||||
var clearPermissions = function () {
|
||||
$scope.membership = null;
|
||||
$scope.isMember = false;
|
||||
$scope.availableRanks = [];
|
||||
$scope.canSetTag = $scope.canSetName = $scope.canSetWebsite = $scope.canSetPicture =
|
||||
$scope.canSetInfo = $scope.canSetRecruiting = $scope.canSendInvite = $scope.canViewInvites =
|
||||
$scope.canDisband = $scope.canKickMembers = $scope.canSetMemberRanks = false;
|
||||
};
|
||||
|
||||
var updatePermissions = function () {
|
||||
var results = $filter('filter')($scope.info.members, {guid:TNSession.getGuid()}, true);
|
||||
$scope.membership = results.pop();
|
||||
$scope.isMember = $scope.membership != null;
|
||||
$scope.canViewInvites = $scope.isMember;
|
||||
if ($scope.isMember) {
|
||||
var rank = $scope.membership.rank;
|
||||
if (rank >= 2) {
|
||||
$scope.canSetRecruiting = $scope.canSendInvite = true;
|
||||
$scope.getInvites();
|
||||
}
|
||||
if (rank >= 3) {
|
||||
$scope.canSetTag = $scope.canSetName = $scope.canSetWebsite = $scope.canSetPicture =
|
||||
$scope.canSetInfo = $scope.canKickMembers = $scope.canSetMemberRanks = true;
|
||||
}
|
||||
if (rank >= 4) {
|
||||
$scope.canDisband = true;
|
||||
}
|
||||
if ($scope.canSetMemberRanks) {
|
||||
var ranks = [{key:'0', value:'0 - Probationary Member'},{key:'1',value:'1 - Member'},
|
||||
{key:'2',value:'2 - Senior Member'},{key:'3',value:'3 - Administrator'}];
|
||||
if (rank >= 4) ranks.push({key:'4',value:'4 - Leader'});
|
||||
$scope.availableRanks = ranks;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$scope.getInfo = function () {
|
||||
clearPermissions();
|
||||
TNSession.sendRequest('clanview', {id: tribeId})
|
||||
.then(function (r) {$scope.info = r; updatePermissions(); },
|
||||
function (e) {notice.error('An error was encountered while retrieving tribe details.\n' + e)});
|
||||
};
|
||||
$scope.getHistory = function () {
|
||||
TNSession.sendRequest('clanhistory', {id: tribeId})
|
||||
.then(function (r) {$scope.history = r},
|
||||
function (e) {notice.error('An error was encountered while retrieving tribe history.\n' + e)});
|
||||
};
|
||||
$scope.getInvites = function () {
|
||||
TNSession.sendRequest('clanviewinvites', {id:tribeId})
|
||||
.then(function (r) {$scope.invites = r;},
|
||||
function (e) {notice.error('An error was encountered while retrieving tribe invitations.\n' + e)});
|
||||
};
|
||||
$scope.setRecruiting = function (isRecruiting) {
|
||||
return TNSession.sendRequest('clanrecruit', {id:tribeId, v:(isRecruiting?'1':'0')})
|
||||
.then(null, function (e) {return e});
|
||||
}
|
||||
$scope.setDescription = function (description) { return TNSession.sendRequest('claninfo', {id:tribeId, v:description}) };
|
||||
|
||||
var oldTag; var oldAppend;
|
||||
$scope.presetTag = function (tag,append) {oldTag=tag; oldAppend=append;};
|
||||
$scope.setTag = function (tag,append) {
|
||||
return TNSession.sendRequest('clantag', {id:tribeId, tag:tag, append:append})
|
||||
.then(null, function (e) {$scope.info.tag=oldTag; $scope.info.append=oldAppend;return e;});
|
||||
};
|
||||
$scope.setWebsite = function (website) { return TNSession.sendRequest('clansite', {id:tribeId, v:website}) };
|
||||
$scope.setName = function (name) { return TNSession.sendRequest('clanname', {id:tribeId, v:name}) };
|
||||
$scope.setPicture = function (path) { return TNSession.sendRequest('clanpicture', {id:tribeId, v:path}) };
|
||||
$scope.setRank = function (target,rank,title) { return TNSession.sendRequest('clanrank', {id:tribeId, to:target, rank:rank, title:title}) };
|
||||
$scope.disband = function (authorize) { return TNSession.sendRequest('clandisband', {id:tribeId, v:authorize})};
|
||||
$scope.kickMember = function (target,accept) {
|
||||
if ('Yes'==accept)
|
||||
return TNSession.sendRequest('clankick', {id:tribeId, to:target})
|
||||
.then(function (r) {$scope.getInfo(); return true;});
|
||||
return false;
|
||||
};
|
||||
|
||||
$scope.searchResults = [];
|
||||
$scope.showSearchResults = false;
|
||||
$scope.searchAlert = null;
|
||||
$scope.searchAlertType = null;
|
||||
|
||||
$scope.searchPlayers = function (name) {
|
||||
$scope.searchResults = [];
|
||||
$scope.searchAlert = $scope.searchAlertType = null;
|
||||
$scope.showSearchResults = false;
|
||||
|
||||
TNSession.sendRequest('usersearch', {q:name})
|
||||
.then(function (r) {
|
||||
$scope.searchResults = r;
|
||||
$scope.showSearchResults = (r != null && r.length > 0);
|
||||
$scope.searchAlertType = 'info';
|
||||
if (!$scope.showSearchResults)
|
||||
$scope.searchAlert = 'No results found';
|
||||
},
|
||||
function (e) {$scope.searchAlertType='danger'; $scope.searchAlert = 'Search failed: ' + e;});
|
||||
};
|
||||
$scope.sendInvite = function(target,accept) {
|
||||
if ('Yes'==accept)
|
||||
return TNSession.sendRequest('claninvite', {id:tribeId, to:target})
|
||||
.then(function (r) {$scope.getInvites(); return true;});
|
||||
return false;
|
||||
};
|
||||
|
||||
$scope.getInfo();
|
||||
$scope.getHistory();
|
||||
}])
|
||||
.controller('SearchCtrl', ['$scope', '$timeout', 'Notice', 'TNSession', function ($scope, $timeout, notice, TNSession) {
|
||||
$scope.messages = notice.messages;
|
||||
$scope.searchMode = 'Tribes';
|
||||
$scope.searchModes = ['Tribes', 'Players'];
|
||||
$scope.resultMode = null;
|
||||
$scope.showResults = false;
|
||||
$scope.playerResults = [];
|
||||
$scope.tribeResults = [];
|
||||
|
||||
var clearResults = function () {
|
||||
$scope.resultMode = null;
|
||||
$scope.showResults = false;
|
||||
$scope.playerResults = [];
|
||||
$scope.tribeResults = [];
|
||||
};
|
||||
|
||||
var setResults = function (mode, results) {
|
||||
switch (mode) {
|
||||
case 'Players': $scope.playerResults = results;
|
||||
case 'Tribes': $scope.tribeResults = results;
|
||||
}
|
||||
$scope.showResults = (results != null && results.length > 0);
|
||||
if (!$scope.showResults)
|
||||
notice.info('No results found');
|
||||
$scope.resultMode = mode;
|
||||
};
|
||||
|
||||
$scope.search = function (name, mode) {
|
||||
var method = ('Tribes' == mode) ? 'clansearch' : 'usersearch';
|
||||
clearResults();
|
||||
TNSession.sendRequest(method, {q:name})
|
||||
.then(function (r) { setResults(mode,r); },
|
||||
function (e) { notice.error('Search failed: ' + e); });
|
||||
};
|
||||
}])
|
||||
.controller('CreateTribeCtrl', ['$scope', '$timeout', 'Notice', 'TNSession', function ($scope, $timeout, notice, TNSession) {
|
||||
$scope.messages = notice.messages;
|
||||
$scope.resetInputs = function () {
|
||||
$scope.name = $scope.tag = $scope.info = null;
|
||||
$scope.append = $scope.recruiting = 'Yes';
|
||||
};
|
||||
$scope.resetInputs();
|
||||
|
||||
$scope.createTribe = function (name, tag, append, recruiting, info) {
|
||||
append = ('Yes' == append) ? '1':'0';
|
||||
recruiting = ('Yes' == recruiting) ? '1':'0';
|
||||
TNSession.sendRequest('createclan', {name:name, tag:tag, append:append, recruiting:recruiting,info:info})
|
||||
.then(function (r) { notice.success('Tribe created successfully.'); $scope.resetInputs()},
|
||||
function (e) { notice.error(e)});
|
||||
};
|
||||
}])
|
||||
.factory('Notice', ['$timeout', function($timeout) {
|
||||
var service = {messages: []};
|
||||
|
||||
var removeAlert = function (alert) {
|
||||
var index = service.messages.indexOf(alert);
|
||||
if (index !== -1) {
|
||||
alert = service.messages.splice(index,1)[0];
|
||||
if (undefined !== alert.expire)
|
||||
$timeout.cancel(alert.expire);
|
||||
}
|
||||
};
|
||||
|
||||
var addAlert = function (type, message, timeout) {
|
||||
if (undefined === timeout) { timeout = 5000; }
|
||||
var alert= {type:type, message:message};
|
||||
alert.pop = function() {removeAlert(alert)};
|
||||
if (timeout > 0)
|
||||
alert.expire = $timeout(alert.pop, timeout);
|
||||
service.messages.push(alert);
|
||||
};
|
||||
|
||||
service.push = addAlert;
|
||||
service.info = function (message, timeout) {addAlert('info', message, timeout)};
|
||||
service.warn = function (message, timeout) {addAlert('warning', message, timeout)};
|
||||
service.error = function (message, timeout) {addAlert('danger', message, timeout)};
|
||||
service.success = function (message, timeout) {addAlert('success', message, timeout)};
|
||||
service.clear = function () {service.messages = []};
|
||||
|
||||
return service;
|
||||
}])
|
||||
.factory('TNSession', ['$timeout', '$rootScope', '$http', '$q', function ($timeout,$root, $http, $q) {
|
||||
var service = { _isLoggedIn:false, username:null, guid:null, uuid:null, expires:null };
|
||||
|
||||
var sendSessionRequest = function (payload) {
|
||||
var params = angular.extend({jsonp:'JSON_CALLBACK'}, payload);
|
||||
var deferred = $q.defer();
|
||||
$http.jsonp('http://thyth.com/tn/json/json_session.php', {params:params})
|
||||
.then(
|
||||
function (response) {
|
||||
if ('error' === response.data.status) { deferred.reject(response.data.message); }
|
||||
else { deferred.resolve(response.data); }
|
||||
},
|
||||
function (error) { deferred.reject(error.data || 'Request failed'); });
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
var clearSessionTimeout = function (reset) {
|
||||
var expires = service.expires;
|
||||
if (expires)
|
||||
$timeout.cancel(expires);
|
||||
if (reset)
|
||||
expires = $timeout(timeoutSession, 15*60000);
|
||||
else
|
||||
expires = null;
|
||||
service.expires = expires;
|
||||
return expires;
|
||||
};
|
||||
|
||||
var clearSession = function () {
|
||||
clearSessionTimeout(false);
|
||||
service._isLoggedIn = false;
|
||||
service.username = service.uuid = service.guid = service.expires = null;
|
||||
};
|
||||
|
||||
var timeoutSession = function () {
|
||||
clearSession();
|
||||
$root.$broadcast('TNSession.expired');
|
||||
};
|
||||
|
||||
service.isLoggedIn = function () { return service._isLoggedIn; };
|
||||
service.getUsername = function () { return service.username; };
|
||||
service.getGuid = function () { return service.guid; };
|
||||
|
||||
service.login = function (username, password) {
|
||||
clearSession();
|
||||
return sendSessionRequest({method:'login', un:username, pw:password})
|
||||
.then(function (response) {
|
||||
service._isLoggedIn = true;
|
||||
service.username = username;
|
||||
service.uuid = response.uuid;
|
||||
service.guid = response.guid;
|
||||
service.expires = clearSessionTimeout(true);
|
||||
$root.$broadcast('TNSession.login');
|
||||
return response;
|
||||
});
|
||||
};
|
||||
|
||||
service.logout = function () {
|
||||
return sendSessionRequest({method:'logout', uuid:service.uuid, guid:service.guid})
|
||||
.then(function () { clearSession(); $root.$broadcast('TNSession.logout'); });
|
||||
};
|
||||
|
||||
service.sendRequest = function (method, payload) {
|
||||
var deferred = $q.defer();
|
||||
var params = {
|
||||
jsonp: 'JSON_CALLBACK',
|
||||
method: method,
|
||||
uuid: service.uuid,
|
||||
guid: service.guid,
|
||||
payload: JSON.stringify(payload)
|
||||
};
|
||||
$http.jsonp('http://thyth.com/tn/json/json_browser.php', {method:'POST', params:params})
|
||||
.then(
|
||||
function (response) {
|
||||
switch (response.data.status) {
|
||||
case 'error': deferred.reject(response.data.msg); break;
|
||||
case 'success': deferred.resolve(response.data['payload']); break;
|
||||
default: deferred.resolve(response.data); break;
|
||||
}
|
||||
clearSessionTimeout(true);
|
||||
},
|
||||
function (error) { deferred.reject(error.data || 'Request failed') });
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
return service;
|
||||
}]);
|
||||
150
app/styles/main.scss
Normal file
150
app/styles/main.scss
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
$icon-font-path: "../bower_components/bootstrap-sass-official/assets/fonts/bootstrap/";
|
||||
// bower:scss
|
||||
@import "bootstrap-sass-official/assets/stylesheets/_bootstrap.scss";
|
||||
// endbower
|
||||
|
||||
.browsehappy {
|
||||
margin: 0.2em 0;
|
||||
background: #ccc;
|
||||
color: #000;
|
||||
padding: 0.2em 0;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 0;
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
/* Everything but the jumbotron gets side spacing for mobile first views */
|
||||
.header,
|
||||
.marketing,
|
||||
.footer {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.page-header { margin-left: 10px; }
|
||||
|
||||
/* Custom page header */
|
||||
.header {
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
margin-bottom: 10px;
|
||||
|
||||
/* Make the masthead heading the same height as the navigation */
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
line-height: 40px;
|
||||
padding-bottom: 19px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Custom page footer */
|
||||
.footer {
|
||||
padding-top: 19px;
|
||||
color: #777;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
|
||||
.tag { color:goldenrod; }
|
||||
|
||||
.row.alerts .alert {
|
||||
padding: 5px;
|
||||
}
|
||||
.row.alerts .btn {
|
||||
padding: 0;
|
||||
float: right;
|
||||
}
|
||||
span.btn-link:hover {cursor:pointer;}
|
||||
|
||||
.sidebar {
|
||||
display: none;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 51px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
display: block;
|
||||
padding: 20px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
|
||||
background-color: #f5f5f5;
|
||||
border-right: 1px solid #eee;
|
||||
}
|
||||
}
|
||||
.nav-sidebar {
|
||||
margin-right: -21px; /* 20px padding + 1px border */
|
||||
margin-bottom: 20px;
|
||||
margin-left: -20px;
|
||||
}
|
||||
.nav-sidebar > li > a {
|
||||
padding-right: 20px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.nav-sidebar > .active > a,
|
||||
.nav-sidebar > .active > a:hover,
|
||||
.nav-sidebar > .active > a:focus {
|
||||
color: #fff;
|
||||
background-color: #428bca;
|
||||
}
|
||||
.navbar-form.nav-sidebar input[type=text] {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#login h2 {
|
||||
margin-top:0;
|
||||
}
|
||||
|
||||
#create .input-group-addon {min-width:8em;}
|
||||
#create .input-group-addon.top { vertical-align:top !important; }
|
||||
|
||||
.container-narrow > hr {
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
/* Main marketing message and sign up button */
|
||||
.jumbotron {
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
|
||||
.btn {
|
||||
font-size: 21px;
|
||||
padding: 14px 24px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Supporting marketing content */
|
||||
.marketing {
|
||||
margin: 40px 0;
|
||||
|
||||
p + h4 {
|
||||
margin-top: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Responsive: Portrait tablets and up */
|
||||
@media screen and (min-width: 768px) {
|
||||
.container {
|
||||
max-width: 730px;
|
||||
}
|
||||
|
||||
/* Remove the padding we set earlier */
|
||||
.header,
|
||||
.marketing,
|
||||
.footer {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
/* Space out the masthead */
|
||||
.header {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
/* Remove the bottom border on the jumbotron for visual effect */
|
||||
.jumbotron {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
48
app/views/create.html
Normal file
48
app/views/create.html
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<div id="create" ng-controller="CreateTribeCtrl as create">
|
||||
<div class="row">
|
||||
<h1 class="page-header">Create a Tribe</h1>
|
||||
</div>
|
||||
<div class="row alerts" ng-repeat="alert in messages">
|
||||
<div class="col-md-4 alert alert-{{alert.type}}">
|
||||
<span>{{alert.message}}</span>
|
||||
<span class="glyphicon glyphicon-remove btn" ng-click="alert.pop()"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row main">
|
||||
<div class="col-md-4">
|
||||
<form role="form" ng-submit="createTribe(name,tag,append,recruiting,info)">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">Name</span>
|
||||
<label for="txtName" class="sr-only">Name</label>
|
||||
<input type="text" id="txtName" class="form-control" ng-model="name" placeholder="Name" required autofocus/>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">Tag</span>
|
||||
<label for="txtTag" class="sr-only">Tag</label>
|
||||
<input type="text" id="txtTag" class="form-control" ng-model="tag" placeholder="Tag" required/>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">Append Tag</span>
|
||||
<label for="cmbAppend" class="sr-only">Append Tag</label>
|
||||
<select id="cmbAppend" class="form-control" ng-model="append" ng-options="v for v in ['Yes','No']"></select>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">Recruiting</span>
|
||||
<label for="cmbRecruiting" class="sr-only">Recruiting</label>
|
||||
<select id="cmbRecruiting" class="form-control" ng-model="recruiting" ng-options="v for v in ['Yes','No']"></select>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon top">Info</span>
|
||||
<label for="txtInfo" class="sr-only">Tribe Information</label>
|
||||
<textarea id="txtInfo" class="form-control" ng-model="info" rows="5" placeholder="Tribe information"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-4 col-md-4" style="margin-top:1em;">
|
||||
<button class="btn btn-success" type="submit">Create</button>
|
||||
<button class="btn btn-link" type="button" ng-click="resetInputs()">Reset</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
41
app/views/login.html
Normal file
41
app/views/login.html
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<div id="login" ng-controller="LoginCtrl as loginCtrl">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<h1 style="margin-top:0;">Warning!</h1>
|
||||
<p class="lead">
|
||||
Your password is sent to the community server unprotected, and could be intercepted by a skilled attacker.
|
||||
If you use this password elsewhere, it is advised that you change it at those locations.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row alerts" ng-repeat="alert in messages">
|
||||
<div class="col-md-4 col-md-offset-4 alert alert-{{alert.type}}">
|
||||
<span>{{alert.message}}</span>
|
||||
<span class="glyphicon glyphicon-remove btn" ng-click="alert.pop()"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" ng-hide="isLoggedIn">
|
||||
<div class="col-md-4 col-md-offset-4">
|
||||
<form name="loginForm" ng-submit="login(username, password)">
|
||||
<h2 class="sub-heading">Please sign in</h2>
|
||||
<label for="inputUsername" class="sr-only">Username</label>
|
||||
<input type="text" id="inputUsername" class="form-control" placeholder="Username" required autofocus ng-model="username"/>
|
||||
<label for="inputPassword" class="sr-only">Password</label>
|
||||
<input type="password" id="inputPassword" class="form-control" placeholder="Password" required ng-model="password"/>
|
||||
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" ng-show="isLoggedIn">
|
||||
<div class="col-md-4 col-md-offset-4">
|
||||
<form name="logoutForm" ng-submit="logout()">
|
||||
<h2 class="sub-heading">Sign out</h2>
|
||||
<label for="displayUsername" class="sr-only">Username</label>
|
||||
<input type="text" id="displayUsername" class="form-control" readonly ng-model="username"/>
|
||||
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign out</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
55
app/views/player.html
Normal file
55
app/views/player.html
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
<div id="player" ng-controller="PlayerCtrl as player">
|
||||
<div class="row">
|
||||
<h1 class="page-header">
|
||||
<span class="tag" ng-show="info.append==0">{{info.tag|preserve_space}}</span>{{info.name|preserve_space}}<span class="tag" ng-show="info.append==1">{{info.tag|preserve_space}}</span>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="row alerts" ng-repeat="alert in messages">
|
||||
<div class="col-md-4 col-md-offset-4 alert alert-{{alert.type}}">
|
||||
<span>{{alert.message}}</span>
|
||||
<span class="glyphicon glyphicon-remove btn" ng-click="alert.pop()"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row main">
|
||||
<div class="col-md-6">
|
||||
<h2 class="sub-header">Profile Details</h2>
|
||||
<table class="table table-striped table-bordered table-rounded">
|
||||
<tbody>
|
||||
<tr><th>GUID</th><th>Name</th><th>Website</th></tr>
|
||||
<tr>
|
||||
<td>{{info.guid}}</td>
|
||||
<td>{{info.name|preserve_space}}</td>
|
||||
<td>{{info.website}}</td>
|
||||
</tr>
|
||||
<tr><th colspan="2">Joined</th><th>Online</th></tr>
|
||||
<tr><td colspan="2">{{info.creation*1000|date:'yyyy-MM-dd HH:mm:ss Z'}}</td><td>{{info.online|yes_no|titleize}}</td></tr>
|
||||
<tr><th colspan="3">Info</th></tr>
|
||||
<tr><td colspan="3"><pre>{{info.info}}</pre></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2 class="sub-header">Tribe Membership</h2>
|
||||
<table class="table table-striped table-bordered table-rounded">
|
||||
<thead><tr><th>ID</th><th>Name</th><th>Title</th><th>Rank</th></tr></thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="tribe in info.memberships">
|
||||
<td>{{tribe.id}}</td>
|
||||
<td><tribe-link id="tribe.id" tag="tribe.tag" name="tribe.name" append="tribe.append"></tribe-link></td>
|
||||
<td>{{tribe.title}}</td>
|
||||
<td>{{tribe.rank}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h2 class="sub-header">History</h2>
|
||||
<table class="table table-striped table-bordered table-rounded">
|
||||
<thead><tr><th>Event</th><th>Time</th><th>Description</th></tr></thead>
|
||||
<tr ng-repeat="item in history">
|
||||
<td>{{item.type}}</td>
|
||||
<td>{{item.time*1000|date:'yyyy-MM-dd HH:mm:ss Z'}}</td>
|
||||
<td history-item></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
81
app/views/profile.html
Normal file
81
app/views/profile.html
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
<div id="profile" ng-controller="ProfileCtrl as profile">
|
||||
<div class="row">
|
||||
<h1 class="page-header">
|
||||
<span class="tag" ng-show="info.append==0">{{info.tag|preserve_space}}</span>
|
||||
{{info.name|preserve_space}}
|
||||
<span class="tag" ng-show="info.append==1">{{info.tag|preserve_space}}</span>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="row alerts" ng-repeat="alert in messages">
|
||||
<div class="col-md-4 col-md-offset-4 alert alert-{{alert.type}}">
|
||||
<span>{{alert.message}}</span>
|
||||
<span class="glyphicon glyphicon-remove btn" ng-click="alert.pop()"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row main">
|
||||
<div class="col-md-7">
|
||||
<h2 class="sub-header">Profile Details</h2>
|
||||
<table class="table table-striped table-bordered table-rounded">
|
||||
<tbody>
|
||||
<tr><th>GUID</th><th>Name</th><th>Website</th></tr>
|
||||
<tr>
|
||||
<td>{{info.guid}}</td>
|
||||
<td><a href='#' editable-text="info.name" e-required onbeforesave="setName($data)">{{info.name|preserve_space}}</a></td>
|
||||
<td><a href='#' editable-text="info.website" onbeforesave="setWebsite($data)">{{info.website}}</a></td>
|
||||
</tr>
|
||||
<tr><th colspan="2">Joined</th><th>Online</th></tr>
|
||||
<tr><td colspan="2">{{info.creation*1000|date:'yyyy-MM-dd HH:mm:ss Z'}}</td><td>{{info.online|yes_no|titleize}}</td></tr>
|
||||
<tr><th colspan="3">Info</th></tr>
|
||||
<tr><td colspan="3"><a href='#' e-rows="7" e-cols="60" editable-textarea="info.info" onbeforesave="setDescription($data)"><pre>{{info.info}}</pre></a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2 class="sub-header">Tribe Membership</h2>
|
||||
<table class="table table-striped table-bordered table-rounded">
|
||||
<thead><tr><th>ID</th><th>Name</th><th>Title</th><th>Rank</th><th>Active</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="tribe in info.memberships">
|
||||
<td>{{tribe.id}}</td>
|
||||
<td><tribe-link id="tribe.id" tag="tribe.tag" name="tribe.name" append="tribe.append"></tribe-link></td>
|
||||
<td>{{tribe.title}}</td>
|
||||
<td>{{tribe.rank}}</td>
|
||||
<td>
|
||||
<input bs-switch type="radio" switch-size="small" ng-model="info.tag" ng-value="tribe.tag" ng-change="setActiveTribe(tribe.id,tribe.tag,tribe.append)"/>
|
||||
</td>
|
||||
<td>
|
||||
<form editable-form name="rowform" onbeforesave="leaveTribe(tribe)" ng-show="rowform.$visible" class="form-buttons form-inline">
|
||||
<button type="submit" ng-disabled="rowform.$waiting" class="btn btn-primary">Yes</button>
|
||||
<button type="button" ng-disabled="rowform.$waiting" class="btn btn-link" ng-click="rowform.$cancel()">Cancel</button>
|
||||
</form>
|
||||
<div class="buttons" ng-show="!rowform.$visible">
|
||||
<button class="btn-link" ng-click="rowform.$show()">Leave</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2 class="sub-header">Tribe Invitations</h2>
|
||||
<table class="table table-striped table-bordered table-rounded">
|
||||
<thead><tr><th>From</th><th>To</th><th>Expires</th><th></th><th></th></tr></thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="invite in invites">
|
||||
<td><player-link guid="invite.sender.guid" tag="invite.sender.tag" append="invite.sender.append" name="invite.sender.name"></player-link></td>
|
||||
<td><tribe-link id="invite.clan.id" tag="invite.clan.tag" append="invite.clan.append" name="invite.clan.name"></tribe-link></td>
|
||||
<td>{{invite.expire*1000|date:'yyyy-MM-dd HH:mm:ss Z'}}</td>
|
||||
<td><span class="btn-link" ng-click="acceptInvite(invite)">Accept</span></td>
|
||||
<td><span class="btn-link" ng-click="rejectInvite(invite)">Reject</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<h2 class="sub-header">History</h2>
|
||||
<table class="table table-striped table-bordered table-rounded">
|
||||
<thead><tr><th>Time</th><th>Description</th></tr></thead>
|
||||
<tr ng-repeat="item in history">
|
||||
<td>{{item.time*1000|date:'yyyy-MM-dd HH:mm:ss'}}</td>
|
||||
<td history-item></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
39
app/views/search.html
Normal file
39
app/views/search.html
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<div id="search" ng-controller="SearchCtrl as search">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar navbar-default">
|
||||
<form class="navbar-form" ng-submit="search(searchName, searchMode)">
|
||||
<label for="txtSearch" class="sr-only">Name</label>
|
||||
<input type="text" id="txtSearch" class="form-control" required autofocus placeholder="Name..." ng-model="searchName" />
|
||||
<label for="cmbMode" class="sr-only">Mode</label>
|
||||
<select class="form-control" ng-model="searchMode" ng-options="v for v in searchModes"></select>
|
||||
<button class="btn btn-primary glyphicon glyphicon-search"></button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="row alerts" ng-repeat="alert in messages">
|
||||
<div class="col-md-4 col-md-offset-4 alert alert-{{alert.type}}">
|
||||
<span>{{alert.message}}</span>
|
||||
<span class="glyphicon glyphicon-remove btn" ng-click="alert.pop()"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" ng-show="showResults">
|
||||
<table class="table table-striped table-bordered table-rounded" ng-show="resultMode=='Tribes'">
|
||||
<caption>Results</caption>
|
||||
<thead><tr><th>ID</th><th>Name</th></tr></thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="tribe in tribeResults">
|
||||
<td>{{tribe.id}}</td><td><tribe-link id="tribe.id" tag="" append="0" name="tribe.name"></tribe-link></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="table table-striped table-bordered table-rounded" ng-show="resultMode=='Players'">
|
||||
<caption>Results</caption>
|
||||
<thead><tr><th>Guid</th><th>Name</th></tr></thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="player in playerResults">
|
||||
<td>{{player.guid}}</td><td><player-link guid="player.guid" tag="player.tag" append="player.append" name="player.name"></player-link></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
134
app/views/tribe.html
Normal file
134
app/views/tribe.html
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
<div id="tribe" ng-controller="TribeCtrl as tribe">
|
||||
<div class="row">
|
||||
<h1 class="page-header">
|
||||
<span class="tag" ng-show="info.append==0">{{info.tag|preserve_space}}</span>{{info.name|preserve_space}}<span class="tag" ng-show="info.append==1">{{info.tag|preserve_space}}</span>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="row alerts" ng-repeat="alert in messages">
|
||||
<div class="col-md-4 col-md-offset-4 alert alert-{{alert.type}}">
|
||||
<span>{{alert.message}}</span>
|
||||
<span class="glyphicon glyphicon-remove btn" ng-click="alert.pop()"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row main">
|
||||
<div class="col-md-6">
|
||||
<h2 class="sub-header">Tribe Details</h2>
|
||||
<table class="table table-striped table-bordered table-rounded">
|
||||
<tbody>
|
||||
<tr><th>ID</th><th colspan="2">Name</th><th colspan="2">Tag</th></tr>
|
||||
<tr>
|
||||
<td>{{info.id}}</td>
|
||||
<td colspan="2">
|
||||
<span ng-hide="canSetName">{{info.name|preserve_space}}</span>
|
||||
<a href="#" ng-show="canSetName" editable-text="info.name" e-required onbeforesave="setName($data)">{{info.name|preserve_space}}</a>
|
||||
</td>
|
||||
<td colspan="2">
|
||||
<span ng-hide="canSetTag">{{info.tag|preserve_space}}</span>
|
||||
<form ng-show="canSetTag" editable-form name="tagform" class="form-buttons form-inline" onbeforesave="presetTag(info.tag,info.append)">
|
||||
<div ng-show="tagform.$visible">
|
||||
<span editable-text="info.tag" e-name="tag" e-required onaftersave="setTag(info.tag,info.append)">{{info.tag|preserve_space}}</span>
|
||||
<span editable-select="info.append" e-name="append" e-required e-ng-options="s.k as s.v for s in [{k:'1',v:'Append'},{k:'0',v:'Prepend'}]">{{info.append|yes_no|titleize}}</span>
|
||||
</div>
|
||||
<span type="button" class="btn-link" ng-click="tagform.$show()" ng-hide="tagform.$visible">{{info.tag|preserve_space}}</span>
|
||||
<span ng-show="tagform.$visible">
|
||||
<button type="submit" class="btn btn-primary glyphicon glyphicon-ok" ng-disabled="tagform.$waiting"></button>
|
||||
<button type="button" class="btn btn-default glyphicon glyphicon-remove" ng-disabled="tagform.$waiting" ng-click="tagform.$cancel()"></button>
|
||||
</span>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><th colspan="5">Website</th></tr>
|
||||
<tr><td colspan="5"><span ng-hide="canSetWebsite">{{info.website}}</span><a href='#' ng-show="canSetWebsite" editable-text="info.website" onbeforesave="setWebsite($data)">{{info.website}}</a></td></tr>
|
||||
<tr><th colspan="3">Created</th><th>Recruiting</th><th>Active</th></tr>
|
||||
<tr>
|
||||
<td colspan="3">{{info.creation*1000|date:'yyyy-MM-dd HH:mm:ss Z'}}</td>
|
||||
<td>
|
||||
<span ng-hide="canSetRecruiting">{{info.recruiting|yes_no|titleize}}</span>
|
||||
<a href='#' ng-show="canSetRecruiting" editable-select="info.recruiting" e-ng-options="s.key as s.value for s in [{key:'1',value:'Yes'}, {key:'0', value:'No'}]">{{info.recruiting|yes_no|titleize}}</a>
|
||||
</td>
|
||||
<td>{{info.active|yes_no|titleize}}</td>
|
||||
</tr>
|
||||
<tr><th colspan="3">Info</th><td colspan="2">Picture</td></tr>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<pre ng-hide="canSetInfo">{{info.info}}</pre>
|
||||
<a href='#' ng-show="canSetInfo" e-rows="7" e-cols="60" editable-textarea="info.info" onbeforesave="setDescription($data)"><pre>{{info.info}}</pre></a>
|
||||
</td>
|
||||
<td colspan="2">
|
||||
<span ng-hide="canSetPicture">{{info.picture || 'Not set'}}</span>
|
||||
<a href='#' ng-show="canSetPicture" editable-text="info.picture" onbeforesave="setPicture($data)">{{info.picture || 'Not set'}}</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-show="canDisband"><th colspan="5">Disband</th></tr>
|
||||
<tr ng-show="canDisband"><td colspan="5">
|
||||
<a href='#' editable-select="info.disband" e-ng-options="s.k as s.v for s in [{k:'0',v:'No'},{k:'1',v:'Yes'}]" onbeforesave="disband($data)">Disband</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2 class="sub-header">Tribe Members</h2>
|
||||
<table class="table table-striped table-bordered table-rounded">
|
||||
<thead><tr><th>GUID</th><th>Name</th><th>Title</th><th>Rank</th><th>Online</th><th ng-show="canKickMembers">Kick</th><th ng-show="canSetMemberRanks">Edit</th></tr></thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="player in info.members">
|
||||
<td>{{player.guid}}</td>
|
||||
<td><player-link guid="player.guid" tag="player.tag" name="player.name" append="player.append"></tribe-link></td>
|
||||
<td>
|
||||
<span ng-hide="canSetRank(player)">{{player.title}}</span>
|
||||
<span ng-show="canSetRank(player)" editable-text="player.title" e-name="title" e-form="rowform">{{player.title}}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span ng-hide="canSetRank(player)">{{player.rank}}</span>
|
||||
<span ng-show="canSetRank(player)" editable-select="player.rank" e-name="rank" e-form="rowform" e-ng-options="s.key as s.value for s in availableRanks">{{player.rank}}</span>
|
||||
</td>
|
||||
<td>{{player.online|yes_no|titleize}}</td>
|
||||
<td ng-show="canKickMembers"><a href='#' ng-show="canKick(player)" editable-select="player.kick" e-ng-options="s for s in ['Yes','No']" onbeforesave="kickMember(player.guid,$data)">Kick</a></td>
|
||||
<td ng-show="canSetMemberRanks"><div ng-show="canSetRank(player)">
|
||||
<form editable-form name="rowform" ng-show="rowform.$visible" class="form-buttons form-inline" onbeforesave="setRank(player.guid,$data.rank,$data.title)">
|
||||
<button type="submit" ng-disabled="rowform.$waiting" class="btn btn-primary">Save</button>
|
||||
<button type="button" ng-disabled="rowform.$waiting" class="btn btn-link" ng-click="rowform.$cancel()">Cancel</button>
|
||||
</form>
|
||||
<button ng-hide="rowform.$visible" ng-click="rowform.$show()" class="btn btn-default">edit</button>
|
||||
</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div ng-show="canSendInvite">
|
||||
<h2 class="sub-header">Send Invitation</h2>
|
||||
<form class="form-buttons form-inline" ng-submit="searchPlayers(searchName)">
|
||||
<label class="sr-only" for="srchName">Player name</label>
|
||||
<input type="text" class="form-control" ng-model="searchName" required placeholder="Player name"/>
|
||||
<button type="submit" class="btn btn-primary glyphicon glyphicon-search"></button>
|
||||
<span ng-show="searchAlert != null" class="alert alert-{{searchAlertType}}">{{searchAlert}}</span>
|
||||
</form>
|
||||
<table class="table table-striped table-bordered table-rounded" ng-show="showSearchResults">
|
||||
<caption>Results</caption>
|
||||
<thead><tr><th>GUID</th><th>Name</th><th>Invite</th></tr></thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="player in searchResults"><td>{{player.guid}}</td><td><player-link guid="player.guid" tag="player.tag" append="player.append" name="player.name"></player-link></td><td>
|
||||
<a href='#' editable-select="player.invite" e-ng-options="s for s in ['Yes','No']" onbeforesave="sendInvite(player.guid,$data)">Invite</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div ng-show="canViewInvites">
|
||||
<h2 class="sub-header">Tribe Invitations</h2>
|
||||
<table class="table table-striped table-bordered table-rounded">
|
||||
<thead><tr><th>Sender</th><th>Recipient</th><th>Expires</th></tr></thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="invite in invites"><td><player-link guid="invite.sender.guid" tag="invite.sender.tag" append="invite.sender.append" name="invite.sender.name"></player-link></td><td><player-link guid="invite.recipient.guid" tag="invite.recipient.tag" append="invite.recipient.append" name="invite.recipient.name"></player-link></td><td>{{invite.expire*1000|date:'yyyy-MM-dd HH:mm:ss Z'}}</td></tr>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h2 class="sub-header">History</h2>
|
||||
<table class="table table-striped table-bordered table-rounded">
|
||||
<thead><tr><th>Time</th><th>Description</th></tr></thead>
|
||||
<tr ng-repeat="item in history">
|
||||
<td>{{item.time*1000|date:'yyyy-MM-dd HH:mm:ss Z'}}</td>
|
||||
<td history-item></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
24
bower.json
Normal file
24
bower.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"name": "tn-community-browser",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"angular": "^1.4.0",
|
||||
"bootstrap-sass-official": "^3.2.0",
|
||||
"angular-route": "^1.4.0",
|
||||
"underscore": "~1.8.3",
|
||||
"underscore.string": "~3.2.3",
|
||||
"angular-xeditable": "~0.1.9",
|
||||
"angular-bootstrap-switch": "~0.4.1"
|
||||
},
|
||||
"appPath": "app",
|
||||
"moduleName": "tnCommunityBrowserApp",
|
||||
"overrides": {
|
||||
"bootstrap": {
|
||||
"main": [
|
||||
"less/bootstrap.less",
|
||||
"dist/css/bootstrap.css",
|
||||
"dist/js/bootstrap.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
39
package.json
Normal file
39
package.json
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"name": "tncommunitybrowser",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"autoprefixer-core": "^5.2.1",
|
||||
"grunt": "^0.4.5",
|
||||
"grunt-angular-templates": "^0.5.7",
|
||||
"grunt-concurrent": "^1.0.0",
|
||||
"grunt-contrib-clean": "^0.6.0",
|
||||
"grunt-contrib-compass": "^1.0.0",
|
||||
"grunt-contrib-concat": "^0.5.0",
|
||||
"grunt-contrib-connect": "^0.9.0",
|
||||
"grunt-contrib-copy": "^0.7.0",
|
||||
"grunt-contrib-cssmin": "^0.12.0",
|
||||
"grunt-contrib-htmlmin": "^0.4.0",
|
||||
"grunt-contrib-imagemin": "^1.0.0",
|
||||
"grunt-contrib-jshint": "^0.11.0",
|
||||
"grunt-contrib-uglify": "^0.7.0",
|
||||
"grunt-contrib-watch": "^0.6.1",
|
||||
"grunt-filerev": "^2.1.2",
|
||||
"grunt-google-cdn": "^0.4.3",
|
||||
"grunt-jscs": "^1.8.0",
|
||||
"grunt-newer": "^1.1.0",
|
||||
"grunt-ng-annotate": "^0.9.2",
|
||||
"grunt-postcss": "^0.5.5",
|
||||
"grunt-svgmin": "^2.0.0",
|
||||
"grunt-usemin": "^3.0.0",
|
||||
"grunt-wiredep": "^2.0.0",
|
||||
"jit-grunt": "^0.9.1",
|
||||
"jshint-stylish": "^1.0.0",
|
||||
"time-grunt": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"underscore": "^1.8.3"
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue