Starting to move to Ember App Kit
This commit is contained in:
parent
30d3642e04
commit
b066b8806c
145 changed files with 3025 additions and 70975 deletions
3
.bowerrc
Normal file
3
.bowerrc
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"directory": "vendor"
|
||||||
|
}
|
20
.gitignore
vendored
Normal file
20
.gitignore
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# compiled output
|
||||||
|
/dist
|
||||||
|
/tmp
|
||||||
|
/docs
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/vendor/*
|
||||||
|
!/vendor/ember-shim.js
|
||||||
|
!/vendor/qunit-shim.js
|
||||||
|
|
||||||
|
# misc
|
||||||
|
/.sass-cache
|
||||||
|
/connect.lock
|
||||||
|
/libpeerconnection.log
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
/coverage/*
|
39
.jshintrc
Normal file
39
.jshintrc
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
"predef": {
|
||||||
|
"document": true,
|
||||||
|
"window": true,
|
||||||
|
"location": true,
|
||||||
|
"setTimeout": true,
|
||||||
|
"Ember": true,
|
||||||
|
"Em": true,
|
||||||
|
"DS": true,
|
||||||
|
"$": true,
|
||||||
|
"moment": true,
|
||||||
|
"sjcl": true
|
||||||
|
},
|
||||||
|
"node" : false,
|
||||||
|
"browser" : false,
|
||||||
|
"boss" : true,
|
||||||
|
"curly": false,
|
||||||
|
"debug": false,
|
||||||
|
"devel": false,
|
||||||
|
"eqeqeq": true,
|
||||||
|
"evil": true,
|
||||||
|
"forin": false,
|
||||||
|
"immed": false,
|
||||||
|
"laxbreak": false,
|
||||||
|
"newcap": true,
|
||||||
|
"noarg": true,
|
||||||
|
"noempty": false,
|
||||||
|
"nonew": false,
|
||||||
|
"nomen": false,
|
||||||
|
"onevar": false,
|
||||||
|
"plusplus": false,
|
||||||
|
"regexp": false,
|
||||||
|
"undef": true,
|
||||||
|
"sub": true,
|
||||||
|
"strict": false,
|
||||||
|
"white": false,
|
||||||
|
"eqnull": true,
|
||||||
|
"esnext": true
|
||||||
|
}
|
7
.travis.yml
Normal file
7
.travis.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
language: node_js
|
||||||
|
node_js:
|
||||||
|
- 0.10
|
||||||
|
before_script:
|
||||||
|
- npm install -g grunt-cli
|
||||||
|
- npm install -g bower
|
||||||
|
- bower install
|
71
CONTRIBUTING.md
Normal file
71
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
# Questions
|
||||||
|
|
||||||
|
This is the issue tracker for Ember App Kit.
|
||||||
|
The Ember App Kit community uses this site
|
||||||
|
to collect and track bugs and discussions of new features.
|
||||||
|
If you are having difficulties or have a question about usage please ask a
|
||||||
|
question on StackOverflow: http://stackoverflow.com/questions/ask and tag
|
||||||
|
your question with `ember-app-kit`.
|
||||||
|
|
||||||
|
# Issues
|
||||||
|
|
||||||
|
Think you've found a bug or have a new feature to suggest? Let us know!
|
||||||
|
|
||||||
|
## Reporting a Bug
|
||||||
|
1. Update to the most recent master release if possible. We may have already
|
||||||
|
fixed your bug.
|
||||||
|
|
||||||
|
2. Search for similar issues. It's possible somebody has encountered
|
||||||
|
this bug already.
|
||||||
|
|
||||||
|
3. Provide with a bug report that shows the problem.
|
||||||
|
|
||||||
|
4. If possible, submit a Pull Request with a failing test. Better yet, take
|
||||||
|
a stab at fixing the bug yourself if you can!
|
||||||
|
|
||||||
|
The more information you provide, the easier it is for us to validate that
|
||||||
|
there is a bug and the faster we'll be able to take action.
|
||||||
|
|
||||||
|
# Pull Requests
|
||||||
|
|
||||||
|
We love pull requests. Here's a quick guide:
|
||||||
|
|
||||||
|
1. Fork the repo.
|
||||||
|
|
||||||
|
2. Run the tests. We only take pull requests with passing tests
|
||||||
|
|
||||||
|
3. Add a test for your change. Only refactoring and documentation changes
|
||||||
|
require no new tests. If you are adding functionality or fixing a bug, we need
|
||||||
|
a test!
|
||||||
|
|
||||||
|
4. Make the test pass.
|
||||||
|
|
||||||
|
5. Commit your changes. If your pull request fixes an issue specify it in the commit message.
|
||||||
|
Here's an example: `git commit -m "Close #52 – Fix controller and viewbindings"`
|
||||||
|
|
||||||
|
6. Push to your fork and submit a pull request. Please provide us with some
|
||||||
|
explanation of why you made the changes you made. For new features make sure to
|
||||||
|
explain a standard use case to us.
|
||||||
|
|
||||||
|
We try to be quick about responding to tickets but sometimes we get a bit
|
||||||
|
backlogged. If the response is slow, try to find someone on IRC (#emberjs) to
|
||||||
|
give the ticket a review.
|
||||||
|
|
||||||
|
# Website / Documentation
|
||||||
|
|
||||||
|
The project documentations is available at http://iamstef.net/ember-app-kit/
|
||||||
|
|
||||||
|
Ember App Kit is still under major development and contributions to the documentation are welcome!
|
||||||
|
|
||||||
|
1. Fork the repo.
|
||||||
|
|
||||||
|
2. Checkout the `gh-pages` branch
|
||||||
|
|
||||||
|
3. Make your changes
|
||||||
|
|
||||||
|
4. Commit and open a pull request against the `gh-pages` branch
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
NOTE: Partially copied from https://raw.github.com/emberjs/ember.js/master/CONTRIBUTING.md
|
||||||
|
|
256
Gruntfile.js
Normal file
256
Gruntfile.js
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
// jshint node:true
|
||||||
|
|
||||||
|
module.exports = function(grunt) {
|
||||||
|
// To support Coffeescript, SASS, LESS and others, just install
|
||||||
|
// the appropriate grunt package and it will be automatically included
|
||||||
|
// in the build process:
|
||||||
|
//
|
||||||
|
// * for Coffeescript, run `npm install --save-dev grunt-contrib-coffee`
|
||||||
|
//
|
||||||
|
// * for SCSS (without SASS), run `npm install --save-dev grunt-sass`
|
||||||
|
// * for SCSS/SASS support (may be slower), run
|
||||||
|
// `npm install --save-dev grunt-contrib-sass`
|
||||||
|
// This depends on the ruby sass gem, which can be installed with
|
||||||
|
// `gem install sass`
|
||||||
|
// * for Compass, run `npm install --save-dev grunt-contrib-compass`
|
||||||
|
// This depends on the ruby compass gem, which can be installed with
|
||||||
|
// `gem install compass`
|
||||||
|
// You should not install SASS if you have installed Compass.
|
||||||
|
//
|
||||||
|
// * for LESS, run `npm install --save-dev grunt-contrib-less`
|
||||||
|
//
|
||||||
|
// * for Stylus/Nib, `npm install --save-dev grunt-contrib-stylus`
|
||||||
|
//
|
||||||
|
// * for Emblem, run the following commands:
|
||||||
|
// `npm uninstall --save-dev grunt-ember-templates`
|
||||||
|
// `npm install --save-dev grunt-emblem`
|
||||||
|
// `bower install emblem.js --save`
|
||||||
|
//
|
||||||
|
// * For EmberScript, run `npm install --save-dev grunt-ember-script`
|
||||||
|
//
|
||||||
|
// * for LiveReload, `npm install --save-dev connect-livereload`
|
||||||
|
//
|
||||||
|
// * for YUIDoc support, `npm install --save-dev grunt-contrib-yuidoc`
|
||||||
|
// It is also nice to use a theme other than default. For example,
|
||||||
|
// simply do: `npm install yuidoc-theme-blue`
|
||||||
|
// Currently, only the `app` directory is used for generating docs.
|
||||||
|
// When installed, visit: http[s]://[host:port]/docs
|
||||||
|
//
|
||||||
|
// * for displaying the execution time of the grunt tasks,
|
||||||
|
// `npm install --save-dev time-grunt`
|
||||||
|
//
|
||||||
|
// * for minimizing the index.html at the end of the dist task
|
||||||
|
// `npm install --save-dev grunt-contrib-htmlmin`
|
||||||
|
//
|
||||||
|
// * for minimizing images in the dist task
|
||||||
|
// `npm install --save-dev grunt-contrib-imagemin`
|
||||||
|
//
|
||||||
|
// * for using images based CSS sprites (http://youtu.be/xD8DW6IQ6r0)
|
||||||
|
// `npm install --save-dev grunt-fancy-sprites`
|
||||||
|
// `bower install --save fancy-sprites-scss`
|
||||||
|
//
|
||||||
|
// * for automatically adding CSS vendor prefixes (autoprefixer)
|
||||||
|
// `npm install --save-dev grunt-autoprefixer`
|
||||||
|
//
|
||||||
|
// * for package import validations
|
||||||
|
// `npm install --save-dev grunt-es6-import-validate`
|
||||||
|
//
|
||||||
|
|
||||||
|
var Helpers = require('./tasks/helpers'),
|
||||||
|
filterAvailable = Helpers.filterAvailableTasks,
|
||||||
|
_ = grunt.util._,
|
||||||
|
path = require('path');
|
||||||
|
|
||||||
|
Helpers.pkg = require("./package.json");
|
||||||
|
|
||||||
|
if (Helpers.isPackageAvailable("time-grunt")) {
|
||||||
|
require("time-grunt")(grunt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loads task options from `tasks/options/` and `tasks/custom-options`
|
||||||
|
// and loads tasks defined in `package.json`
|
||||||
|
var config = _.extend({},
|
||||||
|
require('load-grunt-config')(grunt, {
|
||||||
|
configPath: path.join(__dirname, 'tasks/options'),
|
||||||
|
loadGruntTasks: false,
|
||||||
|
init: false
|
||||||
|
}),
|
||||||
|
require('load-grunt-config')(grunt, { // Custom options have precedence
|
||||||
|
configPath: path.join(__dirname, 'tasks/custom-options'),
|
||||||
|
init: false
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
grunt.loadTasks('tasks'); // Loads tasks in `tasks/` folder
|
||||||
|
|
||||||
|
config.env = process.env;
|
||||||
|
|
||||||
|
|
||||||
|
// App Kit's Main Tasks
|
||||||
|
// ====================
|
||||||
|
|
||||||
|
|
||||||
|
// Generate the production version
|
||||||
|
// ------------------
|
||||||
|
grunt.registerTask('dist', "Build a minified & production-ready version of your app.", [
|
||||||
|
'clean:dist',
|
||||||
|
'build:dist',
|
||||||
|
'copy:assemble',
|
||||||
|
'createDistVersion'
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
// Default Task
|
||||||
|
// ------------------
|
||||||
|
grunt.registerTask('default', "Build (in debug mode) & test your application.", ['test']);
|
||||||
|
|
||||||
|
|
||||||
|
// Servers
|
||||||
|
// -------------------
|
||||||
|
grunt.registerTask('server', "Run your server in development mode, auto-rebuilding when files change.", function(proxyMethod) {
|
||||||
|
var expressServerTask = 'expressServer:debug';
|
||||||
|
if (proxyMethod) {
|
||||||
|
expressServerTask += ':' + proxyMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
grunt.task.run(['clean:debug',
|
||||||
|
'build:debug',
|
||||||
|
expressServerTask,
|
||||||
|
'watch'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
grunt.registerTask('server:dist', "Build and preview a minified & production-ready version of your app.", [
|
||||||
|
'dist',
|
||||||
|
'expressServer:dist:keepalive'
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
// Testing
|
||||||
|
// -------
|
||||||
|
grunt.registerTask('test', "Run your apps's tests once. Uses Google Chrome by default.", [
|
||||||
|
'clean:debug', 'build:debug', 'testem:ci:basic' ]);
|
||||||
|
|
||||||
|
grunt.registerTask('test:ci', "Run your app's tests in PhantomJS. For use in continuous integration (i.e. Travis CI).", [
|
||||||
|
'clean:debug', 'build:debug', 'testem:ci:basic' ]);
|
||||||
|
|
||||||
|
grunt.registerTask('test:browsers', "Run your app's tests in multiple browsers (see tasks/options/testem.js for configuration).", [
|
||||||
|
'clean:debug', 'build:debug', 'testem:ci:browsers' ]);
|
||||||
|
|
||||||
|
grunt.registerTask('test:server', "Alias to `testem:run:basic`. Be sure to install testem first using `npm install -g testem`", [
|
||||||
|
'testem:run:basic' ]);
|
||||||
|
|
||||||
|
// Worker tasks
|
||||||
|
// =================================
|
||||||
|
|
||||||
|
grunt.registerTask('build:dist', filterAvailable([
|
||||||
|
'createResultDirectory', // Create directoy beforehand, fixes race condition
|
||||||
|
'fancySprites:create',
|
||||||
|
'concurrent:buildDist', // Executed in parallel, see config below
|
||||||
|
]));
|
||||||
|
|
||||||
|
grunt.registerTask('build:debug', filterAvailable([
|
||||||
|
'jshint:tooling',
|
||||||
|
'createResultDirectory', // Create directoy beforehand, fixes race condition
|
||||||
|
'fancySprites:create',
|
||||||
|
'concurrent:buildDebug', // Executed in parallel, see config below
|
||||||
|
]));
|
||||||
|
|
||||||
|
grunt.registerTask('createDistVersion', filterAvailable([
|
||||||
|
'useminPrepare', // Configures concat, cssmin and uglify
|
||||||
|
'concat', // Combines css and javascript files
|
||||||
|
|
||||||
|
'cssmin', // Minifies css
|
||||||
|
'uglify', // Minifies javascript
|
||||||
|
'imagemin', // Optimizes image compression
|
||||||
|
// 'svgmin',
|
||||||
|
'copy:dist', // Copies files not covered by concat and imagemin
|
||||||
|
|
||||||
|
'rev', // Appends 8 char hash value to filenames
|
||||||
|
'usemin', // Replaces file references
|
||||||
|
'htmlmin:dist' // Removes comments and whitespace
|
||||||
|
]));
|
||||||
|
|
||||||
|
// Documentation
|
||||||
|
// -------
|
||||||
|
grunt.registerTask('docs', "Build YUIDoc documentation.", [
|
||||||
|
'buildDocs',
|
||||||
|
'server:debug'
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
// Parallelize most of the build process
|
||||||
|
_.merge(config, {
|
||||||
|
concurrent: {
|
||||||
|
buildDist: [
|
||||||
|
"buildTemplates:dist",
|
||||||
|
"buildScripts",
|
||||||
|
"buildStyles",
|
||||||
|
"buildIndexHTML:dist"
|
||||||
|
],
|
||||||
|
buildDebug: [
|
||||||
|
"buildTemplates:debug",
|
||||||
|
"buildScripts",
|
||||||
|
"buildStyles",
|
||||||
|
"buildIndexHTML:debug"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Templates
|
||||||
|
grunt.registerTask('buildTemplates:dist', filterAvailable([
|
||||||
|
'emblem:compile',
|
||||||
|
'emberTemplates:dist'
|
||||||
|
]));
|
||||||
|
|
||||||
|
grunt.registerTask('buildTemplates:debug', filterAvailable([
|
||||||
|
'emblem:compile',
|
||||||
|
'emberTemplates:debug'
|
||||||
|
]));
|
||||||
|
|
||||||
|
// Scripts
|
||||||
|
grunt.registerTask('buildScripts', filterAvailable([
|
||||||
|
'jshint:app',
|
||||||
|
'jshint:tests',
|
||||||
|
'validate-imports:app',
|
||||||
|
'validate-imports:tests',
|
||||||
|
'coffee',
|
||||||
|
'emberscript',
|
||||||
|
'copy:javascriptToTmp',
|
||||||
|
'transpile',
|
||||||
|
'buildDocs',
|
||||||
|
'concat_sourcemap'
|
||||||
|
]));
|
||||||
|
|
||||||
|
// Styles
|
||||||
|
grunt.registerTask('buildStyles', filterAvailable([
|
||||||
|
'compass:compile',
|
||||||
|
'sass:compile',
|
||||||
|
'less:compile',
|
||||||
|
'stylus:compile',
|
||||||
|
'copy:cssToResult',
|
||||||
|
'autoprefixer:app'
|
||||||
|
]));
|
||||||
|
|
||||||
|
// Documentation
|
||||||
|
grunt.registerTask('buildDocs', filterAvailable([
|
||||||
|
'yuidoc:debug',
|
||||||
|
]));
|
||||||
|
|
||||||
|
// Index HTML
|
||||||
|
grunt.registerTask('buildIndexHTML:dist', [
|
||||||
|
'preprocess:indexHTMLDistApp',
|
||||||
|
'preprocess:indexHTMLDistTests'
|
||||||
|
]);
|
||||||
|
|
||||||
|
grunt.registerTask('buildIndexHTML:debug', [
|
||||||
|
'preprocess:indexHTMLDebugApp',
|
||||||
|
'preprocess:indexHTMLDebugTests'
|
||||||
|
]);
|
||||||
|
|
||||||
|
grunt.registerTask('createResultDirectory', function() {
|
||||||
|
grunt.file.mkdir('tmp/result');
|
||||||
|
});
|
||||||
|
|
||||||
|
grunt.initConfig(config);
|
||||||
|
};
|
353
LICENSE
353
LICENSE
|
@ -1,339 +1,22 @@
|
||||||
GNU GENERAL PUBLIC LICENSE
|
Copyright (c) 2013 Stefan Penner and Ember App Kit Contributors
|
||||||
Version 2, June 1991
|
|
||||||
|
|
||||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
MIT License
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
|
||||||
of this license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
Preamble
|
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 licenses for most software are designed to take away your
|
The above copyright notice and this permission notice shall be
|
||||||
freedom to share and change it. By contrast, the GNU General Public
|
included in all copies or substantial portions of the Software.
|
||||||
License is intended to guarantee your freedom to share and change free
|
|
||||||
software--to make sure the software is free for all its users. This
|
|
||||||
General Public License applies to most of the Free Software
|
|
||||||
Foundation's software and to any other program whose authors commit to
|
|
||||||
using it. (Some other Free Software Foundation software is covered by
|
|
||||||
the GNU Lesser General Public License instead.) You can apply it to
|
|
||||||
your programs, too.
|
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom, not
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
price. Our General Public Licenses are designed to make sure that you
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
have the freedom to distribute copies of free software (and charge for
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
this service if you wish), that you receive source code or can get it
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
if you want it, that you can change the software or use pieces of it
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
in new free programs; and that you know you can do these things.
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
To protect your rights, we need to make restrictions that forbid
|
|
||||||
anyone to deny you these rights or to ask you to surrender the rights.
|
|
||||||
These restrictions translate to certain responsibilities for you if you
|
|
||||||
distribute copies of the software, or if you modify it.
|
|
||||||
|
|
||||||
For example, if you distribute copies of such a program, whether
|
|
||||||
gratis or for a fee, you must give the recipients all the rights that
|
|
||||||
you have. You must make sure that they, too, receive or can get the
|
|
||||||
source code. And you must show them these terms so they know their
|
|
||||||
rights.
|
|
||||||
|
|
||||||
We protect your rights with two steps: (1) copyright the software, and
|
|
||||||
(2) offer you this license which gives you legal permission to copy,
|
|
||||||
distribute and/or modify the software.
|
|
||||||
|
|
||||||
Also, for each author's protection and ours, we want to make certain
|
|
||||||
that everyone understands that there is no warranty for this free
|
|
||||||
software. If the software is modified by someone else and passed on, we
|
|
||||||
want its recipients to know that what they have is not the original, so
|
|
||||||
that any problems introduced by others will not reflect on the original
|
|
||||||
authors' reputations.
|
|
||||||
|
|
||||||
Finally, any free program is threatened constantly by software
|
|
||||||
patents. We wish to avoid the danger that redistributors of a free
|
|
||||||
program will individually obtain patent licenses, in effect making the
|
|
||||||
program proprietary. To prevent this, we have made it clear that any
|
|
||||||
patent must be licensed for everyone's free use or not licensed at all.
|
|
||||||
|
|
||||||
The precise terms and conditions for copying, distribution and
|
|
||||||
modification follow.
|
|
||||||
|
|
||||||
GNU GENERAL PUBLIC LICENSE
|
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
|
|
||||||
0. This License applies to any program or other work which contains
|
|
||||||
a notice placed by the copyright holder saying it may be distributed
|
|
||||||
under the terms of this General Public License. The "Program", below,
|
|
||||||
refers to any such program or work, and a "work based on the Program"
|
|
||||||
means either the Program or any derivative work under copyright law:
|
|
||||||
that is to say, a work containing the Program or a portion of it,
|
|
||||||
either verbatim or with modifications and/or translated into another
|
|
||||||
language. (Hereinafter, translation is included without limitation in
|
|
||||||
the term "modification".) Each licensee is addressed as "you".
|
|
||||||
|
|
||||||
Activities other than copying, distribution and modification are not
|
|
||||||
covered by this License; they are outside its scope. The act of
|
|
||||||
running the Program is not restricted, and the output from the Program
|
|
||||||
is covered only if its contents constitute a work based on the
|
|
||||||
Program (independent of having been made by running the Program).
|
|
||||||
Whether that is true depends on what the Program does.
|
|
||||||
|
|
||||||
1. You may copy and distribute verbatim copies of the Program's
|
|
||||||
source code as you receive it, in any medium, provided that you
|
|
||||||
conspicuously and appropriately publish on each copy an appropriate
|
|
||||||
copyright notice and disclaimer of warranty; keep intact all the
|
|
||||||
notices that refer to this License and to the absence of any warranty;
|
|
||||||
and give any other recipients of the Program a copy of this License
|
|
||||||
along with the Program.
|
|
||||||
|
|
||||||
You may charge a fee for the physical act of transferring a copy, and
|
|
||||||
you may at your option offer warranty protection in exchange for a fee.
|
|
||||||
|
|
||||||
2. You may modify your copy or copies of the Program or any portion
|
|
||||||
of it, thus forming a work based on the Program, and copy and
|
|
||||||
distribute such modifications or work under the terms of Section 1
|
|
||||||
above, provided that you also meet all of these conditions:
|
|
||||||
|
|
||||||
a) You must cause the modified files to carry prominent notices
|
|
||||||
stating that you changed the files and the date of any change.
|
|
||||||
|
|
||||||
b) You must cause any work that you distribute or publish, that in
|
|
||||||
whole or in part contains or is derived from the Program or any
|
|
||||||
part thereof, to be licensed as a whole at no charge to all third
|
|
||||||
parties under the terms of this License.
|
|
||||||
|
|
||||||
c) If the modified program normally reads commands interactively
|
|
||||||
when run, you must cause it, when started running for such
|
|
||||||
interactive use in the most ordinary way, to print or display an
|
|
||||||
announcement including an appropriate copyright notice and a
|
|
||||||
notice that there is no warranty (or else, saying that you provide
|
|
||||||
a warranty) and that users may redistribute the program under
|
|
||||||
these conditions, and telling the user how to view a copy of this
|
|
||||||
License. (Exception: if the Program itself is interactive but
|
|
||||||
does not normally print such an announcement, your work based on
|
|
||||||
the Program is not required to print an announcement.)
|
|
||||||
|
|
||||||
These requirements apply to the modified work as a whole. If
|
|
||||||
identifiable sections of that work are not derived from the Program,
|
|
||||||
and can be reasonably considered independent and separate works in
|
|
||||||
themselves, then this License, and its terms, do not apply to those
|
|
||||||
sections when you distribute them as separate works. But when you
|
|
||||||
distribute the same sections as part of a whole which is a work based
|
|
||||||
on the Program, the distribution of the whole must be on the terms of
|
|
||||||
this License, whose permissions for other licensees extend to the
|
|
||||||
entire whole, and thus to each and every part regardless of who wrote it.
|
|
||||||
|
|
||||||
Thus, it is not the intent of this section to claim rights or contest
|
|
||||||
your rights to work written entirely by you; rather, the intent is to
|
|
||||||
exercise the right to control the distribution of derivative or
|
|
||||||
collective works based on the Program.
|
|
||||||
|
|
||||||
In addition, mere aggregation of another work not based on the Program
|
|
||||||
with the Program (or with a work based on the Program) on a volume of
|
|
||||||
a storage or distribution medium does not bring the other work under
|
|
||||||
the scope of this License.
|
|
||||||
|
|
||||||
3. You may copy and distribute the Program (or a work based on it,
|
|
||||||
under Section 2) in object code or executable form under the terms of
|
|
||||||
Sections 1 and 2 above provided that you also do one of the following:
|
|
||||||
|
|
||||||
a) Accompany it with the complete corresponding machine-readable
|
|
||||||
source code, which must be distributed under the terms of Sections
|
|
||||||
1 and 2 above on a medium customarily used for software interchange; or,
|
|
||||||
|
|
||||||
b) Accompany it with a written offer, valid for at least three
|
|
||||||
years, to give any third party, for a charge no more than your
|
|
||||||
cost of physically performing source distribution, a complete
|
|
||||||
machine-readable copy of the corresponding source code, to be
|
|
||||||
distributed under the terms of Sections 1 and 2 above on a medium
|
|
||||||
customarily used for software interchange; or,
|
|
||||||
|
|
||||||
c) Accompany it with the information you received as to the offer
|
|
||||||
to distribute corresponding source code. (This alternative is
|
|
||||||
allowed only for noncommercial distribution and only if you
|
|
||||||
received the program in object code or executable form with such
|
|
||||||
an offer, in accord with Subsection b above.)
|
|
||||||
|
|
||||||
The source code for a work means the preferred form of the work for
|
|
||||||
making modifications to it. For an executable work, complete source
|
|
||||||
code means all the source code for all modules it contains, plus any
|
|
||||||
associated interface definition files, plus the scripts used to
|
|
||||||
control compilation and installation of the executable. However, as a
|
|
||||||
special exception, the source code distributed need not include
|
|
||||||
anything that is normally distributed (in either source or binary
|
|
||||||
form) with the major components (compiler, kernel, and so on) of the
|
|
||||||
operating system on which the executable runs, unless that component
|
|
||||||
itself accompanies the executable.
|
|
||||||
|
|
||||||
If distribution of executable or object code is made by offering
|
|
||||||
access to copy from a designated place, then offering equivalent
|
|
||||||
access to copy the source code from the same place counts as
|
|
||||||
distribution of the source code, even though third parties are not
|
|
||||||
compelled to copy the source along with the object code.
|
|
||||||
|
|
||||||
4. You may not copy, modify, sublicense, or distribute the Program
|
|
||||||
except as expressly provided under this License. Any attempt
|
|
||||||
otherwise to copy, modify, sublicense or distribute the Program is
|
|
||||||
void, and will automatically terminate your rights under this License.
|
|
||||||
However, parties who have received copies, or rights, from you under
|
|
||||||
this License will not have their licenses terminated so long as such
|
|
||||||
parties remain in full compliance.
|
|
||||||
|
|
||||||
5. You are not required to accept this License, since you have not
|
|
||||||
signed it. However, nothing else grants you permission to modify or
|
|
||||||
distribute the Program or its derivative works. These actions are
|
|
||||||
prohibited by law if you do not accept this License. Therefore, by
|
|
||||||
modifying or distributing the Program (or any work based on the
|
|
||||||
Program), you indicate your acceptance of this License to do so, and
|
|
||||||
all its terms and conditions for copying, distributing or modifying
|
|
||||||
the Program or works based on it.
|
|
||||||
|
|
||||||
6. Each time you redistribute the Program (or any work based on the
|
|
||||||
Program), the recipient automatically receives a license from the
|
|
||||||
original licensor to copy, distribute or modify the Program subject to
|
|
||||||
these terms and conditions. You may not impose any further
|
|
||||||
restrictions on the recipients' exercise of the rights granted herein.
|
|
||||||
You are not responsible for enforcing compliance by third parties to
|
|
||||||
this License.
|
|
||||||
|
|
||||||
7. If, as a consequence of a court judgment or allegation of patent
|
|
||||||
infringement or for any other reason (not limited to patent issues),
|
|
||||||
conditions are imposed on you (whether by court order, agreement or
|
|
||||||
otherwise) that contradict the conditions of this License, they do not
|
|
||||||
excuse you from the conditions of this License. If you cannot
|
|
||||||
distribute so as to satisfy simultaneously your obligations under this
|
|
||||||
License and any other pertinent obligations, then as a consequence you
|
|
||||||
may not distribute the Program at all. For example, if a patent
|
|
||||||
license would not permit royalty-free redistribution of the Program by
|
|
||||||
all those who receive copies directly or indirectly through you, then
|
|
||||||
the only way you could satisfy both it and this License would be to
|
|
||||||
refrain entirely from distribution of the Program.
|
|
||||||
|
|
||||||
If any portion of this section is held invalid or unenforceable under
|
|
||||||
any particular circumstance, the balance of the section is intended to
|
|
||||||
apply and the section as a whole is intended to apply in other
|
|
||||||
circumstances.
|
|
||||||
|
|
||||||
It is not the purpose of this section to induce you to infringe any
|
|
||||||
patents or other property right claims or to contest validity of any
|
|
||||||
such claims; this section has the sole purpose of protecting the
|
|
||||||
integrity of the free software distribution system, which is
|
|
||||||
implemented by public license practices. Many people have made
|
|
||||||
generous contributions to the wide range of software distributed
|
|
||||||
through that system in reliance on consistent application of that
|
|
||||||
system; it is up to the author/donor to decide if he or she is willing
|
|
||||||
to distribute software through any other system and a licensee cannot
|
|
||||||
impose that choice.
|
|
||||||
|
|
||||||
This section is intended to make thoroughly clear what is believed to
|
|
||||||
be a consequence of the rest of this License.
|
|
||||||
|
|
||||||
8. If the distribution and/or use of the Program is restricted in
|
|
||||||
certain countries either by patents or by copyrighted interfaces, the
|
|
||||||
original copyright holder who places the Program under this License
|
|
||||||
may add an explicit geographical distribution limitation excluding
|
|
||||||
those countries, so that distribution is permitted only in or among
|
|
||||||
countries not thus excluded. In such case, this License incorporates
|
|
||||||
the limitation as if written in the body of this License.
|
|
||||||
|
|
||||||
9. The Free Software Foundation may publish revised and/or new versions
|
|
||||||
of the General Public License from time to time. Such new versions will
|
|
||||||
be similar in spirit to the present version, but may differ in detail to
|
|
||||||
address new problems or concerns.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the Program
|
|
||||||
specifies a version number of this License which applies to it and "any
|
|
||||||
later version", you have the option of following the terms and conditions
|
|
||||||
either of that version or of any later version published by the Free
|
|
||||||
Software Foundation. If the Program does not specify a version number of
|
|
||||||
this License, you may choose any version ever published by the Free Software
|
|
||||||
Foundation.
|
|
||||||
|
|
||||||
10. If you wish to incorporate parts of the Program into other free
|
|
||||||
programs whose distribution conditions are different, write to the author
|
|
||||||
to ask for permission. For software which is copyrighted by the Free
|
|
||||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
|
||||||
make exceptions for this. Our decision will be guided by the two goals
|
|
||||||
of preserving the free status of all derivatives of our free software and
|
|
||||||
of promoting the sharing and reuse of software generally.
|
|
||||||
|
|
||||||
NO WARRANTY
|
|
||||||
|
|
||||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
|
||||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
|
||||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
|
||||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
|
||||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
|
||||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
|
||||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
|
||||||
REPAIR OR CORRECTION.
|
|
||||||
|
|
||||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
|
||||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
|
||||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
|
||||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
|
||||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
|
||||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
|
||||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
|
||||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
|
||||||
POSSIBILITY OF SUCH DAMAGES.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
How to Apply These Terms to Your New Programs
|
|
||||||
|
|
||||||
If you develop a new program, and you want it to be of the greatest
|
|
||||||
possible use to the public, the best way to achieve this is to make it
|
|
||||||
free software which everyone can redistribute and change under these terms.
|
|
||||||
|
|
||||||
To do so, attach the following notices to the program. It is safest
|
|
||||||
to attach them to the start of each source file to most effectively
|
|
||||||
convey the exclusion of warranty; and each file should have at least
|
|
||||||
the "copyright" line and a pointer to where the full notice is found.
|
|
||||||
|
|
||||||
|
|
||||||
Copyright (C) 2013 jelhan
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
|
||||||
|
|
||||||
If the program is interactive, make it output a short notice like this
|
|
||||||
when it starts in an interactive mode:
|
|
||||||
|
|
||||||
Gnomovision version 69, Copyright (C) year name of author
|
|
||||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
|
||||||
This is free software, and you are welcome to redistribute it
|
|
||||||
under certain conditions; type `show c' for details.
|
|
||||||
|
|
||||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
|
||||||
parts of the General Public License. Of course, the commands you use may
|
|
||||||
be called something other than `show w' and `show c'; they could even be
|
|
||||||
mouse-clicks or menu items--whatever suits your program.
|
|
||||||
|
|
||||||
You should also get your employer (if you work as a programmer) or your
|
|
||||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
|
||||||
necessary. Here is a sample; alter the names:
|
|
||||||
|
|
||||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
|
||||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
|
||||||
|
|
||||||
{signature of Ty Coon}, 1 April 1989
|
|
||||||
Ty Coon, President of Vice
|
|
||||||
|
|
||||||
This General Public License does not permit incorporating your program into
|
|
||||||
proprietary programs. If your program is a subroutine library, you may
|
|
||||||
consider it more useful to permit linking proprietary applications with the
|
|
||||||
library. If this is what you want to do, use the GNU Lesser General
|
|
||||||
Public License instead of this License.
|
|
||||||
|
|
129
README.md
129
README.md
|
@ -1,35 +1,122 @@
|
||||||
croodle
|
# Deprecated
|
||||||
=======
|
|
||||||
|
|
||||||
Croodle is a web application to schedule a date or to do a poll on a general topics. Stored content data like title and description, number and labels of options and available answers and names of users and there selections is encrypted/decrypted in the browser using 256 bits AES.
|
this project has been deprecated in-favour of the ember-cli project:
|
||||||
|
|
||||||
This is an alpha version. Changes could brake backward compatibility. Also it is not well tested and some features are missing. It is not ment for productive use yet.
|
* https://github.com/stefanpenner/ember-cli
|
||||||
|
* http://iamstef.net/ember-cli
|
||||||
|
|
||||||
Croodle is inspired by [ZeroBin](https://github.com/sebsauvage/ZeroBin) and of course by Doodle.
|
|
||||||
|
|
||||||
Security notice
|
|
||||||
-------
|
|
||||||
|
|
||||||
As any other web application based end-to-end encryption Croodle could be attacked by an injection of maluse code on serverside or threw a man-in-the-middle attack. If an attacker could inject for example JavaScript, he would be able to read decrypted content in the browser ot the encryption key used and send it to a server under his controll.
|
|
||||||
|
|
||||||
Therefore you have to
|
|
||||||
* use an encrypted connection to the server hosting Croodle. In most use cases this will be an httpS connection. We strongly recomend people hosting Croodle to force an encrypted connection to Croodle.
|
|
||||||
* trust the server.
|
|
||||||
|
|
||||||
You could check for an attack like this by using an development tool for your browser and check if unencrypted data of your poll or the encryption key is send over network or is stored in a cookie or the localStorage of your browser for later send.
|
|
||||||
|
|
||||||
Requirements
|
# Ember App Kit [![Build Status](https://travis-ci.org/stefanpenner/ember-app-kit.png?branch=master)](https://travis-ci.org/stefanpenner/ember-app-kit)
|
||||||
-------
|
|
||||||
|
|
||||||
Croodle is designed to have as few as possible requirements on the server it is running on. Croodle runs on almost every web space with PHP. Croodle stores the data in textfiles, so there is no need for a database server like mySQL.
|
Ember App Kit aims to be the foundation for ambitious web applications built with Ember. It will soon be replaced by an executable [ember-cli](https://github.com/stefanpenner/ember-cli) which dramatically improves buildtimes (via broccoli) and provides sane-upgrade paths, feel free to check that project out. We intend to provide a sensible upgrade path.
|
||||||
|
|
||||||
Due to security reasons you should have SSL encryption enabled and provide a valid certificate.
|
This project has been extracted out of several real world applications and is actively used. Currently it covers the basics fairly well, but much still needs to be done. As we learn and as more contributors join in it continues to evolve. If you encounter any bugs, clunky features or missing documentation, just submit an issue and we'll respond ASAP.
|
||||||
|
|
||||||
Installation
|
At the very least, it helps setup your Ember.js applications directory structure.
|
||||||
-------
|
|
||||||
|
|
||||||
Download latest version and extract the files into your webfolder.
|
We welcome ideas and experiments.
|
||||||
|
|
||||||
Make sure that data/ folder is writeable by the web server.
|
## Getting Started
|
||||||
|
|
||||||
You should consider to force an SSL encrypted connection.
|
* [Project Documentation Site](http://stefanpenner.github.io/ember-app-kit/)
|
||||||
|
* [Getting Started Guide](http://stefanpenner.github.io/ember-app-kit/guides/getting-started.html)
|
||||||
|
* [ember-app-kit-todos](https://github.com/stefanpenner/ember-app-kit-todos) - the Emberjs [todos](http://emberjs.com/guides/getting-started/) using Ember App Kit
|
||||||
|
* [ember-app-kit-bloggr](https://github.com/pixelhandler/ember-app-kit-example-with-bloggr-client) - bloggr demo
|
||||||
|
* *Safari Books Online Blog* - [Introduction to Ember App Kit](http://blog.safaribooksonline.com/2013/09/18/ember-app-kit/) for more experienced Ember developers by @mixonic
|
||||||
|
* *Ember Sherpa* - [Introduction to Ember App Kit](http://embersherpa.com/articles/introduction-to-ember-app-kit/) for those who are new to the Grunt workflow by @taras
|
||||||
|
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Sane project structure
|
||||||
|
- ES6 module transpiler support (easy, future-proof modules)
|
||||||
|
- Module system-aware resolver (see [Referencing views](https://github.com/stefanpenner/ember-app-kit/wiki/Referencing-Views) and [Using Ember loaders](https://github.com/stefanpenner/ember-app-kit/wiki/Using-Ember-loaders))
|
||||||
|
- Transparent project compilation & minification for easy deploys via [Grunt](http://gruntjs.com/)
|
||||||
|
- Package management via [Bower](https://github.com/bower/bower)
|
||||||
|
- Optional support for CoffeeScript, SASS, LESS or Stylus
|
||||||
|
- Testing via QUnit, Ember Testing and Testem (with examples)
|
||||||
|
- Linting via JSHint (including module syntax)
|
||||||
|
- Catch-all `index.html` for easy reloading of pushState router apps
|
||||||
|
- Generators via [Loom](https://github.com/cavneb/loom-generators-ember-appkit) (to generate routes, controllers, etc.)
|
||||||
|
|
||||||
|
## Migrating to Ember CLI
|
||||||
|
|
||||||
|
First, run `npm install -g ember-cli` to install Ember CLI.
|
||||||
|
Now, on top of your existing EAK project, run `ember init`. Ember CLI
|
||||||
|
will begin to migrate your project, showing you a diff of its overrides as it
|
||||||
|
goes along.
|
||||||
|
|
||||||
|
### Ember Init Overrides
|
||||||
|
|
||||||
|
* tests/.jshintrc
|
||||||
|
* Let ember-cli overwrite this.
|
||||||
|
* app/index.html
|
||||||
|
* Managing vendor assets is now handled via the Brocfile. Let
|
||||||
|
ember-cli overwrite this file.
|
||||||
|
* app/app.js
|
||||||
|
* Ember Configuration is now handled in `config/`
|
||||||
|
* app/router.js
|
||||||
|
* Router's location is now handled via environment configuration.
|
||||||
|
Change this to ENV.locationType.
|
||||||
|
* app/routes/index.js
|
||||||
|
* This will attempt to replace your Index Route with a stub. Usually,
|
||||||
|
you wont't want Ember CLI to override this file.
|
||||||
|
* Brocfile.js
|
||||||
|
* Move your dependencies from app/index.html into this file by calling
|
||||||
|
app.import().
|
||||||
|
* Example: app.import('vendor/ember-data/ember-data.js')
|
||||||
|
* app/templates/application.hbs
|
||||||
|
* This will attempt to replace your application template with a stub.
|
||||||
|
* app/styles/app.css
|
||||||
|
* Another stub.
|
||||||
|
* tests/index.html
|
||||||
|
* Let ember-cli add this file. This is where the test dependencies
|
||||||
|
from `app/index.html` now live.
|
||||||
|
* bower.json
|
||||||
|
* package.json
|
||||||
|
* server
|
||||||
|
* The express server has been exposed and now lives under this
|
||||||
|
directory.
|
||||||
|
* This essentially replaces the API Stub in favor of a real
|
||||||
|
Express App, so you can now enhance and customize the static server
|
||||||
|
or develop the API and turn it into a full MEAN-like solution.
|
||||||
|
|
||||||
|
## Cleanup
|
||||||
|
|
||||||
|
You can remove the Gruntfile, tasks, and the api-stub directory, since we
|
||||||
|
won't be needing them anymore.
|
||||||
|
|
||||||
|
### Troubleshooting
|
||||||
|
|
||||||
|
* Ember CLI now picks up your app namespace. Change the import to
|
||||||
|
reference the name of your project.
|
||||||
|
* If you never changed your application namespace from the default
|
||||||
|
`appkit` then running `ember init` will break any import statements
|
||||||
|
you already have
|
||||||
|
|
||||||
|
* Index Route doesn't exist
|
||||||
|
* You may need to refresh your dependencies. Run `rm -rf npm_modules && npm install && npm
|
||||||
|
cache clean && bower install`
|
||||||
|
|
||||||
|
* Tests
|
||||||
|
* Import `tests/helpers/start-app` into each acceptance test file.
|
||||||
|
* `import startApp from 'your-app/tests/helpers/start-app`
|
||||||
|
* `resolver` and `startApp` still live in `test/helpers/` but
|
||||||
|
`module-for` is now its own package.
|
||||||
|
* If you were using ember-testing-httpRespond
|
||||||
|
* This is now patched for 1.4+
|
||||||
|
* Import it and its dependencies in your Brocfile by using
|
||||||
|
`app.import()`
|
||||||
|
|
||||||
|
## Special Thanks
|
||||||
|
|
||||||
|
Some ideas in ember-app-kit originated in work by Yapp Labs (@yapplabs) with McGraw-Hill Education Labs (@mhelabs) on [yapplabs/glazier](https://github.com/yapplabs/glazier). Thanks to Yapp and MHE for supporting the Ember ecosystem!
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Copyright 2013 by Stefan Penner and Ember App Kit Contributors, and licensed under the MIT License. See included
|
||||||
|
[LICENSE](/stefanpenner/ember-app-kit/blob/master/LICENSE) file for details.
|
||||||
|
|
120
api-stub/README.md
Normal file
120
api-stub/README.md
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
API Stub
|
||||||
|
========
|
||||||
|
|
||||||
|
The stub allows you to implement express routes to fake API calls.
|
||||||
|
Simply add API routes in the routes.js file. The benefit of an API
|
||||||
|
stub is that you can use the REST adapter from the get go. It's a
|
||||||
|
way to use fixtures without having to use the fixture adapter.
|
||||||
|
|
||||||
|
As development progresses, the API stub becomes a functioning spec
|
||||||
|
for the real backend. Once you have a separate development API
|
||||||
|
server running, then switch from the stub to the proxy pass through.
|
||||||
|
|
||||||
|
To configure which API method to use edit **package.json**.
|
||||||
|
|
||||||
|
* Set the **APIMethod** to 'stub' to use these express stub routes.
|
||||||
|
|
||||||
|
* Set the method to 'proxy' and define the **proxyURL** to pass all API requests to the proxy URL.
|
||||||
|
|
||||||
|
Default Example
|
||||||
|
----------------
|
||||||
|
|
||||||
|
1. Create the following models:
|
||||||
|
|
||||||
|
app/models/post.js
|
||||||
|
|
||||||
|
```
|
||||||
|
var attr = DS.attr,
|
||||||
|
hasMany = DS.hasMany,
|
||||||
|
belongsTo = DS.belongsTo;
|
||||||
|
|
||||||
|
var Post = DS.Model.extend({
|
||||||
|
title: attr(),
|
||||||
|
comments: hasMany('comment'),
|
||||||
|
user: attr(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Post;
|
||||||
|
```
|
||||||
|
|
||||||
|
app/models/comment.js
|
||||||
|
|
||||||
|
```
|
||||||
|
var attr = DS.attr,
|
||||||
|
hasMany = DS.hasMany,
|
||||||
|
belongsTo = DS.belongsTo;
|
||||||
|
|
||||||
|
var Comment = DS.Model.extend({
|
||||||
|
body: attr()
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Comment;
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Setup the REST adapter for the application:
|
||||||
|
|
||||||
|
app/adapters/application.js
|
||||||
|
|
||||||
|
```
|
||||||
|
var ApplicationAdapter = DS.RESTAdapter.extend({
|
||||||
|
namespace: 'api'
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ApplicationAdapter;
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Tell the Index router to query for a post:
|
||||||
|
|
||||||
|
app/routes/index.js
|
||||||
|
|
||||||
|
```
|
||||||
|
var IndexRoute = Ember.Route.extend({
|
||||||
|
model: function() {
|
||||||
|
return this.store.find('post', 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default IndexRoute;
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
4. Expose the model properties in the index.hbs template
|
||||||
|
|
||||||
|
app/templates/index.hbs
|
||||||
|
|
||||||
|
```
|
||||||
|
<h2>{{title}}</h2>
|
||||||
|
<p>{{body}}</p>
|
||||||
|
<section class="comments">
|
||||||
|
<ul>
|
||||||
|
{{#each comment in comments}}
|
||||||
|
<li>
|
||||||
|
<div>{{comment.body}}</div>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
```
|
||||||
|
|
||||||
|
When Ember Data queries the store for the post, it will make an API call to
|
||||||
|
http://localhost:8000/api/posts/1, to which the express server will respond with
|
||||||
|
some mock data:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"post": {
|
||||||
|
"id": 1,
|
||||||
|
"title": "Rails is omakase",
|
||||||
|
"comments": ["1", "2"],
|
||||||
|
"user" : "dhh"
|
||||||
|
},
|
||||||
|
|
||||||
|
"comments": [{
|
||||||
|
"id": "1",
|
||||||
|
"body": "Rails is unagi"
|
||||||
|
}, {
|
||||||
|
"id": "2",
|
||||||
|
"body": "Omakase O_o"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
29
api-stub/routes.js
Normal file
29
api-stub/routes.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
module.exports = function(server) {
|
||||||
|
|
||||||
|
// Create an API namespace, so that the root does not
|
||||||
|
// have to be repeated for each end point.
|
||||||
|
server.namespace('/api', function() {
|
||||||
|
|
||||||
|
// Return fixture data for '/api/posts/:id'
|
||||||
|
server.get('/posts/:id', function(req, res) {
|
||||||
|
var post = {
|
||||||
|
"post": {
|
||||||
|
"id": 1,
|
||||||
|
"title": "Rails is omakase",
|
||||||
|
"comments": ["1", "2"],
|
||||||
|
"user" : "dhh"
|
||||||
|
},
|
||||||
|
|
||||||
|
"comments": [{
|
||||||
|
"id": "1",
|
||||||
|
"body": "Rails is unagi"
|
||||||
|
}, {
|
||||||
|
"id": "2",
|
||||||
|
"body": "Omakase O_o"
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
|
res.send(post);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
12
app/adapters/application.js
Normal file
12
app/adapters/application.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
export default DS.RESTAdapter.extend({
|
||||||
|
// set namespace to api.php in same subdirectory
|
||||||
|
namespace: window.location.pathname
|
||||||
|
// remove index.html if it's there
|
||||||
|
.replace(/index.html$/, '')
|
||||||
|
// remove leading and trailing slash
|
||||||
|
.replace(/\/$/, '')
|
||||||
|
// add api.php
|
||||||
|
.concat('/api.php?')
|
||||||
|
// remove leading slash
|
||||||
|
.replace(/^\//g, '')
|
||||||
|
});
|
105
app/app.js
Normal file
105
app/app.js
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
import Resolver from 'ember/resolver';
|
||||||
|
import loadInitializers from 'ember/load-initializers';
|
||||||
|
|
||||||
|
// decrypt / encrypt computed property helper
|
||||||
|
Ember.computed.encrypted = function(encryptedField, dataType) {
|
||||||
|
return Ember.computed(encryptedField, function(key, decryptedValue) {
|
||||||
|
var encryptKey = this.get('encryption.key'),
|
||||||
|
encryptedValue,
|
||||||
|
decryptedJSON,
|
||||||
|
encryptedJSON;
|
||||||
|
|
||||||
|
// check if encryptKey is set
|
||||||
|
if (Ember.isEmpty(encryptKey)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// setter
|
||||||
|
if (arguments.length === 2) {
|
||||||
|
decryptedJSON = JSON.stringify(decryptedValue);
|
||||||
|
|
||||||
|
encryptedValue = Ember.isNone(decryptedValue) ? null : String(sjcl.encrypt(encryptKey , decryptedJSON));
|
||||||
|
this.set(encryptedField, encryptedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get value of field to decrypt
|
||||||
|
encryptedJSON = this.get(encryptedField);
|
||||||
|
|
||||||
|
// check if encryptedField is defined and not null
|
||||||
|
if (typeof encryptedJSON === 'undefined' ||
|
||||||
|
encryptedJSON === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to decrypt value
|
||||||
|
try {
|
||||||
|
decryptedJSON = sjcl.decrypt(encryptKey, encryptedJSON);
|
||||||
|
decryptedValue = JSON.parse(decryptedJSON);
|
||||||
|
} catch (e) {
|
||||||
|
// TODO: should throw an error
|
||||||
|
decryptedValue = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (dataType) {
|
||||||
|
case 'array':
|
||||||
|
return Ember.isNone(decryptedValue) ? null : decryptedValue;
|
||||||
|
|
||||||
|
case 'date':
|
||||||
|
return Ember.isNone(decryptedValue) ? null : Date(decryptedValue);
|
||||||
|
|
||||||
|
case 'string':
|
||||||
|
return Ember.isNone(decryptedValue) ? null : String(decryptedValue);
|
||||||
|
|
||||||
|
case 'boolean':
|
||||||
|
return Ember.isNone(decryptedValue) ? null : Boolean(decryptedValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var App = Ember.Application.extend({
|
||||||
|
modulePrefix: 'croodle', // TODO: loaded via config
|
||||||
|
Resolver: Resolver
|
||||||
|
});
|
||||||
|
|
||||||
|
App.PollTypes = [
|
||||||
|
Ember.Object.create({
|
||||||
|
id : "FindADate",
|
||||||
|
label : "Find a date"
|
||||||
|
}),
|
||||||
|
Ember.Object.create({
|
||||||
|
id : "MakeAPoll",
|
||||||
|
label : "Make a poll"
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
App.AnswerTypes = [
|
||||||
|
Ember.Object.create({
|
||||||
|
id : "YesNo",
|
||||||
|
label : "yes, no",
|
||||||
|
answers : [
|
||||||
|
{label: "yes"},
|
||||||
|
{label: "no"}
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
Ember.Object.create({
|
||||||
|
id : "YesNoMaybe",
|
||||||
|
label : "yes, no, maybe",
|
||||||
|
answers : [
|
||||||
|
{label: "yes"},
|
||||||
|
{label: "no"},
|
||||||
|
{label: "maybe"}
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
Ember.Object.create({
|
||||||
|
id : "FreeText",
|
||||||
|
label : "free text",
|
||||||
|
answers : []
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
import extendTextField from 'croodle/ext/text-field';
|
||||||
|
extendTextField();
|
||||||
|
|
||||||
|
loadInitializers(App, 'croodle');
|
||||||
|
|
||||||
|
export default App;
|
7
app/components/pretty-color.js
Normal file
7
app/components/pretty-color.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
classNames: ['pretty-color'],
|
||||||
|
attributeBindings: ['style'],
|
||||||
|
style: function(){
|
||||||
|
return 'color: ' + this.get('name') + ';';
|
||||||
|
}.property('name')
|
||||||
|
});
|
4
app/components/template-less.js
Normal file
4
app/components/template-less.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
classNames: ['look-ma-no-template'],
|
||||||
|
tagName: ['span']
|
||||||
|
});
|
0
app/controllers/.gitkeep
Normal file
0
app/controllers/.gitkeep
Normal file
17
app/controllers/create/index.js
Normal file
17
app/controllers/create/index.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
export default Ember.ObjectController.extend(Ember.Validations.Mixin, {
|
||||||
|
actions: {
|
||||||
|
submit: function(){
|
||||||
|
// redirect to CreateMeta
|
||||||
|
this.transitionToRoute('create.meta');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
validations: {
|
||||||
|
pollType: {
|
||||||
|
presence: true,
|
||||||
|
inclusion: {
|
||||||
|
in: ['FindADate', 'MakeAPoll']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
17
app/controllers/create/meta.js
Normal file
17
app/controllers/create/meta.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
export default Ember.ObjectController.extend(Ember.Validations.Mixin, {
|
||||||
|
actions: {
|
||||||
|
submit: function(){
|
||||||
|
// redirect to CreateOptions
|
||||||
|
this.transitionToRoute('create.options');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
validations: {
|
||||||
|
title: {
|
||||||
|
presence: true,
|
||||||
|
length: {
|
||||||
|
minimum: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
72
app/controllers/create/options.js
Normal file
72
app/controllers/create/options.js
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
export default Ember.ObjectController.extend(Ember.Validations.Mixin, {
|
||||||
|
actions: {
|
||||||
|
/*
|
||||||
|
* handles submit of option input for poll of type MakeAPoll
|
||||||
|
*/
|
||||||
|
submitMakeAPoll: function() {
|
||||||
|
var options = this.get('model.options'),
|
||||||
|
newOptions = [];
|
||||||
|
|
||||||
|
// remove options without value
|
||||||
|
options.forEach(function(option) {
|
||||||
|
if (option.title !== '') {
|
||||||
|
newOptions.pushObject(option);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// set updated options
|
||||||
|
//
|
||||||
|
// we have to hardly set new options even if they wasn't changed to
|
||||||
|
// trigger computed property; push on array doesn't trigger computed
|
||||||
|
// property to recalculate
|
||||||
|
this.set('model.options', newOptions);
|
||||||
|
|
||||||
|
// tricker save action
|
||||||
|
this.send('save');
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* handles submit of selected dates for poll of type MakeAPoll
|
||||||
|
*/
|
||||||
|
submitFindADate: function() {
|
||||||
|
// tricker save action
|
||||||
|
this.send('save');
|
||||||
|
},
|
||||||
|
|
||||||
|
save: function(){
|
||||||
|
// redirect to CreateSettings
|
||||||
|
this.transitionToRoute('create.settings');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* returns true if required number of options is reached
|
||||||
|
*/
|
||||||
|
enoughOptions: function(){
|
||||||
|
var requiredOptionsLength = 2,
|
||||||
|
givenOptions,
|
||||||
|
filtedOptions;
|
||||||
|
|
||||||
|
givenOptions = this.get('options');
|
||||||
|
|
||||||
|
// check if options are defined
|
||||||
|
if (typeof givenOptions === 'undefined') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reduce array to options which have a title
|
||||||
|
filtedOptions = givenOptions.filterBy('title', '');
|
||||||
|
|
||||||
|
return (givenOptions.length - filtedOptions.length) >= requiredOptionsLength;
|
||||||
|
}.property('options.@each.title'),
|
||||||
|
|
||||||
|
isNotValid: function(){
|
||||||
|
return !this.get('isValid');
|
||||||
|
}.property('isValid'),
|
||||||
|
|
||||||
|
validations: {
|
||||||
|
enoughOptions: {
|
||||||
|
acceptance: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
53
app/controllers/create/settings.js
Normal file
53
app/controllers/create/settings.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
export default Ember.ObjectController.extend(Ember.Validations.Mixin, {
|
||||||
|
actions: {
|
||||||
|
submit: function(){
|
||||||
|
// check if answer type is selected
|
||||||
|
if (this.get('answerType') === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// save poll
|
||||||
|
var self = this;
|
||||||
|
this.get('model').save().then(function(model){
|
||||||
|
// reload as workaround for bug: duplicated records after save
|
||||||
|
model.reload().then(function(model){
|
||||||
|
// redirect to new poll
|
||||||
|
self.transitionToRoute('poll', model, {queryParams: {encryptionKey: self.get('encryption.key')}});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set answers depending on selected answer type
|
||||||
|
*/
|
||||||
|
updateAnswers: function(){
|
||||||
|
var selectedAnswer = this.get('model.answerType'),
|
||||||
|
answers = [];
|
||||||
|
|
||||||
|
if (selectedAnswer !== null) {
|
||||||
|
for (var i=0; i < App.AnswerTypes.length; i++) {
|
||||||
|
if (App.AnswerTypes[i].id === selectedAnswer) {
|
||||||
|
answers = App.AnswerTypes[i].answers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.set('answers', answers);
|
||||||
|
}
|
||||||
|
}.observes('answerType'),
|
||||||
|
|
||||||
|
validations: {
|
||||||
|
answerType: {
|
||||||
|
presence: true,
|
||||||
|
inclusion: {
|
||||||
|
in: ["YesNo", "YesNoMaybe", "FreeText"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
anonymousUser: {
|
||||||
|
presence: true
|
||||||
|
},
|
||||||
|
forceAnswer: {
|
||||||
|
presence: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
5
app/controllers/error.js
Normal file
5
app/controllers/error.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export default Ember.ObjectController.extend({
|
||||||
|
is404: function(){
|
||||||
|
return this.get('status') === 404;
|
||||||
|
}.property('status')
|
||||||
|
});
|
234
app/controllers/poll.js
Normal file
234
app/controllers/poll.js
Normal file
|
@ -0,0 +1,234 @@
|
||||||
|
export default Ember.ObjectController.extend(Ember.Validations.Mixin, {
|
||||||
|
encryptionKey: '',
|
||||||
|
newUserName: '',
|
||||||
|
queryParams: ['encryptionKey'],
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
addNewUser: function(){
|
||||||
|
var newUser = {
|
||||||
|
name: this.get('newUserName'),
|
||||||
|
selections: this.get('newUserSelections')
|
||||||
|
};
|
||||||
|
|
||||||
|
// send new user to controller for saving
|
||||||
|
this.send('saveNewUser', newUser);
|
||||||
|
|
||||||
|
// clear input fields
|
||||||
|
this.set('newUserName', '');
|
||||||
|
this.get('newUserSelections').forEach(function(selection){
|
||||||
|
selection.set('value', '');
|
||||||
|
});
|
||||||
|
|
||||||
|
// reset validation erros
|
||||||
|
this.set('errors.newUserName', '');
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* save a new user
|
||||||
|
*/
|
||||||
|
saveNewUser: function(user){
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// create new user record in store
|
||||||
|
var newUser = this.store.createRecord('user', {
|
||||||
|
name: user.name,
|
||||||
|
creationDate: new Date(),
|
||||||
|
poll: this.get('model'),
|
||||||
|
selections: user.selections
|
||||||
|
});
|
||||||
|
|
||||||
|
// save new user
|
||||||
|
newUser.save().then(function(){
|
||||||
|
self.get('model.users').then(function(users){
|
||||||
|
// assign new user to poll
|
||||||
|
users.pushObject(newUser);
|
||||||
|
});
|
||||||
|
// reload as workaround for bug: duplicated records after save
|
||||||
|
self.get('model').reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* evaluates poll data
|
||||||
|
* if free text answers are allowed evaluation is disabled
|
||||||
|
*/
|
||||||
|
evaluation: function() {
|
||||||
|
// disable evaluation if answer type is free text
|
||||||
|
if (this.get('answerType') === 'FreeText') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var evaluation = [],
|
||||||
|
options = [],
|
||||||
|
lookup = [];
|
||||||
|
|
||||||
|
// init options array
|
||||||
|
this.get('options').forEach(function(option, index){
|
||||||
|
options[index] = 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
// init array of evalutation objects
|
||||||
|
// create object for every possible answer
|
||||||
|
this.get('answers').forEach(function(answer){
|
||||||
|
evaluation.push({
|
||||||
|
id: answer.label,
|
||||||
|
label: answer.label,
|
||||||
|
options: $.extend([], options)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// create object for no answer if answers are not forced
|
||||||
|
if (!this.get('forceAnswer')){
|
||||||
|
evaluation.push({
|
||||||
|
id: null,
|
||||||
|
label: 'no answer',
|
||||||
|
options: $.extend([], options)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// create lookup array
|
||||||
|
evaluation.forEach(function(value, index){
|
||||||
|
lookup[value.id] = index;
|
||||||
|
});
|
||||||
|
|
||||||
|
// loop over all users
|
||||||
|
this.get('users').forEach(function(user){
|
||||||
|
// loop over all selections of the user
|
||||||
|
user.get('selections').forEach(function(selection, optionindex){
|
||||||
|
var answerindex;
|
||||||
|
|
||||||
|
// get answer index by lookup array
|
||||||
|
if (typeof lookup[selection.value] === 'undefined') {
|
||||||
|
answerindex = lookup[null];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
answerindex = lookup[selection.value];
|
||||||
|
}
|
||||||
|
|
||||||
|
// increment counter
|
||||||
|
try {
|
||||||
|
evaluation[answerindex]['options'][optionindex] = evaluation[answerindex]['options'][optionindex] + 1;
|
||||||
|
} catch (e) {
|
||||||
|
// ToDo: Throw an error
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return evaluation;
|
||||||
|
}.property('users.@each'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* returns true if user has selected an answer for every option provided
|
||||||
|
*/
|
||||||
|
everyOptionIsAnswered: function(){
|
||||||
|
try {
|
||||||
|
var newUserSelections = this.get('newUserSelections'),
|
||||||
|
allAnswered = true;
|
||||||
|
|
||||||
|
if (typeof newUserSelections === 'undefined') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
newUserSelections.forEach(function(item, index, enumerable){
|
||||||
|
if (Ember.isEmpty(item.value)) {
|
||||||
|
allAnswered = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return allAnswered;
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}.property('newUserSelections.@each.value'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* calculate colspan for a row which should use all columns in table
|
||||||
|
* used by evaluation row
|
||||||
|
*/
|
||||||
|
fullRowColspan: function(){
|
||||||
|
var colspan = this.get('options.length') + 2;
|
||||||
|
return colspan;
|
||||||
|
}.property('options.@each'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* switch isValid state
|
||||||
|
* is needed for disable submit button
|
||||||
|
*/
|
||||||
|
isNotValid: function(){
|
||||||
|
return !this.get('isValid');
|
||||||
|
}.property('isValid'),
|
||||||
|
|
||||||
|
// array to store selections of new user
|
||||||
|
newUserSelections: function(){
|
||||||
|
var newUserSelections = Ember.A(),
|
||||||
|
options = this.get('options');
|
||||||
|
|
||||||
|
options.forEach(function(){
|
||||||
|
var newSelection = Ember.Object.create({value: ''});
|
||||||
|
newUserSelections.pushObject(newSelection);
|
||||||
|
});
|
||||||
|
|
||||||
|
return newUserSelections;
|
||||||
|
}.property('options'),
|
||||||
|
|
||||||
|
pollUrl: function() {
|
||||||
|
return window.location.href;
|
||||||
|
}.property('currentPath', 'encryptionKey'),
|
||||||
|
|
||||||
|
updateEncryptionKey: function() {
|
||||||
|
// update encryption key
|
||||||
|
this.set('encryption.key', this.get('encryptionKey'));
|
||||||
|
|
||||||
|
// reload content to recalculate computed properties
|
||||||
|
// if encryption key was set before
|
||||||
|
if (this.get('encryption.isSet') === true) {
|
||||||
|
this.get('content').reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.set('encryption.isSet', true);
|
||||||
|
}.observes('encryptionKey'),
|
||||||
|
|
||||||
|
validations: {
|
||||||
|
everyOptionIsAnswered: {
|
||||||
|
/*
|
||||||
|
* validate if every option is answered
|
||||||
|
* if it's forced by poll settings (forceAnswer === true)
|
||||||
|
*
|
||||||
|
* using a computed property therefore which returns true / false
|
||||||
|
* in combinatoin with acceptance validator
|
||||||
|
*
|
||||||
|
* ToDo: Show validation errors
|
||||||
|
*/
|
||||||
|
acceptance: {
|
||||||
|
if: function(object, validator){
|
||||||
|
return object.get('forceAnswer');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
newUserName: {
|
||||||
|
presence: {
|
||||||
|
/*
|
||||||
|
* validate if a user name is given
|
||||||
|
* if it's forced by poll settings (anonymousUser === false)
|
||||||
|
*/
|
||||||
|
unless: function(object, validator){
|
||||||
|
/* have in mind that anonymousUser is undefined on init */
|
||||||
|
return object.get('anonymousUser');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* have to manually rerun validation when encryption key is present in model
|
||||||
|
* otherwise ember-validation is not using correct values for properties in
|
||||||
|
* conditional validators
|
||||||
|
*/
|
||||||
|
validationsFixBug: function() {
|
||||||
|
if(!Ember.isEmpty(this.get('model.encryption.key'))) {
|
||||||
|
this.validate();
|
||||||
|
}
|
||||||
|
}.observes('model.encryption.key'),
|
||||||
|
});
|
4
app/croodle.js
Normal file
4
app/croodle.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
// adding support for attribut data-option-id to input fields
|
||||||
|
|
||||||
|
|
5
app/ext/text-field.js
Normal file
5
app/ext/text-field.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export default function() {
|
||||||
|
Ember.TextField.reopen({
|
||||||
|
attributeBindings: ['data-option']
|
||||||
|
});
|
||||||
|
}
|
0
app/helpers/.gitkeep
Normal file
0
app/helpers/.gitkeep
Normal file
3
app/helpers/formatted-date.js
Normal file
3
app/helpers/formatted-date.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export default Ember.Handlebars.makeBoundHelper(function(date, format) {
|
||||||
|
return moment(date).format(format);
|
||||||
|
});
|
8
app/helpers/reverse-word.js
Normal file
8
app/helpers/reverse-word.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// Please note that Handlebars helpers will only be found automatically by the
|
||||||
|
// resolver if their name contains a dash (reverse-word, translate-text, etc.)
|
||||||
|
// For more details: http://stefanpenner.github.io/ember-app-kit/guides/using-modules.html
|
||||||
|
|
||||||
|
export default Ember.Handlebars.makeBoundHelper(function(word) {
|
||||||
|
return word.split('').reverse().join('');
|
||||||
|
});
|
||||||
|
|
104
app/index.html
Normal file
104
app/index.html
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Croodle</title>
|
||||||
|
|
||||||
|
<!-- build:css(tmp/result) /assets/app.min.css -->
|
||||||
|
<link rel="stylesheet" href="/assets/app.css">
|
||||||
|
<!-- endbuild -->
|
||||||
|
|
||||||
|
<!-- for more details visit: https://github.com/yeoman/grunt-usemin -->
|
||||||
|
|
||||||
|
<!-- build:js(tmp/result) /assets/config.min.js -->
|
||||||
|
|
||||||
|
<script src="/config/environment.js"></script>
|
||||||
|
<!-- @if tests=false -->
|
||||||
|
|
||||||
|
<!-- @if dist=false -->
|
||||||
|
<script src="/config/environments/development.js"></script>
|
||||||
|
<!-- @endif --><!-- @if dist=true -->
|
||||||
|
<script src="/config/environments/production.js"></script>
|
||||||
|
<!-- @endif -->
|
||||||
|
|
||||||
|
<!-- @endif --><!-- @if tests=true -->
|
||||||
|
<script src="/config/environments/test.js"></script>
|
||||||
|
<!-- @endif -->
|
||||||
|
|
||||||
|
<!-- endbuild -->
|
||||||
|
|
||||||
|
<!-- build:js(tmp/result) /assets/vendor.min.js -->
|
||||||
|
|
||||||
|
<script src="/vendor/jquery/jquery.js"></script>
|
||||||
|
|
||||||
|
<!-- @if dist=false -->
|
||||||
|
<script src="/vendor/handlebars/handlebars.js"></script>
|
||||||
|
<script src="/vendor/ember/ember.js"></script>
|
||||||
|
<!-- @endif -->
|
||||||
|
<!-- @if dist=true -->
|
||||||
|
<script src="/vendor/handlebars/handlebars.runtime.js"></script>
|
||||||
|
<script src="/vendor/ember/ember.prod.js"></script>
|
||||||
|
<!-- @endif -->
|
||||||
|
|
||||||
|
<script src="/vendor/ember-data/ember-data.js"></script>
|
||||||
|
<script src="/vendor/loader.js/loader.js"></script>
|
||||||
|
<script src="/vendor/ember-resolver/dist/ember-resolver.js"></script>
|
||||||
|
<script src="/vendor/ember-shim.js"></script>
|
||||||
|
<script src="/vendor/ic-ajax/dist/named-amd/main.js"></script>
|
||||||
|
<script src="/vendor/ember-load-initializers/ember-load-initializers.js"></script>
|
||||||
|
<script src="/vendor/ember-validations/index.js"></script>
|
||||||
|
<script src="/vendor/ember-forms/dist/ember_forms.js"></script>
|
||||||
|
<script src="/vendor/ember-data-extensions/dist/embedded-adapter.js"></script>
|
||||||
|
<script src="/vendor/moment/moment.js"></script>
|
||||||
|
|
||||||
|
<!-- endbuild -->
|
||||||
|
|
||||||
|
<!-- build:js(tmp/result) /assets/app.min.js -->
|
||||||
|
|
||||||
|
<script src="/assets/app.js"></script>
|
||||||
|
<script src="/assets/templates.js"></script>
|
||||||
|
|
||||||
|
<!-- endbuild -->
|
||||||
|
|
||||||
|
<!-- @if dist=false -->
|
||||||
|
<style>
|
||||||
|
@keyframes domChanged { from { background: yellow; } }
|
||||||
|
@-webkit-keyframes domChanged { from { background: yellow; } }
|
||||||
|
.ember-view { animation: domChanged 1s; -webkit-animation: domChanged 1s; }
|
||||||
|
</style>
|
||||||
|
<!-- @endif -->
|
||||||
|
<!-- @if tests=true -->
|
||||||
|
<script src="/vendor/ember-shim.js"></script>
|
||||||
|
<link rel="stylesheet" href="/vendor/qunit/qunit/qunit.css">
|
||||||
|
<script src="/vendor/qunit/qunit/qunit.js"></script>
|
||||||
|
<script src="/vendor/qunit-shim.js"></script>
|
||||||
|
<script src="/vendor/ember-qunit/dist/named-amd/main.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!--[if lt IE 8]>
|
||||||
|
<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]-->
|
||||||
|
|
||||||
|
<!-- @if tests=true -->
|
||||||
|
<div id="qunit"></div>
|
||||||
|
<div id="qunit-fixture"></div>
|
||||||
|
<script src="/tests/tests.js"></script>
|
||||||
|
<script src="/tests/test-helper.js"></script>
|
||||||
|
<script src="/tests/test-loader.js"></script>
|
||||||
|
<script src="/testem.js"></script>
|
||||||
|
<!-- @endif -->
|
||||||
|
|
||||||
|
<!-- @if tests=false -->
|
||||||
|
<script>
|
||||||
|
window.App = require('croodle/app')['default'].create(ENV.APP);
|
||||||
|
</script>
|
||||||
|
<!-- @endif -->
|
||||||
|
</body>
|
||||||
|
</html>
|
0
app/initializers/.gitkeep
Normal file
0
app/initializers/.gitkeep
Normal file
13
app/initializers/inject-encryption-key.js
Normal file
13
app/initializers/inject-encryption-key.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import EncryptionStorage from 'croodle/models/encryption';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "injectEncryptionKey",
|
||||||
|
after: "store",
|
||||||
|
|
||||||
|
initialize: function(container, application) {
|
||||||
|
container.register('encryption:current', EncryptionStorage, {singleton: true});
|
||||||
|
application.inject('controller:poll', 'encryption', 'encryption:current');
|
||||||
|
application.inject('route:create', 'encryption', 'encryption:current');
|
||||||
|
application.inject('model', 'encryption', 'encryption:current');
|
||||||
|
}
|
||||||
|
};
|
0
app/models/.gitkeep
Normal file
0
app/models/.gitkeep
Normal file
4
app/models/encryption.js
Normal file
4
app/models/encryption.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export default Ember.Object.extend({
|
||||||
|
key : '',
|
||||||
|
isSet: false
|
||||||
|
});
|
31
app/models/poll.js
Normal file
31
app/models/poll.js
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
export default DS.Model.extend({
|
||||||
|
encryptedTitle : DS.attr('string'),
|
||||||
|
title : Ember.computed.encrypted('encryptedTitle', 'string'),
|
||||||
|
encryptedDescription : DS.attr('string'),
|
||||||
|
description: Ember.computed.encrypted('encryptedDescription', 'string'),
|
||||||
|
encryptedPollType : DS.attr('string'),
|
||||||
|
pollType : Ember.computed.encrypted('encryptedPollType', 'string'),
|
||||||
|
encryptedAnswerType: DS.attr('string'),
|
||||||
|
answerType : Ember.computed.encrypted('encryptedAnswerType', 'string'),
|
||||||
|
encryptedAnswers : DS.attr('string'),
|
||||||
|
answers : Ember.computed.encrypted('encryptedAnswers', 'array'),
|
||||||
|
encryptedOptions : DS.attr('string'),
|
||||||
|
options : Ember.computed.encrypted('encryptedOptions', 'array'),
|
||||||
|
users : DS.hasMany('user', {async: true}),
|
||||||
|
encryptedCreationDate : DS.attr('string'),
|
||||||
|
creationDate : Ember.computed.encrypted('encryptedCreationDate', 'date'),
|
||||||
|
encryptedForceAnswer : DS.attr('string'),
|
||||||
|
forceAnswer : Ember.computed.encrypted('encryptedForceAnswer', 'boolean'),
|
||||||
|
encryptedAnonymousUser : DS.attr('string'),
|
||||||
|
anonymousUser : Ember.computed.encrypted('encryptedAnonymousUser', 'boolean'),
|
||||||
|
|
||||||
|
isFindADate: function() {
|
||||||
|
return this.get('pollType') === 'FindADate';
|
||||||
|
}.property('pollType'),
|
||||||
|
isFreeText: function() {
|
||||||
|
return this.get('answerType') === 'FreeText';
|
||||||
|
}.property('answerType'),
|
||||||
|
isMakeAPoll: function() {
|
||||||
|
return this.get('pollType') === 'MakeAPoll';
|
||||||
|
}.property('pollType')
|
||||||
|
});
|
9
app/models/user.js
Normal file
9
app/models/user.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
export default DS.Model.extend({
|
||||||
|
poll : DS.belongsTo('poll', {async: true}),
|
||||||
|
encryptedName : DS.attr('string'),
|
||||||
|
name : Ember.computed.encrypted('encryptedName', 'string'),
|
||||||
|
encryptedSelections : DS.attr('string'),
|
||||||
|
selections : Ember.computed.encrypted('encryptedSelections', 'array'),
|
||||||
|
encryptedCreationDate : DS.attr('string'),
|
||||||
|
creationDate : Ember.computed.encrypted('encryptedCreationDate', 'date')
|
||||||
|
});
|
12
app/router.js
Normal file
12
app/router.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
var Router = Ember.Router.extend(); // ensure we don't share routes between all Router instances
|
||||||
|
|
||||||
|
Router.map(function(){
|
||||||
|
this.route('poll', { path: '/poll/:poll_id' });
|
||||||
|
this.resource('create', function(){
|
||||||
|
this.route('meta');
|
||||||
|
this.route('options');
|
||||||
|
this.route('settings');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Router;
|
0
app/routes/.gitkeep
Normal file
0
app/routes/.gitkeep
Normal file
29
app/routes/create.js
Normal file
29
app/routes/create.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
export default Ember.Route.extend({
|
||||||
|
beforeModel: function(){
|
||||||
|
// generate encryptionKey
|
||||||
|
var encryptionKeyLength = 40;
|
||||||
|
var encryptionKeyChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||||
|
|
||||||
|
var encryptionKey = '';
|
||||||
|
var list = encryptionKeyChars.split('');
|
||||||
|
var len = list.length, i = 0;
|
||||||
|
do {
|
||||||
|
i++;
|
||||||
|
var index = Math.floor(Math.random() * len);
|
||||||
|
encryptionKey += list[index];
|
||||||
|
} while(i < encryptionKeyLength);
|
||||||
|
|
||||||
|
// set encryption key
|
||||||
|
this.set('encryption.key', encryptionKey);
|
||||||
|
},
|
||||||
|
|
||||||
|
model: function(){
|
||||||
|
// create empty poll
|
||||||
|
return this.store.createRecord('poll', {
|
||||||
|
creationDate : new Date(),
|
||||||
|
options : [{title: ''}, {title: ''}],
|
||||||
|
forceAnswer: true,
|
||||||
|
anonymousUser: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
5
app/routes/create/index.js
Normal file
5
app/routes/create/index.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export default Ember.Route.extend({
|
||||||
|
model: function(){
|
||||||
|
return this.modelFor('create');
|
||||||
|
}
|
||||||
|
});
|
12
app/routes/create/meta.js
Normal file
12
app/routes/create/meta.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
export default Ember.Route.extend({
|
||||||
|
model: function(){
|
||||||
|
return this.modelFor('create');
|
||||||
|
},
|
||||||
|
|
||||||
|
// redirect to create/index if poll type is not set
|
||||||
|
afterModel: function(create){
|
||||||
|
if (create.get('pollType') === null) {
|
||||||
|
this.transitionTo('create.index');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
12
app/routes/create/options.js
Normal file
12
app/routes/create/options.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
export default Ember.Route.extend({
|
||||||
|
model: function(){
|
||||||
|
return this.modelFor('create');
|
||||||
|
},
|
||||||
|
|
||||||
|
// redirect to create/meta if title is not set
|
||||||
|
afterModel: function(create){
|
||||||
|
if (create.get('title') === null) {
|
||||||
|
this.transitionTo('create.meta');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
7
app/routes/create/poll.js
Normal file
7
app/routes/create/poll.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
export default Ember.Route.extend({
|
||||||
|
model: function(params){
|
||||||
|
return this.store.find('poll', params.poll_id).then(function(poll) {
|
||||||
|
return poll;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
23
app/routes/create/settings.js
Normal file
23
app/routes/create/settings.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
export default Ember.Route.extend({
|
||||||
|
model: function(){
|
||||||
|
return this.modelFor('create');
|
||||||
|
},
|
||||||
|
|
||||||
|
// redirect to create/options if not enough options are defined
|
||||||
|
afterModel: function(create){
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// check if only default options are defined
|
||||||
|
if (create.get('options.length') === 2) {
|
||||||
|
create.get('options').forEach(function(option) {
|
||||||
|
if (option.title === '') {
|
||||||
|
self.transitionTo('create.options');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// check if less then two options are defined
|
||||||
|
else if (create.get('options.length') < 2) {
|
||||||
|
this.transitionTo('create.options');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
0
app/serializers/.gitkeep
Normal file
0
app/serializers/.gitkeep
Normal file
1
app/serializers/application.js
Normal file
1
app/serializers/application.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export default DS.EmbeddedSerializer.extend();
|
5
app/serializers/poll.js
Normal file
5
app/serializers/poll.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export default DS.EmbeddedSerializer.extend({
|
||||||
|
attrs: {
|
||||||
|
users: {embedded: 'load'}
|
||||||
|
}
|
||||||
|
});
|
0
app/styles/.gitkeep
Normal file
0
app/styles/.gitkeep
Normal file
4
app/styles/app.css
Normal file
4
app/styles/app.css
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
/* Put your CSS here */
|
||||||
|
html, body {
|
||||||
|
margin: 20px;
|
||||||
|
}
|
0
app/templates/.gitkeep
Normal file
0
app/templates/.gitkeep
Normal file
4
app/templates/application.hbs
Normal file
4
app/templates/application.hbs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<div class="container-fluid">
|
||||||
|
<h1 class="logo">{{#link-to 'index'}}Croodle{{/link-to}}</h1>
|
||||||
|
{{outlet}}
|
||||||
|
</div>
|
0
app/templates/components/.gitkeep
Normal file
0
app/templates/components/.gitkeep
Normal file
1
app/templates/create.hbs
Normal file
1
app/templates/create.hbs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{{outlet}}
|
13
app/templates/create/index.hbs
Normal file
13
app/templates/create/index.hbs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<div class="box">
|
||||||
|
{{#em-form model=controller submit_button=false}}
|
||||||
|
{{em-select
|
||||||
|
property="pollType"
|
||||||
|
label="poll type"
|
||||||
|
prompt="-select-"
|
||||||
|
contentBinding="App.PollTypes"
|
||||||
|
optionValuePath="content.id"
|
||||||
|
optionLabelPath="content.label"
|
||||||
|
prompt="Please select a poll type"}}
|
||||||
|
{{em-form-submit text="next"}}
|
||||||
|
{{/em-form}}
|
||||||
|
</div>
|
14
app/templates/create/meta.hbs
Normal file
14
app/templates/create/meta.hbs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<div class="box">
|
||||||
|
{{#em-form model=controller submit_button=false}}
|
||||||
|
{{em-input
|
||||||
|
property="title"
|
||||||
|
label="title"
|
||||||
|
placeholder="Enter a title..."}}
|
||||||
|
{{em-text
|
||||||
|
property="description"
|
||||||
|
label="description"
|
||||||
|
placeholder="Enter a description if you like..."
|
||||||
|
rows=4}}
|
||||||
|
{{em-form-submit text="next"}}
|
||||||
|
{{/em-form}}
|
||||||
|
</div>
|
29
app/templates/create/options.hbs
Normal file
29
app/templates/create/options.hbs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<div class="box">
|
||||||
|
{{#if isMakeAPoll}}
|
||||||
|
<form role="form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label">options</label>
|
||||||
|
<fieldset>
|
||||||
|
{{#each option in options}}
|
||||||
|
{{view Ember.TextField valueBinding="option.title" class="form-control"}}<br/>
|
||||||
|
{{/each}}
|
||||||
|
{{em-form-control-help text="You have to enter at least two options."}}
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<button {{action "moreOptions" target="view"}} class="btn btn-default"> add another option </button>
|
||||||
|
<button {{action "submitMakeAPoll"}} class="btn btn-default" {{bind-attr disabled=isNotValid}}> next </button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isFindADate}}
|
||||||
|
<div id="datepicker">
|
||||||
|
{{view App.Datepicker}}
|
||||||
|
{{em-form-control-help text="You have to select at least two dates."}}
|
||||||
|
<div class="form-group">
|
||||||
|
<button {{action "submitFindADate"}} class="btn btn-default" {{bind-attr disabled=isNotValid}}> next </button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
15
app/templates/create/settings.hbs
Normal file
15
app/templates/create/settings.hbs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<div class="box">
|
||||||
|
{{#em-form model=controller submit_button=false}}
|
||||||
|
{{em-select
|
||||||
|
property="answerType"
|
||||||
|
label="available answers"
|
||||||
|
prompt="-select-"
|
||||||
|
contentBinding="App.AnswerTypes"
|
||||||
|
optionValuePath="content.id"
|
||||||
|
optionLabelPath="content.label"
|
||||||
|
prompt="Please define available answers"}}
|
||||||
|
{{em-checkbox label="Allow anonym participation?" property="anonymousUser"}}
|
||||||
|
{{em-checkbox label="Force an answer?" property="forceAnswer"}}
|
||||||
|
{{em-form-submit text="save"}}
|
||||||
|
{{/em-form}}
|
||||||
|
</div>
|
10
app/templates/error.hbs
Normal file
10
app/templates/error.hbs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<div class="box">
|
||||||
|
<h2>error</h2>
|
||||||
|
<p>
|
||||||
|
{{#if is404}}
|
||||||
|
Could not find the poll you tried to open. Perhaps it was
|
||||||
|
deleted?
|
||||||
|
{{else}}
|
||||||
|
We are sorry. An unexpected error occurred.
|
||||||
|
{{/if}}
|
||||||
|
</p>
|
61
app/templates/index.hbs
Normal file
61
app/templates/index.hbs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<div class="index">
|
||||||
|
|
||||||
|
<div class="box teaser">
|
||||||
|
<h2>
|
||||||
|
Croodle simplifies scheduling and decision-making ...<br/>
|
||||||
|
... while keeping your data private
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
|
||||||
|
<div class="box features">
|
||||||
|
<h3>Features</h3>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Easily schedule a date or make a poll with as much
|
||||||
|
people as you like.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
All your private data is encrypted inside your
|
||||||
|
browser.
|
||||||
|
So even the server doesn't know what your schedule
|
||||||
|
or poll is about.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Define as much options as you like. Select dates
|
||||||
|
via calendar or define your options as free text.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Use pre defined answer options like <i>yes</i>,
|
||||||
|
<i>no</i>, <i>maybe</i> or allow your user to enter
|
||||||
|
free text.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
An automatically updated evaluation allways gives you
|
||||||
|
an overview which options are preferred.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="have-a-try">
|
||||||
|
<span class="glyphicon glyphicon-share-alt"></span>
|
||||||
|
{{#link-to 'create'}}have a try right now{{/link-to}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="box features">
|
||||||
|
<h3>Become your own hoster</h3>
|
||||||
|
<p>
|
||||||
|
You don't have to trust our server. You could easily
|
||||||
|
host croodle yourself. All you need is a web space
|
||||||
|
with PHP and SSL encryption enabled. You find the
|
||||||
|
source code and installation instructions on
|
||||||
|
<a href="https://github.com/jelhan/croodle">GitHub</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
13
app/templates/loading.hbs
Normal file
13
app/templates/loading.hbs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<div id="loading" class="box">
|
||||||
|
<div class="loading-animation-container">
|
||||||
|
<div id="loading-animation-circle-1" class="loading-animation-circle"></div>
|
||||||
|
<div id="loading-animation-circle-2" class="loading-animation-circle"></div>
|
||||||
|
<div id="loading-animation-circle-3" class="loading-animation-circle"></div>
|
||||||
|
<div id="loading-animation-circle-4" class="loading-animation-circle"></div>
|
||||||
|
<div id="loading-animation-circle-5" class="loading-animation-circle"></div>
|
||||||
|
<div id="loading-animation-circle-6" class="loading-animation-circle"></div>
|
||||||
|
<div id="loading-animation-circle-7" class="loading-animation-circle"></div>
|
||||||
|
<div id="loading-animation-circle-8" class="loading-animation-circle"></div>
|
||||||
|
</div>
|
||||||
|
loading your poll . . .
|
||||||
|
</div>
|
122
app/templates/poll.hbs
Normal file
122
app/templates/poll.hbs
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
<div id="poll">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6 col-lg-5">
|
||||||
|
<div class="box meta-data">
|
||||||
|
<h2 class="title">{{title}}</h2>
|
||||||
|
<p class="description">{{description}}</p>
|
||||||
|
<p class="creationDate">created on {{creationDate}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6 col-lg-5 col-lg-offset-2">
|
||||||
|
<div class="box share-link">
|
||||||
|
<p>Share the link and invite other people to participate in your poll.</p>
|
||||||
|
<p class="link"><a {{bind-attr href=pollUrl}}>{{pollUrl}}</a></p>
|
||||||
|
<p class="notice">Everyone who knows the link could read the data.
|
||||||
|
If your poll consists of private data you may only share the
|
||||||
|
link via encrypted channels like PGP encrypted email or instant
|
||||||
|
messaging with OTR.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="table-scroll">
|
||||||
|
<table class="user-selections-table table table-striped table-condensed">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th> </th>
|
||||||
|
{{#each option in options}}
|
||||||
|
<th>
|
||||||
|
{{#if isFindADate}}
|
||||||
|
{{formattedDate option.title 'll'}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isMakeAPoll}}
|
||||||
|
{{option.title}}
|
||||||
|
{{/if}}
|
||||||
|
</th>
|
||||||
|
{{/each}}
|
||||||
|
<th> </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<tr class='newUser'>
|
||||||
|
{{! ToDo:
|
||||||
|
should be all together in just one form;
|
||||||
|
not in several forms for each input.
|
||||||
|
This work-around is due to a problem of
|
||||||
|
ember-forms in dealing with propertys of
|
||||||
|
handlebar each helper
|
||||||
|
}}
|
||||||
|
<td>
|
||||||
|
{{#em-form model=controller submit_button=false}}
|
||||||
|
{{em-form-label text="name" extraClass="sr-only"}}
|
||||||
|
{{em-input property="newUserName" placeholder="Enter your name..."}}
|
||||||
|
{{/em-form}}
|
||||||
|
</td>
|
||||||
|
{{#each newUserSelection in controller.newUserSelections}}
|
||||||
|
<td>
|
||||||
|
{{#em-form model=newUserSelection submit_button=false}}
|
||||||
|
{{#if isFreeText}}
|
||||||
|
{{em-input property="value"}}
|
||||||
|
{{else}}
|
||||||
|
{{em-select
|
||||||
|
property="value"
|
||||||
|
contentBinding="answers"
|
||||||
|
optionValuePath="content.label"
|
||||||
|
optionLabelPath="content.label"
|
||||||
|
prompt="choose an answer"}}
|
||||||
|
{{/if}}
|
||||||
|
{{/em-form}}
|
||||||
|
</td>
|
||||||
|
{{/each}}
|
||||||
|
<td>
|
||||||
|
<div class="form-group">
|
||||||
|
<button {{action "addNewUser"}} class="btn btn-default" {{bind-attr disabled=isNotValid}}> save </button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{{#each user in users}}
|
||||||
|
<tr>
|
||||||
|
<td>{{user.name}}</td>
|
||||||
|
{{#each selection in user.selections}}
|
||||||
|
<td>
|
||||||
|
{{#if isFreeText}}
|
||||||
|
{{selection.value}}
|
||||||
|
{{else}}
|
||||||
|
<span {{bind-attr class="selection.value"}}>{{selection.value}}</span>
|
||||||
|
{{/if}}
|
||||||
|
</td>
|
||||||
|
{{/each}}
|
||||||
|
<td> </td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
{{#unless isFreeText}}
|
||||||
|
<tr class='evaluation evaluation-header'>
|
||||||
|
<td {{bind-attr colspan=fullRowColspan}}>Evaluation</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{{#each answer in evaluation}}
|
||||||
|
<tr class='evaluation'>
|
||||||
|
<td>
|
||||||
|
{{answer.label}}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
{{#each option in answer.options}}
|
||||||
|
<td>
|
||||||
|
{{option}}
|
||||||
|
</td>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
<td> </td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
{{/unless}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
0
app/utils/.gitkeep
Normal file
0
app/utils/.gitkeep
Normal file
4
app/utils/ajax.js
Normal file
4
app/utils/ajax.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
/* global ic */
|
||||||
|
export default function ajax(){
|
||||||
|
return ic.ajax.apply(null, arguments);
|
||||||
|
}
|
0
app/views/.gitkeep
Normal file
0
app/views/.gitkeep
Normal file
10
app/views/create/options.js
Normal file
10
app/views/create/options.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
export default Ember.View.extend({
|
||||||
|
title: '',
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
moreOptions: function(){
|
||||||
|
// create new Option
|
||||||
|
this.get('controller.model.options').pushObject({title: ''});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
35
app/views/datepicker.js
Normal file
35
app/views/datepicker.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
export default Em.View.extend({
|
||||||
|
classNames: ['datepicker'],
|
||||||
|
|
||||||
|
didInsertElement: function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this._super();
|
||||||
|
|
||||||
|
$('.datepicker').datepicker({
|
||||||
|
format: "yyyy-mm-dd hh:mm:ss",
|
||||||
|
multidate: true,
|
||||||
|
multidateSeparator: ";",
|
||||||
|
calendarWeeks: true,
|
||||||
|
todayHighlight: true,
|
||||||
|
})
|
||||||
|
// bind date changes to option array in model
|
||||||
|
.on('changeDate', function(e){
|
||||||
|
var dates = e.dates,
|
||||||
|
newOptions = [];
|
||||||
|
|
||||||
|
// sort dates
|
||||||
|
dates.sort(function(a,b){
|
||||||
|
return new Date(a) - new Date(b);
|
||||||
|
});
|
||||||
|
|
||||||
|
// get array in correct form
|
||||||
|
dates.forEach(function(option) {
|
||||||
|
newOptions.pushObject({title: option});
|
||||||
|
});
|
||||||
|
|
||||||
|
// set options
|
||||||
|
self.set('_parentView.controller.options', newOptions);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
23
bower.json
Normal file
23
bower.json
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"name": "croodle",
|
||||||
|
"dependencies": {
|
||||||
|
"handlebars": "~1.1.2",
|
||||||
|
"jquery": "~1.9.1",
|
||||||
|
"qunit": "~1.12.0",
|
||||||
|
"ember-qunit": "https://github.com/rpflorence/ember-qunit.git#master",
|
||||||
|
"ember": "~1.5.0",
|
||||||
|
"ember-data": "~1.0.0-beta.7",
|
||||||
|
"ember-resolver": "~0.1.1",
|
||||||
|
"ic-ajax": "~1.0.4",
|
||||||
|
"ember-testing-httpRespond": "~0.1.1",
|
||||||
|
"loader.js": "git://github.com/stefanpenner/loader.js",
|
||||||
|
"ember-load-initializers": "git://github.com/stefanpenner/ember-load-initializers.git#0.0.1",
|
||||||
|
"moment": "~2.7.0",
|
||||||
|
"sjcl": "~1.0.0",
|
||||||
|
"ember-validations": "http://builds.dockyard.com.s3.amazonaws.com/ember-validations/tags/1-0-beta-2/ember-validations.min.js",
|
||||||
|
"bootstrap": "~3.2.0",
|
||||||
|
"bootstrap-datepicker": "~1.3.0",
|
||||||
|
"ember-forms": "*",
|
||||||
|
"ember-data-extensions": "~1.0.0-beta.7"
|
||||||
|
}
|
||||||
|
}
|
16
config/environment.js
Normal file
16
config/environment.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// Put general configuration here. This file is included
|
||||||
|
// in both production and development BEFORE Ember is
|
||||||
|
// loaded.
|
||||||
|
//
|
||||||
|
// For example to enable a feature on a canary build you
|
||||||
|
// might do:
|
||||||
|
//
|
||||||
|
// window.ENV = {FEATURES: {'with-controller': true}};
|
||||||
|
|
||||||
|
window.ENV = {
|
||||||
|
FEATURES: {
|
||||||
|
'query-params-new': true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.ENV.MODEL_FACTORY_INJECTIONS = true;
|
14
config/environments/development.js
Normal file
14
config/environments/development.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// Put your development configuration here.
|
||||||
|
//
|
||||||
|
// This is useful when using a separate API
|
||||||
|
// endpoint in development than in production.
|
||||||
|
//
|
||||||
|
// window.ENV.public_key = '123456'
|
||||||
|
|
||||||
|
window.ENV.APP = {
|
||||||
|
LOG_ACTIVE_GENERATION: true,
|
||||||
|
LOG_MODULE_RESOLVER: true,
|
||||||
|
LOG_TRANSITIONS: true,
|
||||||
|
LOG_TRANSITIONS_INTERNAL: true,
|
||||||
|
LOG_VIEW_LOOKUPS: true
|
||||||
|
};
|
6
config/environments/production.js
Normal file
6
config/environments/production.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// Put your production configuration here.
|
||||||
|
//
|
||||||
|
// This is useful when using a separate API
|
||||||
|
// endpoint in development than in production.
|
||||||
|
//
|
||||||
|
// window.ENV.public_key = '123456'
|
6
config/environments/test.js
Normal file
6
config/environments/test.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// Put your test configuration here.
|
||||||
|
//
|
||||||
|
// This is useful when using a separate API
|
||||||
|
// endpoint in test than in production.
|
||||||
|
//
|
||||||
|
// window.ENV.public_key = '123456'
|
7
css/bootstrap.min.css
vendored
7
css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
|
@ -1,790 +0,0 @@
|
||||||
/*!
|
|
||||||
* Datepicker for Bootstrap
|
|
||||||
*
|
|
||||||
* Copyright 2012 Stefan Petre
|
|
||||||
* Improvements by Andrew Rowls
|
|
||||||
* Licensed under the Apache License v2.0
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
.datepicker {
|
|
||||||
padding: 4px;
|
|
||||||
border-radius: 4px;
|
|
||||||
direction: ltr;
|
|
||||||
/*.dow {
|
|
||||||
border-top: 1px solid #ddd !important;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
.datepicker-inline {
|
|
||||||
width: 220px;
|
|
||||||
}
|
|
||||||
.datepicker.datepicker-rtl {
|
|
||||||
direction: rtl;
|
|
||||||
}
|
|
||||||
.datepicker.datepicker-rtl table tr td span {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
.datepicker-dropdown {
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
.datepicker-dropdown:before {
|
|
||||||
content: '';
|
|
||||||
display: inline-block;
|
|
||||||
border-left: 7px solid transparent;
|
|
||||||
border-right: 7px solid transparent;
|
|
||||||
border-bottom: 7px solid #ccc;
|
|
||||||
border-top: 0;
|
|
||||||
border-bottom-color: rgba(0, 0, 0, 0.2);
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
.datepicker-dropdown:after {
|
|
||||||
content: '';
|
|
||||||
display: inline-block;
|
|
||||||
border-left: 6px solid transparent;
|
|
||||||
border-right: 6px solid transparent;
|
|
||||||
border-bottom: 6px solid #fff;
|
|
||||||
border-top: 0;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
.datepicker-dropdown.datepicker-orient-left:before {
|
|
||||||
left: 6px;
|
|
||||||
}
|
|
||||||
.datepicker-dropdown.datepicker-orient-left:after {
|
|
||||||
left: 7px;
|
|
||||||
}
|
|
||||||
.datepicker-dropdown.datepicker-orient-right:before {
|
|
||||||
right: 6px;
|
|
||||||
}
|
|
||||||
.datepicker-dropdown.datepicker-orient-right:after {
|
|
||||||
right: 7px;
|
|
||||||
}
|
|
||||||
.datepicker-dropdown.datepicker-orient-top:before {
|
|
||||||
top: -7px;
|
|
||||||
}
|
|
||||||
.datepicker-dropdown.datepicker-orient-top:after {
|
|
||||||
top: -6px;
|
|
||||||
}
|
|
||||||
.datepicker-dropdown.datepicker-orient-bottom:before {
|
|
||||||
bottom: -7px;
|
|
||||||
border-bottom: 0;
|
|
||||||
border-top: 7px solid #999;
|
|
||||||
}
|
|
||||||
.datepicker-dropdown.datepicker-orient-bottom:after {
|
|
||||||
bottom: -6px;
|
|
||||||
border-bottom: 0;
|
|
||||||
border-top: 6px solid #fff;
|
|
||||||
}
|
|
||||||
.datepicker > div {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.datepicker.days div.datepicker-days {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
.datepicker.months div.datepicker-months {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
.datepicker.years div.datepicker-years {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
.datepicker table {
|
|
||||||
margin: 0;
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-khtml-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
.datepicker table tr td,
|
|
||||||
.datepicker table tr th {
|
|
||||||
text-align: center;
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
border-radius: 4px;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
.table-striped .datepicker table tr td,
|
|
||||||
.table-striped .datepicker table tr th {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
.datepicker table tr td.day:hover,
|
|
||||||
.datepicker table tr td.day.focused {
|
|
||||||
background: #eeeeee;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.datepicker table tr td.old,
|
|
||||||
.datepicker table tr td.new {
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
.datepicker table tr td.disabled,
|
|
||||||
.datepicker table tr td.disabled:hover {
|
|
||||||
background: none;
|
|
||||||
color: #999999;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
.datepicker table tr td.today,
|
|
||||||
.datepicker table tr td.today:hover,
|
|
||||||
.datepicker table tr td.today.disabled,
|
|
||||||
.datepicker table tr td.today.disabled:hover {
|
|
||||||
color: #000000;
|
|
||||||
background-color: #ffdb99;
|
|
||||||
border-color: #ffb733;
|
|
||||||
}
|
|
||||||
.datepicker table tr td.today:hover,
|
|
||||||
.datepicker table tr td.today:hover:hover,
|
|
||||||
.datepicker table tr td.today.disabled:hover,
|
|
||||||
.datepicker table tr td.today.disabled:hover:hover,
|
|
||||||
.datepicker table tr td.today:focus,
|
|
||||||
.datepicker table tr td.today:hover:focus,
|
|
||||||
.datepicker table tr td.today.disabled:focus,
|
|
||||||
.datepicker table tr td.today.disabled:hover:focus,
|
|
||||||
.datepicker table tr td.today:active,
|
|
||||||
.datepicker table tr td.today:hover:active,
|
|
||||||
.datepicker table tr td.today.disabled:active,
|
|
||||||
.datepicker table tr td.today.disabled:hover:active,
|
|
||||||
.datepicker table tr td.today.active,
|
|
||||||
.datepicker table tr td.today:hover.active,
|
|
||||||
.datepicker table tr td.today.disabled.active,
|
|
||||||
.datepicker table tr td.today.disabled:hover.active,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.today,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.today:hover,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.today.disabled,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.today.disabled:hover {
|
|
||||||
color: #000000;
|
|
||||||
background-color: #ffcd70;
|
|
||||||
border-color: #f59e00;
|
|
||||||
}
|
|
||||||
.datepicker table tr td.today:active,
|
|
||||||
.datepicker table tr td.today:hover:active,
|
|
||||||
.datepicker table tr td.today.disabled:active,
|
|
||||||
.datepicker table tr td.today.disabled:hover:active,
|
|
||||||
.datepicker table tr td.today.active,
|
|
||||||
.datepicker table tr td.today:hover.active,
|
|
||||||
.datepicker table tr td.today.disabled.active,
|
|
||||||
.datepicker table tr td.today.disabled:hover.active,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.today,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.today:hover,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.today.disabled,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.today.disabled:hover {
|
|
||||||
background-image: none;
|
|
||||||
}
|
|
||||||
.datepicker table tr td.today.disabled,
|
|
||||||
.datepicker table tr td.today:hover.disabled,
|
|
||||||
.datepicker table tr td.today.disabled.disabled,
|
|
||||||
.datepicker table tr td.today.disabled:hover.disabled,
|
|
||||||
.datepicker table tr td.today[disabled],
|
|
||||||
.datepicker table tr td.today:hover[disabled],
|
|
||||||
.datepicker table tr td.today.disabled[disabled],
|
|
||||||
.datepicker table tr td.today.disabled:hover[disabled],
|
|
||||||
fieldset[disabled] .datepicker table tr td.today,
|
|
||||||
fieldset[disabled] .datepicker table tr td.today:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td.today.disabled,
|
|
||||||
fieldset[disabled] .datepicker table tr td.today.disabled:hover,
|
|
||||||
.datepicker table tr td.today.disabled:hover,
|
|
||||||
.datepicker table tr td.today:hover.disabled:hover,
|
|
||||||
.datepicker table tr td.today.disabled.disabled:hover,
|
|
||||||
.datepicker table tr td.today.disabled:hover.disabled:hover,
|
|
||||||
.datepicker table tr td.today[disabled]:hover,
|
|
||||||
.datepicker table tr td.today:hover[disabled]:hover,
|
|
||||||
.datepicker table tr td.today.disabled[disabled]:hover,
|
|
||||||
.datepicker table tr td.today.disabled:hover[disabled]:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td.today:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td.today:hover:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td.today.disabled:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td.today.disabled:hover:hover,
|
|
||||||
.datepicker table tr td.today.disabled:focus,
|
|
||||||
.datepicker table tr td.today:hover.disabled:focus,
|
|
||||||
.datepicker table tr td.today.disabled.disabled:focus,
|
|
||||||
.datepicker table tr td.today.disabled:hover.disabled:focus,
|
|
||||||
.datepicker table tr td.today[disabled]:focus,
|
|
||||||
.datepicker table tr td.today:hover[disabled]:focus,
|
|
||||||
.datepicker table tr td.today.disabled[disabled]:focus,
|
|
||||||
.datepicker table tr td.today.disabled:hover[disabled]:focus,
|
|
||||||
fieldset[disabled] .datepicker table tr td.today:focus,
|
|
||||||
fieldset[disabled] .datepicker table tr td.today:hover:focus,
|
|
||||||
fieldset[disabled] .datepicker table tr td.today.disabled:focus,
|
|
||||||
fieldset[disabled] .datepicker table tr td.today.disabled:hover:focus,
|
|
||||||
.datepicker table tr td.today.disabled:active,
|
|
||||||
.datepicker table tr td.today:hover.disabled:active,
|
|
||||||
.datepicker table tr td.today.disabled.disabled:active,
|
|
||||||
.datepicker table tr td.today.disabled:hover.disabled:active,
|
|
||||||
.datepicker table tr td.today[disabled]:active,
|
|
||||||
.datepicker table tr td.today:hover[disabled]:active,
|
|
||||||
.datepicker table tr td.today.disabled[disabled]:active,
|
|
||||||
.datepicker table tr td.today.disabled:hover[disabled]:active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.today:active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.today:hover:active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.today.disabled:active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.today.disabled:hover:active,
|
|
||||||
.datepicker table tr td.today.disabled.active,
|
|
||||||
.datepicker table tr td.today:hover.disabled.active,
|
|
||||||
.datepicker table tr td.today.disabled.disabled.active,
|
|
||||||
.datepicker table tr td.today.disabled:hover.disabled.active,
|
|
||||||
.datepicker table tr td.today[disabled].active,
|
|
||||||
.datepicker table tr td.today:hover[disabled].active,
|
|
||||||
.datepicker table tr td.today.disabled[disabled].active,
|
|
||||||
.datepicker table tr td.today.disabled:hover[disabled].active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.today.active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.today:hover.active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.today.disabled.active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.today.disabled:hover.active {
|
|
||||||
background-color: #ffdb99;
|
|
||||||
border-color: #ffb733;
|
|
||||||
}
|
|
||||||
.datepicker table tr td.today:hover:hover {
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
.datepicker table tr td.today.active:hover {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
.datepicker table tr td.range,
|
|
||||||
.datepicker table tr td.range:hover,
|
|
||||||
.datepicker table tr td.range.disabled,
|
|
||||||
.datepicker table tr td.range.disabled:hover {
|
|
||||||
background: #eeeeee;
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
.datepicker table tr td.range.today,
|
|
||||||
.datepicker table tr td.range.today:hover,
|
|
||||||
.datepicker table tr td.range.today.disabled,
|
|
||||||
.datepicker table tr td.range.today.disabled:hover {
|
|
||||||
color: #000000;
|
|
||||||
background-color: #f7ca77;
|
|
||||||
border-color: #f1a417;
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
.datepicker table tr td.range.today:hover,
|
|
||||||
.datepicker table tr td.range.today:hover:hover,
|
|
||||||
.datepicker table tr td.range.today.disabled:hover,
|
|
||||||
.datepicker table tr td.range.today.disabled:hover:hover,
|
|
||||||
.datepicker table tr td.range.today:focus,
|
|
||||||
.datepicker table tr td.range.today:hover:focus,
|
|
||||||
.datepicker table tr td.range.today.disabled:focus,
|
|
||||||
.datepicker table tr td.range.today.disabled:hover:focus,
|
|
||||||
.datepicker table tr td.range.today:active,
|
|
||||||
.datepicker table tr td.range.today:hover:active,
|
|
||||||
.datepicker table tr td.range.today.disabled:active,
|
|
||||||
.datepicker table tr td.range.today.disabled:hover:active,
|
|
||||||
.datepicker table tr td.range.today.active,
|
|
||||||
.datepicker table tr td.range.today:hover.active,
|
|
||||||
.datepicker table tr td.range.today.disabled.active,
|
|
||||||
.datepicker table tr td.range.today.disabled:hover.active,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.range.today,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.range.today:hover,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.range.today.disabled,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.range.today.disabled:hover {
|
|
||||||
color: #000000;
|
|
||||||
background-color: #f4bb51;
|
|
||||||
border-color: #bf800c;
|
|
||||||
}
|
|
||||||
.datepicker table tr td.range.today:active,
|
|
||||||
.datepicker table tr td.range.today:hover:active,
|
|
||||||
.datepicker table tr td.range.today.disabled:active,
|
|
||||||
.datepicker table tr td.range.today.disabled:hover:active,
|
|
||||||
.datepicker table tr td.range.today.active,
|
|
||||||
.datepicker table tr td.range.today:hover.active,
|
|
||||||
.datepicker table tr td.range.today.disabled.active,
|
|
||||||
.datepicker table tr td.range.today.disabled:hover.active,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.range.today,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.range.today:hover,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.range.today.disabled,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.range.today.disabled:hover {
|
|
||||||
background-image: none;
|
|
||||||
}
|
|
||||||
.datepicker table tr td.range.today.disabled,
|
|
||||||
.datepicker table tr td.range.today:hover.disabled,
|
|
||||||
.datepicker table tr td.range.today.disabled.disabled,
|
|
||||||
.datepicker table tr td.range.today.disabled:hover.disabled,
|
|
||||||
.datepicker table tr td.range.today[disabled],
|
|
||||||
.datepicker table tr td.range.today:hover[disabled],
|
|
||||||
.datepicker table tr td.range.today.disabled[disabled],
|
|
||||||
.datepicker table tr td.range.today.disabled:hover[disabled],
|
|
||||||
fieldset[disabled] .datepicker table tr td.range.today,
|
|
||||||
fieldset[disabled] .datepicker table tr td.range.today:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td.range.today.disabled,
|
|
||||||
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover,
|
|
||||||
.datepicker table tr td.range.today.disabled:hover,
|
|
||||||
.datepicker table tr td.range.today:hover.disabled:hover,
|
|
||||||
.datepicker table tr td.range.today.disabled.disabled:hover,
|
|
||||||
.datepicker table tr td.range.today.disabled:hover.disabled:hover,
|
|
||||||
.datepicker table tr td.range.today[disabled]:hover,
|
|
||||||
.datepicker table tr td.range.today:hover[disabled]:hover,
|
|
||||||
.datepicker table tr td.range.today.disabled[disabled]:hover,
|
|
||||||
.datepicker table tr td.range.today.disabled:hover[disabled]:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td.range.today:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td.range.today:hover:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover:hover,
|
|
||||||
.datepicker table tr td.range.today.disabled:focus,
|
|
||||||
.datepicker table tr td.range.today:hover.disabled:focus,
|
|
||||||
.datepicker table tr td.range.today.disabled.disabled:focus,
|
|
||||||
.datepicker table tr td.range.today.disabled:hover.disabled:focus,
|
|
||||||
.datepicker table tr td.range.today[disabled]:focus,
|
|
||||||
.datepicker table tr td.range.today:hover[disabled]:focus,
|
|
||||||
.datepicker table tr td.range.today.disabled[disabled]:focus,
|
|
||||||
.datepicker table tr td.range.today.disabled:hover[disabled]:focus,
|
|
||||||
fieldset[disabled] .datepicker table tr td.range.today:focus,
|
|
||||||
fieldset[disabled] .datepicker table tr td.range.today:hover:focus,
|
|
||||||
fieldset[disabled] .datepicker table tr td.range.today.disabled:focus,
|
|
||||||
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover:focus,
|
|
||||||
.datepicker table tr td.range.today.disabled:active,
|
|
||||||
.datepicker table tr td.range.today:hover.disabled:active,
|
|
||||||
.datepicker table tr td.range.today.disabled.disabled:active,
|
|
||||||
.datepicker table tr td.range.today.disabled:hover.disabled:active,
|
|
||||||
.datepicker table tr td.range.today[disabled]:active,
|
|
||||||
.datepicker table tr td.range.today:hover[disabled]:active,
|
|
||||||
.datepicker table tr td.range.today.disabled[disabled]:active,
|
|
||||||
.datepicker table tr td.range.today.disabled:hover[disabled]:active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.range.today:active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.range.today:hover:active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.range.today.disabled:active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover:active,
|
|
||||||
.datepicker table tr td.range.today.disabled.active,
|
|
||||||
.datepicker table tr td.range.today:hover.disabled.active,
|
|
||||||
.datepicker table tr td.range.today.disabled.disabled.active,
|
|
||||||
.datepicker table tr td.range.today.disabled:hover.disabled.active,
|
|
||||||
.datepicker table tr td.range.today[disabled].active,
|
|
||||||
.datepicker table tr td.range.today:hover[disabled].active,
|
|
||||||
.datepicker table tr td.range.today.disabled[disabled].active,
|
|
||||||
.datepicker table tr td.range.today.disabled:hover[disabled].active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.range.today.active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.range.today:hover.active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.range.today.disabled.active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover.active {
|
|
||||||
background-color: #f7ca77;
|
|
||||||
border-color: #f1a417;
|
|
||||||
}
|
|
||||||
.datepicker table tr td.selected,
|
|
||||||
.datepicker table tr td.selected:hover,
|
|
||||||
.datepicker table tr td.selected.disabled,
|
|
||||||
.datepicker table tr td.selected.disabled:hover {
|
|
||||||
color: #ffffff;
|
|
||||||
background-color: #999999;
|
|
||||||
border-color: #555555;
|
|
||||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
|
||||||
}
|
|
||||||
.datepicker table tr td.selected:hover,
|
|
||||||
.datepicker table tr td.selected:hover:hover,
|
|
||||||
.datepicker table tr td.selected.disabled:hover,
|
|
||||||
.datepicker table tr td.selected.disabled:hover:hover,
|
|
||||||
.datepicker table tr td.selected:focus,
|
|
||||||
.datepicker table tr td.selected:hover:focus,
|
|
||||||
.datepicker table tr td.selected.disabled:focus,
|
|
||||||
.datepicker table tr td.selected.disabled:hover:focus,
|
|
||||||
.datepicker table tr td.selected:active,
|
|
||||||
.datepicker table tr td.selected:hover:active,
|
|
||||||
.datepicker table tr td.selected.disabled:active,
|
|
||||||
.datepicker table tr td.selected.disabled:hover:active,
|
|
||||||
.datepicker table tr td.selected.active,
|
|
||||||
.datepicker table tr td.selected:hover.active,
|
|
||||||
.datepicker table tr td.selected.disabled.active,
|
|
||||||
.datepicker table tr td.selected.disabled:hover.active,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.selected,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.selected:hover,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.selected.disabled,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.selected.disabled:hover {
|
|
||||||
color: #ffffff;
|
|
||||||
background-color: #858585;
|
|
||||||
border-color: #373737;
|
|
||||||
}
|
|
||||||
.datepicker table tr td.selected:active,
|
|
||||||
.datepicker table tr td.selected:hover:active,
|
|
||||||
.datepicker table tr td.selected.disabled:active,
|
|
||||||
.datepicker table tr td.selected.disabled:hover:active,
|
|
||||||
.datepicker table tr td.selected.active,
|
|
||||||
.datepicker table tr td.selected:hover.active,
|
|
||||||
.datepicker table tr td.selected.disabled.active,
|
|
||||||
.datepicker table tr td.selected.disabled:hover.active,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.selected,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.selected:hover,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.selected.disabled,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.selected.disabled:hover {
|
|
||||||
background-image: none;
|
|
||||||
}
|
|
||||||
.datepicker table tr td.selected.disabled,
|
|
||||||
.datepicker table tr td.selected:hover.disabled,
|
|
||||||
.datepicker table tr td.selected.disabled.disabled,
|
|
||||||
.datepicker table tr td.selected.disabled:hover.disabled,
|
|
||||||
.datepicker table tr td.selected[disabled],
|
|
||||||
.datepicker table tr td.selected:hover[disabled],
|
|
||||||
.datepicker table tr td.selected.disabled[disabled],
|
|
||||||
.datepicker table tr td.selected.disabled:hover[disabled],
|
|
||||||
fieldset[disabled] .datepicker table tr td.selected,
|
|
||||||
fieldset[disabled] .datepicker table tr td.selected:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td.selected.disabled,
|
|
||||||
fieldset[disabled] .datepicker table tr td.selected.disabled:hover,
|
|
||||||
.datepicker table tr td.selected.disabled:hover,
|
|
||||||
.datepicker table tr td.selected:hover.disabled:hover,
|
|
||||||
.datepicker table tr td.selected.disabled.disabled:hover,
|
|
||||||
.datepicker table tr td.selected.disabled:hover.disabled:hover,
|
|
||||||
.datepicker table tr td.selected[disabled]:hover,
|
|
||||||
.datepicker table tr td.selected:hover[disabled]:hover,
|
|
||||||
.datepicker table tr td.selected.disabled[disabled]:hover,
|
|
||||||
.datepicker table tr td.selected.disabled:hover[disabled]:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td.selected:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td.selected:hover:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td.selected.disabled:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td.selected.disabled:hover:hover,
|
|
||||||
.datepicker table tr td.selected.disabled:focus,
|
|
||||||
.datepicker table tr td.selected:hover.disabled:focus,
|
|
||||||
.datepicker table tr td.selected.disabled.disabled:focus,
|
|
||||||
.datepicker table tr td.selected.disabled:hover.disabled:focus,
|
|
||||||
.datepicker table tr td.selected[disabled]:focus,
|
|
||||||
.datepicker table tr td.selected:hover[disabled]:focus,
|
|
||||||
.datepicker table tr td.selected.disabled[disabled]:focus,
|
|
||||||
.datepicker table tr td.selected.disabled:hover[disabled]:focus,
|
|
||||||
fieldset[disabled] .datepicker table tr td.selected:focus,
|
|
||||||
fieldset[disabled] .datepicker table tr td.selected:hover:focus,
|
|
||||||
fieldset[disabled] .datepicker table tr td.selected.disabled:focus,
|
|
||||||
fieldset[disabled] .datepicker table tr td.selected.disabled:hover:focus,
|
|
||||||
.datepicker table tr td.selected.disabled:active,
|
|
||||||
.datepicker table tr td.selected:hover.disabled:active,
|
|
||||||
.datepicker table tr td.selected.disabled.disabled:active,
|
|
||||||
.datepicker table tr td.selected.disabled:hover.disabled:active,
|
|
||||||
.datepicker table tr td.selected[disabled]:active,
|
|
||||||
.datepicker table tr td.selected:hover[disabled]:active,
|
|
||||||
.datepicker table tr td.selected.disabled[disabled]:active,
|
|
||||||
.datepicker table tr td.selected.disabled:hover[disabled]:active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.selected:active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.selected:hover:active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.selected.disabled:active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.selected.disabled:hover:active,
|
|
||||||
.datepicker table tr td.selected.disabled.active,
|
|
||||||
.datepicker table tr td.selected:hover.disabled.active,
|
|
||||||
.datepicker table tr td.selected.disabled.disabled.active,
|
|
||||||
.datepicker table tr td.selected.disabled:hover.disabled.active,
|
|
||||||
.datepicker table tr td.selected[disabled].active,
|
|
||||||
.datepicker table tr td.selected:hover[disabled].active,
|
|
||||||
.datepicker table tr td.selected.disabled[disabled].active,
|
|
||||||
.datepicker table tr td.selected.disabled:hover[disabled].active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.selected.active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.selected:hover.active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.selected.disabled.active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.selected.disabled:hover.active {
|
|
||||||
background-color: #999999;
|
|
||||||
border-color: #555555;
|
|
||||||
}
|
|
||||||
.datepicker table tr td.active,
|
|
||||||
.datepicker table tr td.active:hover,
|
|
||||||
.datepicker table tr td.active.disabled,
|
|
||||||
.datepicker table tr td.active.disabled:hover {
|
|
||||||
color: #ffffff;
|
|
||||||
background-color: #428bca;
|
|
||||||
border-color: #357ebd;
|
|
||||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
|
||||||
}
|
|
||||||
.datepicker table tr td.active:hover,
|
|
||||||
.datepicker table tr td.active:hover:hover,
|
|
||||||
.datepicker table tr td.active.disabled:hover,
|
|
||||||
.datepicker table tr td.active.disabled:hover:hover,
|
|
||||||
.datepicker table tr td.active:focus,
|
|
||||||
.datepicker table tr td.active:hover:focus,
|
|
||||||
.datepicker table tr td.active.disabled:focus,
|
|
||||||
.datepicker table tr td.active.disabled:hover:focus,
|
|
||||||
.datepicker table tr td.active:active,
|
|
||||||
.datepicker table tr td.active:hover:active,
|
|
||||||
.datepicker table tr td.active.disabled:active,
|
|
||||||
.datepicker table tr td.active.disabled:hover:active,
|
|
||||||
.datepicker table tr td.active.active,
|
|
||||||
.datepicker table tr td.active:hover.active,
|
|
||||||
.datepicker table tr td.active.disabled.active,
|
|
||||||
.datepicker table tr td.active.disabled:hover.active,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.active,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.active:hover,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.active.disabled,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.active.disabled:hover {
|
|
||||||
color: #ffffff;
|
|
||||||
background-color: #3276b1;
|
|
||||||
border-color: #285e8e;
|
|
||||||
}
|
|
||||||
.datepicker table tr td.active:active,
|
|
||||||
.datepicker table tr td.active:hover:active,
|
|
||||||
.datepicker table tr td.active.disabled:active,
|
|
||||||
.datepicker table tr td.active.disabled:hover:active,
|
|
||||||
.datepicker table tr td.active.active,
|
|
||||||
.datepicker table tr td.active:hover.active,
|
|
||||||
.datepicker table tr td.active.disabled.active,
|
|
||||||
.datepicker table tr td.active.disabled:hover.active,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.active,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.active:hover,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.active.disabled,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td.active.disabled:hover {
|
|
||||||
background-image: none;
|
|
||||||
}
|
|
||||||
.datepicker table tr td.active.disabled,
|
|
||||||
.datepicker table tr td.active:hover.disabled,
|
|
||||||
.datepicker table tr td.active.disabled.disabled,
|
|
||||||
.datepicker table tr td.active.disabled:hover.disabled,
|
|
||||||
.datepicker table tr td.active[disabled],
|
|
||||||
.datepicker table tr td.active:hover[disabled],
|
|
||||||
.datepicker table tr td.active.disabled[disabled],
|
|
||||||
.datepicker table tr td.active.disabled:hover[disabled],
|
|
||||||
fieldset[disabled] .datepicker table tr td.active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.active:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td.active.disabled,
|
|
||||||
fieldset[disabled] .datepicker table tr td.active.disabled:hover,
|
|
||||||
.datepicker table tr td.active.disabled:hover,
|
|
||||||
.datepicker table tr td.active:hover.disabled:hover,
|
|
||||||
.datepicker table tr td.active.disabled.disabled:hover,
|
|
||||||
.datepicker table tr td.active.disabled:hover.disabled:hover,
|
|
||||||
.datepicker table tr td.active[disabled]:hover,
|
|
||||||
.datepicker table tr td.active:hover[disabled]:hover,
|
|
||||||
.datepicker table tr td.active.disabled[disabled]:hover,
|
|
||||||
.datepicker table tr td.active.disabled:hover[disabled]:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td.active:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td.active:hover:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td.active.disabled:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td.active.disabled:hover:hover,
|
|
||||||
.datepicker table tr td.active.disabled:focus,
|
|
||||||
.datepicker table tr td.active:hover.disabled:focus,
|
|
||||||
.datepicker table tr td.active.disabled.disabled:focus,
|
|
||||||
.datepicker table tr td.active.disabled:hover.disabled:focus,
|
|
||||||
.datepicker table tr td.active[disabled]:focus,
|
|
||||||
.datepicker table tr td.active:hover[disabled]:focus,
|
|
||||||
.datepicker table tr td.active.disabled[disabled]:focus,
|
|
||||||
.datepicker table tr td.active.disabled:hover[disabled]:focus,
|
|
||||||
fieldset[disabled] .datepicker table tr td.active:focus,
|
|
||||||
fieldset[disabled] .datepicker table tr td.active:hover:focus,
|
|
||||||
fieldset[disabled] .datepicker table tr td.active.disabled:focus,
|
|
||||||
fieldset[disabled] .datepicker table tr td.active.disabled:hover:focus,
|
|
||||||
.datepicker table tr td.active.disabled:active,
|
|
||||||
.datepicker table tr td.active:hover.disabled:active,
|
|
||||||
.datepicker table tr td.active.disabled.disabled:active,
|
|
||||||
.datepicker table tr td.active.disabled:hover.disabled:active,
|
|
||||||
.datepicker table tr td.active[disabled]:active,
|
|
||||||
.datepicker table tr td.active:hover[disabled]:active,
|
|
||||||
.datepicker table tr td.active.disabled[disabled]:active,
|
|
||||||
.datepicker table tr td.active.disabled:hover[disabled]:active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.active:active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.active:hover:active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.active.disabled:active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.active.disabled:hover:active,
|
|
||||||
.datepicker table tr td.active.disabled.active,
|
|
||||||
.datepicker table tr td.active:hover.disabled.active,
|
|
||||||
.datepicker table tr td.active.disabled.disabled.active,
|
|
||||||
.datepicker table tr td.active.disabled:hover.disabled.active,
|
|
||||||
.datepicker table tr td.active[disabled].active,
|
|
||||||
.datepicker table tr td.active:hover[disabled].active,
|
|
||||||
.datepicker table tr td.active.disabled[disabled].active,
|
|
||||||
.datepicker table tr td.active.disabled:hover[disabled].active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.active.active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.active:hover.active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.active.disabled.active,
|
|
||||||
fieldset[disabled] .datepicker table tr td.active.disabled:hover.active {
|
|
||||||
background-color: #428bca;
|
|
||||||
border-color: #357ebd;
|
|
||||||
}
|
|
||||||
.datepicker table tr td span {
|
|
||||||
display: block;
|
|
||||||
width: 23%;
|
|
||||||
height: 54px;
|
|
||||||
line-height: 54px;
|
|
||||||
float: left;
|
|
||||||
margin: 1%;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.datepicker table tr td span:hover {
|
|
||||||
background: #eeeeee;
|
|
||||||
}
|
|
||||||
.datepicker table tr td span.disabled,
|
|
||||||
.datepicker table tr td span.disabled:hover {
|
|
||||||
background: none;
|
|
||||||
color: #999999;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
.datepicker table tr td span.active,
|
|
||||||
.datepicker table tr td span.active:hover,
|
|
||||||
.datepicker table tr td span.active.disabled,
|
|
||||||
.datepicker table tr td span.active.disabled:hover {
|
|
||||||
color: #ffffff;
|
|
||||||
background-color: #428bca;
|
|
||||||
border-color: #357ebd;
|
|
||||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
|
||||||
}
|
|
||||||
.datepicker table tr td span.active:hover,
|
|
||||||
.datepicker table tr td span.active:hover:hover,
|
|
||||||
.datepicker table tr td span.active.disabled:hover,
|
|
||||||
.datepicker table tr td span.active.disabled:hover:hover,
|
|
||||||
.datepicker table tr td span.active:focus,
|
|
||||||
.datepicker table tr td span.active:hover:focus,
|
|
||||||
.datepicker table tr td span.active.disabled:focus,
|
|
||||||
.datepicker table tr td span.active.disabled:hover:focus,
|
|
||||||
.datepicker table tr td span.active:active,
|
|
||||||
.datepicker table tr td span.active:hover:active,
|
|
||||||
.datepicker table tr td span.active.disabled:active,
|
|
||||||
.datepicker table tr td span.active.disabled:hover:active,
|
|
||||||
.datepicker table tr td span.active.active,
|
|
||||||
.datepicker table tr td span.active:hover.active,
|
|
||||||
.datepicker table tr td span.active.disabled.active,
|
|
||||||
.datepicker table tr td span.active.disabled:hover.active,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td span.active,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td span.active:hover,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td span.active.disabled,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td span.active.disabled:hover {
|
|
||||||
color: #ffffff;
|
|
||||||
background-color: #3276b1;
|
|
||||||
border-color: #285e8e;
|
|
||||||
}
|
|
||||||
.datepicker table tr td span.active:active,
|
|
||||||
.datepicker table tr td span.active:hover:active,
|
|
||||||
.datepicker table tr td span.active.disabled:active,
|
|
||||||
.datepicker table tr td span.active.disabled:hover:active,
|
|
||||||
.datepicker table tr td span.active.active,
|
|
||||||
.datepicker table tr td span.active:hover.active,
|
|
||||||
.datepicker table tr td span.active.disabled.active,
|
|
||||||
.datepicker table tr td span.active.disabled:hover.active,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td span.active,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td span.active:hover,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td span.active.disabled,
|
|
||||||
.open .dropdown-toggle.datepicker table tr td span.active.disabled:hover {
|
|
||||||
background-image: none;
|
|
||||||
}
|
|
||||||
.datepicker table tr td span.active.disabled,
|
|
||||||
.datepicker table tr td span.active:hover.disabled,
|
|
||||||
.datepicker table tr td span.active.disabled.disabled,
|
|
||||||
.datepicker table tr td span.active.disabled:hover.disabled,
|
|
||||||
.datepicker table tr td span.active[disabled],
|
|
||||||
.datepicker table tr td span.active:hover[disabled],
|
|
||||||
.datepicker table tr td span.active.disabled[disabled],
|
|
||||||
.datepicker table tr td span.active.disabled:hover[disabled],
|
|
||||||
fieldset[disabled] .datepicker table tr td span.active,
|
|
||||||
fieldset[disabled] .datepicker table tr td span.active:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td span.active.disabled,
|
|
||||||
fieldset[disabled] .datepicker table tr td span.active.disabled:hover,
|
|
||||||
.datepicker table tr td span.active.disabled:hover,
|
|
||||||
.datepicker table tr td span.active:hover.disabled:hover,
|
|
||||||
.datepicker table tr td span.active.disabled.disabled:hover,
|
|
||||||
.datepicker table tr td span.active.disabled:hover.disabled:hover,
|
|
||||||
.datepicker table tr td span.active[disabled]:hover,
|
|
||||||
.datepicker table tr td span.active:hover[disabled]:hover,
|
|
||||||
.datepicker table tr td span.active.disabled[disabled]:hover,
|
|
||||||
.datepicker table tr td span.active.disabled:hover[disabled]:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td span.active:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td span.active:hover:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td span.active.disabled:hover,
|
|
||||||
fieldset[disabled] .datepicker table tr td span.active.disabled:hover:hover,
|
|
||||||
.datepicker table tr td span.active.disabled:focus,
|
|
||||||
.datepicker table tr td span.active:hover.disabled:focus,
|
|
||||||
.datepicker table tr td span.active.disabled.disabled:focus,
|
|
||||||
.datepicker table tr td span.active.disabled:hover.disabled:focus,
|
|
||||||
.datepicker table tr td span.active[disabled]:focus,
|
|
||||||
.datepicker table tr td span.active:hover[disabled]:focus,
|
|
||||||
.datepicker table tr td span.active.disabled[disabled]:focus,
|
|
||||||
.datepicker table tr td span.active.disabled:hover[disabled]:focus,
|
|
||||||
fieldset[disabled] .datepicker table tr td span.active:focus,
|
|
||||||
fieldset[disabled] .datepicker table tr td span.active:hover:focus,
|
|
||||||
fieldset[disabled] .datepicker table tr td span.active.disabled:focus,
|
|
||||||
fieldset[disabled] .datepicker table tr td span.active.disabled:hover:focus,
|
|
||||||
.datepicker table tr td span.active.disabled:active,
|
|
||||||
.datepicker table tr td span.active:hover.disabled:active,
|
|
||||||
.datepicker table tr td span.active.disabled.disabled:active,
|
|
||||||
.datepicker table tr td span.active.disabled:hover.disabled:active,
|
|
||||||
.datepicker table tr td span.active[disabled]:active,
|
|
||||||
.datepicker table tr td span.active:hover[disabled]:active,
|
|
||||||
.datepicker table tr td span.active.disabled[disabled]:active,
|
|
||||||
.datepicker table tr td span.active.disabled:hover[disabled]:active,
|
|
||||||
fieldset[disabled] .datepicker table tr td span.active:active,
|
|
||||||
fieldset[disabled] .datepicker table tr td span.active:hover:active,
|
|
||||||
fieldset[disabled] .datepicker table tr td span.active.disabled:active,
|
|
||||||
fieldset[disabled] .datepicker table tr td span.active.disabled:hover:active,
|
|
||||||
.datepicker table tr td span.active.disabled.active,
|
|
||||||
.datepicker table tr td span.active:hover.disabled.active,
|
|
||||||
.datepicker table tr td span.active.disabled.disabled.active,
|
|
||||||
.datepicker table tr td span.active.disabled:hover.disabled.active,
|
|
||||||
.datepicker table tr td span.active[disabled].active,
|
|
||||||
.datepicker table tr td span.active:hover[disabled].active,
|
|
||||||
.datepicker table tr td span.active.disabled[disabled].active,
|
|
||||||
.datepicker table tr td span.active.disabled:hover[disabled].active,
|
|
||||||
fieldset[disabled] .datepicker table tr td span.active.active,
|
|
||||||
fieldset[disabled] .datepicker table tr td span.active:hover.active,
|
|
||||||
fieldset[disabled] .datepicker table tr td span.active.disabled.active,
|
|
||||||
fieldset[disabled] .datepicker table tr td span.active.disabled:hover.active {
|
|
||||||
background-color: #428bca;
|
|
||||||
border-color: #357ebd;
|
|
||||||
}
|
|
||||||
.datepicker table tr td span.old,
|
|
||||||
.datepicker table tr td span.new {
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
.datepicker th.datepicker-switch {
|
|
||||||
width: 145px;
|
|
||||||
}
|
|
||||||
.datepicker thead tr:first-child th,
|
|
||||||
.datepicker tfoot tr th {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.datepicker thead tr:first-child th:hover,
|
|
||||||
.datepicker tfoot tr th:hover {
|
|
||||||
background: #eeeeee;
|
|
||||||
}
|
|
||||||
.datepicker .cw {
|
|
||||||
font-size: 10px;
|
|
||||||
width: 12px;
|
|
||||||
padding: 0 2px 0 5px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
.datepicker thead tr:first-child th.cw {
|
|
||||||
cursor: default;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
.input-group.date .input-group-addon i {
|
|
||||||
cursor: pointer;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
}
|
|
||||||
.input-daterange input {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.input-daterange input:first-child {
|
|
||||||
border-radius: 3px 0 0 3px;
|
|
||||||
}
|
|
||||||
.input-daterange input:last-child {
|
|
||||||
border-radius: 0 3px 3px 0;
|
|
||||||
}
|
|
||||||
.input-daterange .input-group-addon {
|
|
||||||
width: auto;
|
|
||||||
min-width: 16px;
|
|
||||||
padding: 4px 5px;
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: 1.428571429;
|
|
||||||
text-align: center;
|
|
||||||
text-shadow: 0 1px 0 #fff;
|
|
||||||
vertical-align: middle;
|
|
||||||
background-color: #eeeeee;
|
|
||||||
border: solid #cccccc;
|
|
||||||
border-width: 1px 0;
|
|
||||||
margin-left: -5px;
|
|
||||||
margin-right: -5px;
|
|
||||||
}
|
|
||||||
.datepicker.dropdown-menu {
|
|
||||||
position: absolute;
|
|
||||||
top: 100%;
|
|
||||||
left: 0;
|
|
||||||
z-index: 1000;
|
|
||||||
float: left;
|
|
||||||
display: none;
|
|
||||||
min-width: 160px;
|
|
||||||
list-style: none;
|
|
||||||
background-color: #ffffff;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
|
||||||
border-radius: 5px;
|
|
||||||
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
|
||||||
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
|
||||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
|
||||||
-webkit-background-clip: padding-box;
|
|
||||||
-moz-background-clip: padding;
|
|
||||||
background-clip: padding-box;
|
|
||||||
*border-right-width: 2px;
|
|
||||||
*border-bottom-width: 2px;
|
|
||||||
color: #333333;
|
|
||||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
||||||
font-size: 13px;
|
|
||||||
line-height: 1.428571429;
|
|
||||||
}
|
|
||||||
.datepicker.dropdown-menu th,
|
|
||||||
.datepicker.dropdown-menu td {
|
|
||||||
padding: 4px 5px;
|
|
||||||
}
|
|
425
css/normalize.css
vendored
425
css/normalize.css
vendored
|
@ -1,425 +0,0 @@
|
||||||
/*! normalize.css v3.0.1 | MIT License | git.io/normalize */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1. Set default font family to sans-serif.
|
|
||||||
* 2. Prevent iOS text size adjust after orientation change, without disabling
|
|
||||||
* user zoom.
|
|
||||||
*/
|
|
||||||
|
|
||||||
html {
|
|
||||||
font-family: sans-serif; /* 1 */
|
|
||||||
-ms-text-size-adjust: 100%; /* 2 */
|
|
||||||
-webkit-text-size-adjust: 100%; /* 2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove default margin.
|
|
||||||
*/
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* HTML5 display definitions
|
|
||||||
========================================================================== */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Correct `block` display not defined for any HTML5 element in IE 8/9.
|
|
||||||
* Correct `block` display not defined for `details` or `summary` in IE 10/11 and Firefox.
|
|
||||||
* Correct `block` display not defined for `main` in IE 11.
|
|
||||||
*/
|
|
||||||
|
|
||||||
article,
|
|
||||||
aside,
|
|
||||||
details,
|
|
||||||
figcaption,
|
|
||||||
figure,
|
|
||||||
footer,
|
|
||||||
header,
|
|
||||||
hgroup,
|
|
||||||
main,
|
|
||||||
nav,
|
|
||||||
section,
|
|
||||||
summary {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1. Correct `inline-block` display not defined in IE 8/9.
|
|
||||||
* 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
|
|
||||||
*/
|
|
||||||
|
|
||||||
audio,
|
|
||||||
canvas,
|
|
||||||
progress,
|
|
||||||
video {
|
|
||||||
display: inline-block; /* 1 */
|
|
||||||
vertical-align: baseline; /* 2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prevent modern browsers from displaying `audio` without controls.
|
|
||||||
* Remove excess height in iOS 5 devices.
|
|
||||||
*/
|
|
||||||
|
|
||||||
audio:not([controls]) {
|
|
||||||
display: none;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address `[hidden]` styling not present in IE 8/9/10.
|
|
||||||
* Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
|
|
||||||
*/
|
|
||||||
|
|
||||||
[hidden],
|
|
||||||
template {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Links
|
|
||||||
========================================================================== */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the gray background color from active links in IE 10.
|
|
||||||
*/
|
|
||||||
|
|
||||||
a {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Improve readability when focused and also mouse hovered in all browsers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
a:active,
|
|
||||||
a:hover {
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Text-level semantics
|
|
||||||
========================================================================== */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address styling not present in IE 8/9/10/11, Safari, and Chrome.
|
|
||||||
*/
|
|
||||||
|
|
||||||
abbr[title] {
|
|
||||||
border-bottom: 1px dotted;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
|
|
||||||
*/
|
|
||||||
|
|
||||||
b,
|
|
||||||
strong {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address styling not present in Safari and Chrome.
|
|
||||||
*/
|
|
||||||
|
|
||||||
dfn {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address variable `h1` font-size and margin within `section` and `article`
|
|
||||||
* contexts in Firefox 4+, Safari, and Chrome.
|
|
||||||
*/
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 2em;
|
|
||||||
margin: 0.67em 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address styling not present in IE 8/9.
|
|
||||||
*/
|
|
||||||
|
|
||||||
mark {
|
|
||||||
background: #ff0;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address inconsistent and variable font size in all browsers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
small {
|
|
||||||
font-size: 80%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
sub,
|
|
||||||
sup {
|
|
||||||
font-size: 75%;
|
|
||||||
line-height: 0;
|
|
||||||
position: relative;
|
|
||||||
vertical-align: baseline;
|
|
||||||
}
|
|
||||||
|
|
||||||
sup {
|
|
||||||
top: -0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub {
|
|
||||||
bottom: -0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Embedded content
|
|
||||||
========================================================================== */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove border when inside `a` element in IE 8/9/10.
|
|
||||||
*/
|
|
||||||
|
|
||||||
img {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Correct overflow not hidden in IE 9/10/11.
|
|
||||||
*/
|
|
||||||
|
|
||||||
svg:not(:root) {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Grouping content
|
|
||||||
========================================================================== */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address margin not present in IE 8/9 and Safari.
|
|
||||||
*/
|
|
||||||
|
|
||||||
figure {
|
|
||||||
margin: 1em 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address differences between Firefox and other browsers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
hr {
|
|
||||||
-moz-box-sizing: content-box;
|
|
||||||
box-sizing: content-box;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Contain overflow in all browsers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
pre {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address odd `em`-unit font size rendering in all browsers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
code,
|
|
||||||
kbd,
|
|
||||||
pre,
|
|
||||||
samp {
|
|
||||||
font-family: monospace, monospace;
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Forms
|
|
||||||
========================================================================== */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Known limitation: by default, Chrome and Safari on OS X allow very limited
|
|
||||||
* styling of `select`, unless a `border` property is set.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1. Correct color not being inherited.
|
|
||||||
* Known issue: affects color of disabled elements.
|
|
||||||
* 2. Correct font properties not being inherited.
|
|
||||||
* 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
|
|
||||||
*/
|
|
||||||
|
|
||||||
button,
|
|
||||||
input,
|
|
||||||
optgroup,
|
|
||||||
select,
|
|
||||||
textarea {
|
|
||||||
color: inherit; /* 1 */
|
|
||||||
font: inherit; /* 2 */
|
|
||||||
margin: 0; /* 3 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address `overflow` set to `hidden` in IE 8/9/10/11.
|
|
||||||
*/
|
|
||||||
|
|
||||||
button {
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address inconsistent `text-transform` inheritance for `button` and `select`.
|
|
||||||
* All other form control elements do not inherit `text-transform` values.
|
|
||||||
* Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
|
|
||||||
* Correct `select` style inheritance in Firefox.
|
|
||||||
*/
|
|
||||||
|
|
||||||
button,
|
|
||||||
select {
|
|
||||||
text-transform: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
|
|
||||||
* and `video` controls.
|
|
||||||
* 2. Correct inability to style clickable `input` types in iOS.
|
|
||||||
* 3. Improve usability and consistency of cursor style between image-type
|
|
||||||
* `input` and others.
|
|
||||||
*/
|
|
||||||
|
|
||||||
button,
|
|
||||||
html input[type="button"], /* 1 */
|
|
||||||
input[type="reset"],
|
|
||||||
input[type="submit"] {
|
|
||||||
-webkit-appearance: button; /* 2 */
|
|
||||||
cursor: pointer; /* 3 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Re-set default cursor for disabled elements.
|
|
||||||
*/
|
|
||||||
|
|
||||||
button[disabled],
|
|
||||||
html input[disabled] {
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove inner padding and border in Firefox 4+.
|
|
||||||
*/
|
|
||||||
|
|
||||||
button::-moz-focus-inner,
|
|
||||||
input::-moz-focus-inner {
|
|
||||||
border: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
|
|
||||||
* the UA stylesheet.
|
|
||||||
*/
|
|
||||||
|
|
||||||
input {
|
|
||||||
line-height: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* It's recommended that you don't attempt to style these elements.
|
|
||||||
* Firefox's implementation doesn't respect box-sizing, padding, or width.
|
|
||||||
*
|
|
||||||
* 1. Address box sizing set to `content-box` in IE 8/9/10.
|
|
||||||
* 2. Remove excess padding in IE 8/9/10.
|
|
||||||
*/
|
|
||||||
|
|
||||||
input[type="checkbox"],
|
|
||||||
input[type="radio"] {
|
|
||||||
box-sizing: border-box; /* 1 */
|
|
||||||
padding: 0; /* 2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fix the cursor style for Chrome's increment/decrement buttons. For certain
|
|
||||||
* `font-size` values of the `input`, it causes the cursor style of the
|
|
||||||
* decrement button to change from `default` to `text`.
|
|
||||||
*/
|
|
||||||
|
|
||||||
input[type="number"]::-webkit-inner-spin-button,
|
|
||||||
input[type="number"]::-webkit-outer-spin-button {
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1. Address `appearance` set to `searchfield` in Safari and Chrome.
|
|
||||||
* 2. Address `box-sizing` set to `border-box` in Safari and Chrome
|
|
||||||
* (include `-moz` to future-proof).
|
|
||||||
*/
|
|
||||||
|
|
||||||
input[type="search"] {
|
|
||||||
-webkit-appearance: textfield; /* 1 */
|
|
||||||
-moz-box-sizing: content-box;
|
|
||||||
-webkit-box-sizing: content-box; /* 2 */
|
|
||||||
box-sizing: content-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove inner padding and search cancel button in Safari and Chrome on OS X.
|
|
||||||
* Safari (but not Chrome) clips the cancel button when the search input has
|
|
||||||
* padding (and `textfield` appearance).
|
|
||||||
*/
|
|
||||||
|
|
||||||
input[type="search"]::-webkit-search-cancel-button,
|
|
||||||
input[type="search"]::-webkit-search-decoration {
|
|
||||||
-webkit-appearance: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define consistent border, margin, and padding.
|
|
||||||
*/
|
|
||||||
|
|
||||||
fieldset {
|
|
||||||
border: 1px solid #c0c0c0;
|
|
||||||
margin: 0 2px;
|
|
||||||
padding: 0.35em 0.625em 0.75em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1. Correct `color` not being inherited in IE 8/9/10/11.
|
|
||||||
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
|
|
||||||
*/
|
|
||||||
|
|
||||||
legend {
|
|
||||||
border: 0; /* 1 */
|
|
||||||
padding: 0; /* 2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove default vertical scrollbar in IE 8/9/10/11.
|
|
||||||
*/
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Don't inherit the `font-weight` (applied by a rule above).
|
|
||||||
* NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
|
|
||||||
*/
|
|
||||||
|
|
||||||
optgroup {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tables
|
|
||||||
========================================================================== */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove most spacing between table cells.
|
|
||||||
*/
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
border-spacing: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
td,
|
|
||||||
th {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
362
index.html
362
index.html
|
@ -1,362 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<title>Croodle</title>
|
|
||||||
|
|
||||||
<link href="css/normalize.css" rel="stylesheet">
|
|
||||||
<link href="css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<link href="css/datepicker3.css" rel="stylesheet">
|
|
||||||
<link href="css/croodle.css" rel="stylesheet">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<!--
|
|
||||||
TEMPLATES
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- application template -->
|
|
||||||
<script type="text/x-handlebars">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<h1 class="logo">{{#link-to 'index'}}Croodle{{/link-to}}</h1>
|
|
||||||
{{outlet}}
|
|
||||||
</div>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- index template -->
|
|
||||||
<script type="text/x-handlebars" id="index">
|
|
||||||
<div class="index">
|
|
||||||
|
|
||||||
<div class="box teaser">
|
|
||||||
<h2>
|
|
||||||
Croodle simplifies scheduling and decision-making ...<br/>
|
|
||||||
... while keeping your data private
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
|
|
||||||
<div class="box features">
|
|
||||||
<h3>Features</h3>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Easily schedule a date or make a poll with as much
|
|
||||||
people as you like.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
All your private data is encrypted inside your
|
|
||||||
browser.
|
|
||||||
So even the server doesn't know what your schedule
|
|
||||||
or poll is about.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Define as much options as you like. Select dates
|
|
||||||
via calendar or define your options as free text.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Use pre defined answer options like <i>yes</i>,
|
|
||||||
<i>no</i>, <i>maybe</i> or allow your user to enter
|
|
||||||
free text.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
An automatically updated evaluation allways gives you
|
|
||||||
an overview which options are preferred.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<p class="have-a-try">
|
|
||||||
<span class="glyphicon glyphicon-share-alt"></span>
|
|
||||||
{{#link-to 'create'}}have a try right now{{/link-to}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="box features">
|
|
||||||
<h3>Become your own hoster</h3>
|
|
||||||
<p>
|
|
||||||
You don't have to trust our server. You could easily
|
|
||||||
host croodle yourself. All you need is a web space
|
|
||||||
with PHP and SSL encryption enabled. You find the
|
|
||||||
source code and installation instructions on
|
|
||||||
<a href="https://github.com/jelhan/croodle">GitHub</a>.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- loading template -->
|
|
||||||
<script type="text/x-handlebars" id="loading">
|
|
||||||
<div id="loading" class="box">
|
|
||||||
<div class="loading-animation-container">
|
|
||||||
<div id="loading-animation-circle-1" class="loading-animation-circle"></div>
|
|
||||||
<div id="loading-animation-circle-2" class="loading-animation-circle"></div>
|
|
||||||
<div id="loading-animation-circle-3" class="loading-animation-circle"></div>
|
|
||||||
<div id="loading-animation-circle-4" class="loading-animation-circle"></div>
|
|
||||||
<div id="loading-animation-circle-5" class="loading-animation-circle"></div>
|
|
||||||
<div id="loading-animation-circle-6" class="loading-animation-circle"></div>
|
|
||||||
<div id="loading-animation-circle-7" class="loading-animation-circle"></div>
|
|
||||||
<div id="loading-animation-circle-8" class="loading-animation-circle"></div>
|
|
||||||
</div>
|
|
||||||
loading your poll . . .
|
|
||||||
</div>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- error template -->
|
|
||||||
<script type="text/x-handlebars" id="error">
|
|
||||||
<div class="box">
|
|
||||||
<h2>error</h2>
|
|
||||||
<p>
|
|
||||||
{{#if is404}}
|
|
||||||
Could not find the poll you tried to open. Perhaps it was
|
|
||||||
deleted?
|
|
||||||
{{else}}
|
|
||||||
We are sorry. An unexpected error occurred.
|
|
||||||
{{/if}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- poll template -->
|
|
||||||
<script type="text/x-handlebars" id="poll">
|
|
||||||
<div id="poll">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-6 col-lg-5">
|
|
||||||
<div class="box meta-data">
|
|
||||||
<h2 class="title">{{title}}</h2>
|
|
||||||
<p class="description">{{description}}</p>
|
|
||||||
<p class="creationDate">created on {{creationDate}}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6 col-lg-5 col-lg-offset-2">
|
|
||||||
<div class="box share-link">
|
|
||||||
<p>Share the link and invite other people to participate in your poll.</p>
|
|
||||||
<p class="link"><a {{bind-attr href=pollUrl}}>{{pollUrl}}</a></p>
|
|
||||||
<p class="notice">Everyone who knows the link could read the data.
|
|
||||||
If your poll consists of private data you may only share the
|
|
||||||
link via encrypted channels like PGP encrypted email or instant
|
|
||||||
messaging with OTR.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="box">
|
|
||||||
<div class="table-scroll">
|
|
||||||
<table class="user-selections-table table table-striped table-condensed">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th> </th>
|
|
||||||
{{#each option in options}}
|
|
||||||
<th>
|
|
||||||
{{#if isFindADate}}
|
|
||||||
{{formattedDate option.title 'll'}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if isMakeAPoll}}
|
|
||||||
{{option.title}}
|
|
||||||
{{/if}}
|
|
||||||
</th>
|
|
||||||
{{/each}}
|
|
||||||
<th> </th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
|
||||||
<tr class='newUser'>
|
|
||||||
{{! ToDo:
|
|
||||||
should be all together in just one form;
|
|
||||||
not in several forms for each input.
|
|
||||||
This work-around is due to a problem of
|
|
||||||
ember-forms in dealing with propertys of
|
|
||||||
handlebar each helper
|
|
||||||
}}
|
|
||||||
<td>
|
|
||||||
{{#em-form model=controller submit_button=false}}
|
|
||||||
{{em-form-label text="name" extraClass="sr-only"}}
|
|
||||||
{{em-input property="newUserName" placeholder="Enter your name..."}}
|
|
||||||
{{/em-form}}
|
|
||||||
</td>
|
|
||||||
{{#each newUserSelection in controller.newUserSelections}}
|
|
||||||
<td>
|
|
||||||
{{#em-form model=newUserSelection submit_button=false}}
|
|
||||||
{{#if isFreeText}}
|
|
||||||
{{em-input property="value"}}
|
|
||||||
{{else}}
|
|
||||||
{{em-select
|
|
||||||
property="value"
|
|
||||||
contentBinding="answers"
|
|
||||||
optionValuePath="content.label"
|
|
||||||
optionLabelPath="content.label"
|
|
||||||
prompt="choose an answer"}}
|
|
||||||
{{/if}}
|
|
||||||
{{/em-form}}
|
|
||||||
</td>
|
|
||||||
{{/each}}
|
|
||||||
<td>
|
|
||||||
<div class="form-group">
|
|
||||||
<button {{action "addNewUser"}} class="btn btn-default" {{bind-attr disabled=isNotValid}}> save </button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
{{#each user in users}}
|
|
||||||
<tr>
|
|
||||||
<td>{{user.name}}</td>
|
|
||||||
{{#each selection in user.selections}}
|
|
||||||
<td>
|
|
||||||
{{#if isFreeText}}
|
|
||||||
{{selection.value}}
|
|
||||||
{{else}}
|
|
||||||
<span {{bind-attr class="selection.value"}}>{{selection.value}}</span>
|
|
||||||
{{/if}}
|
|
||||||
</td>
|
|
||||||
{{/each}}
|
|
||||||
<td> </td>
|
|
||||||
</tr>
|
|
||||||
{{/each}}
|
|
||||||
|
|
||||||
{{#unless isFreeText}}
|
|
||||||
<tr class='evaluation evaluation-header'>
|
|
||||||
<td {{bind-attr colspan=fullRowColspan}}>Evaluation</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
{{#each answer in evaluation}}
|
|
||||||
<tr class='evaluation'>
|
|
||||||
<td>
|
|
||||||
{{answer.label}}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
{{#each option in answer.options}}
|
|
||||||
<td>
|
|
||||||
{{option}}
|
|
||||||
</td>
|
|
||||||
{{/each}}
|
|
||||||
|
|
||||||
<td> </td>
|
|
||||||
</tr>
|
|
||||||
{{/each}}
|
|
||||||
{{/unless}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- create templates -->
|
|
||||||
<script type="text/x-handlebars" id="create">
|
|
||||||
{{outlet}}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script type="text/x-handlebars" id="create/index">
|
|
||||||
<div class="box">
|
|
||||||
{{#em-form model=controller submit_button=false}}
|
|
||||||
{{em-select
|
|
||||||
property="pollType"
|
|
||||||
label="poll type"
|
|
||||||
prompt="-select-"
|
|
||||||
contentBinding="App.PollTypes"
|
|
||||||
optionValuePath="content.id"
|
|
||||||
optionLabelPath="content.label"
|
|
||||||
prompt="Please select a poll type"}}
|
|
||||||
{{em-form-submit text="next"}}
|
|
||||||
{{/em-form}}
|
|
||||||
</div>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script type="text/x-handlebars" id="create/meta">
|
|
||||||
<div class="box">
|
|
||||||
{{#em-form model=controller submit_button=false}}
|
|
||||||
{{em-input
|
|
||||||
property="title"
|
|
||||||
label="title"
|
|
||||||
placeholder="Enter a title..."}}
|
|
||||||
{{em-text
|
|
||||||
property="description"
|
|
||||||
label="description"
|
|
||||||
placeholder="Enter a description if you like..."
|
|
||||||
rows=4}}
|
|
||||||
{{em-form-submit text="next"}}
|
|
||||||
{{/em-form}}
|
|
||||||
</div>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script type="text/x-handlebars" id="create/options">
|
|
||||||
<div class="box">
|
|
||||||
{{#if isMakeAPoll}}
|
|
||||||
<form role="form">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label">options</label>
|
|
||||||
<fieldset>
|
|
||||||
{{#each option in options}}
|
|
||||||
{{view Ember.TextField valueBinding="option.title" class="form-control"}}<br/>
|
|
||||||
{{/each}}
|
|
||||||
{{em-form-control-help text="You have to enter at least two options."}}
|
|
||||||
</fieldset>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<button {{action "moreOptions" target="view"}} class="btn btn-default"> add another option </button>
|
|
||||||
<button {{action "submitMakeAPoll"}} class="btn btn-default" {{bind-attr disabled=isNotValid}}> next </button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if isFindADate}}
|
|
||||||
<div id="datepicker">
|
|
||||||
{{view App.Datepicker}}
|
|
||||||
{{em-form-control-help text="You have to select at least two dates."}}
|
|
||||||
<div class="form-group">
|
|
||||||
<button {{action "submitFindADate"}} class="btn btn-default" {{bind-attr disabled=isNotValid}}> next </button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script type="text/x-handlebars" id="create/settings">
|
|
||||||
<div class="box">
|
|
||||||
{{#em-form model=controller submit_button=false}}
|
|
||||||
{{em-select
|
|
||||||
property="answerType"
|
|
||||||
label="available answers"
|
|
||||||
prompt="-select-"
|
|
||||||
contentBinding="App.AnswerTypes"
|
|
||||||
optionValuePath="content.id"
|
|
||||||
optionLabelPath="content.label"
|
|
||||||
prompt="Please define available answers"}}
|
|
||||||
{{em-checkbox label="Allow anonym participation?" property="anonymousUser"}}
|
|
||||||
{{em-checkbox label="Force an answer?" property="forceAnswer"}}
|
|
||||||
{{em-form-submit text="save"}}
|
|
||||||
{{/em-form}}
|
|
||||||
</div>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- configuration -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
// enable ember query-params-new feature
|
|
||||||
window.ENV = {
|
|
||||||
FEATURES: {
|
|
||||||
'query-params-new': true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- loading libaries -->
|
|
||||||
<script src="js/lib/sjcl.js"></script>
|
|
||||||
<script src="js/lib/jquery.js"></script>
|
|
||||||
<script src="js/lib/handlebars.js"></script>
|
|
||||||
<script src="js/lib/ember.js"></script>
|
|
||||||
<script src="js/lib/ember-data.js"></script>
|
|
||||||
<script src="js/lib/embedded-adapter.js"></script>
|
|
||||||
<script src="js/lib/ember_forms.js"></script>
|
|
||||||
<script src="js/lib/ember-validations.js"></script>
|
|
||||||
<script src="js/lib/bootstrap.js"></script>
|
|
||||||
<script src="js/lib/bootstrap-datepicker.js"></script>
|
|
||||||
<script src="js/lib/moment.min.js"></script>
|
|
||||||
<script src="js/croodle.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
769
js/croodle.js
769
js/croodle.js
|
@ -1,769 +0,0 @@
|
||||||
// app initialization
|
|
||||||
Ember.MODEL_FACTORY_INJECTIONS = true;
|
|
||||||
|
|
||||||
window.App = Ember.Application.create({
|
|
||||||
ready: function(){
|
|
||||||
this.register('encryption:current', App.Encryption, {singleton: true});
|
|
||||||
this.inject('controller:poll', 'encryption', 'encryption:current');
|
|
||||||
this.inject('route:create', 'encryption', 'encryption:current');
|
|
||||||
this.inject('model', 'encryption', 'encryption:current');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// adapter initialization
|
|
||||||
App.ApplicationAdapter = DS.RESTAdapter.extend({
|
|
||||||
// set namespace to api.php in same subdirectory
|
|
||||||
namespace: window.location.pathname
|
|
||||||
// remove index.html if it's there
|
|
||||||
.replace(/index.html$/, '')
|
|
||||||
// remove leading and trailing slash
|
|
||||||
.replace(/\/$/, '')
|
|
||||||
// add api.php
|
|
||||||
.concat('/api.php?')
|
|
||||||
// remove leading slash
|
|
||||||
.replace(/^\//g, '')
|
|
||||||
});
|
|
||||||
|
|
||||||
// serializer initialization
|
|
||||||
App.ApplicationSerializer = DS.EmbeddedSerializer.extend();
|
|
||||||
|
|
||||||
// adding support for attribut data-option-id to input fields
|
|
||||||
Ember.TextField.reopen({
|
|
||||||
attributeBindings: ['data-option']
|
|
||||||
});
|
|
||||||
|
|
||||||
// decrypt / encrypt computed property helper
|
|
||||||
Ember.computed.encrypted = function(encryptedField, dataType) {
|
|
||||||
return Ember.computed(encryptedField, function(key, decryptedValue) {
|
|
||||||
var encryptKey = this.get('encryption.key'),
|
|
||||||
encryptedValue;
|
|
||||||
|
|
||||||
// check if encryptKey is set
|
|
||||||
if (Ember.isEmpty(encryptKey)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// setter
|
|
||||||
if (arguments.length === 2) {
|
|
||||||
var decryptedJSON = JSON.stringify(decryptedValue);
|
|
||||||
|
|
||||||
encryptedValue = Ember.isNone(decryptedValue) ? null : String(sjcl.encrypt(encryptKey , decryptedJSON));
|
|
||||||
this.set(encryptedField, encryptedValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// get value of field to decrypt
|
|
||||||
encryptedJSON = this.get(encryptedField);
|
|
||||||
|
|
||||||
// check if encryptedField is defined and not null
|
|
||||||
if (typeof encryptedJSON === 'undefined' ||
|
|
||||||
encryptedJSON === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to decrypt value
|
|
||||||
try {
|
|
||||||
decryptedJSON = sjcl.decrypt(encryptKey, encryptedJSON);
|
|
||||||
decryptedValue = JSON.parse(decryptedJSON);
|
|
||||||
} catch (e) {
|
|
||||||
console.log('Error on decrypting ' + encryptedField);
|
|
||||||
console.log(e);
|
|
||||||
console.log('Perhaps a wrong encryption key?');
|
|
||||||
console.log('Encryption key used: ' + encryptKey);
|
|
||||||
decryptedValue = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (dataType) {
|
|
||||||
case 'array':
|
|
||||||
return Ember.isNone(decryptedValue) ? null : decryptedValue;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'date':
|
|
||||||
return Ember.isNone(decryptedValue) ? null : Date(decryptedValue);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'string':
|
|
||||||
return Ember.isNone(decryptedValue) ? null : String(decryptedValue);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'boolean':
|
|
||||||
return Ember.isNone(decryptedValue) ? null : Boolean(decryptedValue);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* models
|
|
||||||
*/
|
|
||||||
|
|
||||||
// poll model
|
|
||||||
App.Poll = DS.Model.extend({
|
|
||||||
encryptedTitle : DS.attr('string'),
|
|
||||||
title : Ember.computed.encrypted('encryptedTitle', 'string'),
|
|
||||||
encryptedDescription : DS.attr('string'),
|
|
||||||
description: Ember.computed.encrypted('encryptedDescription', 'string'),
|
|
||||||
encryptedPollType : DS.attr('string'),
|
|
||||||
pollType : Ember.computed.encrypted('encryptedPollType', 'string'),
|
|
||||||
encryptedAnswerType: DS.attr('string'),
|
|
||||||
answerType : Ember.computed.encrypted('encryptedAnswerType', 'string'),
|
|
||||||
encryptedAnswers : DS.attr('string'),
|
|
||||||
answers : Ember.computed.encrypted('encryptedAnswers', 'array'),
|
|
||||||
encryptedOptions : DS.attr('string'),
|
|
||||||
options : Ember.computed.encrypted('encryptedOptions', 'array'),
|
|
||||||
users : DS.hasMany('user', {async: true}),
|
|
||||||
encryptedCreationDate : DS.attr('string'),
|
|
||||||
creationDate : Ember.computed.encrypted('encryptedCreationDate', 'date'),
|
|
||||||
encryptedForceAnswer : DS.attr('string'),
|
|
||||||
forceAnswer : Ember.computed.encrypted('encryptedForceAnswer', 'boolean'),
|
|
||||||
encryptedAnonymousUser : DS.attr('string'),
|
|
||||||
anonymousUser : Ember.computed.encrypted('encryptedAnonymousUser', 'boolean'),
|
|
||||||
|
|
||||||
isFindADate: function() {
|
|
||||||
return this.get('pollType') === 'FindADate';
|
|
||||||
}.property('pollType'),
|
|
||||||
isFreeText: function() {
|
|
||||||
return this.get('answerType') === 'FreeText';
|
|
||||||
}.property('answerType'),
|
|
||||||
isMakeAPoll: function() {
|
|
||||||
return this.get('pollType') === 'MakeAPoll';
|
|
||||||
}.property('pollType')
|
|
||||||
});
|
|
||||||
|
|
||||||
// user model
|
|
||||||
// used by poll model
|
|
||||||
App.User = DS.Model.extend({
|
|
||||||
poll : DS.belongsTo('poll', {async: true}),
|
|
||||||
encryptedName : DS.attr('string'),
|
|
||||||
name : Ember.computed.encrypted('encryptedName', 'string'),
|
|
||||||
encryptedSelections : DS.attr('string'),
|
|
||||||
selections : Ember.computed.encrypted('encryptedSelections', 'array'),
|
|
||||||
encryptedCreationDate : DS.attr('string'),
|
|
||||||
creationDate : Ember.computed.encrypted('encryptedCreationDate', 'date')
|
|
||||||
});
|
|
||||||
|
|
||||||
App.Encryption = Ember.Object.extend({
|
|
||||||
key : '',
|
|
||||||
isSet: false
|
|
||||||
});
|
|
||||||
|
|
||||||
App.PollTypes = [
|
|
||||||
Ember.Object.create({
|
|
||||||
id : "FindADate",
|
|
||||||
label : "Find a date"
|
|
||||||
}),
|
|
||||||
Ember.Object.create({
|
|
||||||
id : "MakeAPoll",
|
|
||||||
label : "Make a poll"
|
|
||||||
})
|
|
||||||
];
|
|
||||||
|
|
||||||
App.AnswerTypes = [
|
|
||||||
Ember.Object.create({
|
|
||||||
id : "YesNo",
|
|
||||||
label : "yes, no",
|
|
||||||
answers : [
|
|
||||||
{label: "yes"},
|
|
||||||
{label: "no"}
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
Ember.Object.create({
|
|
||||||
id : "YesNoMaybe",
|
|
||||||
label : "yes, no, maybe",
|
|
||||||
answers : [
|
|
||||||
{label: "yes"},
|
|
||||||
{label: "no"},
|
|
||||||
{label: "maybe"}
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
Ember.Object.create({
|
|
||||||
id : "FreeText",
|
|
||||||
label : "free text",
|
|
||||||
answers : []
|
|
||||||
})
|
|
||||||
];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Serializer
|
|
||||||
*/
|
|
||||||
App.PollSerializer = App.ApplicationSerializer.extend({
|
|
||||||
attrs: {
|
|
||||||
users: {embedded: 'load'}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
* routes
|
|
||||||
*/
|
|
||||||
|
|
||||||
// defining routes of app
|
|
||||||
App.Router.map(function(){
|
|
||||||
this.route('poll', { path: '/poll/:poll_id' });
|
|
||||||
this.resource('create', function(){
|
|
||||||
this.route('meta');
|
|
||||||
this.route('options');
|
|
||||||
this.route('settings');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
App.CreateRoute = Ember.Route.extend({
|
|
||||||
beforeModel: function(){
|
|
||||||
// generate encryptionKey
|
|
||||||
var encryptionKeyLength = 40;
|
|
||||||
var encryptionKeyChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
|
||||||
|
|
||||||
var encryptionKey = '';
|
|
||||||
var list = encryptionKeyChars.split('');
|
|
||||||
var len = list.length, i = 0;
|
|
||||||
do {
|
|
||||||
i++;
|
|
||||||
var index = Math.floor(Math.random() * len);
|
|
||||||
encryptionKey += list[index];
|
|
||||||
} while(i < encryptionKeyLength);
|
|
||||||
|
|
||||||
// set encryption key
|
|
||||||
this.set('encryption.key', encryptionKey);
|
|
||||||
},
|
|
||||||
|
|
||||||
model: function(){
|
|
||||||
// create empty poll
|
|
||||||
return this.store.createRecord('poll', {
|
|
||||||
creationDate : new Date(),
|
|
||||||
options : [{title: ''}, {title: ''}],
|
|
||||||
forceAnswer: true,
|
|
||||||
anonymousUser: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
App.CreateIndexRoute = Ember.Route.extend({
|
|
||||||
model: function(){
|
|
||||||
return this.modelFor('create');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
App.CreateMetaRoute = Ember.Route.extend({
|
|
||||||
model: function(){
|
|
||||||
return this.modelFor('create');
|
|
||||||
},
|
|
||||||
|
|
||||||
// redirect to create/index if poll type is not set
|
|
||||||
afterModel: function(create){
|
|
||||||
if (create.get('pollType') === null) {
|
|
||||||
this.transitionTo('create.index');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
App.CreateOptionsRoute = Ember.Route.extend({
|
|
||||||
model: function(){
|
|
||||||
return this.modelFor('create');
|
|
||||||
},
|
|
||||||
|
|
||||||
// redirect to create/meta if title is not set
|
|
||||||
afterModel: function(create){
|
|
||||||
if (create.get('title') === null) {
|
|
||||||
this.transitionTo('create.meta');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
App.CreateSettingsRoute = Ember.Route.extend({
|
|
||||||
model: function(){
|
|
||||||
return this.modelFor('create');
|
|
||||||
},
|
|
||||||
|
|
||||||
// redirect to create/options if not enough options are defined
|
|
||||||
afterModel: function(create){
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
// check if only default options are defined
|
|
||||||
if (create.get('options.length') === 2) {
|
|
||||||
create.get('options').forEach(function(option) {
|
|
||||||
if (option.title === '') {
|
|
||||||
self.transitionTo('create.options');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// check if less then two options are defined
|
|
||||||
else if (create.get('options.length') < 2) {
|
|
||||||
this.transitionTo('create.options');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
App.PollRoute = Ember.Route.extend({
|
|
||||||
model: function(params){
|
|
||||||
return this.store.find('poll', params.poll_id).then(function(poll) {
|
|
||||||
return poll;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
* controller
|
|
||||||
*/
|
|
||||||
App.CreateIndexController = Ember.ObjectController.extend(Ember.Validations.Mixin);
|
|
||||||
App.CreateIndexController.reopen({
|
|
||||||
actions: {
|
|
||||||
submit: function(){
|
|
||||||
// redirect to CreateMeta
|
|
||||||
this.transitionToRoute('create.meta');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
validations: {
|
|
||||||
pollType: {
|
|
||||||
presence: true,
|
|
||||||
inclusion: {
|
|
||||||
in: ['FindADate', 'MakeAPoll']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
App.CreateMetaController = Ember.ObjectController.extend(Ember.Validations.Mixin);
|
|
||||||
App.CreateMetaController.reopen({
|
|
||||||
actions: {
|
|
||||||
submit: function(){
|
|
||||||
// redirect to CreateOptions
|
|
||||||
this.transitionToRoute('create.options');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
validations: {
|
|
||||||
title: {
|
|
||||||
presence: true,
|
|
||||||
length: {
|
|
||||||
minimum: 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
App.CreateOptionsController = Ember.ObjectController.extend(Ember.Validations.Mixin);
|
|
||||||
App.CreateOptionsController.reopen({
|
|
||||||
actions: {
|
|
||||||
/*
|
|
||||||
* handles submit of option input for poll of type MakeAPoll
|
|
||||||
*/
|
|
||||||
submitMakeAPoll: function() {
|
|
||||||
var options = this.get('model.options'),
|
|
||||||
newOptions = [];
|
|
||||||
|
|
||||||
// remove options without value
|
|
||||||
options.forEach(function(option) {
|
|
||||||
if (option.title !== '') {
|
|
||||||
newOptions.pushObject(option);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// set updated options
|
|
||||||
//
|
|
||||||
// we have to hardly set new options even if they wasn't changed to
|
|
||||||
// trigger computed property; push on array doesn't trigger computed
|
|
||||||
// property to recalculate
|
|
||||||
this.set('model.options', newOptions);
|
|
||||||
|
|
||||||
// tricker save action
|
|
||||||
this.send('save');
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
* handles submit of selected dates for poll of type MakeAPoll
|
|
||||||
*/
|
|
||||||
submitFindADate: function() {
|
|
||||||
// tricker save action
|
|
||||||
this.send('save');
|
|
||||||
},
|
|
||||||
|
|
||||||
save: function(){
|
|
||||||
// redirect to CreateSettings
|
|
||||||
this.transitionToRoute('create.settings');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
* returns true if required number of options is reached
|
|
||||||
*/
|
|
||||||
enoughOptions: function(){
|
|
||||||
var requiredOptionsLength = 2,
|
|
||||||
givenOptions,
|
|
||||||
filtedOptions;
|
|
||||||
|
|
||||||
givenOptions = this.get('options');
|
|
||||||
|
|
||||||
// check if options are defined
|
|
||||||
if (typeof givenOptions === 'undefined') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// reduce array to options which have a title
|
|
||||||
filtedOptions = givenOptions.filterBy('title', '');
|
|
||||||
|
|
||||||
return (givenOptions.length - filtedOptions.length) >= requiredOptionsLength;
|
|
||||||
}.property('options.@each.title'),
|
|
||||||
|
|
||||||
isNotValid: function(){
|
|
||||||
return !this.get('isValid');
|
|
||||||
}.property('isValid'),
|
|
||||||
|
|
||||||
validations: {
|
|
||||||
enoughOptions: {
|
|
||||||
acceptance: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
App.CreateSettingsController = Ember.ObjectController.extend(Ember.Validations.Mixin);
|
|
||||||
App.CreateSettingsController.reopen({
|
|
||||||
actions: {
|
|
||||||
submit: function(){
|
|
||||||
// check if answer type is selected
|
|
||||||
if (this.get('answerType') === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// save poll
|
|
||||||
var self = this;
|
|
||||||
this.get('model').save().then(function(model){
|
|
||||||
// reload as workaround for bug: duplicated records after save
|
|
||||||
model.reload().then(function(model){
|
|
||||||
// redirect to new poll
|
|
||||||
self.transitionToRoute('poll', model, {queryParams: {encryptionKey: self.get('encryption.key')}});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
* set answers depending on selected answer type
|
|
||||||
*/
|
|
||||||
updateAnswers: function(){
|
|
||||||
var selectedAnswer = this.get('model.answerType'),
|
|
||||||
answers = [];
|
|
||||||
|
|
||||||
if (selectedAnswer !== null) {
|
|
||||||
for (var i=0; i<App.AnswerTypes.length; i++) {
|
|
||||||
if (App.AnswerTypes[i].id === selectedAnswer) {
|
|
||||||
answers = App.AnswerTypes[i].answers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.set('answers', answers);
|
|
||||||
}
|
|
||||||
}.observes('answerType'),
|
|
||||||
|
|
||||||
validations: {
|
|
||||||
answerType: {
|
|
||||||
presence: true,
|
|
||||||
inclusion: {
|
|
||||||
in: ["YesNo", "YesNoMaybe", "FreeText"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
anonymousUser: {
|
|
||||||
presence: true
|
|
||||||
},
|
|
||||||
forceAnswer: {
|
|
||||||
presence: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
App.ErrorController = Ember.ObjectController.extend({
|
|
||||||
is404: function(){
|
|
||||||
return this.get('status') === 404;
|
|
||||||
}.property('status')
|
|
||||||
});
|
|
||||||
|
|
||||||
App.PollController = Ember.ObjectController.extend(Ember.Validations.Mixin);
|
|
||||||
App.PollController.reopen({
|
|
||||||
encryptionKey: '',
|
|
||||||
newUserName: '',
|
|
||||||
queryParams: ['encryptionKey'],
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
addNewUser: function(){
|
|
||||||
var newUser = {
|
|
||||||
name: this.get('newUserName'),
|
|
||||||
selections: this.get('newUserSelections')
|
|
||||||
};
|
|
||||||
|
|
||||||
// send new user to controller for saving
|
|
||||||
this.send('saveNewUser', newUser);
|
|
||||||
|
|
||||||
// clear input fields
|
|
||||||
this.set('newUserName', '');
|
|
||||||
this.get('newUserSelections').forEach(function(selection){
|
|
||||||
selection.set('value', '');
|
|
||||||
});
|
|
||||||
|
|
||||||
// reset validation erros
|
|
||||||
this.set('errors.newUserName', '');
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
* save a new user
|
|
||||||
*/
|
|
||||||
saveNewUser: function(user){
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
// create new user record in store
|
|
||||||
var newUser = this.store.createRecord('user', {
|
|
||||||
name: user.name,
|
|
||||||
creationDate: new Date(),
|
|
||||||
poll: this.get('model'),
|
|
||||||
selections: user.selections
|
|
||||||
});
|
|
||||||
|
|
||||||
// save new user
|
|
||||||
newUser.save().then(function(){
|
|
||||||
self.get('model.users').then(function(users){
|
|
||||||
// assign new user to poll
|
|
||||||
users.pushObject(newUser);
|
|
||||||
});
|
|
||||||
// reload as workaround for bug: duplicated records after save
|
|
||||||
self.get('model').reload();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
* evaluates poll data
|
|
||||||
* if free text answers are allowed evaluation is disabled
|
|
||||||
*/
|
|
||||||
evaluation: function() {
|
|
||||||
// disable evaluation if answer type is free text
|
|
||||||
if (this.get('answerType') === 'FreeText') {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
var evaluation = [],
|
|
||||||
options = [],
|
|
||||||
lookup = [];
|
|
||||||
|
|
||||||
// init options array
|
|
||||||
this.get('options').forEach(function(option, index){
|
|
||||||
options[index] = 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
// init array of evalutation objects
|
|
||||||
// create object for every possible answer
|
|
||||||
this.get('answers').forEach(function(answer){
|
|
||||||
evaluation.push({
|
|
||||||
id: answer.label,
|
|
||||||
label: answer.label,
|
|
||||||
options: jQuery.extend([], options)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// create object for no answer if answers are not forced
|
|
||||||
if (!this.get('forceAnswer')){
|
|
||||||
evaluation.push({
|
|
||||||
id: null,
|
|
||||||
label: 'no answer',
|
|
||||||
options: jQuery.extend([], options)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// create lookup array
|
|
||||||
evaluation.forEach(function(value, index){
|
|
||||||
lookup[value.id] = index;
|
|
||||||
});
|
|
||||||
|
|
||||||
// loop over all users
|
|
||||||
this.get('users').forEach(function(user){
|
|
||||||
// loop over all selections of the user
|
|
||||||
user.get('selections').forEach(function(selection, optionindex){
|
|
||||||
var answerindex;
|
|
||||||
|
|
||||||
// get answer index by lookup array
|
|
||||||
if (typeof lookup[selection.value] === 'undefined') {
|
|
||||||
answerindex = lookup[null];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
answerindex = lookup[selection.value];
|
|
||||||
}
|
|
||||||
|
|
||||||
// increment counter
|
|
||||||
try {
|
|
||||||
evaluation[answerindex]['options'][optionindex] = evaluation[answerindex]['options'][optionindex] + 1;
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return evaluation;
|
|
||||||
}.property('users.@each'),
|
|
||||||
|
|
||||||
/*
|
|
||||||
* returns true if user has selected an answer for every option provided
|
|
||||||
*/
|
|
||||||
everyOptionIsAnswered: function(){
|
|
||||||
try {
|
|
||||||
var newUserSelections = this.get('newUserSelections'),
|
|
||||||
allAnswered = true;
|
|
||||||
|
|
||||||
if (typeof newUserSelections === 'undefined') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
newUserSelections.forEach(function(item, index, enumerable){
|
|
||||||
if (Ember.isEmpty(item.value)) {
|
|
||||||
allAnswered = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return allAnswered;
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}.property('newUserSelections.@each.value'),
|
|
||||||
|
|
||||||
/*
|
|
||||||
* calculate colspan for a row which should use all columns in table
|
|
||||||
* used by evaluation row
|
|
||||||
*/
|
|
||||||
fullRowColspan: function(){
|
|
||||||
var colspan = this.get('options.length') + 2;
|
|
||||||
return colspan;
|
|
||||||
}.property('options.@each'),
|
|
||||||
|
|
||||||
/*
|
|
||||||
* switch isValid state
|
|
||||||
* is needed for disable submit button
|
|
||||||
*/
|
|
||||||
isNotValid: function(){
|
|
||||||
return !this.get('isValid');
|
|
||||||
}.property('isValid'),
|
|
||||||
|
|
||||||
// array to store selections of new user
|
|
||||||
newUserSelections: function(){
|
|
||||||
var newUserSelections = Ember.A(),
|
|
||||||
options = this.get('options');
|
|
||||||
|
|
||||||
options.forEach(function(){
|
|
||||||
var newSelection = Ember.Object.create({value: ''});
|
|
||||||
newUserSelections.pushObject(newSelection);
|
|
||||||
});
|
|
||||||
|
|
||||||
return newUserSelections;
|
|
||||||
}.property('options'),
|
|
||||||
|
|
||||||
pollUrl: function() {
|
|
||||||
return window.location.href;
|
|
||||||
}.property('currentPath', 'encryptionKey'),
|
|
||||||
|
|
||||||
updateEncryptionKey: function() {
|
|
||||||
// update encryption key
|
|
||||||
this.set('encryption.key', this.get('encryptionKey'));
|
|
||||||
|
|
||||||
// reload content to recalculate computed properties
|
|
||||||
// if encryption key was set before
|
|
||||||
if (this.get('encryption.isSet') === true) {
|
|
||||||
this.get('content').reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.set('encryption.isSet', true);
|
|
||||||
}.observes('encryptionKey'),
|
|
||||||
|
|
||||||
validations: {
|
|
||||||
everyOptionIsAnswered: {
|
|
||||||
/*
|
|
||||||
* validate if every option is answered
|
|
||||||
* if it's forced by poll settings (forceAnswer === true)
|
|
||||||
*
|
|
||||||
* using a computed property therefore which returns true / false
|
|
||||||
* in combinatoin with acceptance validator
|
|
||||||
*
|
|
||||||
* ToDo: Show validation errors
|
|
||||||
*/
|
|
||||||
acceptance: {
|
|
||||||
if: function(object, validator){
|
|
||||||
return object.get('forceAnswer');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
newUserName: {
|
|
||||||
presence: {
|
|
||||||
/*
|
|
||||||
* validate if a user name is given
|
|
||||||
* if it's forced by poll settings (anonymousUser === false)
|
|
||||||
*/
|
|
||||||
unless: function(object, validator){
|
|
||||||
/* have in mind that anonymousUser is undefined on init */
|
|
||||||
return object.get('anonymousUser');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
* have to manually rerun validation when encryption key is present in model
|
|
||||||
* otherwise ember-validation is not using correct values for properties in
|
|
||||||
* conditional validators
|
|
||||||
*/
|
|
||||||
validationsFixBug: function() {
|
|
||||||
if(!Ember.isEmpty(this.get('model.encryption.key'))) {
|
|
||||||
this.validate();
|
|
||||||
}
|
|
||||||
}.observes('model.encryption.key'),
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
* views
|
|
||||||
*/
|
|
||||||
App.CreateOptionsView = Ember.View.extend({
|
|
||||||
title: '',
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
moreOptions: function(){
|
|
||||||
// create new Option
|
|
||||||
this.get('controller.model.options').pushObject({title: ''});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
App.Datepicker = Em.View.extend({
|
|
||||||
classNames: ['datepicker'],
|
|
||||||
|
|
||||||
didInsertElement: function() {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
this._super();
|
|
||||||
|
|
||||||
$('.datepicker').datepicker({
|
|
||||||
format: "yyyy-mm-dd hh:mm:ss",
|
|
||||||
multidate: true,
|
|
||||||
multidateSeparator: ";",
|
|
||||||
calendarWeeks: true,
|
|
||||||
todayHighlight: true,
|
|
||||||
})
|
|
||||||
// bind date changes to option array in model
|
|
||||||
.on('changeDate', function(e){
|
|
||||||
var dates = e.dates,
|
|
||||||
newOptions = [];
|
|
||||||
|
|
||||||
// sort dates
|
|
||||||
dates.sort(function(a,b){
|
|
||||||
return new Date(a) - new Date(b);
|
|
||||||
});
|
|
||||||
|
|
||||||
// get array in correct form
|
|
||||||
dates.forEach(function(option) {
|
|
||||||
newOptions.pushObject({title: option});
|
|
||||||
});
|
|
||||||
|
|
||||||
// set options
|
|
||||||
self.set('_parentView.controller.options', newOptions);
|
|
||||||
});;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Handlebars helper
|
|
||||||
*/
|
|
||||||
Ember.Handlebars.registerBoundHelper('formattedDate', function(date, format) {
|
|
||||||
return moment(date).format(format);
|
|
||||||
});
|
|
1671
js/lib/bootstrap-datepicker.js
vendored
1671
js/lib/bootstrap-datepicker.js
vendored
File diff suppressed because it is too large
Load diff
1951
js/lib/bootstrap.js
vendored
1951
js/lib/bootstrap.js
vendored
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
12053
js/lib/ember-data.js
12053
js/lib/ember-data.js
File diff suppressed because it is too large
Load diff
|
@ -1,784 +0,0 @@
|
||||||
// ==========================================================================
|
|
||||||
// Project: Ember Validations
|
|
||||||
// Copyright: Copyright 2013 DockYard, LLC. and contributors.
|
|
||||||
// License: Licensed under MIT license (see license.js)
|
|
||||||
// ==========================================================================
|
|
||||||
|
|
||||||
|
|
||||||
// Version: 1.0.0.beta.2
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
Ember.Validations = Ember.Namespace.create({
|
|
||||||
VERSION: '1.0.0.beta.2'
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
Ember.Validations.messages = {
|
|
||||||
render: function(attribute, context) {
|
|
||||||
if (Ember.I18n) {
|
|
||||||
return Ember.I18n.t('errors.' + attribute, context);
|
|
||||||
} else {
|
|
||||||
var regex = new RegExp("{{(.*?)}}"),
|
|
||||||
attributeName = "";
|
|
||||||
if (regex.test(this.defaults[attribute])) {
|
|
||||||
attributeName = regex.exec(this.defaults[attribute])[1];
|
|
||||||
}
|
|
||||||
return this.defaults[attribute].replace(regex, context[attributeName]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
defaults: {
|
|
||||||
inclusion: "is not included in the list",
|
|
||||||
exclusion: "is reserved",
|
|
||||||
invalid: "is invalid",
|
|
||||||
confirmation: "doesn't match {{attribute}}",
|
|
||||||
accepted: "must be accepted",
|
|
||||||
empty: "can't be empty",
|
|
||||||
blank: "can't be blank",
|
|
||||||
present: "must be blank",
|
|
||||||
tooLong: "is too long (maximum is {{count}} characters)",
|
|
||||||
tooShort: "is too short (minimum is {{count}} characters)",
|
|
||||||
wrongLength: "is the wrong length (should be {{count}} characters)",
|
|
||||||
notANumber: "is not a number",
|
|
||||||
notAnInteger: "must be an integer",
|
|
||||||
greaterThan: "must be greater than {{count}}",
|
|
||||||
greaterThanOrEqualTo: "must be greater than or equal to {{count}}",
|
|
||||||
equalTo: "must be equal to {{count}}",
|
|
||||||
lessThan: "must be less than {{count}}",
|
|
||||||
lessThanOrEqualTo: "must be less than or equal to {{count}}",
|
|
||||||
otherThan: "must be other than {{count}}",
|
|
||||||
odd: "must be odd",
|
|
||||||
even: "must be even",
|
|
||||||
url: "is not a valid URL"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
Ember.Validations.Errors = Ember.Object.extend({
|
|
||||||
unknownProperty: function(property) {
|
|
||||||
this.set(property, Ember.makeArray());
|
|
||||||
return this.get(property);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
var setValidityMixin = Ember.Mixin.create({
|
|
||||||
isValid: function() {
|
|
||||||
return this.get('validators').compact().filterBy('isValid', false).get('length') === 0;
|
|
||||||
}.property('validators.@each.isValid'),
|
|
||||||
isInvalid: Ember.computed.not('isValid')
|
|
||||||
});
|
|
||||||
|
|
||||||
var pushValidatableObject = function(model, property) {
|
|
||||||
var content = model.get(property);
|
|
||||||
|
|
||||||
model.removeObserver(property, pushValidatableObject);
|
|
||||||
if (Ember.isArray(content)) {
|
|
||||||
model.validators.pushObject(ArrayValidatorProxy.create({model: model, property: property, contentBinding: 'model.' + property}));
|
|
||||||
} else {
|
|
||||||
model.validators.pushObject(content);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var findValidator = function(validator) {
|
|
||||||
var klass = validator.classify();
|
|
||||||
return Ember.Validations.validators.local[klass] || Ember.Validations.validators.remote[klass];
|
|
||||||
};
|
|
||||||
|
|
||||||
var ArrayValidatorProxy = Ember.ArrayProxy.extend(setValidityMixin, {
|
|
||||||
validate: function() {
|
|
||||||
return this._validate();
|
|
||||||
},
|
|
||||||
_validate: function() {
|
|
||||||
var promises = this.get('content').invoke('_validate').without(undefined);
|
|
||||||
return Ember.RSVP.all(promises);
|
|
||||||
}.on('init'),
|
|
||||||
validators: Ember.computed.alias('content')
|
|
||||||
});
|
|
||||||
|
|
||||||
Ember.Validations.Mixin = Ember.Mixin.create(setValidityMixin, {
|
|
||||||
init: function() {
|
|
||||||
this._super();
|
|
||||||
this.errors = Ember.Validations.Errors.create();
|
|
||||||
this._dependentValidationKeys = {};
|
|
||||||
this.validators = Ember.makeArray();
|
|
||||||
if (this.get('validations') === undefined) {
|
|
||||||
this.validations = {};
|
|
||||||
}
|
|
||||||
this.buildValidators();
|
|
||||||
this.validators.forEach(function(validator) {
|
|
||||||
validator.addObserver('errors.[]', this, function(sender, key, value, context, rev) {
|
|
||||||
var errors = Ember.makeArray();
|
|
||||||
this.validators.forEach(function(validator) {
|
|
||||||
if (validator.property === sender.property) {
|
|
||||||
errors = errors.concat(validator.errors);
|
|
||||||
}
|
|
||||||
}, this);
|
|
||||||
this.set('errors.' + sender.property, errors);
|
|
||||||
});
|
|
||||||
}, this);
|
|
||||||
},
|
|
||||||
buildValidators: function() {
|
|
||||||
var property, validator;
|
|
||||||
|
|
||||||
for (property in this.validations) {
|
|
||||||
if (this.validations[property].constructor === Object) {
|
|
||||||
this.buildRuleValidator(property);
|
|
||||||
} else {
|
|
||||||
this.buildObjectValidator(property);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
buildRuleValidator: function(property) {
|
|
||||||
var validator;
|
|
||||||
for (validator in this.validations[property]) {
|
|
||||||
if (this.validations[property].hasOwnProperty(validator)) {
|
|
||||||
this.validators.pushObject(findValidator(validator).create({model: this, property: property, options: this.validations[property][validator]}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
buildObjectValidator: function(property) {
|
|
||||||
if (Ember.isNone(this.get(property))) {
|
|
||||||
this.addObserver(property, this, pushValidatableObject);
|
|
||||||
} else {
|
|
||||||
pushValidatableObject(this, property);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
validate: function() {
|
|
||||||
var self = this;
|
|
||||||
return this._validate().then(function(vals) {
|
|
||||||
var errors = self.get('errors');
|
|
||||||
if (vals.contains(false)) {
|
|
||||||
return Ember.RSVP.reject(errors);
|
|
||||||
}
|
|
||||||
return errors;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
_validate: function() {
|
|
||||||
var promises = this.validators.invoke('_validate').without(undefined);
|
|
||||||
return Ember.RSVP.all(promises);
|
|
||||||
}.on('init')
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
Ember.Validations.patterns = Ember.Namespace.create({
|
|
||||||
numericality: /^(-|\+)?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d*)?$/,
|
|
||||||
blank: /^\s*$/
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
Ember.Validations.validators = Ember.Namespace.create();
|
|
||||||
Ember.Validations.validators.local = Ember.Namespace.create();
|
|
||||||
Ember.Validations.validators.remote = Ember.Namespace.create();
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
Ember.Validations.validators.Base = Ember.Object.extend({
|
|
||||||
init: function() {
|
|
||||||
this.set('errors', Ember.makeArray());
|
|
||||||
this._dependentValidationKeys = Ember.makeArray();
|
|
||||||
this.conditionals = {
|
|
||||||
'if': this.get('options.if'),
|
|
||||||
unless: this.get('options.unless')
|
|
||||||
};
|
|
||||||
this.model.addObserver(this.property, this, this._validate);
|
|
||||||
},
|
|
||||||
addObserversForDependentValidationKeys: function() {
|
|
||||||
this._dependentValidationKeys.forEach(function(key) {
|
|
||||||
this.model.addObserver(key, this, this._validate);
|
|
||||||
}, this);
|
|
||||||
}.on('init'),
|
|
||||||
pushDependentValidationKeyToModel: function() {
|
|
||||||
var model = this.get('model');
|
|
||||||
if (model._dependentValidationKeys[this.property] === undefined) {
|
|
||||||
model._dependentValidationKeys[this.property] = Ember.makeArray();
|
|
||||||
}
|
|
||||||
model._dependentValidationKeys[this.property].addObjects(this._dependentValidationKeys);
|
|
||||||
}.on('init'),
|
|
||||||
call: function () {
|
|
||||||
throw 'Not implemented!';
|
|
||||||
},
|
|
||||||
unknownProperty: function(key) {
|
|
||||||
var model = this.get('model');
|
|
||||||
if (model) {
|
|
||||||
return model.get(key);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isValid: Ember.computed.empty('errors.[]'),
|
|
||||||
validate: function() {
|
|
||||||
var self = this;
|
|
||||||
return this._validate().then(function(success) {
|
|
||||||
// Convert validation failures to rejects.
|
|
||||||
var errors = self.get('model.errors');
|
|
||||||
if (success) {
|
|
||||||
return errors;
|
|
||||||
} else {
|
|
||||||
return Ember.RSVP.reject(errors);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
_validate: function() {
|
|
||||||
this.errors.clear();
|
|
||||||
if (this.canValidate()) {
|
|
||||||
this.call();
|
|
||||||
}
|
|
||||||
if (this.get('isValid')) {
|
|
||||||
return Ember.RSVP.resolve(true);
|
|
||||||
} else {
|
|
||||||
return Ember.RSVP.resolve(false);
|
|
||||||
}
|
|
||||||
}.on('init'),
|
|
||||||
canValidate: function() {
|
|
||||||
if (typeof(this.conditionals) === 'object') {
|
|
||||||
if (this.conditionals['if']) {
|
|
||||||
if (typeof(this.conditionals['if']) === 'function') {
|
|
||||||
return this.conditionals['if'](this.model, this.property);
|
|
||||||
} else if (typeof(this.conditionals['if']) === 'string') {
|
|
||||||
if (typeof(this.model[this.conditionals['if']]) === 'function') {
|
|
||||||
return this.model[this.conditionals['if']]();
|
|
||||||
} else {
|
|
||||||
return this.model.get(this.conditionals['if']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (this.conditionals.unless) {
|
|
||||||
if (typeof(this.conditionals.unless) === 'function') {
|
|
||||||
return !this.conditionals.unless(this.model, this.property);
|
|
||||||
} else if (typeof(this.conditionals.unless) === 'string') {
|
|
||||||
if (typeof(this.model[this.conditionals.unless]) === 'function') {
|
|
||||||
return !this.model[this.conditionals.unless]();
|
|
||||||
} else {
|
|
||||||
return !this.model.get(this.conditionals.unless);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
Ember.Validations.validators.local.Absence = Ember.Validations.validators.Base.extend({
|
|
||||||
init: function() {
|
|
||||||
this._super();
|
|
||||||
/*jshint expr:true*/
|
|
||||||
if (this.options === true) {
|
|
||||||
this.set('options', {});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.options.message === undefined) {
|
|
||||||
this.set('options.message', Ember.Validations.messages.render('present', this.options));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
call: function() {
|
|
||||||
if (!Ember.isEmpty(this.model.get(this.property))) {
|
|
||||||
this.errors.pushObject(this.options.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
Ember.Validations.validators.local.Acceptance = Ember.Validations.validators.Base.extend({
|
|
||||||
init: function() {
|
|
||||||
this._super();
|
|
||||||
/*jshint expr:true*/
|
|
||||||
if (this.options === true) {
|
|
||||||
this.set('options', {});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.options.message === undefined) {
|
|
||||||
this.set('options.message', Ember.Validations.messages.render('accepted', this.options));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
call: function() {
|
|
||||||
if (this.options.accept) {
|
|
||||||
if (this.model.get(this.property) !== this.options.accept) {
|
|
||||||
this.errors.pushObject(this.options.message);
|
|
||||||
}
|
|
||||||
} else if (this.model.get(this.property) !== '1' && this.model.get(this.property) !== 1 && this.model.get(this.property) !== true) {
|
|
||||||
this.errors.pushObject(this.options.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
Ember.Validations.validators.local.Confirmation = Ember.Validations.validators.Base.extend({
|
|
||||||
init: function() {
|
|
||||||
this.originalProperty = this.property;
|
|
||||||
this.property = this.property + 'Confirmation';
|
|
||||||
this._super();
|
|
||||||
this._dependentValidationKeys.pushObject(this.originalProperty);
|
|
||||||
/*jshint expr:true*/
|
|
||||||
if (this.options === true) {
|
|
||||||
this.set('options', { attribute: this.originalProperty });
|
|
||||||
this.set('options', { message: Ember.Validations.messages.render('confirmation', this.options) });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
call: function() {
|
|
||||||
if (this.model.get(this.originalProperty) !== this.model.get(this.property)) {
|
|
||||||
this.errors.pushObject(this.options.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
Ember.Validations.validators.local.Exclusion = Ember.Validations.validators.Base.extend({
|
|
||||||
init: function() {
|
|
||||||
this._super();
|
|
||||||
if (this.options.constructor === Array) {
|
|
||||||
this.set('options', { 'in': this.options });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.options.message === undefined) {
|
|
||||||
this.set('options.message', Ember.Validations.messages.render('exclusion', this.options));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
call: function() {
|
|
||||||
/*jshint expr:true*/
|
|
||||||
var message, lower, upper;
|
|
||||||
|
|
||||||
if (Ember.isEmpty(this.model.get(this.property))) {
|
|
||||||
if (this.options.allowBlank === undefined) {
|
|
||||||
this.errors.pushObject(this.options.message);
|
|
||||||
}
|
|
||||||
} else if (this.options['in']) {
|
|
||||||
if (Ember.$.inArray(this.model.get(this.property), this.options['in']) !== -1) {
|
|
||||||
this.errors.pushObject(this.options.message);
|
|
||||||
}
|
|
||||||
} else if (this.options.range) {
|
|
||||||
lower = this.options.range[0];
|
|
||||||
upper = this.options.range[1];
|
|
||||||
|
|
||||||
if (this.model.get(this.property) >= lower && this.model.get(this.property) <= upper) {
|
|
||||||
this.errors.pushObject(this.options.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
Ember.Validations.validators.local.Format = Ember.Validations.validators.Base.extend({
|
|
||||||
init: function() {
|
|
||||||
this._super();
|
|
||||||
if (this.options.constructor === RegExp) {
|
|
||||||
this.set('options', { 'with': this.options });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.options.message === undefined) {
|
|
||||||
this.set('options.message', Ember.Validations.messages.render('invalid', this.options));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
call: function() {
|
|
||||||
if (Ember.isEmpty(this.model.get(this.property))) {
|
|
||||||
if (this.options.allowBlank === undefined) {
|
|
||||||
this.errors.pushObject(this.options.message);
|
|
||||||
}
|
|
||||||
} else if (this.options['with'] && !this.options['with'].test(this.model.get(this.property))) {
|
|
||||||
this.errors.pushObject(this.options.message);
|
|
||||||
} else if (this.options.without && this.options.without.test(this.model.get(this.property))) {
|
|
||||||
this.errors.pushObject(this.options.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
Ember.Validations.validators.local.Inclusion = Ember.Validations.validators.Base.extend({
|
|
||||||
init: function() {
|
|
||||||
this._super();
|
|
||||||
if (this.options.constructor === Array) {
|
|
||||||
this.set('options', { 'in': this.options });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.options.message === undefined) {
|
|
||||||
this.set('options.message', Ember.Validations.messages.render('inclusion', this.options));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
call: function() {
|
|
||||||
var message, lower, upper;
|
|
||||||
if (Ember.isEmpty(this.model.get(this.property))) {
|
|
||||||
if (this.options.allowBlank === undefined) {
|
|
||||||
this.errors.pushObject(this.options.message);
|
|
||||||
}
|
|
||||||
} else if (this.options['in']) {
|
|
||||||
if (Ember.$.inArray(this.model.get(this.property), this.options['in']) === -1) {
|
|
||||||
this.errors.pushObject(this.options.message);
|
|
||||||
}
|
|
||||||
} else if (this.options.range) {
|
|
||||||
lower = this.options.range[0];
|
|
||||||
upper = this.options.range[1];
|
|
||||||
|
|
||||||
if (this.model.get(this.property) < lower || this.model.get(this.property) > upper) {
|
|
||||||
this.errors.pushObject(this.options.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
Ember.Validations.validators.local.Length = Ember.Validations.validators.Base.extend({
|
|
||||||
init: function() {
|
|
||||||
var index, key;
|
|
||||||
this._super();
|
|
||||||
/*jshint expr:true*/
|
|
||||||
if (typeof(this.options) === 'number') {
|
|
||||||
this.set('options', { 'is': this.options });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.options.messages === undefined) {
|
|
||||||
this.set('options.messages', {});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (index = 0; index < this.messageKeys().length; index++) {
|
|
||||||
key = this.messageKeys()[index];
|
|
||||||
if (this.options[key] !== undefined && this.options[key].constructor === String) {
|
|
||||||
this.model.addObserver(this.options[key], this, this._validate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.options.tokenizer = this.options.tokenizer || function(value) { return value.split(''); };
|
|
||||||
// if (typeof(this.options.tokenizer) === 'function') {
|
|
||||||
// debugger;
|
|
||||||
// // this.tokenizedLength = new Function('value', 'return '
|
|
||||||
// } else {
|
|
||||||
// this.tokenizedLength = new Function('value', 'return (value || "").' + (this.options.tokenizer || 'split("")') + '.length');
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
CHECKS: {
|
|
||||||
'is' : '==',
|
|
||||||
'minimum' : '>=',
|
|
||||||
'maximum' : '<='
|
|
||||||
},
|
|
||||||
MESSAGES: {
|
|
||||||
'is' : 'wrongLength',
|
|
||||||
'minimum' : 'tooShort',
|
|
||||||
'maximum' : 'tooLong'
|
|
||||||
},
|
|
||||||
getValue: function(key) {
|
|
||||||
if (this.options[key].constructor === String) {
|
|
||||||
return this.model.get(this.options[key]) || 0;
|
|
||||||
} else {
|
|
||||||
return this.options[key];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
messageKeys: function() {
|
|
||||||
return Ember.keys(this.MESSAGES);
|
|
||||||
},
|
|
||||||
checkKeys: function() {
|
|
||||||
return Ember.keys(this.CHECKS);
|
|
||||||
},
|
|
||||||
renderMessageFor: function(key) {
|
|
||||||
var options = {count: this.getValue(key)}, _key;
|
|
||||||
for (_key in this.options) {
|
|
||||||
options[_key] = this.options[_key];
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.options.messages[this.MESSAGES[key]] || Ember.Validations.messages.render(this.MESSAGES[key], options);
|
|
||||||
},
|
|
||||||
renderBlankMessage: function() {
|
|
||||||
if (this.options.is) {
|
|
||||||
return this.renderMessageFor('is');
|
|
||||||
} else if (this.options.minimum) {
|
|
||||||
return this.renderMessageFor('minimum');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
call: function() {
|
|
||||||
var check, fn, message, operator, key;
|
|
||||||
|
|
||||||
if (Ember.isEmpty(this.model.get(this.property))) {
|
|
||||||
if (this.options.allowBlank === undefined && (this.options.is || this.options.minimum)) {
|
|
||||||
this.errors.pushObject(this.renderBlankMessage());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (key in this.CHECKS) {
|
|
||||||
operator = this.CHECKS[key];
|
|
||||||
if (!this.options[key]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn = new Function('return ' + this.options.tokenizer(this.model.get(this.property)).length + ' ' + operator + ' ' + this.getValue(key));
|
|
||||||
if (!fn()) {
|
|
||||||
this.errors.pushObject(this.renderMessageFor(key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
Ember.Validations.validators.local.Numericality = Ember.Validations.validators.Base.extend({
|
|
||||||
init: function() {
|
|
||||||
/*jshint expr:true*/
|
|
||||||
var index, keys, key;
|
|
||||||
this._super();
|
|
||||||
|
|
||||||
if (this.options === true) {
|
|
||||||
this.options = {};
|
|
||||||
} else if (this.options.constructor === String) {
|
|
||||||
key = this.options;
|
|
||||||
this.options = {};
|
|
||||||
this.options[key] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.options.messages === undefined || this.options.messages.numericality === undefined) {
|
|
||||||
this.options.messages = this.options.messages || {};
|
|
||||||
this.options.messages = { numericality: Ember.Validations.messages.render('notANumber', this.options) };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.options.onlyInteger !== undefined && this.options.messages.onlyInteger === undefined) {
|
|
||||||
this.options.messages.onlyInteger = Ember.Validations.messages.render('notAnInteger', this.options);
|
|
||||||
}
|
|
||||||
|
|
||||||
keys = Ember.keys(this.CHECKS).concat(['odd', 'even']);
|
|
||||||
for(index = 0; index < keys.length; index++) {
|
|
||||||
key = keys[index];
|
|
||||||
|
|
||||||
if (isNaN(this.options[key])) {
|
|
||||||
this.model.addObserver(this.options[key], this, this._validate);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.options[key] !== undefined && this.options.messages[key] === undefined) {
|
|
||||||
if (Ember.$.inArray(key, Ember.keys(this.CHECKS)) !== -1) {
|
|
||||||
this.options.count = this.options[key];
|
|
||||||
}
|
|
||||||
this.options.messages[key] = Ember.Validations.messages.render(key, this.options);
|
|
||||||
if (this.options.count !== undefined) {
|
|
||||||
delete this.options.count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
CHECKS: {
|
|
||||||
equalTo :'===',
|
|
||||||
greaterThan : '>',
|
|
||||||
greaterThanOrEqualTo : '>=',
|
|
||||||
lessThan : '<',
|
|
||||||
lessThanOrEqualTo : '<='
|
|
||||||
},
|
|
||||||
call: function() {
|
|
||||||
var check, checkValue, fn, form, operator, val;
|
|
||||||
|
|
||||||
if (Ember.isEmpty(this.model.get(this.property))) {
|
|
||||||
if (this.options.allowBlank === undefined) {
|
|
||||||
this.errors.pushObject(this.options.messages.numericality);
|
|
||||||
}
|
|
||||||
} else if (!Ember.Validations.patterns.numericality.test(this.model.get(this.property))) {
|
|
||||||
this.errors.pushObject(this.options.messages.numericality);
|
|
||||||
} else if (this.options.onlyInteger === true && !(/^[+\-]?\d+$/.test(this.model.get(this.property)))) {
|
|
||||||
this.errors.pushObject(this.options.messages.onlyInteger);
|
|
||||||
} else if (this.options.odd && parseInt(this.model.get(this.property), 10) % 2 === 0) {
|
|
||||||
this.errors.pushObject(this.options.messages.odd);
|
|
||||||
} else if (this.options.even && parseInt(this.model.get(this.property), 10) % 2 !== 0) {
|
|
||||||
this.errors.pushObject(this.options.messages.even);
|
|
||||||
} else {
|
|
||||||
for (check in this.CHECKS) {
|
|
||||||
operator = this.CHECKS[check];
|
|
||||||
|
|
||||||
if (this.options[check] === undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isNaN(parseFloat(this.options[check])) && isFinite(this.options[check])) {
|
|
||||||
checkValue = this.options[check];
|
|
||||||
} else if (this.model.get(this.options[check]) !== undefined) {
|
|
||||||
checkValue = this.model.get(this.options[check]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn = new Function('return ' + this.model.get(this.property) + ' ' + operator + ' ' + checkValue);
|
|
||||||
|
|
||||||
if (!fn()) {
|
|
||||||
this.errors.pushObject(this.options.messages[check]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
Ember.Validations.validators.local.Presence = Ember.Validations.validators.Base.extend({
|
|
||||||
init: function() {
|
|
||||||
this._super();
|
|
||||||
/*jshint expr:true*/
|
|
||||||
if (this.options === true) {
|
|
||||||
this.options = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.options.message === undefined) {
|
|
||||||
this.options.message = Ember.Validations.messages.render('blank', this.options);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
call: function() {
|
|
||||||
if (Ember.isEmpty(this.model.get(this.property))) {
|
|
||||||
this.errors.pushObject(this.options.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
Ember.Validations.validators.local.Url = Ember.Validations.validators.Base.extend({
|
|
||||||
regexp: null,
|
|
||||||
regexp_ip: null,
|
|
||||||
|
|
||||||
init: function() {
|
|
||||||
this._super();
|
|
||||||
|
|
||||||
if (this.get('options.message') === undefined) {
|
|
||||||
this.set('options.message', Ember.Validations.messages.render('url', this.options));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.get('options.protocols') === undefined) {
|
|
||||||
this.set('options.protocols', ['http', 'https']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regular Expression Parts
|
|
||||||
var dec_octet = '(25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9]|[1-9][0-9]|[0-9])'; // 0-255
|
|
||||||
var ipaddress = '(' + dec_octet + '(\\.' + dec_octet + '){3})';
|
|
||||||
var hostname = '([a-zA-Z0-9\\-]+\\.)+([a-zA-Z]{2,})';
|
|
||||||
var encoded = '%[0-9a-fA-F]{2}';
|
|
||||||
var characters = 'a-zA-Z0-9$\\-_.+!*\'(),;:@&=';
|
|
||||||
var segment = '([' + characters + ']|' + encoded + ')*';
|
|
||||||
|
|
||||||
// Build Regular Expression
|
|
||||||
var regex_str = '^';
|
|
||||||
|
|
||||||
if (this.get('options.domainOnly') === true) {
|
|
||||||
regex_str += hostname;
|
|
||||||
} else {
|
|
||||||
regex_str += '(' + this.get('options.protocols').join('|') + '):\\/\\/'; // Protocol
|
|
||||||
|
|
||||||
// Username and password
|
|
||||||
if (this.get('options.allowUserPass') === true) {
|
|
||||||
regex_str += '(([a-zA-Z0-9$\\-_.+!*\'(),;:&=]|' + encoded + ')+@)?'; // Username & passwords
|
|
||||||
}
|
|
||||||
|
|
||||||
// IP Addresses?
|
|
||||||
if (this.get('options.allowIp') === true) {
|
|
||||||
regex_str += '(' + hostname + '|' + ipaddress + ')'; // Hostname OR IP
|
|
||||||
} else {
|
|
||||||
regex_str += '(' + hostname + ')'; // Hostname only
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ports
|
|
||||||
if (this.get('options.allowPort') === true) {
|
|
||||||
regex_str += '(:[0-9]+)?'; // Port
|
|
||||||
}
|
|
||||||
|
|
||||||
regex_str += '(\\/';
|
|
||||||
regex_str += '(' + segment + '(\\/' + segment + ')*)?'; // Path
|
|
||||||
regex_str += '(\\?' + '([' + characters + '/?]|' + encoded + ')*)?'; // Query
|
|
||||||
regex_str += '(\\#' + '([' + characters + '/?]|' + encoded + ')*)?'; // Anchor
|
|
||||||
regex_str += ')?';
|
|
||||||
}
|
|
||||||
|
|
||||||
regex_str += '$';
|
|
||||||
|
|
||||||
// RegExp
|
|
||||||
this.regexp = new RegExp(regex_str);
|
|
||||||
this.regexp_ip = new RegExp(ipaddress);
|
|
||||||
},
|
|
||||||
call: function() {
|
|
||||||
var url = this.model.get(this.property);
|
|
||||||
|
|
||||||
if (Ember.isEmpty(url)) {
|
|
||||||
if (this.get('options.allowBlank') !== true) {
|
|
||||||
this.errors.pushObject(this.get('options.message'));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (this.get('options.allowIp') !== true) {
|
|
||||||
if (this.regexp_ip.test(url)) {
|
|
||||||
this.errors.pushObject(this.get('options.message'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.regexp.test(url)) {
|
|
||||||
this.errors.pushObject(this.get('options.message'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (typeof location !== 'undefined' && (location.hostname === 'localhost' || location.hostname === '127.0.0.1')) {
|
|
||||||
Ember.Logger.warn("You are running a production build of Ember on localhost and won't receive detailed error messages. "+
|
|
||||||
"If you want full error messages please use the non-minified build provided on the Ember website.");
|
|
||||||
}
|
|
47041
js/lib/ember.js
47041
js/lib/ember.js
File diff suppressed because it is too large
Load diff
|
@ -1,956 +0,0 @@
|
||||||
(function() {
|
|
||||||
|
|
||||||
var _ref;
|
|
||||||
|
|
||||||
Ember.Forms = Ember.Namespace.create();
|
|
||||||
|
|
||||||
Ember.Forms.VERSION = '0.0.1';
|
|
||||||
|
|
||||||
if ((_ref = Ember.libraries) != null) {
|
|
||||||
_ref.register('Ember Forms', Ember.Forms.VERSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
Ember.Forms.utils = {};
|
|
||||||
|
|
||||||
Ember.Forms.utils.createBoundSwitchAccessor = function(switchValue, myProperty, myDefault) {
|
|
||||||
if (myDefault == null) {
|
|
||||||
myDefault = 'default';
|
|
||||||
}
|
|
||||||
return (function(key, value) {
|
|
||||||
if (arguments.length === 2) {
|
|
||||||
this.set(myProperty, (value ? switchValue : myDefault));
|
|
||||||
}
|
|
||||||
return this.get(myProperty) === switchValue;
|
|
||||||
}).property(myProperty);
|
|
||||||
};
|
|
||||||
|
|
||||||
Ember.Forms.utils.namelize = function(string) {
|
|
||||||
return string.underscore().split('_').join(' ').capitalize();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
|
|
||||||
/***
|
|
||||||
Mixin that should be applied for all controls
|
|
||||||
*/
|
|
||||||
Ember.Forms.ControlMixin = Ember.Mixin.create({
|
|
||||||
classNameBindings: ['class'],
|
|
||||||
"class": 'form-control',
|
|
||||||
init: function() {
|
|
||||||
this._super();
|
|
||||||
return Ember.Binding.from("model." + (this.get('propertyName'))).to('value').connect(this);
|
|
||||||
},
|
|
||||||
hasValue: (function() {
|
|
||||||
return this.get('value') !== null;
|
|
||||||
}).property('value').readOnly()
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
A mixin that enriches a view that is attached to a model property.
|
|
||||||
|
|
||||||
The property name by default is taken from the parentView unless explictly
|
|
||||||
defined in the `property` variable.
|
|
||||||
|
|
||||||
This mixin also binds a property named `errors` to the model's `model.errors.@propertyName` array
|
|
||||||
*/
|
|
||||||
Em.Forms.HasPropertyMixin = Em.Mixin.create({
|
|
||||||
property: void 0,
|
|
||||||
propertyName: (function() {
|
|
||||||
if (this.get('property')) {
|
|
||||||
return this.get('property');
|
|
||||||
} else if (this.get('parentView.property')) {
|
|
||||||
return this.get('parentView.property');
|
|
||||||
} else {
|
|
||||||
return Em.assert(false, 'Property could not be found.');
|
|
||||||
}
|
|
||||||
}).property('parentView.property'),
|
|
||||||
init: function() {
|
|
||||||
this._super();
|
|
||||||
return Em.Binding.from('model.errors.' + this.get('propertyName')).to('errors').connect(this);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
A mixin that enriches a view that is attached to a model property that has validation
|
|
||||||
support.
|
|
||||||
|
|
||||||
This mixin binds a property named `errors` to the model's `model.errors.@propertyName` array
|
|
||||||
*/
|
|
||||||
Em.Forms.HasPropertyValidationMixin = Em.Mixin.create({
|
|
||||||
init: function() {
|
|
||||||
this._super();
|
|
||||||
Em.assert(!Em.isNone(this.get('propertyName')), 'propertyName is required.');
|
|
||||||
return Em.Binding.from('model.errors.' + this.get('propertyName')).to('errors').connect(this);
|
|
||||||
},
|
|
||||||
status: (function() {
|
|
||||||
if (this.get('errors.length')) {
|
|
||||||
return 'error';
|
|
||||||
} else {
|
|
||||||
return 'success';
|
|
||||||
}
|
|
||||||
}).property('errors.length')
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Find the form of the view that merges this mixin
|
|
||||||
*/
|
|
||||||
Ember.Forms.InFormMixin = Em.Mixin.create({
|
|
||||||
form: (function() {
|
|
||||||
var parentView;
|
|
||||||
parentView = this.get('parentView');
|
|
||||||
while (parentView) {
|
|
||||||
if (parentView.get('tagName') === 'form') {
|
|
||||||
return parentView;
|
|
||||||
}
|
|
||||||
parentView = parentView.get('parentView');
|
|
||||||
}
|
|
||||||
return Ember.assert(false, 'Cannot find form');
|
|
||||||
}).property('parentView'),
|
|
||||||
model: (function() {
|
|
||||||
return this.get('form.model');
|
|
||||||
}).property('form')
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
Ember.TEMPLATES["components/form-control-help"] = Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) {
|
|
||||||
this.compilerInfo = [4,'>= 1.0.0'];
|
|
||||||
helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {};
|
|
||||||
var stack1;
|
|
||||||
|
|
||||||
|
|
||||||
stack1 = helpers._triageMustache.call(depth0, "helpText", {hash:{},hashTypes:{},hashContexts:{},contexts:[depth0],types:["ID"],data:data});
|
|
||||||
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
|
|
||||||
else { data.buffer.push(''); }
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
Ember.TEMPLATES["components/form-group"] = Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) {
|
|
||||||
this.compilerInfo = [4,'>= 1.0.0'];
|
|
||||||
helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {};
|
|
||||||
var stack1, escapeExpression=this.escapeExpression, helperMissing=helpers.helperMissing, self=this;
|
|
||||||
|
|
||||||
function program1(depth0,data) {
|
|
||||||
|
|
||||||
var buffer = '', helper, options;
|
|
||||||
data.buffer.push("\n <div ");
|
|
||||||
data.buffer.push(escapeExpression(helpers['bind-attr'].call(depth0, {hash:{
|
|
||||||
'class': ("wrapperClass")
|
|
||||||
},hashTypes:{'class': "ID"},hashContexts:{'class': depth0},contexts:[],types:[],data:data})));
|
|
||||||
data.buffer.push(">\n ");
|
|
||||||
data.buffer.push(escapeExpression((helper = helpers.partial || (depth0 && depth0.partial),options={hash:{},hashTypes:{},hashContexts:{},contexts:[depth0],types:["STRING"],data:data},helper ? helper.call(depth0, "components/formgroup/form-group", options) : helperMissing.call(depth0, "partial", "components/formgroup/form-group", options))));
|
|
||||||
data.buffer.push("\n </div>\n");
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
function program3(depth0,data) {
|
|
||||||
|
|
||||||
var buffer = '', helper, options;
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
data.buffer.push(escapeExpression((helper = helpers.partial || (depth0 && depth0.partial),options={hash:{},hashTypes:{},hashContexts:{},contexts:[depth0],types:["STRING"],data:data},helper ? helper.call(depth0, "components/formgroup/form-group", options) : helperMissing.call(depth0, "partial", "components/formgroup/form-group", options))));
|
|
||||||
data.buffer.push("\n");
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
stack1 = helpers['if'].call(depth0, "wrapperClass", {hash:{},hashTypes:{},hashContexts:{},inverse:self.program(3, program3, data),fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],data:data});
|
|
||||||
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
|
|
||||||
else { data.buffer.push(''); }
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
Ember.TEMPLATES["components/form-label"] = Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) {
|
|
||||||
this.compilerInfo = [4,'>= 1.0.0'];
|
|
||||||
helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {};
|
|
||||||
var buffer = '', stack1;
|
|
||||||
|
|
||||||
|
|
||||||
stack1 = helpers._triageMustache.call(depth0, "yield", {hash:{},hashTypes:{},hashContexts:{},contexts:[depth0],types:["ID"],data:data});
|
|
||||||
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
|
|
||||||
data.buffer.push("\n");
|
|
||||||
stack1 = helpers._triageMustache.call(depth0, "text", {hash:{},hashTypes:{},hashContexts:{},contexts:[depth0],types:["ID"],data:data});
|
|
||||||
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
|
|
||||||
return buffer;
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
Ember.TEMPLATES["components/form-submit-button"] = Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) {
|
|
||||||
this.compilerInfo = [4,'>= 1.0.0'];
|
|
||||||
helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {};
|
|
||||||
var buffer = '', stack1, escapeExpression=this.escapeExpression, self=this;
|
|
||||||
|
|
||||||
function program1(depth0,data) {
|
|
||||||
|
|
||||||
var buffer = '', stack1;
|
|
||||||
data.buffer.push("\n <div ");
|
|
||||||
data.buffer.push(escapeExpression(helpers['bind-attr'].call(depth0, {hash:{
|
|
||||||
'class': ("horiClass")
|
|
||||||
},hashTypes:{'class': "ID"},hashContexts:{'class': depth0},contexts:[],types:[],data:data})));
|
|
||||||
data.buffer.push(">\n <button ");
|
|
||||||
data.buffer.push(escapeExpression(helpers['bind-attr'].call(depth0, {hash:{
|
|
||||||
'class': ("classes")
|
|
||||||
},hashTypes:{'class': "ID"},hashContexts:{'class': depth0},contexts:[],types:[],data:data})));
|
|
||||||
data.buffer.push(" ");
|
|
||||||
data.buffer.push(escapeExpression(helpers['bind-attr'].call(depth0, {hash:{
|
|
||||||
'disabled': ("disabled")
|
|
||||||
},hashTypes:{'disabled': "ID"},hashContexts:{'disabled': depth0},contexts:[],types:[],data:data})));
|
|
||||||
data.buffer.push(">");
|
|
||||||
stack1 = helpers._triageMustache.call(depth0, "text", {hash:{},hashTypes:{},hashContexts:{},contexts:[depth0],types:["ID"],data:data});
|
|
||||||
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
|
|
||||||
data.buffer.push("</button>\n </div>\n");
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
function program3(depth0,data) {
|
|
||||||
|
|
||||||
var buffer = '', stack1;
|
|
||||||
data.buffer.push("\n <button ");
|
|
||||||
data.buffer.push(escapeExpression(helpers['bind-attr'].call(depth0, {hash:{
|
|
||||||
'class': ("classes")
|
|
||||||
},hashTypes:{'class': "ID"},hashContexts:{'class': depth0},contexts:[],types:[],data:data})));
|
|
||||||
data.buffer.push(" ");
|
|
||||||
data.buffer.push(escapeExpression(helpers['bind-attr'].call(depth0, {hash:{
|
|
||||||
'disabled': ("disabled")
|
|
||||||
},hashTypes:{'disabled': "ID"},hashContexts:{'disabled': depth0},contexts:[],types:[],data:data})));
|
|
||||||
data.buffer.push(">");
|
|
||||||
stack1 = helpers._triageMustache.call(depth0, "text", {hash:{},hashTypes:{},hashContexts:{},contexts:[depth0],types:["ID"],data:data});
|
|
||||||
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
|
|
||||||
data.buffer.push("</button>\n");
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
stack1 = helpers['if'].call(depth0, "form.isHorizontal", {hash:{},hashTypes:{},hashContexts:{},inverse:self.program(3, program3, data),fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],data:data});
|
|
||||||
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
|
|
||||||
data.buffer.push("\n");
|
|
||||||
return buffer;
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
Ember.TEMPLATES["components/form"] = Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) {
|
|
||||||
this.compilerInfo = [4,'>= 1.0.0'];
|
|
||||||
helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {};
|
|
||||||
var buffer = '', stack1, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, self=this;
|
|
||||||
|
|
||||||
function program1(depth0,data) {
|
|
||||||
|
|
||||||
var buffer = '', helper, options;
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
data.buffer.push(escapeExpression((helper = helpers['em-form-submit'] || (depth0 && depth0['em-form-submit']),options={hash:{
|
|
||||||
'text': ("submit_button_text"),
|
|
||||||
'class': ("submit_button_class")
|
|
||||||
},hashTypes:{'text': "ID",'class': "ID"},hashContexts:{'text': depth0,'class': depth0},contexts:[],types:[],data:data},helper ? helper.call(depth0, options) : helperMissing.call(depth0, "em-form-submit", options))));
|
|
||||||
data.buffer.push("\n");
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
stack1 = helpers._triageMustache.call(depth0, "yield", {hash:{},hashTypes:{},hashContexts:{},contexts:[depth0],types:["ID"],data:data});
|
|
||||||
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
|
|
||||||
data.buffer.push("\n");
|
|
||||||
stack1 = helpers['if'].call(depth0, "submit_button", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],data:data});
|
|
||||||
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
|
|
||||||
return buffer;
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
Ember.TEMPLATES["components/formgroup/control-within-label"] = Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) {
|
|
||||||
this.compilerInfo = [4,'>= 1.0.0'];
|
|
||||||
helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {};
|
|
||||||
var stack1, helper, options, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, self=this;
|
|
||||||
|
|
||||||
function program1(depth0,data) {
|
|
||||||
|
|
||||||
var buffer = '', helper, options;
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
data.buffer.push(escapeExpression((helper = helpers.partial || (depth0 && depth0.partial),options={hash:{},hashTypes:{},hashContexts:{},contexts:[depth0],types:["STRING"],data:data},helper ? helper.call(depth0, "components/formgroup/form-group-control", options) : helperMissing.call(depth0, "partial", "components/formgroup/form-group-control", options))));
|
|
||||||
data.buffer.push("\n");
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
stack1 = (helper = helpers['em-form-label'] || (depth0 && depth0['em-form-label']),options={hash:{
|
|
||||||
'text': ("label"),
|
|
||||||
'horiClass': (""),
|
|
||||||
'inlineClass': (""),
|
|
||||||
'viewName': ("labelViewName")
|
|
||||||
},hashTypes:{'text': "ID",'horiClass': "STRING",'inlineClass': "STRING",'viewName': "ID"},hashContexts:{'text': depth0,'horiClass': depth0,'inlineClass': depth0,'viewName': depth0},inverse:self.noop,fn:self.program(1, program1, data),contexts:[],types:[],data:data},helper ? helper.call(depth0, options) : helperMissing.call(depth0, "em-form-label", options));
|
|
||||||
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
|
|
||||||
else { data.buffer.push(''); }
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
Ember.TEMPLATES["components/formgroup/form-group-control"] = Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) {
|
|
||||||
this.compilerInfo = [4,'>= 1.0.0'];
|
|
||||||
helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {};
|
|
||||||
var buffer = '', stack1, escapeExpression=this.escapeExpression, self=this;
|
|
||||||
|
|
||||||
function program1(depth0,data) {
|
|
||||||
|
|
||||||
var buffer = '';
|
|
||||||
data.buffer.push("\n <div ");
|
|
||||||
data.buffer.push(escapeExpression(helpers['bind-attr'].call(depth0, {hash:{
|
|
||||||
'class': ("controlWrapper")
|
|
||||||
},hashTypes:{'class': "ID"},hashContexts:{'class': depth0},contexts:[],types:[],data:data})));
|
|
||||||
data.buffer.push(">\n ");
|
|
||||||
data.buffer.push(escapeExpression(helpers.view.call(depth0, "controlView", {hash:{
|
|
||||||
'viewName': ("controlViewName"),
|
|
||||||
'property': ("propertyName")
|
|
||||||
},hashTypes:{'viewName': "ID",'property': "ID"},hashContexts:{'viewName': depth0,'property': depth0},contexts:[depth0],types:["ID"],data:data})));
|
|
||||||
data.buffer.push("\n </div>\n");
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
function program3(depth0,data) {
|
|
||||||
|
|
||||||
var buffer = '';
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
data.buffer.push(escapeExpression(helpers.view.call(depth0, "controlView", {hash:{
|
|
||||||
'viewName': ("controlViewName"),
|
|
||||||
'property': ("propertyName")
|
|
||||||
},hashTypes:{'viewName': "ID",'property': "ID"},hashContexts:{'viewName': depth0,'property': depth0},contexts:[depth0],types:["ID"],data:data})));
|
|
||||||
data.buffer.push("\n");
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
stack1 = helpers['if'].call(depth0, "controlWrapper", {hash:{},hashTypes:{},hashContexts:{},inverse:self.program(3, program3, data),fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],data:data});
|
|
||||||
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
|
|
||||||
data.buffer.push("\n");
|
|
||||||
return buffer;
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
Ember.TEMPLATES["components/formgroup/form-group"] = Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) {
|
|
||||||
this.compilerInfo = [4,'>= 1.0.0'];
|
|
||||||
helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {};
|
|
||||||
var stack1, escapeExpression=this.escapeExpression, helperMissing=helpers.helperMissing, self=this;
|
|
||||||
|
|
||||||
function program1(depth0,data) {
|
|
||||||
|
|
||||||
var buffer = '', stack1;
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
stack1 = helpers['if'].call(depth0, "label", {hash:{},hashTypes:{},hashContexts:{},inverse:self.program(13, program13, data),fn:self.program(2, program2, data),contexts:[depth0],types:["ID"],data:data});
|
|
||||||
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
|
|
||||||
data.buffer.push("\n\n ");
|
|
||||||
stack1 = helpers['if'].call(depth0, "v_icons", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(15, program15, data),contexts:[depth0],types:["ID"],data:data});
|
|
||||||
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
|
|
||||||
data.buffer.push("\n\n \n ");
|
|
||||||
stack1 = helpers.unless.call(depth0, "form.isInline", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(17, program17, data),contexts:[depth0],types:["ID"],data:data});
|
|
||||||
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
|
|
||||||
data.buffer.push("\n");
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
function program2(depth0,data) {
|
|
||||||
|
|
||||||
var buffer = '', stack1;
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
stack1 = helpers['if'].call(depth0, "yieldInLabel", {hash:{},hashTypes:{},hashContexts:{},inverse:self.program(8, program8, data),fn:self.program(3, program3, data),contexts:[depth0],types:["ID"],data:data});
|
|
||||||
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
function program3(depth0,data) {
|
|
||||||
|
|
||||||
var buffer = '', stack1;
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
stack1 = helpers['if'].call(depth0, "labelWrapperClass", {hash:{},hashTypes:{},hashContexts:{},inverse:self.program(6, program6, data),fn:self.program(4, program4, data),contexts:[depth0],types:["ID"],data:data});
|
|
||||||
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
function program4(depth0,data) {
|
|
||||||
|
|
||||||
var buffer = '', helper, options;
|
|
||||||
data.buffer.push("\n <div ");
|
|
||||||
data.buffer.push(escapeExpression(helpers['bind-attr'].call(depth0, {hash:{
|
|
||||||
'class': ("labelWrapperClass")
|
|
||||||
},hashTypes:{'class': "ID"},hashContexts:{'class': depth0},contexts:[],types:[],data:data})));
|
|
||||||
data.buffer.push(">\n ");
|
|
||||||
data.buffer.push(escapeExpression((helper = helpers.partial || (depth0 && depth0.partial),options={hash:{},hashTypes:{},hashContexts:{},contexts:[depth0],types:["STRING"],data:data},helper ? helper.call(depth0, "components/formgroup/control-within-label", options) : helperMissing.call(depth0, "partial", "components/formgroup/control-within-label", options))));
|
|
||||||
data.buffer.push("\n </div>\n ");
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
function program6(depth0,data) {
|
|
||||||
|
|
||||||
var buffer = '', helper, options;
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
data.buffer.push(escapeExpression((helper = helpers.partial || (depth0 && depth0.partial),options={hash:{},hashTypes:{},hashContexts:{},contexts:[depth0],types:["STRING"],data:data},helper ? helper.call(depth0, "components/formgroup/control-within-label", options) : helperMissing.call(depth0, "partial", "components/formgroup/control-within-label", options))));
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
function program8(depth0,data) {
|
|
||||||
|
|
||||||
var buffer = '', stack1;
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
stack1 = helpers['if'].call(depth0, "labelWrapperClass", {hash:{},hashTypes:{},hashContexts:{},inverse:self.program(11, program11, data),fn:self.program(9, program9, data),contexts:[depth0],types:["ID"],data:data});
|
|
||||||
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
function program9(depth0,data) {
|
|
||||||
|
|
||||||
var buffer = '', helper, options;
|
|
||||||
data.buffer.push("\n <div ");
|
|
||||||
data.buffer.push(escapeExpression(helpers['bind-attr'].call(depth0, {hash:{
|
|
||||||
'class': ("labelWrapperClass")
|
|
||||||
},hashTypes:{'class': "ID"},hashContexts:{'class': depth0},contexts:[],types:[],data:data})));
|
|
||||||
data.buffer.push(">\n ");
|
|
||||||
data.buffer.push(escapeExpression((helper = helpers['em-form-label'] || (depth0 && depth0['em-form-label']),options={hash:{
|
|
||||||
'text': ("label"),
|
|
||||||
'viewName': ("labelViewName")
|
|
||||||
},hashTypes:{'text': "ID",'viewName': "ID"},hashContexts:{'text': depth0,'viewName': depth0},contexts:[],types:[],data:data},helper ? helper.call(depth0, options) : helperMissing.call(depth0, "em-form-label", options))));
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
data.buffer.push(escapeExpression((helper = helpers.partial || (depth0 && depth0.partial),options={hash:{},hashTypes:{},hashContexts:{},contexts:[depth0],types:["STRING"],data:data},helper ? helper.call(depth0, "components/formgroup/form-group-control", options) : helperMissing.call(depth0, "partial", "components/formgroup/form-group-control", options))));
|
|
||||||
data.buffer.push("\n </div>\n ");
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
function program11(depth0,data) {
|
|
||||||
|
|
||||||
var buffer = '', helper, options;
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
data.buffer.push(escapeExpression((helper = helpers['em-form-label'] || (depth0 && depth0['em-form-label']),options={hash:{
|
|
||||||
'text': ("label"),
|
|
||||||
'viewName': ("labelViewName")
|
|
||||||
},hashTypes:{'text': "ID",'viewName': "ID"},hashContexts:{'text': depth0,'viewName': depth0},contexts:[],types:[],data:data},helper ? helper.call(depth0, options) : helperMissing.call(depth0, "em-form-label", options))));
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
data.buffer.push(escapeExpression((helper = helpers.partial || (depth0 && depth0.partial),options={hash:{},hashTypes:{},hashContexts:{},contexts:[depth0],types:["STRING"],data:data},helper ? helper.call(depth0, "components/formgroup/form-group-control", options) : helperMissing.call(depth0, "partial", "components/formgroup/form-group-control", options))));
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
function program13(depth0,data) {
|
|
||||||
|
|
||||||
var buffer = '', helper, options;
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
data.buffer.push(escapeExpression((helper = helpers.partial || (depth0 && depth0.partial),options={hash:{},hashTypes:{},hashContexts:{},contexts:[depth0],types:["STRING"],data:data},helper ? helper.call(depth0, "components/formgroup/form-group-control", options) : helperMissing.call(depth0, "partial", "components/formgroup/form-group-control", options))));
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
function program15(depth0,data) {
|
|
||||||
|
|
||||||
var buffer = '';
|
|
||||||
data.buffer.push("\n <span class=\"form-control-feedback\"><i ");
|
|
||||||
data.buffer.push(escapeExpression(helpers['bind-attr'].call(depth0, {hash:{
|
|
||||||
'class': ("v_icon")
|
|
||||||
},hashTypes:{'class': "ID"},hashContexts:{'class': depth0},contexts:[],types:[],data:data})));
|
|
||||||
data.buffer.push("></i></span>\n ");
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
function program17(depth0,data) {
|
|
||||||
|
|
||||||
var buffer = '', stack1;
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
stack1 = helpers['if'].call(depth0, "canShowErrors", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(18, program18, data),contexts:[depth0],types:["ID"],data:data});
|
|
||||||
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
function program18(depth0,data) {
|
|
||||||
|
|
||||||
var buffer = '', helper, options;
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
data.buffer.push(escapeExpression((helper = helpers['em-form-control-help'] || (depth0 && depth0['em-form-control-help']),options={hash:{
|
|
||||||
'text': ("help"),
|
|
||||||
'viewName': ("helpViewName")
|
|
||||||
},hashTypes:{'text': "ID",'viewName': "ID"},hashContexts:{'text': depth0,'viewName': depth0},contexts:[],types:[],data:data},helper ? helper.call(depth0, options) : helperMissing.call(depth0, "em-form-control-help", options))));
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
function program20(depth0,data) {
|
|
||||||
|
|
||||||
var buffer = '', stack1;
|
|
||||||
data.buffer.push("\n ");
|
|
||||||
stack1 = helpers._triageMustache.call(depth0, "yield", {hash:{},hashTypes:{},hashContexts:{},contexts:[depth0],types:["ID"],data:data});
|
|
||||||
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
|
|
||||||
data.buffer.push("\n");
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
stack1 = helpers.unless.call(depth0, "template", {hash:{},hashTypes:{},hashContexts:{},inverse:self.program(20, program20, data),fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],data:data});
|
|
||||||
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
|
|
||||||
else { data.buffer.push(''); }
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Form View
|
|
||||||
|
|
||||||
A component for rendering a form element.
|
|
||||||
|
|
||||||
Syntax:
|
|
||||||
{{em-form
|
|
||||||
//The layout of the form
|
|
||||||
form_layout="form|inline|horizontal"
|
|
||||||
//The model bound to the form if any
|
|
||||||
model="some_model_instance"
|
|
||||||
//The action to be invoked on the controller when a form is submitted.
|
|
||||||
action="some_action"
|
|
||||||
//if true a submit button will be rendered
|
|
||||||
submit_button=true|false
|
|
||||||
//the submit button text
|
|
||||||
submit_button_text="Add"
|
|
||||||
//if true validation icons will be rendered
|
|
||||||
v_icons=true|false
|
|
||||||
}}
|
|
||||||
*/
|
|
||||||
Ember.Forms.FormComponent = Ember.Component.extend({
|
|
||||||
tagName: 'form',
|
|
||||||
layoutName: 'components/form',
|
|
||||||
classNameBindings: ['form_layout_class'],
|
|
||||||
attributeBindings: ['role'],
|
|
||||||
role: 'form',
|
|
||||||
form_layout_class: (function() {
|
|
||||||
switch (this.get('form_layout')) {
|
|
||||||
case 'horizontal':
|
|
||||||
case 'inline':
|
|
||||||
return "form-" + (this.get('form_layout'));
|
|
||||||
default:
|
|
||||||
return 'form';
|
|
||||||
}
|
|
||||||
}).property('form_layout'),
|
|
||||||
isDefaultLayout: Ember.Forms.utils.createBoundSwitchAccessor('form', 'form_layout', 'form'),
|
|
||||||
isInline: Ember.Forms.utils.createBoundSwitchAccessor('inline', 'form_layout', 'form'),
|
|
||||||
isHorizontal: Ember.Forms.utils.createBoundSwitchAccessor('horizontal', 'form_layout', 'form'),
|
|
||||||
action: 'submit',
|
|
||||||
model: void 0,
|
|
||||||
form_layout: 'form',
|
|
||||||
submit_button: true,
|
|
||||||
submit_button_text: "Submit",
|
|
||||||
submit_button_class: void 0,
|
|
||||||
v_icons: true,
|
|
||||||
|
|
||||||
/*
|
|
||||||
Form submit
|
|
||||||
|
|
||||||
Optionally execute model validations and perform a form submission.
|
|
||||||
*/
|
|
||||||
submit: function(e) {
|
|
||||||
var promise;
|
|
||||||
if (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
if (Ember.isNone(this.get('model.validate'))) {
|
|
||||||
return this.get('targetObject').send(this.get('action'));
|
|
||||||
} else {
|
|
||||||
promise = this.get('model').validate();
|
|
||||||
return promise.then((function(_this) {
|
|
||||||
return function() {
|
|
||||||
if (_this.get('model.isValid')) {
|
|
||||||
return _this.get('targetObject').send(_this.get('action'));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Ember.Handlebars.helper('em-form', Ember.Forms.FormComponent);
|
|
||||||
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Form Control Help
|
|
||||||
|
|
||||||
Renders a textual help of the control.
|
|
||||||
|
|
||||||
Note: currently must be a direct descendant of a form-group or 'property' must be explicitly defined
|
|
||||||
|
|
||||||
Syntax:
|
|
||||||
{{em-form-control-help}}
|
|
||||||
*/
|
|
||||||
Em.Forms.FormControlHelpComponent = Em.Component.extend(Em.Forms.InFormMixin, Em.Forms.HasPropertyMixin, {
|
|
||||||
tagName: 'span',
|
|
||||||
classNames: ['help-block'],
|
|
||||||
classNameBindings: ['extraClass', 'horiClassCalc'],
|
|
||||||
layoutName: 'components/form-control-help',
|
|
||||||
text: void 0,
|
|
||||||
extraClass: void 0,
|
|
||||||
horiClass: 'col-sm-offset-2 col-sm-10',
|
|
||||||
horiClassCalc: (function() {
|
|
||||||
if (this.get('form.isHorizontal') && this.get('horiClass')) {
|
|
||||||
return this.get('horiClass');
|
|
||||||
}
|
|
||||||
}).property('form.isHorizontal'),
|
|
||||||
init: function() {
|
|
||||||
this._super();
|
|
||||||
return Em.Binding.from('model.errors.' + this.get('propertyName')).to('errors').connect(this);
|
|
||||||
},
|
|
||||||
helpText: (function() {
|
|
||||||
return this.get('errors.firstObject') || this.get('text');
|
|
||||||
}).property('text', 'errors.firstObject'),
|
|
||||||
hasHelp: (function() {
|
|
||||||
var _ref;
|
|
||||||
return ((_ref = this.get('helpText')) != null ? _ref.length : void 0) > 0;
|
|
||||||
}).property('helpText'),
|
|
||||||
hasError: (function() {
|
|
||||||
var _ref;
|
|
||||||
return (_ref = this.get('errors')) != null ? _ref.length : void 0;
|
|
||||||
}).property('errors.length')
|
|
||||||
});
|
|
||||||
|
|
||||||
Em.Handlebars.helper('em-form-control-help', Em.Forms.FormControlHelpComponent);
|
|
||||||
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Form Group
|
|
||||||
|
|
||||||
Wraps labels, controls and help message for optimum spacing and validation styles.
|
|
||||||
A wrapper for a single input with its assistances views such as label, help message.
|
|
||||||
|
|
||||||
A form group can yield the control's view after or within a label, this is dependent on the control
|
|
||||||
required layout and is defined byt he yieldInLabel property
|
|
||||||
|
|
||||||
|
|
||||||
Syntax:
|
|
||||||
{{em-form-group
|
|
||||||
//The state of the form group
|
|
||||||
status="none|error|warning|success"
|
|
||||||
//If true the control view is yieled within the label
|
|
||||||
yieldInLabel=true|false
|
|
||||||
//If true validation icons will be rendered, by default inherited from the form
|
|
||||||
v_icons: true
|
|
||||||
//Label of the form group, default is a human friendly form of the property name
|
|
||||||
label="Some label"
|
|
||||||
}}
|
|
||||||
*/
|
|
||||||
Em.Forms.FormGroupComponent = Em.Component.extend(Em.Forms.InFormMixin, Em.Forms.HasPropertyMixin, Em.Forms.HasPropertyValidationMixin, {
|
|
||||||
tagName: 'div',
|
|
||||||
layoutName: 'components/form-group',
|
|
||||||
"class": 'form-group',
|
|
||||||
classNameBindings: ['class', 'hasSuccess', 'hasWarning', 'hasError', 'v_icons:has-feedback'],
|
|
||||||
attributeBindings: ['disabled'],
|
|
||||||
hasSuccess: (function() {
|
|
||||||
return this.get('validations') && this.get('status') === 'success' && this.get('canShowErrors');
|
|
||||||
}).property('status', 'canShowErrors'),
|
|
||||||
hasWarning: (function() {
|
|
||||||
return this.get('validations') && this.get('status') === 'warning' && this.get('canShowErrors');
|
|
||||||
}).property('status', 'canShowErrors'),
|
|
||||||
hasError: (function() {
|
|
||||||
return this.get('validations') && this.get('status') === 'error' && this.get('canShowErrors');
|
|
||||||
}).property('status', 'canShowErrors'),
|
|
||||||
v_icons: Em.computed.alias('form.v_icons'),
|
|
||||||
v_success_icon: 'fa fa-check',
|
|
||||||
v_warn_icon: 'fa fa-exclamation-triangle',
|
|
||||||
v_error_icon: 'fa fa-times',
|
|
||||||
validations: true,
|
|
||||||
yieldInLabel: false,
|
|
||||||
v_icon: (function() {
|
|
||||||
if (!this.get('canShowErrors')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (this.get('status')) {
|
|
||||||
case 'success':
|
|
||||||
return this.get('v_success_icon');
|
|
||||||
case 'warning':
|
|
||||||
case 'warn':
|
|
||||||
return this.get('v_warn_icon');
|
|
||||||
case 'error':
|
|
||||||
return this.get('v_error_icon');
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}).property('status', 'canShowErrors'),
|
|
||||||
init: function() {
|
|
||||||
return this._super();
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
Observes the helpHasErrors of the help control and modify the 'status' property accordingly.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
Listen to the focus out of the form group and display the errors
|
|
||||||
*/
|
|
||||||
focusOut: function() {
|
|
||||||
return this.set('canShowErrors', true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Em.Handlebars.helper('em-form-group', Em.Forms.FormGroupComponent);
|
|
||||||
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Form Label
|
|
||||||
|
|
||||||
When styled with bootstrap, when form is rendered horizontally, the label require the 'extraClass' property to
|
|
||||||
be set to a value such 'col-sm-2' to be aligned properly.
|
|
||||||
|
|
||||||
Syntax:
|
|
||||||
{{em-form-label
|
|
||||||
text="Some label"
|
|
||||||
extraClass="col-sm-2"
|
|
||||||
}}
|
|
||||||
|
|
||||||
Or can serve as a block helper for elements that needs to be wrapped within label element.
|
|
||||||
{{#em-form-label text="Active?"}}
|
|
||||||
{{em-checkbox}}
|
|
||||||
{{/em-form-label}}
|
|
||||||
*/
|
|
||||||
Ember.Forms.FormLabelComponent = Ember.Component.extend(Em.Forms.InFormMixin, {
|
|
||||||
tagName: 'label',
|
|
||||||
layoutName: 'components/form-label',
|
|
||||||
classNames: ['control-label'],
|
|
||||||
classNameBindings: ['extraClass', 'inlineClassCalc', 'horiClassCalc'],
|
|
||||||
attributeBindings: ['for'],
|
|
||||||
horiClass: 'col-sm-2',
|
|
||||||
horiClassCalc: (function() {
|
|
||||||
if (this.get('form.isHorizontal') && this.get('horiClass')) {
|
|
||||||
return this.get('horiClass');
|
|
||||||
}
|
|
||||||
}).property('form.isHorizontal'),
|
|
||||||
inlineClass: 'sr-only',
|
|
||||||
inlineClassCalc: (function() {
|
|
||||||
if (this.get('form.isInline') && this.get('inlineClass')) {
|
|
||||||
return this.get('inlineClass');
|
|
||||||
}
|
|
||||||
}).property('form.form_layout')
|
|
||||||
});
|
|
||||||
|
|
||||||
Ember.Handlebars.helper('em-form-label', Ember.Forms.FormLabelComponent);
|
|
||||||
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Form Input
|
|
||||||
|
|
||||||
Syntax:
|
|
||||||
{{em-input property="property name"}}
|
|
||||||
*/
|
|
||||||
Em.Forms.FormInputComponent = Em.Forms.FormGroupComponent.extend({
|
|
||||||
controlView: Em.TextField.extend(Em.Forms.ControlMixin, {
|
|
||||||
attributeBindings: ['placeholder'],
|
|
||||||
placeholder: Em.computed.alias('parentView.placeholder'),
|
|
||||||
type: Em.computed.alias('parentView.type'),
|
|
||||||
model: Em.computed.alias('parentView.model'),
|
|
||||||
propertyName: Em.computed.alias('parentView.propertyName')
|
|
||||||
}),
|
|
||||||
property: void 0,
|
|
||||||
label: void 0,
|
|
||||||
placeholder: void 0,
|
|
||||||
controlWrapper: (function() {
|
|
||||||
if (this.get('form.form_layout') === 'horizontal') {
|
|
||||||
return 'col-sm-10';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}).property('form.form_layout')
|
|
||||||
});
|
|
||||||
|
|
||||||
Ember.Handlebars.helper('em-input', function(options) {
|
|
||||||
return Ember.Handlebars.helpers.view.call(this, Ember.Forms.FormInputComponent, options);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Form Input
|
|
||||||
|
|
||||||
Syntax:
|
|
||||||
{{em-text property="property name" rows=4}}
|
|
||||||
*/
|
|
||||||
Em.Forms.FormTextComponent = Em.Forms.FormGroupComponent.extend({
|
|
||||||
controlView: Em.TextArea.extend(Em.Forms.ControlMixin, {
|
|
||||||
attributeBindings: ['placeholder'],
|
|
||||||
placeholder: Em.computed.alias('parentView.placeholder'),
|
|
||||||
model: Em.computed.alias('parentView.model'),
|
|
||||||
propertyName: Em.computed.alias('parentView.propertyName'),
|
|
||||||
rows: Em.computed.alias('parentView.rows')
|
|
||||||
}),
|
|
||||||
property: void 0,
|
|
||||||
label: void 0,
|
|
||||||
placeholder: void 0,
|
|
||||||
rows: 4,
|
|
||||||
controlWrapper: (function() {
|
|
||||||
if (this.get('form.form_layout') === 'horizontal') {
|
|
||||||
return 'col-sm-10';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}).property('form.form_layout')
|
|
||||||
});
|
|
||||||
|
|
||||||
Ember.Handlebars.helper('em-text', function(options) {
|
|
||||||
return Ember.Handlebars.helpers.view.call(this, Ember.Forms.FormTextComponent, options);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Form Input
|
|
||||||
|
|
||||||
Syntax:
|
|
||||||
{{em-checkbox property="property name"}}
|
|
||||||
*/
|
|
||||||
Em.Forms.FormCheckboxComponent = Em.Forms.FormGroupComponent.extend({
|
|
||||||
v_icons: false,
|
|
||||||
validations: false,
|
|
||||||
yieldInLabel: true,
|
|
||||||
controlView: Em.Checkbox.extend(Em.Forms.ControlMixin, {
|
|
||||||
"class": false,
|
|
||||||
model: Em.computed.alias('parentView.parentView.model'),
|
|
||||||
propertyName: Em.computed.alias('parentView.parentView.propertyName'),
|
|
||||||
init: function() {
|
|
||||||
this._super();
|
|
||||||
return Ember.Binding.from("model." + (this.get('propertyName'))).to('checked').connect(this);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
wrapperClass: (function() {
|
|
||||||
if (this.get('form.form_layout') === 'horizontal') {
|
|
||||||
return 'col-sm-offset-2 col-sm-10';
|
|
||||||
}
|
|
||||||
}).property('form.form_layout'),
|
|
||||||
labelWrapperClass: (function() {
|
|
||||||
if (this.get('form.form_layout') === 'horizontal') {
|
|
||||||
return 'checkbox';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}).property('form.form_layout'),
|
|
||||||
"class": (function() {
|
|
||||||
if (this.get('form.form_layout') !== 'horizontal') {
|
|
||||||
return 'checkbox';
|
|
||||||
}
|
|
||||||
return 'form-group';
|
|
||||||
}).property('form.form_layout')
|
|
||||||
});
|
|
||||||
|
|
||||||
Ember.Handlebars.helper('em-checkbox', function(options) {
|
|
||||||
return Ember.Handlebars.helpers.view.call(this, Ember.Forms.FormCheckboxComponent, options);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Form Select
|
|
||||||
|
|
||||||
Syntax:
|
|
||||||
{{em-select property="property name"
|
|
||||||
content=array_of_options
|
|
||||||
optionValuePath=keyForValue
|
|
||||||
optionLabelPath=keyForLabel
|
|
||||||
prompt="Optional default prompt"}}
|
|
||||||
*/
|
|
||||||
Em.Forms.FormSelectComponent = Em.Forms.FormGroupComponent.extend({
|
|
||||||
v_icons: false,
|
|
||||||
controlView: Em.Select.extend(Em.Forms.ControlMixin, {
|
|
||||||
model: Em.computed.alias('parentView.model'),
|
|
||||||
propertyName: Em.computed.alias('parentView.propertyName'),
|
|
||||||
content: Em.computed.alias('parentView.content'),
|
|
||||||
optionValuePath: Em.computed.alias('parentView.optionValuePath'),
|
|
||||||
optionLabelPath: Em.computed.alias('parentView.optionLabelPath'),
|
|
||||||
prompt: Em.computed.alias('parentView.prompt')
|
|
||||||
}),
|
|
||||||
property: void 0,
|
|
||||||
content: void 0,
|
|
||||||
optionValuePath: void 0,
|
|
||||||
optionLabelPath: void 0,
|
|
||||||
prompt: void 0,
|
|
||||||
controlWrapper: (function() {
|
|
||||||
if (this.get('form.form_layout') === 'horizontal') {
|
|
||||||
return 'col-sm-10';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}).property('form.form_layout')
|
|
||||||
});
|
|
||||||
|
|
||||||
Ember.Handlebars.helper('em-select', function(options) {
|
|
||||||
return Ember.Handlebars.helpers.view.call(this, Ember.Forms.FormSelectComponent, options);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Form Submit Button
|
|
||||||
|
|
||||||
Syntax:
|
|
||||||
{{em-form-submit text="Submit"}}
|
|
||||||
*/
|
|
||||||
Ember.Forms.FormSubmitComponent = Ember.Component.extend(Ember.Forms.InFormMixin, {
|
|
||||||
classes: 'btn btn-default',
|
|
||||||
layoutName: 'components/form-submit-button',
|
|
||||||
classNames: ['form-group'],
|
|
||||||
text: 'Submit',
|
|
||||||
type: 'submit',
|
|
||||||
attributeBindings: ['disabled'],
|
|
||||||
horiClass: 'col-sm-offset-2 col-sm-10',
|
|
||||||
disabled: (function() {
|
|
||||||
if (!Ember.isNone(this.get('model.isValid'))) {
|
|
||||||
return !this.get('model.isValid');
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}).property('model.isValid')
|
|
||||||
});
|
|
||||||
|
|
||||||
Ember.Handlebars.helper('em-form-submit', Ember.Forms.FormSubmitComponent);
|
|
||||||
|
|
||||||
|
|
||||||
})();
|
|
2746
js/lib/handlebars.js
2746
js/lib/handlebars.js
File diff suppressed because it is too large
Load diff
4
js/lib/jquery.js
vendored
4
js/lib/jquery.js
vendored
File diff suppressed because one or more lines are too long
6
js/lib/moment.min.js
vendored
6
js/lib/moment.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,47 +0,0 @@
|
||||||
"use strict";function q(a){throw a;}var t=void 0,u=!1;var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a},notReady:function(a){this.toString=function(){return"NOT READY: "+this.message};this.message=a}}};
|
|
||||||
"undefined"!=typeof module&&module.exports&&(module.exports=sjcl);
|
|
||||||
sjcl.cipher.aes=function(a){this.j[0][0][0]||this.D();var b,c,d,e,f=this.j[0][4],g=this.j[1];b=a.length;var h=1;4!==b&&(6!==b&&8!==b)&&q(new sjcl.exception.invalid("invalid aes key size"));this.a=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(0===a%b||8===b&&4===a%b)c=f[c>>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255],0===a%b&&(c=c<<8^c>>>24^h<<24,h=h<<1^283*(h>>7));d[a]=d[a-b]^c}for(b=0;a;b++,a--)c=d[b&3?a:a-4],e[b]=4>=a||4>b?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^g[3][f[c&
|
|
||||||
255]]};
|
|
||||||
sjcl.cipher.aes.prototype={encrypt:function(a){return y(this,a,0)},decrypt:function(a){return y(this,a,1)},j:[[[],[],[],[],[]],[[],[],[],[],[]]],D:function(){var a=this.j[0],b=this.j[1],c=a[4],d=b[4],e,f,g,h=[],l=[],k,n,m,p;for(e=0;0x100>e;e++)l[(h[e]=e<<1^283*(e>>7))^e]=e;for(f=g=0;!c[f];f^=k||1,g=l[g]||1){m=g^g<<1^g<<2^g<<3^g<<4;m=m>>8^m&255^99;c[f]=m;d[m]=f;n=h[e=h[k=h[f]]];p=0x1010101*n^0x10001*e^0x101*k^0x1010100*f;n=0x101*h[m]^0x1010100*m;for(e=0;4>e;e++)a[e][f]=n=n<<24^n>>>8,b[e][m]=p=p<<24^p>>>8}for(e=
|
|
||||||
0;5>e;e++)a[e]=a[e].slice(0),b[e]=b[e].slice(0)}};
|
|
||||||
function y(a,b,c){4!==b.length&&q(new sjcl.exception.invalid("invalid aes block size"));var d=a.a[c],e=b[0]^d[0],f=b[c?3:1]^d[1],g=b[2]^d[2];b=b[c?1:3]^d[3];var h,l,k,n=d.length/4-2,m,p=4,s=[0,0,0,0];h=a.j[c];a=h[0];var r=h[1],v=h[2],w=h[3],x=h[4];for(m=0;m<n;m++)h=a[e>>>24]^r[f>>16&255]^v[g>>8&255]^w[b&255]^d[p],l=a[f>>>24]^r[g>>16&255]^v[b>>8&255]^w[e&255]^d[p+1],k=a[g>>>24]^r[b>>16&255]^v[e>>8&255]^w[f&255]^d[p+2],b=a[b>>>24]^r[e>>16&255]^v[f>>8&255]^w[g&255]^d[p+3],p+=4,e=h,f=l,g=k;for(m=0;4>
|
|
||||||
m;m++)s[c?3&-m:m]=x[e>>>24]<<24^x[f>>16&255]<<16^x[g>>8&255]<<8^x[b&255]^d[p++],h=e,e=f,f=g,g=b,b=h;return s}
|
|
||||||
sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.O(a.slice(b/32),32-(b&31)).slice(1);return c===t?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var d=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-d^a[b/32+1|0]>>>d:a[b/32|0]>>>d)&(1<<c)-1},concat:function(a,b){if(0===a.length||0===b.length)return a.concat(b);var c=a[a.length-1],d=sjcl.bitArray.getPartial(c);return 32===d?a.concat(b):sjcl.bitArray.O(b,d,c|0,a.slice(0,a.length-1))},bitLength:function(a){var b=a.length;return 0===
|
|
||||||
b?0:32*(b-1)+sjcl.bitArray.getPartial(a[b-1])},clamp:function(a,b){if(32*a.length<b)return a;a=a.slice(0,Math.ceil(b/32));var c=a.length;b&=31;0<c&&b&&(a[c-1]=sjcl.bitArray.partial(b,a[c-1]&2147483648>>b-1,1));return a},partial:function(a,b,c){return 32===a?b:(c?b|0:b<<32-a)+0x10000000000*a},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return u;var c=0,d;for(d=0;d<a.length;d++)c|=a[d]^b[d];return 0===
|
|
||||||
c},O:function(a,b,c,d){var e;e=0;for(d===t&&(d=[]);32<=b;b-=32)d.push(c),c=0;if(0===b)return d.concat(a);for(e=0;e<a.length;e++)d.push(c|a[e]>>>b),c=a[e]<<32-b;e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,32<b+a?c:d.pop(),1));return d},k:function(a,b){return[a[0]^b[0],a[1]^b[1],a[2]^b[2],a[3]^b[3]]}};
|
|
||||||
sjcl.codec.utf8String={fromBits:function(a){var b="",c=sjcl.bitArray.bitLength(a),d,e;for(d=0;d<c/8;d++)0===(d&3)&&(e=a[d/4]),b+=String.fromCharCode(e>>>24),e<<=8;return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c<a.length;c++)d=d<<8|a.charCodeAt(c),3===(c&3)&&(b.push(d),d=0);c&3&&b.push(sjcl.bitArray.partial(8*(c&3),d));return b}};
|
|
||||||
sjcl.codec.hex={fromBits:function(a){var b="",c;for(c=0;c<a.length;c++)b+=((a[c]|0)+0xf00000000000).toString(16).substr(4);return b.substr(0,sjcl.bitArray.bitLength(a)/4)},toBits:function(a){var b,c=[],d;a=a.replace(/\s|0x/g,"");d=a.length;a+="00000000";for(b=0;b<a.length;b+=8)c.push(parseInt(a.substr(b,8),16)^0);return sjcl.bitArray.clamp(c,4*d)}};
|
|
||||||
sjcl.codec.base64={I:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",fromBits:function(a,b,c){var d="",e=0,f=sjcl.codec.base64.I,g=0,h=sjcl.bitArray.bitLength(a);c&&(f=f.substr(0,62)+"-_");for(c=0;6*d.length<h;)d+=f.charAt((g^a[c]>>>e)>>>26),6>e?(g=a[c]<<6-e,e+=26,c++):(g<<=6,e-=6);for(;d.length&3&&!b;)d+="=";return d},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],d,e=0,f=sjcl.codec.base64.I,g=0,h;b&&(f=f.substr(0,62)+"-_");for(d=0;d<a.length;d++)h=f.indexOf(a.charAt(d)),
|
|
||||||
0>h&&q(new sjcl.exception.invalid("this isn't base64!")),26<e?(e-=26,c.push(g^h>>>e),g=h<<32-e):(e+=6,g^=h<<32-e);e&56&&c.push(sjcl.bitArray.partial(e&56,g,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.a[0]||this.D();a?(this.q=a.q.slice(0),this.m=a.m.slice(0),this.g=a.g):this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()};
|
|
||||||
sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.q=this.M.slice(0);this.m=[];this.g=0;return this},update:function(a){"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.m=sjcl.bitArray.concat(this.m,a);b=this.g;a=this.g=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)z(this,c.splice(0,16));return this},finalize:function(){var a,b=this.m,c=this.q,b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.g/
|
|
||||||
4294967296));for(b.push(this.g|0);b.length;)z(this,b.splice(0,16));this.reset();return c},M:[],a:[],D:function(){function a(a){return 0x100000000*(a-Math.floor(a))|0}var b=0,c=2,d;a:for(;64>b;c++){for(d=2;d*d<=c;d++)if(0===c%d)continue a;8>b&&(this.M[b]=a(Math.pow(c,0.5)));this.a[b]=a(Math.pow(c,1/3));b++}}};
|
|
||||||
function z(a,b){var c,d,e,f=b.slice(0),g=a.q,h=a.a,l=g[0],k=g[1],n=g[2],m=g[3],p=g[4],s=g[5],r=g[6],v=g[7];for(c=0;64>c;c++)16>c?d=f[c]:(d=f[c+1&15],e=f[c+14&15],d=f[c&15]=(d>>>7^d>>>18^d>>>3^d<<25^d<<14)+(e>>>17^e>>>19^e>>>10^e<<15^e<<13)+f[c&15]+f[c+9&15]|0),d=d+v+(p>>>6^p>>>11^p>>>25^p<<26^p<<21^p<<7)+(r^p&(s^r))+h[c],v=r,r=s,s=p,p=m+d|0,m=n,n=k,k=l,l=d+(k&n^m&(k^n))+(k>>>2^k>>>13^k>>>22^k<<30^k<<19^k<<10)|0;g[0]=g[0]+l|0;g[1]=g[1]+k|0;g[2]=g[2]+n|0;g[3]=g[3]+m|0;g[4]=g[4]+p|0;g[5]=g[5]+s|0;g[6]=
|
|
||||||
g[6]+r|0;g[7]=g[7]+v|0}
|
|
||||||
sjcl.mode.ccm={name:"ccm",encrypt:function(a,b,c,d,e){var f,g=b.slice(0),h=sjcl.bitArray,l=h.bitLength(c)/8,k=h.bitLength(g)/8;e=e||64;d=d||[];7>l&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(f=2;4>f&&k>>>8*f;f++);f<15-l&&(f=15-l);c=h.clamp(c,8*(15-f));b=sjcl.mode.ccm.K(a,b,c,d,e,f);g=sjcl.mode.ccm.n(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),l=f.clamp(b,h-e),k=f.bitSlice(b,
|
|
||||||
h-e),h=(h-e)/8;7>g&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(b=2;4>b&&h>>>8*b;b++);b<15-g&&(b=15-g);c=f.clamp(c,8*(15-b));l=sjcl.mode.ccm.n(a,l,c,k,e,b);a=sjcl.mode.ccm.K(a,l.data,c,d,e,b);f.equal(l.tag,a)||q(new sjcl.exception.corrupt("ccm: tag doesn't match"));return l.data},K:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,l=h.k;e/=8;(e%2||4>e||16<e)&&q(new sjcl.exception.invalid("ccm: invalid tag length"));(0xffffffff<d.length||0xffffffff<b.length)&&q(new sjcl.exception.bug("ccm: can't deal with 4GiB or more data"));
|
|
||||||
f=[h.partial(8,(d.length?64:0)|e-2<<2|f-1)];f=h.concat(f,c);f[3]|=h.bitLength(b)/8;f=a.encrypt(f);if(d.length){c=h.bitLength(d)/8;65279>=c?g=[h.partial(16,c)]:0xffffffff>=c&&(g=h.concat([h.partial(16,65534)],[c]));g=h.concat(g,d);for(d=0;d<g.length;d+=4)f=a.encrypt(l(f,g.slice(d,d+4).concat([0,0,0])))}for(d=0;d<b.length;d+=4)f=a.encrypt(l(f,b.slice(d,d+4).concat([0,0,0])));return h.clamp(f,8*e)},n:function(a,b,c,d,e,f){var g,h=sjcl.bitArray;g=h.k;var l=b.length,k=h.bitLength(b);c=h.concat([h.partial(8,
|
|
||||||
f-1)],c).concat([0,0,0]).slice(0,4);d=h.bitSlice(g(d,a.encrypt(c)),0,e);if(!l)return{tag:d,data:[]};for(g=0;g<l;g+=4)c[3]++,e=a.encrypt(c),b[g]^=e[0],b[g+1]^=e[1],b[g+2]^=e[2],b[g+3]^=e[3];return{tag:d,data:h.clamp(b,k)}}};
|
|
||||||
sjcl.mode.ocb2={name:"ocb2",encrypt:function(a,b,c,d,e,f){128!==sjcl.bitArray.bitLength(c)&&q(new sjcl.exception.invalid("ocb iv must be 128 bits"));var g,h=sjcl.mode.ocb2.G,l=sjcl.bitArray,k=l.k,n=[0,0,0,0];c=h(a.encrypt(c));var m,p=[];d=d||[];e=e||64;for(g=0;g+4<b.length;g+=4)m=b.slice(g,g+4),n=k(n,m),p=p.concat(k(c,a.encrypt(k(c,m)))),c=h(c);m=b.slice(g);b=l.bitLength(m);g=a.encrypt(k(c,[0,0,0,b]));m=l.clamp(k(m.concat([0,0,0]),g),b);n=k(n,k(m.concat([0,0,0]),g));n=a.encrypt(k(n,k(c,h(c))));d.length&&
|
|
||||||
(n=k(n,f?d:sjcl.mode.ocb2.pmac(a,d)));return p.concat(l.concat(m,l.clamp(n,e)))},decrypt:function(a,b,c,d,e,f){128!==sjcl.bitArray.bitLength(c)&&q(new sjcl.exception.invalid("ocb iv must be 128 bits"));e=e||64;var g=sjcl.mode.ocb2.G,h=sjcl.bitArray,l=h.k,k=[0,0,0,0],n=g(a.encrypt(c)),m,p,s=sjcl.bitArray.bitLength(b)-e,r=[];d=d||[];for(c=0;c+4<s/32;c+=4)m=l(n,a.decrypt(l(n,b.slice(c,c+4)))),k=l(k,m),r=r.concat(m),n=g(n);p=s-32*c;m=a.encrypt(l(n,[0,0,0,p]));m=l(m,h.clamp(b.slice(c),p).concat([0,0,0]));
|
|
||||||
k=l(k,m);k=a.encrypt(l(k,l(n,g(n))));d.length&&(k=l(k,f?d:sjcl.mode.ocb2.pmac(a,d)));h.equal(h.clamp(k,e),h.bitSlice(b,s))||q(new sjcl.exception.corrupt("ocb: tag doesn't match"));return r.concat(h.clamp(m,p))},pmac:function(a,b){var c,d=sjcl.mode.ocb2.G,e=sjcl.bitArray,f=e.k,g=[0,0,0,0],h=a.encrypt([0,0,0,0]),h=f(h,d(d(h)));for(c=0;c+4<b.length;c+=4)h=d(h),g=f(g,a.encrypt(f(h,b.slice(c,c+4))));c=b.slice(c);128>e.bitLength(c)&&(h=f(h,d(h)),c=e.concat(c,[-2147483648,0,0,0]));g=f(g,c);return a.encrypt(f(d(f(h,
|
|
||||||
d(h))),g))},G:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^135*(a[0]>>>31)]}};
|
|
||||||
sjcl.mode.gcm={name:"gcm",encrypt:function(a,b,c,d,e){var f=b.slice(0);b=sjcl.bitArray;d=d||[];a=sjcl.mode.gcm.n(!0,a,f,d,c,e||128);return b.concat(a.data,a.tag)},decrypt:function(a,b,c,d,e){var f=b.slice(0),g=sjcl.bitArray,h=g.bitLength(f);e=e||128;d=d||[];e<=h?(b=g.bitSlice(f,h-e),f=g.bitSlice(f,0,h-e)):(b=f,f=[]);a=sjcl.mode.gcm.n(u,a,f,d,c,e);g.equal(a.tag,b)||q(new sjcl.exception.corrupt("gcm: tag doesn't match"));return a.data},U:function(a,b){var c,d,e,f,g,h=sjcl.bitArray.k;e=[0,0,0,0];f=b.slice(0);
|
|
||||||
for(c=0;128>c;c++){(d=0!==(a[Math.floor(c/32)]&1<<31-c%32))&&(e=h(e,f));g=0!==(f[3]&1);for(d=3;0<d;d--)f[d]=f[d]>>>1|(f[d-1]&1)<<31;f[0]>>>=1;g&&(f[0]^=-0x1f000000)}return e},f:function(a,b,c){var d,e=c.length;b=b.slice(0);for(d=0;d<e;d+=4)b[0]^=0xffffffff&c[d],b[1]^=0xffffffff&c[d+1],b[2]^=0xffffffff&c[d+2],b[3]^=0xffffffff&c[d+3],b=sjcl.mode.gcm.U(b,a);return b},n:function(a,b,c,d,e,f){var g,h,l,k,n,m,p,s,r=sjcl.bitArray;m=c.length;p=r.bitLength(c);s=r.bitLength(d);h=r.bitLength(e);g=b.encrypt([0,
|
|
||||||
0,0,0]);96===h?(e=e.slice(0),e=r.concat(e,[1])):(e=sjcl.mode.gcm.f(g,[0,0,0,0],e),e=sjcl.mode.gcm.f(g,e,[0,0,Math.floor(h/0x100000000),h&0xffffffff]));h=sjcl.mode.gcm.f(g,[0,0,0,0],d);n=e.slice(0);d=h.slice(0);a||(d=sjcl.mode.gcm.f(g,h,c));for(k=0;k<m;k+=4)n[3]++,l=b.encrypt(n),c[k]^=l[0],c[k+1]^=l[1],c[k+2]^=l[2],c[k+3]^=l[3];c=r.clamp(c,p);a&&(d=sjcl.mode.gcm.f(g,h,c));a=[Math.floor(s/0x100000000),s&0xffffffff,Math.floor(p/0x100000000),p&0xffffffff];d=sjcl.mode.gcm.f(g,d,a);l=b.encrypt(e);d[0]^=l[0];
|
|
||||||
d[1]^=l[1];d[2]^=l[2];d[3]^=l[3];return{tag:r.bitSlice(d,0,f),data:c}}};sjcl.misc.hmac=function(a,b){this.L=b=b||sjcl.hash.sha256;var c=[[],[]],d,e=b.prototype.blockSize/32;this.o=[new b,new b];a.length>e&&(a=b.hash(a));for(d=0;d<e;d++)c[0][d]=a[d]^909522486,c[1][d]=a[d]^1549556828;this.o[0].update(c[0]);this.o[1].update(c[1])};sjcl.misc.hmac.prototype.encrypt=sjcl.misc.hmac.prototype.mac=function(a){a=(new this.L(this.o[0])).update(a).finalize();return(new this.L(this.o[1])).update(a).finalize()};
|
|
||||||
sjcl.misc.pbkdf2=function(a,b,c,d,e){c=c||1E3;(0>d||0>c)&&q(sjcl.exception.invalid("invalid params to pbkdf2"));"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));e=e||sjcl.misc.hmac;a=new e(a);var f,g,h,l,k=[],n=sjcl.bitArray;for(l=1;32*k.length<(d||1);l++){e=f=a.encrypt(n.concat(b,[l]));for(g=1;g<c;g++){f=a.encrypt(f);for(h=0;h<f.length;h++)e[h]^=f[h]}k=k.concat(e)}d&&(k=n.clamp(k,d));return k};
|
|
||||||
sjcl.prng=function(a){this.b=[new sjcl.hash.sha256];this.h=[0];this.F=0;this.t={};this.C=0;this.J={};this.N=this.c=this.i=this.T=0;this.a=[0,0,0,0,0,0,0,0];this.e=[0,0,0,0];this.A=t;this.B=a;this.p=u;this.z={progress:{},seeded:{}};this.l=this.S=0;this.u=1;this.w=2;this.Q=0x10000;this.H=[0,48,64,96,128,192,0x100,384,512,768,1024];this.R=3E4;this.P=80};
|
|
||||||
sjcl.prng.prototype={randomWords:function(a,b){var c=[],d;d=this.isReady(b);var e;d===this.l&&q(new sjcl.exception.notReady("generator isn't seeded"));if(d&this.w){d=!(d&this.u);e=[];var f=0,g;this.N=e[0]=(new Date).valueOf()+this.R;for(g=0;16>g;g++)e.push(0x100000000*Math.random()|0);for(g=0;g<this.b.length&&!(e=e.concat(this.b[g].finalize()),f+=this.h[g],this.h[g]=0,!d&&this.F&1<<g);g++);this.F>=1<<this.b.length&&(this.b.push(new sjcl.hash.sha256),this.h.push(0));this.c-=f;f>this.i&&(this.i=f);this.F++;
|
|
||||||
this.a=sjcl.hash.sha256.hash(this.a.concat(e));this.A=new sjcl.cipher.aes(this.a);for(d=0;4>d&&!(this.e[d]=this.e[d]+1|0,this.e[d]);d++);}for(d=0;d<a;d+=4)0===(d+1)%this.Q&&A(this),e=B(this),c.push(e[0],e[1],e[2],e[3]);A(this);return c.slice(0,a)},setDefaultParanoia:function(a){this.B=a},addEntropy:function(a,b,c){c=c||"user";var d,e,f=(new Date).valueOf(),g=this.t[c],h=this.isReady(),l=0;d=this.J[c];d===t&&(d=this.J[c]=this.T++);g===t&&(g=this.t[c]=0);this.t[c]=(this.t[c]+1)%this.b.length;switch(typeof a){case "number":b===
|
|
||||||
t&&(b=1);this.b[g].update([d,this.C++,1,b,f,1,a|0]);break;case "object":c=Object.prototype.toString.call(a);if("[object Uint32Array]"===c){e=[];for(c=0;c<a.length;c++)e.push(a[c]);a=e}else{"[object Array]"!==c&&(l=1);for(c=0;c<a.length&&!l;c++)"number"!=typeof a[c]&&(l=1)}if(!l){if(b===t)for(c=b=0;c<a.length;c++)for(e=a[c];0<e;)b++,e>>>=1;this.b[g].update([d,this.C++,2,b,f,a.length].concat(a))}break;case "string":b===t&&(b=a.length);this.b[g].update([d,this.C++,3,b,f,a.length]);this.b[g].update(a);
|
|
||||||
break;default:l=1}l&&q(new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string"));this.h[g]+=b;this.c+=b;h===this.l&&(this.isReady()!==this.l&&C("seeded",Math.max(this.i,this.c)),C("progress",this.getProgress()))},isReady:function(a){a=this.H[a!==t?a:this.B];return this.i&&this.i>=a?this.h[0]>this.P&&(new Date).valueOf()>this.N?this.w|this.u:this.u:this.c>=a?this.w|this.l:this.l},getProgress:function(a){a=this.H[a?a:this.B];return this.i>=a?1:this.c>a?1:this.c/
|
|
||||||
a},startCollectors:function(){this.p||(window.addEventListener?(window.addEventListener("load",this.r,u),window.addEventListener("mousemove",this.s,u)):document.attachEvent?(document.attachEvent("onload",this.r),document.attachEvent("onmousemove",this.s)):q(new sjcl.exception.bug("can't attach event")),this.p=!0)},stopCollectors:function(){this.p&&(window.removeEventListener?(window.removeEventListener("load",this.r,u),window.removeEventListener("mousemove",this.s,u)):window.detachEvent&&(window.detachEvent("onload",
|
|
||||||
this.r),window.detachEvent("onmousemove",this.s)),this.p=u)},addEventListener:function(a,b){this.z[a][this.S++]=b},removeEventListener:function(a,b){var c,d,e=this.z[a],f=[];for(d in e)e.hasOwnProperty(d)&&e[d]===b&&f.push(d);for(c=0;c<f.length;c++)d=f[c],delete e[d]},s:function(a){sjcl.random.addEntropy([a.x||a.clientX||a.offsetX||0,a.y||a.clientY||a.offsetY||0],2,"mouse")},r:function(){sjcl.random.addEntropy((new Date).valueOf(),2,"loadtime")}};
|
|
||||||
function C(a,b){var c,d=sjcl.random.z[a],e=[];for(c in d)d.hasOwnProperty(c)&&e.push(d[c]);for(c=0;c<e.length;c++)e[c](b)}function A(a){a.a=B(a).concat(B(a));a.A=new sjcl.cipher.aes(a.a)}function B(a){for(var b=0;4>b&&!(a.e[b]=a.e[b]+1|0,a.e[b]);b++);return a.A.encrypt(a.e)}sjcl.random=new sjcl.prng(6);try{var D=new Uint32Array(32);crypto.getRandomValues(D);sjcl.random.addEntropy(D,1024,"crypto['getRandomValues']")}catch(E){}
|
|
||||||
sjcl.json={defaults:{v:1,iter:1E3,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},encrypt:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,f=e.d({iv:sjcl.random.randomWords(4,0)},e.defaults),g;e.d(f,c);c=f.adata;"string"===typeof f.salt&&(f.salt=sjcl.codec.base64.toBits(f.salt));"string"===typeof f.iv&&(f.iv=sjcl.codec.base64.toBits(f.iv));(!sjcl.mode[f.mode]||!sjcl.cipher[f.cipher]||"string"===typeof a&&100>=f.iter||64!==f.ts&&96!==f.ts&&128!==f.ts||128!==f.ks&&192!==f.ks&&0x100!==f.ks||2>f.iv.length||
|
|
||||||
4<f.iv.length)&&q(new sjcl.exception.invalid("json encrypt: invalid parameters"));"string"===typeof a&&(g=sjcl.misc.cachedPbkdf2(a,f),a=g.key.slice(0,f.ks/32),f.salt=g.salt);"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));"string"===typeof c&&(c=sjcl.codec.utf8String.toBits(c));g=new sjcl.cipher[f.cipher](a);e.d(d,f);d.key=a;f.ct=sjcl.mode[f.mode].encrypt(g,b,f.iv,c,f.ts);return e.encode(f)},decrypt:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json;b=e.d(e.d(e.d({},e.defaults),e.decode(b)),
|
|
||||||
c,!0);var f;c=b.adata;"string"===typeof b.salt&&(b.salt=sjcl.codec.base64.toBits(b.salt));"string"===typeof b.iv&&(b.iv=sjcl.codec.base64.toBits(b.iv));(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||"string"===typeof a&&100>=b.iter||64!==b.ts&&96!==b.ts&&128!==b.ts||128!==b.ks&&192!==b.ks&&0x100!==b.ks||!b.iv||2>b.iv.length||4<b.iv.length)&&q(new sjcl.exception.invalid("json decrypt: invalid parameters"));"string"===typeof a&&(f=sjcl.misc.cachedPbkdf2(a,b),a=f.key.slice(0,b.ks/32),b.salt=f.salt);"string"===
|
|
||||||
typeof c&&(c=sjcl.codec.utf8String.toBits(c));f=new sjcl.cipher[b.cipher](a);c=sjcl.mode[b.mode].decrypt(f,b.ct,b.iv,c,b.ts);e.d(d,b);d.key=a;return sjcl.codec.utf8String.fromBits(c)},encode:function(a){var b,c="{",d="";for(b in a)if(a.hasOwnProperty(b))switch(b.match(/^[a-z0-9]+$/i)||q(new sjcl.exception.invalid("json encode: invalid property name")),c+=d+'"'+b+'":',d=",",typeof a[b]){case "number":case "boolean":c+=a[b];break;case "string":c+='"'+escape(a[b])+'"';break;case "object":c+='"'+sjcl.codec.base64.fromBits(a[b],
|
|
||||||
0)+'"';break;default:q(new sjcl.exception.bug("json encode: unsupported type"))}return c+"}"},decode:function(a){a=a.replace(/\s/g,"");a.match(/^\{.*\}$/)||q(new sjcl.exception.invalid("json decode: this isn't json!"));a=a.replace(/^\{|\}$/g,"").split(/,/);var b={},c,d;for(c=0;c<a.length;c++)(d=a[c].match(/^(?:(["']?)([a-z][a-z0-9]*)\1):(?:(\d+)|"([a-z0-9+\/%*_.@=\-]*)")$/i))||q(new sjcl.exception.invalid("json decode: this isn't json!")),b[d[2]]=d[3]?parseInt(d[3],10):d[2].match(/^(ct|salt|iv)$/)?
|
|
||||||
sjcl.codec.base64.toBits(d[4]):unescape(d[4]);return b},d:function(a,b,c){a===t&&(a={});if(b===t)return a;for(var d in b)b.hasOwnProperty(d)&&(c&&(a[d]!==t&&a[d]!==b[d])&&q(new sjcl.exception.invalid("required parameter overridden")),a[d]=b[d]);return a},X:function(a,b){var c={},d;for(d in a)a.hasOwnProperty(d)&&a[d]!==b[d]&&(c[d]=a[d]);return c},W:function(a,b){var c={},d;for(d=0;d<b.length;d++)a[b[d]]!==t&&(c[b[d]]=a[b[d]]);return c}};sjcl.encrypt=sjcl.json.encrypt;sjcl.decrypt=sjcl.json.decrypt;
|
|
||||||
sjcl.misc.V={};sjcl.misc.cachedPbkdf2=function(a,b){var c=sjcl.misc.V,d;b=b||{};d=b.iter||1E3;c=c[a]=c[a]||{};d=c[d]=c[d]||{firstSalt:b.salt&&b.salt.length?b.salt.slice(0):sjcl.random.randomWords(2,0)};c=b.salt===t?d.firstSalt:b.salt;d[c]=d[c]||sjcl.misc.pbkdf2(a,c,b.iter);return{key:d[c].slice(0),salt:c.slice(0)}};
|
|
0
nbproject/private/config.properties
Normal file
0
nbproject/private/config.properties
Normal file
6
nbproject/private/private.properties
Normal file
6
nbproject/private/private.properties
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
copy.src.files=false
|
||||||
|
copy.src.on.open=false
|
||||||
|
copy.src.target=/var/www/croodleEAK
|
||||||
|
index.file=
|
||||||
|
run.as=LOCAL
|
||||||
|
url=http://localhost/croodleEAK/
|
6
nbproject/private/private.xml
Normal file
6
nbproject/private/private.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
|
||||||
|
<open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/2">
|
||||||
|
<group/>
|
||||||
|
</open-files>
|
||||||
|
</project-private>
|
14
nbproject/project.properties
Normal file
14
nbproject/project.properties
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.expand-tabs=true
|
||||||
|
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.indent-shift-width=2
|
||||||
|
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.spaces-per-tab=2
|
||||||
|
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.tab-size=2
|
||||||
|
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width=80
|
||||||
|
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-line-wrap=none
|
||||||
|
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.usedProfile=project
|
||||||
|
include.path=${php.global.include.path}
|
||||||
|
php.version=PHP_53
|
||||||
|
source.encoding=UTF-8
|
||||||
|
src.dir=.
|
||||||
|
tags.asp=false
|
||||||
|
tags.short=false
|
||||||
|
web.root=.
|
9
nbproject/project.xml
Normal file
9
nbproject/project.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://www.netbeans.org/ns/project/1">
|
||||||
|
<type>org.netbeans.modules.php.project</type>
|
||||||
|
<configuration>
|
||||||
|
<data xmlns="http://www.netbeans.org/ns/php-project/1">
|
||||||
|
<name>croodle EAK</name>
|
||||||
|
</data>
|
||||||
|
</configuration>
|
||||||
|
</project>
|
56
package.json
Normal file
56
package.json
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
{
|
||||||
|
"name": "croodle",
|
||||||
|
"namespace": "croodle",
|
||||||
|
"APIMethod": "stub",
|
||||||
|
"proxyURL": "http://localhost:8000",
|
||||||
|
"proxyPath": "/api",
|
||||||
|
"docURL": "http://localhost:8000/docs/index.html",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"directories": {
|
||||||
|
"doc": "doc",
|
||||||
|
"test": "test"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "grunt server",
|
||||||
|
"build": "grunt build:debug",
|
||||||
|
"test": "grunt test:ci",
|
||||||
|
"postinstall": "bower install"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/stefanpenner/ember-app-kit.git"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"express": "~3.4.8",
|
||||||
|
"lockfile": "~0.4.2",
|
||||||
|
"bower": "~1.3.2",
|
||||||
|
"grunt": "~0.4.2",
|
||||||
|
"grunt-cli": "~0.1.9",
|
||||||
|
"load-grunt-config": "~0.7.0",
|
||||||
|
"grunt-contrib-watch": "~0.5.3",
|
||||||
|
"grunt-contrib-copy": "~0.4.1",
|
||||||
|
"grunt-contrib-concat": "~0.3.0",
|
||||||
|
"grunt-contrib-clean": "~0.5.0",
|
||||||
|
"grunt-contrib-jshint": "~0.8.0",
|
||||||
|
"grunt-contrib-uglify": "~0.2.7",
|
||||||
|
"grunt-contrib-cssmin": "~0.6.2",
|
||||||
|
"grunt-preprocess": "~3.0.1",
|
||||||
|
"grunt-es6-module-transpiler": "~0.6.0",
|
||||||
|
"grunt-concat-sourcemap": "~0.4.0",
|
||||||
|
"grunt-concurrent": "~0.4.3",
|
||||||
|
"grunt-usemin": "~0.1.13",
|
||||||
|
"grunt-rev": "~0.1.0",
|
||||||
|
"grunt-ember-templates": "~0.4.18",
|
||||||
|
"grunt-contrib-testem": "~0.5.16",
|
||||||
|
"express-namespace": "~0.1.1",
|
||||||
|
"request": "~2.33.0",
|
||||||
|
"loom-generators-ember-appkit": "~1.0.5",
|
||||||
|
"originate": "~0.1.5",
|
||||||
|
"loom": "~3.1.2",
|
||||||
|
"connect-livereload": "~0.3.1",
|
||||||
|
"grunt-es6-import-validate": "0.0.6"
|
||||||
|
}
|
||||||
|
}
|
0
public/assets/.gitkeep
Normal file
0
public/assets/.gitkeep
Normal file
15
public/crossdomain.xml
Normal file
15
public/crossdomain.xml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
|
||||||
|
<cross-domain-policy>
|
||||||
|
<!-- Read this: www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html -->
|
||||||
|
|
||||||
|
<!-- Most restrictive policy: -->
|
||||||
|
<site-control permitted-cross-domain-policies="none"/>
|
||||||
|
|
||||||
|
<!-- Least restrictive policy: -->
|
||||||
|
<!--
|
||||||
|
<site-control permitted-cross-domain-policies="all"/>
|
||||||
|
<allow-access-from domain="*" to-ports="*" secure="false"/>
|
||||||
|
<allow-http-request-headers-from domain="*" headers="*" secure="false"/>
|
||||||
|
-->
|
||||||
|
</cross-domain-policy>
|
0
public/data/index.html
Normal file
0
public/data/index.html
Normal file
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue