Copied starter Meteor App files.

Cut and paste of the BasicMeteorApp.
This commit is contained in:
2018-07-30 14:15:39 -07:00
parent b65fc15fb8
commit 94000458e4
89 changed files with 27017 additions and 1 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
node_modules
.idea
private

View File

@@ -0,0 +1,17 @@
# This file contains information which helps Meteor properly upgrade your
# app when you run 'meteor update'. You should check it into version control
# with your project.
notices-for-0.9.0
notices-for-0.9.1
0.9.4-platform-file
notices-for-facebook-graph-api-2
1.2.0-standard-minifiers-package
1.2.0-meteor-platform-split
1.2.0-cordova-changes
1.2.0-breaking-changes
1.3.0-split-minifiers-package
1.4.0-remove-old-dev-bundle-link
1.4.1-add-shell-server-package
1.4.3-split-account-service-packages
1.5-add-dynamic-import-package

1
.meteor/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
local

7
.meteor/.id Normal file
View File

@@ -0,0 +1,7 @@
# This file contains a token that is unique to your project.
# Check it into your repository along with the rest of this directory.
# It can be used for purposes such as:
# - ensuring you don't accidentally deploy one app on top of another
# - providing package authors with aggregated statistics
1v2pn7n1jbklfu1hg4tnm

67
.meteor/packages Normal file
View File

@@ -0,0 +1,67 @@
# Meteor packages used by this project, one per line.
# Check this file (and the other files in this directory) into your repository.
#
# 'meteor add' and 'meteor remove' will edit this file for you,
# but you can also edit it by hand.
meteor-base@1.3.0 # Packages every Meteor app needs to have
mobile-experience@1.0.5 # Packages for a great mobile UX
mongo@1.4.2 # The database Meteor supports right now
blaze-html-templates@1.0.4 # Compile .html files into Meteor Blaze views
reactive-var@1.0.11 # Reactive variable for tracker
reactive-dict@1.2.0 # ???
tracker@1.1.3 # Meteor's client-side reactive programming library
tomwasd:history-polyfill # Adds IE 8/9 support for HTML5 history.
email@1.2.3 # Adds the Meteor/Email package for sending lost password emails
standard-minifier-css@1.4.0 # CSS minifier run for production mode
standard-minifier-js@2.3.1 # JS minifier run for production mode
es5-shim@4.7.0 # ECMAScript 5 compatibility for older browsers.
poorvavyas:es6-shim
ecmascript@0.10.0 # Enable ECMAScript2015+ syntax in app code
#accounts-ui
#accounts-base
accounts-password@1.5.0
useraccounts:core
useraccounts:unstyled
useraccounts:flow-routing # Configures email flows. Used for AccountsTemplates class.
alanning:roles # Adds roles to the user mix. https://atmospherejs.com/alanning/roles && https://github.com/alanning/meteor-roles/blob/master/examples/flow-router/
kadira:flow-router
arillo:flow-router-helpers # Provides various template helpers such as {{pathFor 'templateName'}}
#tomwasd:flow-router-seo
kadira:blaze-layout
shell-server@0.3.1 # ???
meteortoys:allthings
session@1.1.7
##browser-policy # Adds support for specifying browser level security rules related to content and what's allowed to laod on the page.
check@1.3.0 # Allows for checking the structure and types of arguments passed to Meteor methods and publications.
#audit-argument-checks # Used in combination with the Check package for checking the structure and types of arguments passed to Meteor methods and publications. Automatically alerts when a method or publication does not use a check() call.
aldeed:simple-schema@1.5.3
aldeed:collection2
#matb33:collection-hooks # Allows the collections to register handlers that run before or after database interactions.
#zimme:collection-softremovable
#aldeed:autoform@5.8.1
#aldeed:collection2-core@2.0.1
#aldeed:schema-deny # Addon for Collection2-core to use denyInsert or denyUpdate options.
#aldeed:schema-index # Addon for Collection2-core to use index or unique options.
#skehoe1989:autoform-relations # Adds relations to autoform
#twbs:bootstrap # Requires jquery 1.9-2.x, not 3+
fortawesome:fontawesome
momentjs:moment
mizzao:bootboxjs # ???
aldeed:template-extension
juliancwirko:s-alert # Client error/alert handling
jcbernack:reactive-aggregate # Allows us to create a new client collection (from the server) with the contents being an aggregate of server data. Note that aggregation can only be done on the server currently as mini-mongo does not support it.
ostrio:logger
ostrio:loggermongo
dynamic-import@0.3.0
markdown@1.0.12
wcrisman:jquery-custom-scrollbar
stylus@=2.513.14 # This package is no longer supported, but it still works and is available. It provides support for reading .styl files on the client and converting them to CSS on the fly. The alternative would be to compile the styl files to CSS on the server ahead of time.

2
.meteor/platforms Normal file
View File

@@ -0,0 +1,2 @@
server
browser

1
.meteor/release Normal file
View File

@@ -0,0 +1 @@
METEOR@1.6.1

140
.meteor/versions Normal file
View File

@@ -0,0 +1,140 @@
accounts-base@1.4.2
accounts-password@1.5.0
alanning:roles@1.2.16
aldeed:collection2@2.10.0
aldeed:collection2-core@1.2.0
aldeed:schema-deny@1.1.0
aldeed:schema-index@1.1.1
aldeed:simple-schema@1.5.3
aldeed:template-extension@4.1.0
allow-deny@1.1.0
arillo:flow-router-helpers@0.5.2
autoupdate@1.4.0
babel-compiler@7.0.0
babel-runtime@1.2.2
base64@1.0.10
binary-heap@1.0.10
blaze@2.3.2
blaze-html-templates@1.1.2
blaze-tools@1.0.10
boilerplate-generator@1.4.0
caching-compiler@1.1.11
caching-html-compiler@1.1.2
callback-hook@1.1.0
check@1.3.0
coffeescript@1.0.17
ddp@1.4.0
ddp-client@2.3.1
ddp-common@1.4.0
ddp-rate-limiter@1.0.7
ddp-server@2.1.2
deps@1.0.12
diff-sequence@1.1.0
dynamic-import@0.3.0
ecmascript@0.10.0
ecmascript-runtime@0.5.0
ecmascript-runtime-client@0.6.0
ecmascript-runtime-server@0.5.0
ejson@1.1.0
email@1.2.3
es5-shim@4.7.3
fortawesome:fontawesome@4.7.0
geojson-utils@1.0.10
hot-code-push@1.0.4
html-tools@1.0.11
htmljs@1.0.11
http@1.4.0
id-map@1.1.0
jcbernack:reactive-aggregate@0.7.0
jquery@1.11.10
juliancwirko:s-alert@3.2.0
kadira:blaze-layout@2.3.0
kadira:flow-router@2.12.1
launch-screen@1.1.1
livedata@1.0.18
localstorage@1.2.0
logging@1.1.19
markdown@1.0.12
mdg:validation-error@0.2.0
meteor@1.8.2
meteor-base@1.3.0
meteorhacks:aggregate@1.3.0
meteorhacks:collection-utils@1.2.0
meteortoys:allthings@4.0.0
meteortoys:authenticate@4.0.0
meteortoys:autopub@4.0.0
meteortoys:blueprint@4.0.0
meteortoys:email@4.0.0
meteortoys:hotreload@4.0.0
meteortoys:listen@4.0.0
meteortoys:method@4.0.0
meteortoys:mobile@4.0.0
meteortoys:pub@4.0.0
meteortoys:result@4.0.0
meteortoys:shell@4.0.0
meteortoys:status@4.0.0
meteortoys:sub@4.0.0
meteortoys:throttle@4.0.0
meteortoys:toggle@4.0.0
meteortoys:toykit@4.0.1
minifier-css@1.3.0
minifier-js@2.3.1
minimongo@1.4.3
mizzao:bootboxjs@4.4.0
mobile-experience@1.0.5
mobile-status-bar@1.0.14
modules@0.11.3
modules-runtime@0.9.2
momentjs:moment@2.20.1
mongo@1.4.2
mongo-dev-server@1.1.0
mongo-id@1.0.6
mongo-livedata@1.0.12
msavin:jetsetter@4.0.0
msavin:mongol@4.0.1
npm-bcrypt@0.9.3
npm-mongo@2.2.34
observe-sequence@1.0.16
ordered-dict@1.1.0
ostrio:logger@2.0.6
ostrio:loggermongo@2.0.3
poorvavyas:es6-shim@0.21.1
promise@0.10.1
raix:eventemitter@0.1.3
random@1.1.0
rate-limit@1.0.8
reactive-dict@1.2.0
reactive-var@1.0.11
reload@1.2.0
retry@1.1.0
routepolicy@1.0.12
server-render@0.3.0
service-configuration@1.0.11
session@1.1.7
sha@1.0.9
shell-server@0.3.1
shim-common@0.1.0
socket-stream-client@0.1.0
softwarerero:accounts-t9n@1.3.11
spacebars@1.0.15
spacebars-compiler@1.1.3
srp@1.0.10
standard-minifier-css@1.4.0
standard-minifier-js@2.3.1
stylus@2.513.14
templating@1.3.2
templating-compiler@1.3.3
templating-runtime@1.3.2
templating-tools@1.1.2
tomwasd:history-polyfill@0.0.1
tracker@1.1.3
ui@1.0.13
underscore@1.0.10
url@1.2.0
useraccounts:core@1.14.2
useraccounts:flow-routing@1.14.2
useraccounts:unstyled@1.14.2
wcrisman:jquery-custom-scrollbar@3.0.0
webapp@1.5.0
webapp-hashing@1.0.9
zimme:active-route@2.3.2

19
README IMPORT.md Normal file
View File

@@ -0,0 +1,19 @@
To import data, place the data in the /private folder, then write a script in the /server folder (javascript).
The script file should define Meteor methods:
```
Meteor.methods({
"importSomethingOfMine": function() {
//Your script here.
}
});
```
Then run meteor.
Then at the terminal (in your WebStorm UI, or at a command line (at the project root path), run `meteor shell` to open a server side shell.
Once in the shell, you can write javascript code that will be executed.
Type `Meteor.call('importSomethingOfMine')` and hit the enter key and it will execute the method you defined earlier.

View File

@@ -1 +1,9 @@
AVEF
Clone this project as a separate project and rename it. In WebStorm, right click the project and rename the project.
Next run "meteor" from the command line within WebStorm (or at the command line in the project outside a development environment). This will install what is needed and run the server.
Finally, run a browser and direct it at localhost:3000 (this is linked in the console after starting the server).
Run `meteor update` to get the latest version of meteor installed for this application. This can take a fair bit of time to finish.
Run `meteor npm install --save xxxxx` to install npm packages.

61
client/bootstrap.styl vendored Normal file
View File

@@ -0,0 +1,61 @@
textarea:focus,
input[type="text"]:focus,
input[type="password"]:focus,
input[type="datetime"]:focus,
input[type="datetime-local"]:focus,
input[type="date"]:focus,
input[type="month"]:focus,
input[type="time"]:focus,
input[type="week"]:focus,
input[type="number"]:focus,
input[type="email"]:focus,
input[type="url"]:focus,
input[type="search"]:focus,
input[type="tel"]:focus,
input[type="color"]:focus,
.uneditable-input:focus,
.list-group:focus
border-color: rgba(82, 168, 236, 0.8)
outline: 0
outline: thin dotted \9
/* IE6-9 */
/*
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6);
-moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6);
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6);
*/
-webkit-box-shadow: 0px 0px 46px -13px rgba(230,28,230,1) !important
-moz-box-shadow: 0px 0px 46px -13px rgba(230,28,230,1) !important
box-shadow: 0px 0px 46px -13px rgba(230,28,230,1) !important
.form-control
font-size: 14px
margin-bottom: 0px
.input-group
margin-bottom: 15px
.select2 .select2-selection
border-color: #ccc
.select2-container--default.select2-container--focus .select2-selection--multiple
border-color: rgba(101, 174, 231, 0.823529)
outline: 0
outline: thin dotted \9
-webkit-box-shadow: 0px 0px 46px -11px rgba(230,28,230,1) !important
-moz-box-shadow: 0px 0px 46px -11px rgba(230,28,230,1) !important
box-shadow: 0px 0px 46px -11px rgba(230,28,230,1) !important
.has-error
border-color: #a94442 !important
/*
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075) !important;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075) !important;
*/
/*
-webkit-box-shadow: 0px 0px 46px -13px rgba(255,28,230,1) !important;
-moz-box-shadow: 0px 0px 46px -13px rgba(255,28,230,1) !important;
box-shadow: 0px 0px 46px -13px rgba(255,28,230,1) !important;
*/

64
client/client.js Normal file
View File

@@ -0,0 +1,64 @@
import {Meteor} from 'meteor/meteor';
import '/imports/startup/client';
import '/imports/startup/both';
import '/imports/api';
import '/imports/ui/helpers.js';
// import '/imports/util/normalize.css';
import '/imports/util/validator.js';
import '/imports/util/polyfills/blaze.js';
import '/imports/util/polyfills/regex.js';
import '/imports/util/polyfills/date.js';
import '/imports/util/polyfills/array.js';
import '/imports/util/de.combo.js';
import '/imports/util/resize/ResizeSensor.js';
import '/imports/util/resize/ElementQueries.js';
import '/imports/ui/layouts/Public.js';
import '/imports/ui/layouts/Admin.js';
import '/imports/ui/layouts/Login.js';
import '/imports/ui/accounts/accounts.js';
import '/imports/util/select2/select2.css';
import '/imports/util/select2/select2.full.js';
//The SweetAlert2 NPM package is where this is being pulled from. The js file that actually wants to use it should import it (see Sales.js).
import 'sweetalert2/dist/sweetalert2.min.css';
import '/imports/util/simplegrid.css';
import 'dragula/dist/dragula.css';
//import 'malihu-custom-scrollbar-plugin/jquery.mCustomScrollbar.css';
Blaze._allowJavascriptUrls();
Meteor.subscribe("measures");
Meteor.subscribe("venues");
Meteor.subscribe("categories");
Meteor.subscribe("subcategories");
Meteor.subscribe("items");
Meteor.startup(function () {
sAlert.config({
effect: '',
position: 'bottom-right',
timeout: 5000,
html: false,
onRouteClose: true,
stack: true,
// or you can pass an object:
// stack: {
// spacing: 10 // in px
// limit: 3 // when fourth alert appears all previous ones are cleared
// }
offset: 0, // in px - will be added to first alert (bottom or top - depends of the position in config)
beep: false,
// examples:
// beep: '/beep.mp3' // or you can pass an object:
// beep: {
// info: '/beep-info.mp3',
// error: '/beep-error.mp3',
// success: '/beep-success.mp3',
// warning: '/beep-warning.mp3'
// }
onClose: _.noop //
// examples:
// onClose: function() {
// /* Code here will be executed once the alert closes. */
// }
});
});

6
client/head.html Normal file
View File

@@ -0,0 +1,6 @@
<head>
<title>PT App</title>
<!--<meta http-equiv="content-type" content="text/html; charset=UTF8">-->
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="UTF8">
</head>

147
client/main.styl Normal file
View File

@@ -0,0 +1,147 @@
@import url('//fonts.googleapis.com/css?family=PT+Sans|Grand+Hotel|Open+Sans:400,600');
*
-webkit-tap-highlight-color: transparent
-webkit-font-smoothing: antialiased
*, *:after, *:before
-webkit-box-sizing: border-box
-moz-box-sizing: border-box
box-sizing: border-box
padding: 0
margin: 0
html
scrollbar-face-color: #808080
scrollbar-highlight-color: #808080
scrollbar-3dlight-color: #707070
scrollbar-darkshadow-color: #808080
scrollbar-shadow-color: #7e7e7e
scrollbar-arrow-color: #ffffff
scrollbar-track-color: #505050
height: 100%
min-height: 100%
body
font-family: verdana, arial, helvetica, sans-serif
font-size: 1.0em
height: 100%
min-height: 100%
background: #F6F6F6
#__blaze-root
height: 100%
//Standard Stylings
.noselect
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Chrome/Safari/Opera */
-khtml-user-select: none; /* Konqueror */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently not supported by any browser */
.clickable
cursor: pointer
.nonclickable
cursor: default
.left
text-align: left
.right
text-align: right
.center
text-align: center
.floatLeft
float: left
.floatRight
float: right
//Table Styles
.table
padding: 0
margin: 0
border-collapse: collapse
border: 1px solid #ddddf9
> thead
> tr
> th
border: 0
padding: 4px 4px 8px 4px
vertical-align: top
color: white
background: #6f6fec
input
padding: 2px
border-radius: 3px
> tbody
> tr
border-bottom: 1px solid #aaa
> td
padding: 4px 4px
> tr.selected
background-attachment: fixed
background-repeat: no-repeat
background-position: 0 0
background-image: linear-gradient(to left, #E0DCBA 70%,#f1da36 100%)
> tr:nth-child(odd).selected
background-attachment: fixed
background-repeat: no-repeat
background-position: 0 0
background-image: linear-gradient(to left, #FCF8D1 70%,#f1da36 100%)
.table-striped > tbody > tr:nth-child(even)
background-color: #f4f4f4
.table-striped > tbody > tr:nth-child(odd)
background-color: white
.table-hover > tbody > tr:hover
background-color: #ded
.pagination
text-align: right
font-size: 15px
line-height: 34px
font-family: "Arial Black", "Arial Bold", Gadget, sans-serif
margin: 0 0 10px 0
overflow: visible
white-space: nowrap
display: inline-block
span
padding: 2px 8px 3px 8px
margin: 0 8px
border: 2px solid #7b9961
border-radius: 5px
background-color: #90b272
cursor: pointer
overflow: visible
whitespace: nowrap
span:hover
background-color: #4ca84c
span:active
background-color: #3c983c
span.disabled
background-color: #ccc
border-color: #c5c5c5
color: white
cursor: default
span.disabled:hover
background-color: #ccc
span.disabled:active
background-color: #ccc
// Keep the custom scroll bars on top so they can be interacted with. They are placed outside the content div that they scroll.
.mCSB_1_scrollbar
z-index: 999
@import "../imports/ui/styles/effects.import.styl"
@import "../imports/ui/styles/buttons.import.styl"
@import "../imports/ui/styles/maxHeightLayout.import.styl"
@import "../imports/ui/styles/tabs.import.styl"
@import "../imports/ui/styles/forms.import.styl"
@import "../imports/util/de.combo.import.styl"
@import "../imports/util/bootstrap-like-btn.import.styl"
@import "../imports/ui/layouts/Public.import.styl"
@import "../imports/ui/layouts/Admin.import.styl"
@import "../imports/ui/layouts/Login.import.styl"
@import "../imports/ui/Home.import.styl"
@import "../imports/ui/AdminHome.import.styl"
@import "../imports/ui/UserManagement.import.styl"

1245
client/titatoggle-dist.css Normal file

File diff suppressed because it is too large Load Diff

17
imports/api/Roles.js Normal file
View File

@@ -0,0 +1,17 @@
if(Meteor.isServer) {
Meteor.publish('roles', function() {
if(Roles.userIsInRole(this.userId, ['manage'])) {
return Meteor.roles.find({}, {fields: {name: 1}});
}
else throw new Meteor.Error(403, "Not authorized to view roles.");
});
}
let ROLE_MANAGE = "manage";
let ROLE_UPDATE = "update";
Meteor.UserRoles = {ROLE_MANAGE, ROLE_UPDATE};
export default Meteor.roles;

69
imports/api/User.js Normal file
View File

@@ -0,0 +1,69 @@
import {Random} from 'meteor/random';
if(Meteor.isServer) {
Meteor.publish('users', function() {
if(Roles.userIsInRole(this.userId, ['manage'])) {
return Meteor.users.find({}, {fields: {username: 1, emails: 1, roles: 1}});
}
else throw new Meteor.Error(403, "Not authorized to view users.");
});
Meteor.methods({
"insertUser": function(user) {
check(user, {
username: String,
email: String,
roles: [String]
});
//Verify the currently logged in user has authority to manage users.
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_MANAGE])) {
//Verify the user name isn't already used.
if(Meteor.collections.Users.findOne({username: user.username}) === undefined) {
let pwd = Random.secret(20);
let id = Accounts.createUser({password: pwd, username: user.username, email: user.email});
//Requires the alanning:roles package.
Roles.addUsersToRoles(id, user.roles);
}
else {
throw new Meteor.Error(400, "User already exists.");
}
}
else throw new Meteor.Error(403, "Not authorized to add users.");
},
"updateUser": function(user) {
check(user, {
_id: String,
username: String,
emails: [{
address: String,
verified: Boolean
}],
roles: [String]
});
//Verify the currently logged in user has authority to manage users.
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_MANAGE])) {
//Verify the user name isn't already used with a different ID.
if(Meteor.collections.Users.findOne({username: user.username, _id: {$ne: user._id}}) == undefined) {
//Update the user. Note: I am using direct mongo modification, versus attempting to go through the Accounts and Roles objects. This could cause problems in the future if these packages change their data structures.
Meteor.collections.Users.update(user._id, {$set: {username: user.username, emails: user.emails, roles: user.roles}});
}
else {
throw new Meteor.Error(400, "User name already exists.");
}
}
else throw new Meteor.Error(403, "Not authorized to update users.");
},
"deleteUser": function(id) {
check(id, String);
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_MANAGE])) {
Meteor.collections.Users.remove(id);
}
else throw new Meteor.Error(403, "Not authorized to remove users.");
}
});
}
export default Meteor.users;

23
imports/api/index.js Normal file
View File

@@ -0,0 +1,23 @@
import Users from "./User.js";
import UserRoles from "./Roles.js";
//Save the collections in the Meteor.collections property for easy access without name conflicts.
Meteor.collections = {Users, UserRoles};
//If this is the server then setup the default admin user if none exist.
if(Meteor.isServer) {
//Change this to find admin users, create a default admin user if none exists.
if(Users.find({}).count() == 0) {
try {
console.log("Creating a default admin user: admin/admin");
let id = Accounts.createUser({password: 'admin', username: 'admin'});
//Requires the alanning:roles package.
Roles.addUsersToRoles(id, [Meteor.UserRoles.ROLE_MANAGE, Meteor.UserRoles.ROLE_UPDATE]);
}
catch(err) {
console.log(err);
}
}
}

View File

@@ -0,0 +1,83 @@
import { AccountsTemplates } from 'meteor/useraccounts:core';
AccountsTemplates.configure({
forbidClientAccountCreation: true, //Turn off client side account creation. The app is expected to have a feature that will do this.
showForgotPasswordLink: true,
defaultTemplate: 'OverrideAtForm',
//defaultTemplate: 'AuthorizationPage', //The template for all the forms related to logging in or out.
defaultLayout: 'Login', //What page template to place the defaultTemplate in.
defaultContentRegion: 'content', //The content region of the page template to place the defaultTemplate in.
defaultLayoutRegions: {},
// defaultLayout: 'Body',
// defaultContentRegion: 'content',
// defaultLayoutRegions: {}
texts: {
title: {
signIn: ""
},
button: {
signIn: "Enter"
}
}
});
// This removes the password field but returns it,
// so that you can re-add it later, preserving the
// desired order of the fields
// let pwd = AccountsTemplates.removeField('password');
// AccountsTemplates.removeField('email');
// AccountsTemplates.addFields([
// {
// _id: "username",
// type: "text",
// displayName: "username",
// required: true,
// minLength: 5,
// },
// pwd
// ]);
let pwd = AccountsTemplates.removeField('password');
AccountsTemplates.removeField('email');
AccountsTemplates.addFields([
{
_id: "username",
type: "text",
displayName: "username",
required: true,
minLength: 5,
},
{
_id: 'email',
type: 'email',
required: true,
displayName: "email",
re: /.+@(.+){2,}\.(.+){2,}/,
errStr: 'Invalid email',
},
{
_id: 'username_and_email',
type: 'text',
required: true,
displayName: "Login",
placeholder: "Login / Email"
},
pwd
]);
//AccountsTemplates.configureRoute('signIn', {
// name: 'signin',
// path: '/Admin/SignIn'
//});
//// AccountsTemplates.configureRoute('signUp', {
//// name: 'join',
//// path: '/join'
//// });
//AccountsTemplates.configureRoute('forgotPwd', {
// name: 'forgotPwd',
// path: '/Admin/ForgotPwd'
//});
//AccountsTemplates.configureRoute('resetPwd', {
// name: 'resetPwd',
// path: '/Admin/ResetPwd'
//});

View File

@@ -0,0 +1 @@
import './accounts.js';

View File

@@ -0,0 +1 @@
import './routes.js';

View File

@@ -0,0 +1,56 @@
require('/imports/startup/both/accounts.js'); //Ensure that the user accounts templates are setup first. We have included the AccountTemplates routing in this document to keep all routing in one place.
//**** GROUPS
let pub = FlowRouter.group({
});
let pri = FlowRouter.group({
//TODO: Require SSL
triggersEnter: [AccountsTemplates.ensureSignedIn]
});
//**** ADMIN
pri.route("/admin", {
triggersEnter: [function(context, redirect) {redirect("/Admin/Home");}]
});
pri.route("/Admin", {
triggersEnter: [function(context, redirect) {redirect("/Admin/Home");}]
});
AccountsTemplates.configureRoute('signIn', {
name: 'SignIn',
path: '/Admin/SignIn'
});
AccountsTemplates.configureRoute('resetPwd', {
name: 'ResetPwd',
path: '/Admin/ResetPwd'
});
AccountsTemplates.configureRoute('forgotPwd', {
name: 'ForgotPwd',
path: '/Admin/ForgotPwd'
});
pri.route("/Admin/Home", {
name: "AdminHome",
action: function(params, queryParams) {
require("/imports/ui/AdminHome.js");
BlazeLayout.render("Admin", {content: "AdminHome"})
}
});
pri.route("/Admin/UserManagement", {
name: "UserManagement",
action: function(params, queryParams) {
require("/imports/ui/UserManagement.js");
BlazeLayout.render("Admin", {content: "UserManagement"})
}
});
//*** PUBLIC
pub.route('/', {
triggersEnter: [function(context, redirect) {redirect("/Home");}]
});
pub.route("/Home", {
name: 'Home',
action: function(params, queryParams) {
require("/imports/ui/Home.js");
BlazeLayout.render("Public", {content: "Home"});
}
});

View File

@@ -0,0 +1,12 @@
Accounts.emailTemplates.from = "Do Not Reply <administrator@declarativeengineering.com>";
Accounts.emailTemplates.siteName = "Petit Teton App";
// Accounts.emailTemplates.verifyEmail.subject = function (user) {
// return "Welcome to My Site! Please verify your email";
// };
//
// Accounts.emailTemplates.verifyEmail.html = function (user, url) {
// return "Hi " + user.profile.firstName + " " + user.profile.lastName + ",\n\n" +
// " Please verify your email by simply clicking the link below:\n\n" +
// url;
// };

View File

@@ -0,0 +1,2 @@
import "./email.js"
import "./../../util/polyfills/date.js"

View File

@@ -0,0 +1,33 @@
let AppVersion = new Mongo.Collection('AppVersion');
let AppVersionSchema = new SimpleSchema({
version: {
type: Number,
optional: false,
defaultValue: 0
}
});
AppVersion.attachSchema(AppVersionSchema);
try {
let appVersions = AppVersion.find({}).fetch();
let appVersion;
if(!appVersions || appVersions.length == 0) { //This will happen only when first creating a database.
appVersion = {version: 0};
appVersion._id = AppVersion.insert(appVersion);
}
else if(appVersions.length > 1) { //This should never happen. Remove all but the first app version.
for(let i = 1; i < appVersions.length; i++) {
AppVersion.remove(appVersions[i]._id);
}
}
else {
appVersion = appVersions[0];
}
}
catch(err) {
console.log("Caught an error while upgrading the app version: " + err);
process.exit(1);
}

View File

@@ -0,0 +1,5 @@
<template name="AdminHome">
<div id="adminHome">
Hello World
</div>
</template>

2
imports/ui/AdminHome.import.styl vendored Normal file
View File

@@ -0,0 +1,2 @@
#adminHome
margin: 20px 40px

2
imports/ui/AdminHome.js Normal file
View File

@@ -0,0 +1,2 @@
import './AdminHome.html';

5
imports/ui/Home.html Normal file
View File

@@ -0,0 +1,5 @@
<template name="Home">
<div id="homePage">
{{#markdown}}This is a test of __markdown__.{{/markdown}}
</div>
</template>

3
imports/ui/Home.import.styl vendored Normal file
View File

@@ -0,0 +1,3 @@
#homePage
display: block
min-height: 300px

2
imports/ui/Home.js Normal file
View File

@@ -0,0 +1,2 @@
import './Home.html';

View File

@@ -0,0 +1,76 @@
<template name="UserManagement">
<div id="userManagement">
{{#if Template.subscriptionsReady}}
<div class="tableControls">
<div class="contentControls">
<a class="loadMoreLink {{#if disableLoadMore}}disabled{{/if}}" href="javascript:">Load More...</a>
</div>
</div>
<div class="separatedTableHeader">
<table class="table table-striped table-hover">
<thead>
<tr>
<th class="username">Username {{>UserSearch columnName='username'}}</th>
<th class="email">Email {{>UserSearch columnName='email' collectionQueryColumnName='name' collection='Items' collectionResultColumnName='_id'}}</th>
<th class="roles">Roles</th>
<th class="actions">Actions <span class="newUserButton btn btn-success"><i class="fa fa-plus-circle" aria-hidden="true"></i><i class="fa fa-times-circle" aria-hidden="true"></i></span></th>
</tr>
</thead>
</table>
</div>
<div class="listRow">
<div class="listCell">
<div class="tableContainer mCustomScrollbar" data-mcs-theme="dark">
<table class="dataTable table table-striped table-hover">
<tbody>
{{#if displayNewUser}}
<tr>{{> UserEditor isNew=true}}</tr>
{{/if}}
{{#each users}}
{{> User}}
{{/each}}
</tbody>
</table>
</div>
</div>
</div>
{{else}}
{{/if}}
</div>
</template>
<template name="User">
<tr>
{{#if editing}}
{{> UserEditor}}
{{else}}
<td class="username tdLarge noselect nonclickable">{{username}}</td>
<td class="email tdLarge noselect nonclickable">{{email}}</td>
<td class="roles tdLarge noselect nonclickable">{{roles}}</td>
<td class="actions center tdLarge"><i class="userRemove fa fa-times-circle fa-lg noselect clickable" aria-hidden="true"></i>&nbsp;/&nbsp;<i class="userEdit fa fa-pencil-square-o fa-lg noselect clickable" aria-hidden="true"></i></td>
{{/if}}
</tr>
</template>
<template name="UserEditor">
<td colspan="3" class="userEditor measureEditorTd">
<div>
<div class="username editorDiv"><label>User Name:</label><input name="username" class="form-control" type="text" value="{{username}}" autocomplete="off" required></div>
<div class="email editorDiv"><label>User Email:</label><input name="email" class="form-control" type="text" value="{{email}}" autocomplete="off" required></div>
<div class="rolesContainer editorDiv"><label>Roles:</label>
<div class="roles center" style="font-size: 1.2em">
{{#each allRoles}}
<span class="role {{getRoleState this}} noselect">{{name}}</span>
{{/each}}
</div>
</div>
</div>
</td>
<td class="actions center measureEditorTd"><i class="editorApply fa fa-check-square-o fa-lg noselect clickable" aria-hidden="true"></i>&nbsp;/&nbsp;<i class="editorCancel fa fa-times-circle fa-lg noselect clickable" aria-hidden="true"></i></td>
</template>
<template name="UserSearch">
<div class="">
<input type="text" class="searchInput" placeholder="Filter..." value="{{searchValue}}" style="width: 90%"/>
</div>
</template>

145
imports/ui/UserManagement.import.styl vendored Normal file
View File

@@ -0,0 +1,145 @@
#userManagement
display: table
content-box: border-box
padding: 10px 20px
height: 100%
width: 100%
text-align: left
.tableControls
display: table
width: 100%
text-align: right
margin-right: 20px
.contentControls
vertical-align: bottom
display: table-cell
text-align: right
min-width: 100px
a
font-size: 12px
font-family: "Arial", san-serif
font-weight: 800
color: #2d1b8c
text-decoration: none
a:hover
text-decoration: underline
a.disabled
visibility: hidden
.editor
height: 100%
overflow-y: auto
.insert
flex: none
width: 100%
.col-md-6
padding: 10px 30px 0 30px
background: #EFEFEF
border-radius: 1em
.formGroupHeading
font-size: 1.6em
font-family: "Arial Black", "Arial Bold", Gadget, sans-serif
font-style: normal
font-variant: normal
font-weight: 500
.table
table-layout: fixed
min-width: 100%
thead, tbody
> tr
> .username
width: 50%
min-width: 100px
> .email
width: 50%
min-width: 100px
> .roles
width: 260px
min-width: 260px
> .actions
width: 80px
min-width: 80px
.separatedTableHeader
.actions
text-align: center
.newUserButton
margin-top: 4px
padding: 0 12px
.fa-plus-circle
display: inline-block
.fa-times-circle
display: none
.newUserButton.active
background-color: #fb557b
color: black
.fa-times-circle
display: inline-block
.fa-plus-circle
display: none
.listRow
display: table-row
.listCell
display: table-cell
position: relative
height: 100%
width: 100%
.tableContainer
position: absolute
top: 0
bottom: 0
left: 0
right: 0
width: auto
height: auto
border: 0
font-size: 12.5px
overflow-y: auto
table
table-layout: fixed
width: 100%
thead
display: none
visibility: hidden
.userRemove
color: red
.userEdit
color: darkblue
.editorApply
color: green
.editorCancel
color: red
.userEditor > div
display: table
> div
display: table-cell
padding: 10px
.roles
.role
vertical-align: middle
td.roles
.role
padding: 4px 4px
border: 1px solid #555
border-radius: .25em
background: white
color: #999
cursor: pointer
.selected
color: black
div.roles
padding: 4px 0
.role
padding: 4px 4px
border: 1px solid #555
border-radius: .25em
background: white
color: #999
cursor: pointer
.selected
color: black
.center
vertical-align: middle !important

View File

@@ -0,0 +1,223 @@
import './UserManagement.html';
import '/imports/util/selectize/selectize.js'
let QUERY_LIMIT = 100;
let QUERY_LIMIT_INCREMENT = 100;
let PREFIX = "UserManagement";
Tracker.autorun(function() {
Meteor.subscribe("users", Session.get(PREFIX + 'searchQuery'));
Meteor.subscribe("roles");
});
Template.UserManagement.onCreated(function() {
Session.set(PREFIX + "displayNewUser", false);
Session.set(PREFIX + "queryLimit", QUERY_LIMIT);
});
Template.UserManagement.onRendered(function() {
$(".tableContainer").mCustomScrollbar({
scrollButtons: {enable:true},
theme: "light-thick",
scrollbarPosition: "outside",
scrollEasing: "linear"
});
});
Template.UserManagement.helpers({
displayNewUser: function() {
return Session.get(PREFIX + "displayNewUser");
},
users: function() {
let skipCount = Session.get(PREFIX + 'skipCount') || 0;
let query = Session.get(PREFIX + 'searchQuery');
let dbQuery = [];
if(query) {
_.each(_.keys(query), function(key) {
if(_.isFunction(query[key])) dbQuery.push({[key]: query[key]}); //dbQuery[key] = query[key]();
else if(_.isObject(query[key])) dbQuery.push({[key]: query[key]}); //dbQuery[key] = query[key]; //Will look something like: {$in: [xxx,xxx,xxx]}
else if(_.isNumber(query[key])) dbQuery.push({[key]: query[key]}); //dbQuery[key] = query[key];
else {
//dbQuery[key] = {$regex: query[key], $options: 'i'};
let searchValue = query[key];
let searches = searchValue && searchValue.length > 0 ? searchValue.split(/\s+/) : undefined;
for(let search of searches) {
dbQuery.push({[key]: {$regex: '\\b' + search, $options: 'i'}});
}
}
})
}
if(!Session.get(PREFIX + "showHidden")) {
//Ignore any hidden elements by showing those not hidden, or those without the hidden field.
dbQuery.push({$or: [{hidden: false}, {hidden: {$exists:false}}]});
}
dbQuery = dbQuery.length > 0 ? {$and: dbQuery} : {};
Session.set(PREFIX + 'userCount', Meteor.collections.Users.find(dbQuery).count()); //Always get a full count.
return Meteor.collections.Users.find(dbQuery, {limit: Session.get(PREFIX + "queryLimit"), skip: skipCount, sort: {username: 1}});
},
disableLoadMore: function() {
return Session.get(PREFIX + 'userCount') - (Session.get(PREFIX + 'skipCount') || 0) - Session.get(PREFIX + "queryLimit") <= 0;
}
});
Template.UserManagement.events({
'click .loadMoreLink': function(event, template) {
event.preventDefault();
Session.set(PREFIX + 'queryLimit', Session.get(PREFIX + "queryLimit") + QUERY_LIMIT_INCREMENT);
},
'click .newUserButton': function(event, template) {
if(template.$('.newUserButton').hasClass('active')) {
Session.set(PREFIX + 'displayNewUser', false);
}
else {
Session.set(PREFIX + 'displayNewUser', true);
Session.set(PREFIX + "editedUser", undefined); //Clear the edited user so that only one editor is open at a time.
}
template.$('.newUserButton').toggleClass('active');
}
});
Template.User.onCreated(function() {
this.edited = new ReactiveVar();
});
Template.User.events({
"click .userEdit": function(event, template) {
//template.edited.set(this);
Session.set(PREFIX + "editedUser", this._id);
Session.set(PREFIX + 'displayNewUser', false); //Ensure the new measure editor is closed.
template.parentTemplate().$('.newUserButton').removeClass('active');
},
"click .userRemove": function(event, template) {
let _this = this;
bootbox.confirm({
message: "Delete the user?",
buttons: {confirm: {label: "Yes", className: 'btn-success'}, cancel: {label: "No", className: "btn-danger"}},
callback: function(result) {
if(result) {
Meteor.call('deleteUser', _this._id, function(error, result) {
if(error) {
sAlert.error(error);
}
else {
sAlert.success("User removed.");
}
});
}
}
});
}
});
Template.User.helpers({
email: function() {
return this.emails && this.emails.length > 0 ? this.emails[0].address : "";
},
editing: function() {
let editedUser = Session.get(PREFIX + "editedUser");
return editedUser == this._id;
}
});
Template.UserEditor.helpers({
email: function() {
return this.emails && this.emails.length > 0 ? this.emails[0].address : "";
},
allRoles: function() {
return Meteor.collections.UserRoles.find();
},
getRoleState: function(role) {
let user = Template.parentData(1);
return !user.isNew && user.roles.includes(role.name) ? "selected" : "";
}
});
Template.UserEditor.events({
"click .editorCancel": function(event, template) {
Session.set(PREFIX + "editedUser", undefined);
Session.set(PREFIX + 'displayNewUser', false);
template.parentTemplate().$('.newUserButton').removeClass('active');
},
"click .editorApply": function(event, template) {
let user = {};
let roles = [];
user.username = template.$('input[name="username"]').val();
user.email = template.$('input[name="email"]').val();
let roleSpans = template.$('.role.selected');
for(let i = 0; i < roleSpans.length; i++) {
roles.push($(roleSpans[i]).text());
}
user.roles = roles;
if(Session.get(PREFIX + 'displayNewUser')) {
Meteor.call('insertUser', user, function(error, result) {
if(error) {
sAlert.error(error);
}
else {
sAlert.success("User created.");
Session.set(PREFIX + 'displayNewUser', false);
template.parentTemplate().$('.newUserButton').removeClass('active');
}
});
}
else {
user._id = this._id;
Meteor.call("updateUser", user, function(error, result) {
if(error) sAlert.error(error);
else {
sAlert.success("User updated.");
Session.set(PREFIX + "editedUser", undefined);
template.parentTemplate().$('.newUserButton').removeClass('active');
}
});
}
},
"click .role": function(event, template) {
$(event.target).toggleClass("selected");
}
});
Template.UserSearch.events({
"keyup .searchInput": _.throttle(function(event, template) {
let searchQuery = Session.get(PREFIX + 'searchQuery') || {};
let searchFields = Session.get(PREFIX + 'searchFields') || {};
let searchValue = template.$('.searchInput').val();
if(searchValue) {
if(this.number) searchValue = parseFloat(searchValue);
if(this.collection) {
let ids = Meteor.collections[this.collection].find({[this.collectionQueryColumnName]: {$regex: searchValue, $options: 'i'}}, {fields: {[this.collectionResultColumnName]: 1}}).fetch();
//Convert the ids to an array of ids instead of an array of objects containing an id.
for(let i = 0; i < ids.length; i++) {ids[i] = ids[i]._id;}
searchQuery[this.columnName] = {$in: ids};
searchFields[this.columnName] = searchValue;
}
else {
searchFields[this.columnName] = searchQuery[this.columnName] = searchValue;
}
}
else {
//Remove columns from the search query whose values are empty so we don't bother the database with them.
delete searchQuery[this.columnName];
delete searchFields[this.columnName];
}
Session.set(PREFIX + 'searchQuery', searchQuery);
Session.set(PREFIX + 'skipCount', 0); //Reset the paging of the results.
}, 500)
});
Template.UserSearch.helpers({
searchValue: function() {
let searchFields = Session.get(PREFIX + 'searchFields');
return (searchFields && searchFields[this.columnName]) ? searchFields[this.columnName] : '';
}
});

View File

@@ -0,0 +1,58 @@
<template name="OverrideAtForm">
{{#unless hide}}
<div class="at-form">
<!--{{#if showTitle}}-->
<!--{{> atTitle}}-->
<!--{{/if}}-->
{{#if showOauthServices}}
{{> atOauth}}
{{/if}}
{{#if showServicesSeparator}}
{{> atSep}}
{{/if}}
{{#if showError}}
{{> atError}}
{{/if}}
{{#if showResult}}
{{> atResult}}
{{/if}}
{{#if showMessage}}
{{> atMessage}}
{{/if}}
{{#if showPwdForm}}
{{> OverrideAtPwdForm}}
{{/if}}
{{#if showTermsLink}}
{{> atTermsLink}}
{{/if}}
{{#if showSignInLink}}
{{> atSigninLink}}
{{/if}}
{{#if showSignUpLink}}
{{> atSignupLink}}
{{/if}}
{{#if showResendVerificationEmailLink}}
{{> atResendVerificationEmailLink}}
{{/if}}
</div>
{{/unless}}
</template>
<template name="OverrideAtPwdForm">
<div class="at-pwd-form">
<form role="form" id="at-pwd-form" novalidate action="#" method="POST">
<fieldset {{disabled}}>
{{#each fields}}
{{> atInput}}
{{/each}}
{{#if showReCaptcha}}
{{> atReCaptcha}}
{{/if}}
{{> atPwdFormBtn}}
{{#if showForgotPasswordLink}}
{{> atPwdLink}}
{{/if}}
</fieldset>
</form>
</div>
</template>

View File

@@ -0,0 +1,18 @@
import { Template } from 'meteor/templating';
import './accounts.html';
// Simply 'inherits' helpers from AccountsTemplates
Template.OverrideAtForm.helpers(AccountsTemplates.atFormHelpers);
// Simply 'inherits' helpers and events from AccountsTemplates
Template.OverrideAtPwdForm.helpers(AccountsTemplates.atPwdFormHelpers);
Template.OverrideAtPwdForm.events(AccountsTemplates.atPwdFormEvents);
// We identified the templates that need to be overridden by looking at the available templates
// here: https://github.com/meteor-useraccounts/unstyled/tree/master/lib
// Template['override-atPwdFormBtn'].replaces('atPwdFormBtn');
// Template['override-atPwdForm'].replaces('atPwdForm');
// Template['override-atTextInput'].replaces('atTextInput');
// Template['override-atTitle'].replaces('atTitle');
// Template['override-atError'].replaces('atError');

8
imports/ui/helpers.js Normal file
View File

@@ -0,0 +1,8 @@
// General use helpers - available to all views.
UI.registerHelper('currentUserName', function() {
if(Meteor.user()){
return Meteor.user().emails[0].address;
}
});

View File

@@ -0,0 +1,32 @@
<template name="Admin">
{{> sAlert}}
<div id="mainAdmin">
<nav class="leftSidebarContainer generalSidebar">
<a href="javascript:" class="fa fa-bars leftSidebarMenuButton generalMenuButton" aria-hidden="true"></a>
<div class="leftSidebar">
<div class="logoArea">
<i class="fa fa-sign-out fa-2x signOut" aria-hidden="true"></i>
<div class="logo"><img src="/images/Logo_v1.png" height="60px"/></div>
</div>
<div class="menuArea generalMenu">
<ul>
<li class="{{isActiveRoute 'UserManagement'}}">
<a href="{{pathFor 'UserManagement'}}">
Users
</a>
</li>
<!-- Below is a second menu with a tag attached. -->
<!--<li class="{{isActiveRoute 'Misc'}}">-->
<!--<a href="{{pathFor 'Misc'}}">-->
<!--Misc &lt;!&ndash; <span class="tag">sample tag</span>&ndash;&gt;-->
<!--</a>-->
<!--</li>-->
</ul>
</div>
</div>
</nav>
<div class="contentBody verticalStack">
{{> Template.dynamic template=content}}
</div>
</div>
</template>

189
imports/ui/layouts/Admin.import.styl vendored Normal file
View File

@@ -0,0 +1,189 @@
#mainAdmin
margin: 0
padding: 0
border: 0
height: 100%
width: 100%
nav.leftSidebarContainer
z-index:999
position: fixed
top: 0
width: 220px
padding: 0
height: 100%
border: 0
vertical-align: top
text-align: left
background-color: #90b272 //Old browsers
background: -moz-linear-gradient(-180deg, #90b272 0%, #4d7727 100%) //FF3.6-15
background: -webkit-linear-gradient(-180deg, #90b272 0%,#4d7727 100%) //Chrome10-25,Safari5.1-6
background: linear-gradient(180deg, #90b272 0%,#4d7727 100%) //W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+
font-size: 14px
font-weight: 700
overflow: visible
margin: 0 0 0 -220px
-webkit-transition: .5s ease-in
-moz-transition: .5s ease-in
-o-transition: .5s ease-in
-ms-transition: .5s ease-in
transition: .5s ease-in
.leftSidebarMenuButton
position: absolute
right: -30px
-webkit-transition: .5s ease-in
-moz-transition: .5s ease-in
-o-transition: .5s ease-in
-ms-transition: .5s ease-in
transition: .5s ease-in
-webkit-border-top-right-radius: 5px
-webkit-border-bottom-right-radius: 5px
-moz-border-radius-topright: 5px
-moz-border-radius-bottomright: 5px
border-top-right-radius: 5px
border-bottom-right-radius: 5px
color: black
font-size: 20px
line-height: 20px
font-weight: 900
text-align: center
text-decoration: none
width: 30px
height: 30px
padding: 5px 0
background-color: #90b272
display: block
border-top: 1px solid #494
border-right: 1px solid #494
border-bottom: 1px solid #494
.leftSidebarMenuButton:hover
color: rgba(150,0,0,.5)
nav.generalSidebar
.leftSidebarMenuButton
top: 10px
nav.menuHide .leftSidebarMenuButton
right: 60px
nav.menuShow
margin: 0
nav.menuShow .leftSidebarMenuButton
right: -15px
-webkit-transform: rotate(45deg) !important
-moz-transform: rotate(45deg) !important
-o-transform: rotate(45deg) !important
-ms-transform: rotate(45deg) !important
transform: rotate(45deg) !important
-moz-border-radius-bottomright: 0
//border-top-right-radius: 0
border-bottom-right-radius: 0
border-bottom: 0
.leftSidebar
height: 100%
//position: absolute
border: 0
vertical-align: top
padding: 0
text-align: left
//top: 0px
//left: 0px
//bottom: 0px
width: 220px
//Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#627d4d+0,1f3b08+100;Olive+3D
background-color: #90b272 //Old browsers
background: -moz-linear-gradient(-180deg, #90b272 0%, #4d7727 100%) //FF3.6-15
background: -webkit-linear-gradient(-180deg, #90b272 0%,#4d7727 100%) //Chrome10-25,Safari5.1-6
background: linear-gradient(180deg, #90b272 0%,#4d7727 100%) //W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+
font-size: 14px
font-weight: 700
overflow: hidden
.logoArea
width: 100%
min-height: 50px
.signOut
position: absolute
left: 10px
top: 10px
color: white
cursor: pointer
.signOut:hover
color: #BBB
.signOut:active
color: black
.logo
text-align: center
margin-top: 20px
img:hover
//-webkit-animation: neon6_drop 1.5s ease-in-out infinite alternate;
//-moz-animation: neon6_drop 1.5s ease-in-out infinite alternate;
animation: neon6_drop 1.5s ease-in-out infinite alternate;
.menuArea
width: 100%
ul
padding: 20px 0 0 0
margin: 0
list-style: none
li:first-child
border-top: 1px solid #e4e5e7
li
border-bottom: 1px solid #e4e5e7
color: #96a2ae
text-transform: uppercase
display: block
a
color: black
padding: 10px 20px
cursor: pointer
text-decoration: none
display: block
.tag
padding: .3em .6em
margin-top: -.2em
font-size: .8em
color: #ddd
white-space: nowrap
vertical-align: baseline
border-radius: .5em
border: 1px solid #000000
float: right
.subMenu
background-color: #999
padding: .3em .6em
margin-top: -.2em
font-size: .8em
color: #fff
white-space: nowrap
vertical-align: baseline
border-radius: .5em
border: 1px solid #000000
float: right
.subMenu.active
background-color: #333
li:hover
// Note: neon6 is defined in effects.import.styl
background-color: #666
-webkit-animation: neon6 1.5s ease-in-out infinite alternate
-moz-animation: neon6 1.5s ease-in-out infinite alternate
animation: neon6 1.5s ease-in-out infinite alternate
.subMenu
// Note: neon6 is defined in effects.import.styl
background-color: #999
-webkit-animation: neon7 1.5s ease-in-out infinite alternate
-moz-animation: neon7 1.5s ease-in-out infinite alternate
animation: neon7 1.5s ease-in-out infinite alternate
li.active
background-color: #333
> a
color: #96a2ae
li.active:hover
background-color: #333
> a
color: white
.contentBody
flex: 1 1 1px
padding: 10px 20px
-webkit-box-shadow: inset 4px 2px 10px -3px rgba(168,165,168,1)
-moz-box-shadow: inset 4px 2px 10px -3px rgba(168,165,168,1)
box-shadow: inset 8px 0px 10px -3px rgba(168,165,168,1)
overflow: hidden

View File

@@ -0,0 +1,54 @@
import { Template } from 'meteor/templating';
import './Admin.html';
Template.Admin.toggleMenu = function($sidebar) {
let $sidebars = $('nav.leftSidebarContainer');
for(let i = 0; i < $sidebars.length; i++) {
if($sidebars[i] == $sidebar[0]) {
$sidebar.toggleClass('menuShow');
}
else {
$($sidebars[i]).toggleClass('menuHide');
}
}
};
Template.Admin.events({
"click .signOut": function(event, template) {
AccountsTemplates.logout();
},
// General Menu
"click .generalSidebar .leftSidebarMenuButton": function(event, template) {
event.preventDefault();
Template.Admin.toggleMenu($('nav.generalSidebar'));
},
"click .generalSidebar .leftSidebar a": function(event, template) {
Template.Admin.toggleMenu($('nav.generalSidebar'));
},
"click .generalSidebar .leftSidebar a .subMenu": function(event, template) {
Template.Admin.toggleMenu($('nav.generalSidebar'));
},
// Graphs Menu
"click .graphsSidebar .leftSidebarMenuButton": function(event, template) {
event.preventDefault();
Template.Admin.toggleMenu($('nav.graphsSidebar'));
},
"click .graphsSidebar .leftSidebar a": function(event, template) {
Template.Admin.toggleMenu($('nav.graphsSidebar'));
},
"click .graphsSidebar .leftSidebar a .subMenu": function(event, template) {
Template.Admin.toggleMenu($('nav.graphsSidebar'));
},
// Settings Menu
"click .settingsSidebar .leftSidebarMenuButton": function(event, template) {
event.preventDefault();
Template.Admin.toggleMenu($('nav.settingsSidebar'));
},
"click .settingsSidebar .leftSidebar a": function(event, template) {
Template.Admin.toggleMenu($('nav.settingsSidebar'));
},
"click .settingsSidebar .leftSidebar a .subMenu": function(event, template) {
Template.Admin.toggleMenu($('nav.settingsSidebar'));
}
});

View File

@@ -0,0 +1,9 @@
<template name="Login">
<div id="login" class="content">
<div class="spacer"> </div>
<div class="contentBox">
<img src="/images/Logo_v1.png"/>
<div class="form">{{> Template.dynamic template=content}}</div>
</div>
</div>
</template>

63
imports/ui/layouts/Login.import.styl vendored Normal file
View File

@@ -0,0 +1,63 @@
#login.content
background: white
height: 100%
.spacer
height: 10%
.contentBox
margin: 0 auto
max-width: 600px
background-color: #88d15a
-webkit-box-shadow: 10px 10px 5px 0px rgba(0,0,0,0.5);
-moz-box-shadow: 10px 10px 5px 0px rgba(0,0,0,0.5);
box-shadow: 10px 10px 5px 0px rgba(0,0,0,0.5);
padding: 40px 10px
text-align: center
img
height: 120px
vertical-align: top
.form
display: inline-block
margin-left: 20px
input
padding: 8px
width: 300px
margin-bottom: 10px
label
display: none
fieldset
border: none
.at-btn
margin-bottom: 6px
text-align: center
width: 300px
background: #34d955;
background-image: -webkit-linear-gradient(top, #5d942b, #4b7d26)
background-image: -moz-linear-gradient(top, #5d942b, #4b7d26)
background-image: -ms-linear-gradient(top, #5d942b, #4b7d26)
background-image: -o-linear-gradient(top, #5d942b, #4b7d26)
background-image: linear-gradient(to bottom, #5d942b, #4b7d26)
font-family: "Arial Black", Arial
color: #ffffff
font-size: 14px
line-height: 16px
padding: 10px 20px 10px 20px
text-decoration: none
text-transform: uppercase
border: none
.at-btn:hover
background: #29b54f
background-image: -webkit-linear-gradient(top, #29b54f, #186b31)
background-image: -moz-linear-gradient(top, #29b54f, #186b31)
background-image: -ms-linear-gradient(top, #29b54f, #186b31)
background-image: -o-linear-gradient(top, #29b54f, #186b31)
background-image: linear-gradient(to bottom, #29b54f, #186b31)
text-decoration: none
cursor: pointer
.at-link
color: #1555b4
font: Arial
font-size: 12px
font-weight: 800
text-decoration: none
.at-link:hover
text-decoration: underline

View File

@@ -0,0 +1 @@
import './Login.html';

View File

@@ -0,0 +1,29 @@
<template name="Public">
{{> sAlert}}
<div id="publicBody">
<div id="page">
<div id="menu"><!-- Note: Comment out spacing between the elements since the browser will interpret the spaces as characters to be displayed.
--><a href="/Home">Home</a><!--
--></div>
<div id="links">
<!-- Note: Comment out spacing between the elements since the browser will interpret the spaces as characters to be displayed. -->
<a id="linkFacebook" href="" target='_blank' rel="nofollow"></a><!--
--><a id="linkGoogle" href="" target='_blank' rel="nofollow"></a><!--
--><a id="linkTwitter" href="" target='_blank' rel="nofollow"></a>
</div>
<div id="menuBackground"></div> <!-- A spacer between the menu and the header image. -->
<div id="head">
<!-- Logo click takes the user back to home -->
<div id="logo" onclick="window.location='/Home'"></div>
</div>
<div id="content">
{{> Template.dynamic template=content}}
</div>
</div>
<!-- footer lines -->
<div id="footer">&copy; 2018 Just Me | My Email <a href="mailto:me@somewhere.com">me@somewhere.com</a></div>
<div id="designedBy" style="">
Web Site By: <a href="http://somewhere.com" style="font-weight: normal" target="_blank">Your Company</a>
</div>
</div>
</template>

181
imports/ui/layouts/Public.import.styl vendored Normal file
View File

@@ -0,0 +1,181 @@
#publicBody
position: relative
max-width: 950px
min-width: 250px
margin: 0 auto
#page
background: #FDFDFD
@media(max-width: 549px)
#head
margin: 0 auto
height: 0
#logo
position: absolute
right: 5px
top: 0
width: 40px
height: 40px
background: url(images/Logo_v1.png) no-repeat top center
background-size: 40px 40px
cursor: pointer
.page
margin: 0 auto
padding: 6px 4px
position: relative
#menuBackground
height: 20px
max-width: 950px
background: #FFF
#menu
white-space: nowrap
position: absolute
height: 20px
z-index: 200
font-family: "Open Sans", Arial, Helvetica, sans-serif
font-weight: 600
font-size: .8em
#menu a
margin: 0 0 0 5px
text-decoration: none
color: black
line-height: 20px
display: inline-block
height: 20px
border-bottom: 1px solid transparent
/* Force the browser to include padding and border as part of the size of the block. */
-webkit-box-sizing: border-box /* Safari/Chrome, other WebKit */
-moz-box-sizing: border-box /* Firefox, other Gecko */
box-sizing: border-box /* Opera/IE 8+ */
#menu a:hover
opacity: .7
color: black
border-bottom: 1px solid red
#links
white-space: nowrap
position: absolute
right: 50px
top: 26px
height: 15px
width: 60px
text-align: left
z-index: 100
font-family: Arial, Helvetica, sans-serif
font-size: .8em
font-weight: 800
#links a
display: inline-block
width: 20px
height: 15px
border-bottom: 1px solid transparent
/* Force the browser to include padding and border as part of the size of the block. */
-webkit-box-sizing: border-box /* Safari/Chrome, other WebKit */
-moz-box-sizing: border-box /* Firefox, other Gecko */
box-sizing: border-box /* Opera/IE 8+ */
#links a:hover
border-bottom: 1px solid red
opacity: .7
#linkFacebook
background: url('images/Facebook_v2.png') no-repeat center center
background-size: auto 9px
#linkGoogle
background: url('images/GooglePlus_v2.png') no-repeat center center
background-size: 12px auto
#linkTwitter
background: url('images/Twitter_v2.png') no-repeat center center
background-size: 12px auto
@media(min-width: 550px)
#head
margin: 0 auto
background: url(images/Header_v1.jpg) no-repeat top center
background-size: contain
max-width: 950px
height: 171px
#logo
position: absolute
right: 10px
top: 10px
width: 120px
height: 120px
background: url(images/Logo_v1.png) no-repeat top center
background-size: 120px 120px
cursor: pointer
.page
margin: 0 auto
padding: 30px 20px
position: relative
#menuBackground
height: 31px
max-width: 950px
background: #FFF
#menu
white-space: nowrap
position: absolute
height: 30px
margin-bottom: 1px
z-index: 200
font-family: "Open Sans", Arial, Helvetica, sans-serif
font-weight: 600
font-size: 1em
#menu a
margin: 0 0 0 16px
text-decoration: none
color: black
line-height: 30px
display: inline-block
height: 30px
border-bottom: 3px solid transparent
/* Force the browser to include padding and border as part of the size of the block. */
-webkit-box-sizing: border-box /* Safari/Chrome, other WebKit */
-moz-box-sizing: border-box /* Firefox, other Gecko */
box-sizing: border-box /* Opera/IE 8+ */
#menu a.holidayMenuItem
font-family: "Grand Hotel", "Open Sans", Arial, Helvetica, sans-serif
font-weight: 400
font-size: 1.3em
letter-spacing: 1px
vertical-align: top
#menu a.shippingMenuItem
font-family: "Grand Hotel", "Open Sans", Arial, Helvetica, sans-serif
font-weight: 400
font-size: 1.3em
letter-spacing: 1px
vertical-align: top
#menu a:hover
opacity: 1
color: black
background: transparent
border-bottom: 3px solid #a20010
#link
white-space: nowrap
position: absolute
right: 10px
top: 130px
height: 30px
width: 120px
text-align: center
z-index: 200
font-family: Arial, Helvetica, sans-serif
font-weight: 800
font-size: 1em
#links a
display: inline-block
width: 30px
height: 30px
border-bottom: 2px solid transparent
/* Force the browser to include padding and border as part of the size of the block. */
-webkit-box-sizing: border-box /* Safari/Chrome, other WebKit */
-moz-box-sizing: border-box /* Firefox, other Gecko */
box-sizing: border-box /* Opera/IE 8+ */
#links a:hover
border-bottom: 2px solid rgb(200, 146, 186) /*#a20010;*/
opacity: 1
#linkFacebook
background: url('images/Facebook_white_v2.png') no-repeat center 5px
background-size: 20px auto
#linkGoogle
background: url('images/GooglePlus_white_v2.png') no-repeat center 6px
background-size: 20px auto
#linkTwitter
background: url('images/Twitter_white_v2.png') no-repeat center 7px
background-size: 20px auto

View File

@@ -0,0 +1,6 @@
import { Template } from 'meteor/templating';
import './Public.html';
Template.Public.events({
});

52
imports/ui/styles/buttons.import.styl vendored Normal file
View File

@@ -0,0 +1,52 @@
span.button
margin: 0 0 0 1px
padding: 0.5em 1em
border: 1px solid #d4d4d4
border-radius: 50em
text-overflow: ellipsis
white-space: nowrap
overflow: hidden
cursor: pointer
outline: none
background-color: #ececec
color: #333
font: 11px/normal sans-serif
text-shadow: 1px 1px 0 #fff
text-align: center
text-decoration: none
//Prevent selection
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Chrome/Safari/Opera */
-khtml-user-select: none; /* Konqueror */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently not supported by any browser */
span.button:hover
color: blue
span.button:active
color: #fff
background-color: #141414
text-shadow: 1px 1px 0 #000
border: 1px solid #292929
span.button.primary
font-weight: 800
span.button.selected //Use this if they are toggle buttons
color: #fff
background-color: #141414
text-shadow: 1px 1px 0 #000
cursor: default
span.buttonGroup
:not(:first-child):not(:last-child)
border-radius: 0
:first-child
border-top-left-radius: 50em
border-bottom-left-radius: 50em
border-top-right-radius: 0
border-bottom-right-radius: 0
margin-left: 0
:last-child
border-top-left-radius: 0
border-bottom-left-radius: 0
border-top-right-radius: 50em
border-bottom-right-radius: 50em

52
imports/ui/styles/effects.import.styl vendored Normal file
View File

@@ -0,0 +1,52 @@
// For black text.
@-webkit-keyframes neon6
from
text-shadow: 0 0 10px #fff, 0 0 20px #fff, 0 0 30px #fff, 0 0 40px #ff00de, 0 0 70px #ff00de, 0 0 80px #ff00de, 0 0 100px #ff00de, 0 0 150px #ff00de
to
text-shadow: 0 0 5px #fff, 0 0 10px #fff, 0 0 15px #fff, 0 0 20px #ff00de, 0 0 35px #ff00de, 0 0 40px #ff00de, 0 0 50px #ff00de, 0 0 75px #ff00de
@-moz-keyframes neon6
from
text-shadow: 0 0 10px #fff, 0 0 20px #fff, 0 0 30px #fff, 0 0 40px #ff00de, 0 0 70px #ff00de, 0 0 80px #ff00de, 0 0 100px #ff00de, 0 0 150px #ff00de
to
text-shadow: 0 0 5px #fff, 0 0 10px #fff, 0 0 15px #fff, 0 0 20px #ff00de, 0 0 35px #ff00de, 0 0 40px #ff00de, 0 0 50px #ff00de, 0 0 75px #ff00de
@keyframes neon6
from
text-shadow: 0 0 10px #fff, 0 0 20px #fff, 0 0 30px #fff, 0 0 40px #ff00de, 0 0 70px #ff00de, 0 0 80px #ff00de, 0 0 100px #ff00de, 0 0 150px #ff00de
to
text-shadow: 0 0 5px #fff, 0 0 10px #fff, 0 0 15px #fff, 0 0 20px #ff00de, 0 0 35px #ff00de, 0 0 40px #ff00de, 0 0 50px #ff00de, 0 0 75px #ff00de
// For white text.
@-webkit-keyframes neon7
from
text-shadow: 0 0 10px #bbb, 0 0 20px #bbb, 0 0 30px #bbb, 0 0 40px #ff00de, 0 0 70px #ff00de, 0 0 80px #ff00de, 0 0 100px #ff00de, 0 0 150px #ff00de
to
text-shadow: 0 0 5px #bbb, 0 0 10px #bbb, 0 0 15px #bbb, 0 0 20px #ff00de, 0 0 35px #ff00de, 0 0 40px #ff00de, 0 0 50px #ff00de, 0 0 75px #ff00de
@-moz-keyframes neon7
from
text-shadow: 0 0 10px #bbb, 0 0 20px #bbb, 0 0 30px #bbb, 0 0 40px #ff00de, 0 0 70px #ff00de, 0 0 80px #ff00de, 0 0 100px #ff00de, 0 0 150px #ff00de
to
text-shadow: 0 0 5px #bbb, 0 0 10px #bbb, 0 0 15px #bbb, 0 0 20px #ff00de, 0 0 35px #ff00de, 0 0 40px #ff00de, 0 0 50px #ff00de, 0 0 75px #ff00de
@keyframes neon7
from
text-shadow: 0 0 10px #bbb, 0 0 20px #bbb, 0 0 30px #bbb, 0 0 40px #ff00de, 0 0 70px #ff00de, 0 0 80px #ff00de, 0 0 100px #ff00de, 0 0 150px #ff00de
to
text-shadow: 0 0 5px #bbb, 0 0 10px #bbb, 0 0 15px #bbb, 0 0 20px #ff00de, 0 0 35px #ff00de, 0 0 40px #ff00de, 0 0 50px #ff00de, 0 0 75px #ff00de
//@-webkit-keyframes neon6_drop
// from
// -webkit-filter: drop-shadow(0px 0px 20px rgba(194,0,0,0.7))
// filter: url(shadow.svg#drop-shadow)
// 50%
// -webkit-filter: drop-shadow(0px 0px 20px rgba(255,0,0,1))
// filter: url(shadow.svg#drop-shadow)
// to
// -webkit-filter: drop-shadow(0px 0px 20px rgba(194,0,0,0.7))
// filter: url(shadow.svg#drop-shadow)
@keyframes neon6_drop
from
filter: drop-shadow(0px 0px 20px rgba(194,0,0,0.7)) brightness(120%)
50%
filter: drop-shadow(0px 0px 20px rgba(255,0,0,1)) brightness(80%)
to
filter: drop-shadow(0px 0px 20px rgba(194,0,0,0.7)) brightness(100%)

117
imports/ui/styles/forms.import.styl vendored Normal file
View File

@@ -0,0 +1,117 @@
//Form Styles
.select2-container
font-size: 10px
.select2-selection
font-size: 13px //Make the font small enough the control can have a height similar to a standard input field.
margin-bottom: 0px
min-height: 10px !important //This is what really sets the height of the box containing the selection(s)
padding-bottom: 2px //Add a little space below the selections to balance it all out.
input
padding: 6px
border-radius: 4px
border-width: 1px
border-style: solid
border-color: #ccc
//input[type='button'].btn-success, input[type='submit'].btn-success
// background-color: #5cb85c
// :hover
// background-color:
//input[type='button'].btn-danger, input[type='submit'].btn-danger
// background-color: #e55b46
.form-control, .select2-selection //?
font-size: 14px
margin-bottom: 0px
.form-group
margin: 4px 0
.has-error .form-control
border-color: #a94442
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075)
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075)
@media screen and (-webkit-min-device-pixel-ratio: 0)
input[type="date"].form-control, input[type="time"].form-control, input[type="datetime-local"].form-control, input[type="month"].form-control
line-height: 34px
.form-control
display: block
width: 100%
height: 34px
padding: 6px 12px
font-size: 14px
line-height: 1.42857143
color: #555
background-color: #fff
background-image: none
border: 1px solid #ccc
border-radius: 4px
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075)
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075)
-webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s
-o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s
input[type="date"], input[type="datetime-local"], input[type="month"], input[type="time"], input[type="week"]
align-items: center
-webkit-padding-start: 1px
overflow: hidden
padding-left: 10px
input
-webkit-appearance: textfield
background-color: white
-webkit-rtl-ordering: logical
user-select: text
cursor: auto
padding: 1px
border-width: 2px
border-style: inset
border-color: initial
border-image: initial
.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control
background-color: #eee
opacity: 1
input, textarea, keygen, select, button
text-rendering: auto
color: initial
letter-spacing: normal
word-spacing: normal
text-transform: none
text-indent: 0px
text-shadow: none
display: inline-block
text-align: start
margin: 0em 0em 0em 0em
font: 13.3333px Arial
input, textarea, keygen, select, button, meter, progress
-webkit-writing-mode: horizontal-tb
//.btn.disabled, .btn[disabled], fieldset[disabled] .btn
// cursor: not-allowed
// filter: unquote("alpha(opacity=65)")
// -webkit-box-shadow: none
// box-shadow: none
// opacity: .65
//button, html input[type="button"], input[type="reset"], input[type="submit"]
// -webkit-appearance: button
// cursor: pointer
//button, html input[type="button"], input[type="reset"], input[type="submit"]
// -webkit-appearance: button
// cursor: pointer
//.btn
// display: inline-block;
// padding: 6px 12px;
// margin-bottom: 0;
// font-size: 14px;
// font-weight: normal;
// line-height: 1.42857143;
// text-align: center;
// white-space: nowrap;
// vertical-align: middle;
// -ms-touch-action: manipulation;
// touch-action: manipulation;
// cursor: pointer;
// -webkit-user-select: none;
// -moz-user-select: none;
// -ms-user-select: none;
// user-select: none;
// background-image: none;
// border: 1px solid transparent;
// border-radius: 4px;

View File

@@ -0,0 +1,133 @@
section.maxHeightContainer, div.maxHeightContainer
display: table
height: 100%
width: 100%
section.maxHeightRow, div.maxHeightRow
display: table-row
section.maxHeightContent, div.maxHeightContent //Use this for a row of content that should shrink to fit the content size.
display: table-cell
height: 1px
section.maxHeightContentExpandAndScroll, div.maxHeightContentExpandAndScroll //Use this for a row of content that should take up all remaining space and will contain potentially scrolled content.
display: table-cell
height: 100%
position: relative
section.maxHeightContentScrolled, div.maxHeightContentScrolled //Use this to create the scrolled content. Can use any display within it.
position: absolute
top: 0
bottom: 0
left: 0
right: 0
width: auto
height: auto
overflow-y: auto
// *****************
// ** Vertical Stack
// ** Use .verticalStack on containers, and .vscFixed or .vscExpand on children (they can also be containers).
// ** Designed to use Flexbox to allow full screen (vertical) layouts. Fixed children will fit the content, and expand children will consume all available vertical space, but will not exceed the vertical space.
// ** Use .columnContainer to setup a horizontally scrolling, full height container where children are tiled down first, then wrap to the next column.
/*
Test Code:
-------CSS------
* {
margin: 0;
padding: 0;
}
html {
height: 100%;
min-height: 100%;
}
body {
background: purple;
color: black;
min-height: 100%;
height: 100%;
}
.vscFixed {
flex: 0 0 auto;
width: 100%;
}
.vscExpand {
flex: 1 1 1px;
width: 100%;
}
.verticalStack {
width: 100%;
height: 100%;
display: flex;
flex-flow: column;
justify-content: flex-start;
align-items: flex-start;
align-content: stretch;
}
.columnContainer {
width: 100%;
height: 100%;
display: flex;
flex-flow: column wrap;
justify-content: flex-start;
align-items: stretch;
align-content: flex-start;
overflow-x: auto;
background: white;
}
.columnContent {
flex: none;
width: 300px;
}
-------Javascript------
var container = document.querySelector('.columnContainer');
for(var i = 0; i < 400; i++) {
var child = document.createElement("div");
child.innerHTML = "Element " + i;
child.className = "columnContent";
container.appendChild(child);
}
-------HTML------
<div class="verticalStack">
<div class='vscFixed' style="width: 100%; background:green">
<p>
Some content.
</p>
<p>
More content...
</p>
</div>
<div class="verticalStack vscExpand">
<div class='vscFixed' style="background: yellow;">
Test bar.
</div>
<div class="columnContainer vscExpand">
</div>
</div>
</div>
*/
.vscFixed {
flex: 0 0 auto;
width: 100%;
}
.vscExpand {
flex: 1 1 1px;
width: 100%;
}
.verticalStack {
width: 100%;
height: 100%;
display: flex;
flex-flow: column;
justify-content: flex-start;
align-items: flex-start;
align-content: stretch;
}
.columnContainer {
display: flex;
flex-direction: column;
flex-wrap: wrap;
justify-content: flex-start;
align-items: stretch;
align-content: flex-start;
overflow-x: auto;
}
.columnContent {
flex: none;
}

61
imports/ui/styles/tabs.import.styl vendored Normal file
View File

@@ -0,0 +1,61 @@
ul.tabRow
position: relative
text-align: left
list-style: none
margin: 0
padding: 0 0 0 10px
line-height: 24px
height: 26px
font-size: 12px
overflow: hidden
li
position: relative
z-index: 0
border: 1px solid #AAA
background: #D1D1D1
display: inline-block
border-top-left-radius: 6px
border-top-right-radius: 6px
background: -o-linear-gradient(top, #ECECEC 50%, #D1D1D1 100%)
background: -ms-linear-gradient(top, #ECECEC 50%, #D1D1D1 100%)
background: -moz-linear-gradient(top, #ECECEC 50%, #D1D1D1 100%)
background: -webkit-linear-gradient(top, #ECECEC 50%, #D1D1D1 100%)
background: linear-gradient(top, #ECECEC 50%, #D1D1D1 100%)
box-shadow: 0 3px 3px rgba(0, 0, 0, 0.4), inset 0 1px 0 #FFF
text-shadow: 0 1px #FFF
margin: 0 -5px
padding: 0 20px
li:before, li:after
position: absolute
bottom: -1px
width: 5px
height: 5px
content: " "
border: 1px solid #AAA
li:before
left: -6px
border-bottom-right-radius: 6px
border-width: 0 1px 1px 0
box-shadow: 2px 2px 0 #D1D1D1
li:after
right: -6px
border-bottom-left-radius: 6px
border-width: 0 0 1px 1px
box-shadow: -2px 2px 0 #D1D1D1
li.selected
z-index: 2
background: white
color: #333
border-bottom-color: #FFF
li.selected:before
box-shadow: 2px 2px 0 #FFF
li.selected:after
box-shadow: -2px 2px 0 #FFF
ul.tabRow:before
position: absolute
width: 100%
bottom: 0
left: 0
border-bottom: 1px solid #AAA
z-index: 1
content: " "

View File

@@ -0,0 +1,463 @@
.btn
display: inline-block
padding: 6px 12px
margin-bottom: 0
font-size: 14px
font-weight: normal
line-height: 1.42857143
text-align: center
white-space: nowrap
vertical-align: middle
-ms-touch-action: manipulation
touch-action: manipulation
cursor: pointer
-webkit-user-select: none
-moz-user-select: none
-ms-user-select: none
user-select: none
background-image: none
border: 1px solid transparent
border-radius: 4px
.btn:focus,
.btn:active:focus,
.btn.active:focus,
.btn.focus,
.btn:active.focus,
.btn.active.focus
outline: 5px auto -webkit-focus-ring-color
outline-offset: -2px
.btn:hover,
.btn:focus,
.btn.focus
color: #333
text-decoration: none
.btn:active,
.btn.active
background-image: none
outline: 0
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125)
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125)
.btn.disabled,
.btn[disabled],
fieldset[disabled] .btn
cursor: not-allowed
filter: unquote("alpha(opacity=65)")
-webkit-box-shadow: none
box-shadow: none
opacity: .65
a.btn.disabled,
fieldset[disabled] a.btn
pointer-events: none
.btn-default
color: #333
background-color: #fff
border-color: #ccc
.btn-default:focus,
.btn-default.focus
color: #333
background-color: #e6e6e6
border-color: #8c8c8c
.btn-default:hover
color: #333
background-color: #e6e6e6
border-color: #adadad
.btn-default:active,
.btn-default.active,
.open > .dropdown-toggle.btn-default
color: #333
background-color: #e6e6e6
border-color: #adadad
.btn-default:active:hover,
.btn-default.active:hover,
.open > .dropdown-toggle.btn-default:hover,
.btn-default:active:focus,
.btn-default.active:focus,
.open > .dropdown-toggle.btn-default:focus,
.btn-default:active.focus,
.btn-default.active.focus,
.open > .dropdown-toggle.btn-default.focus
color: #333
background-color: #d4d4d4
border-color: #8c8c8c
.btn-default:active,
.btn-default.active,
.open > .dropdown-toggle.btn-default
background-image: none
.btn-default.disabled:hover,
.btn-default[disabled]:hover,
fieldset[disabled] .btn-default:hover,
.btn-default.disabled:focus,
.btn-default[disabled]:focus,
fieldset[disabled] .btn-default:focus,
.btn-default.disabled.focus,
.btn-default[disabled].focus,
fieldset[disabled] .btn-default.focus
background-color: #fff
border-color: #ccc
.btn-default .badge
color: #fff
background-color: #333
.btn-primary
color: #fff
background-color: #337ab7
border-color: #2e6da4
.btn-primary:focus,
.btn-primary.focus
color: #fff
background-color: #286090
border-color: #122b40
.btn-primary:hover
color: #fff
background-color: #286090
border-color: #204d74
.btn-primary:active,
.btn-primary.active,
.open > .dropdown-toggle.btn-primary
color: #fff
background-color: #286090
border-color: #204d74
.btn-primary:active:hover,
.btn-primary.active:hover,
.open > .dropdown-toggle.btn-primary:hover,
.btn-primary:active:focus,
.btn-primary.active:focus,
.open > .dropdown-toggle.btn-primary:focus,
.btn-primary:active.focus,
.btn-primary.active.focus,
.open > .dropdown-toggle.btn-primary.focus
color: #fff
background-color: #204d74
border-color: #122b40
.btn-primary:active,
.btn-primary.active,
.open > .dropdown-toggle.btn-primary
background-image: none
.btn-primary.disabled:hover,
.btn-primary[disabled]:hover,
fieldset[disabled] .btn-primary:hover,
.btn-primary.disabled:focus,
.btn-primary[disabled]:focus,
fieldset[disabled] .btn-primary:focus,
.btn-primary.disabled.focus,
.btn-primary[disabled].focus,
fieldset[disabled] .btn-primary.focus
background-color: #337ab7
border-color: #2e6da4
.btn-primary .badge
color: #337ab7
background-color: #fff
.btn-success
color: #fff
background-color: #5cb85c
border-color: #4cae4c
.btn-success:focus,
.btn-success.focus
color: #fff
background-color: #449d44
border-color: #255625
.btn-success:hover
color: #fff
background-color: #449d44
border-color: #398439
.btn-success:active,
.btn-success.active,
.open > .dropdown-toggle.btn-success
color: #fff
background-color: #449d44
border-color: #398439
.btn-success:active:hover,
.btn-success.active:hover,
.open > .dropdown-toggle.btn-success:hover,
.btn-success:active:focus,
.btn-success.active:focus,
.open > .dropdown-toggle.btn-success:focus,
.btn-success:active.focus,
.btn-success.active.focus,
.open > .dropdown-toggle.btn-success.focus
color: #fff
background-color: #398439
border-color: #255625
.btn-success:active,
.btn-success.active,
.open > .dropdown-toggle.btn-success
background-image: none
.btn-success.disabled:hover,
.btn-success[disabled]:hover,
fieldset[disabled] .btn-success:hover,
.btn-success.disabled:focus,
.btn-success[disabled]:focus,
fieldset[disabled] .btn-success:focus,
.btn-success.disabled.focus,
.btn-success[disabled].focus,
fieldset[disabled] .btn-success.focus
background-color: #5cb85c
border-color: #4cae4c
.btn-success .badge
color: #5cb85c
background-color: #fff
.btn-info
color: #fff
background-color: #5bc0de
border-color: #46b8da
.btn-info:focus,
.btn-info.focus
color: #fff
background-color: #31b0d5
border-color: #1b6d85
.btn-info:hover
color: #fff
background-color: #31b0d5
border-color: #269abc
.btn-info:active,
.btn-info.active,
.open > .dropdown-toggle.btn-info
color: #fff
background-color: #31b0d5
border-color: #269abc
.btn-info:active:hover,
.btn-info.active:hover,
.open > .dropdown-toggle.btn-info:hover,
.btn-info:active:focus,
.btn-info.active:focus,
.open > .dropdown-toggle.btn-info:focus,
.btn-info:active.focus,
.btn-info.active.focus,
.open > .dropdown-toggle.btn-info.focus
color: #fff
background-color: #269abc
border-color: #1b6d85
.btn-info:active,
.btn-info.active,
.open > .dropdown-toggle.btn-info
background-image: none
.btn-info.disabled:hover,
.btn-info[disabled]:hover,
fieldset[disabled] .btn-info:hover,
.btn-info.disabled:focus,
.btn-info[disabled]:focus,
fieldset[disabled] .btn-info:focus,
.btn-info.disabled.focus,
.btn-info[disabled].focus,
fieldset[disabled] .btn-info.focus
background-color: #5bc0de
border-color: #46b8da
.btn-info .badge
color: #5bc0de
background-color: #fff
.btn-warning
color: #fff
background-color: #f0ad4e
border-color: #eea236
.btn-warning:focus,
.btn-warning.focus
color: #fff
background-color: #ec971f
border-color: #985f0d
.btn-warning:hover
color: #fff
background-color: #ec971f
border-color: #d58512
.btn-warning:active,
.btn-warning.active,
.open > .dropdown-toggle.btn-warning
color: #fff
background-color: #ec971f
border-color: #d58512
.btn-warning:active:hover,
.btn-warning.active:hover,
.open > .dropdown-toggle.btn-warning:hover,
.btn-warning:active:focus,
.btn-warning.active:focus,
.open > .dropdown-toggle.btn-warning:focus,
.btn-warning:active.focus,
.btn-warning.active.focus,
.open > .dropdown-toggle.btn-warning.focus
color: #fff
background-color: #d58512
border-color: #985f0d
.btn-warning:active,
.btn-warning.active,
.open > .dropdown-toggle.btn-warning
background-image: none
.btn-warning.disabled:hover,
.btn-warning[disabled]:hover,
fieldset[disabled] .btn-warning:hover,
.btn-warning.disabled:focus,
.btn-warning[disabled]:focus,
fieldset[disabled] .btn-warning:focus,
.btn-warning.disabled.focus,
.btn-warning[disabled].focus,
fieldset[disabled] .btn-warning.focus
background-color: #f0ad4e
border-color: #eea236
.btn-warning .badge
color: #f0ad4e
background-color: #fff
.btn-danger
color: #fff
background-color: #d9534f
border-color: #d43f3a
.btn-danger:focus,
.btn-danger.focus
color: #fff
background-color: #c9302c
border-color: #761c19
.btn-danger:hover
color: #fff
background-color: #c9302c
border-color: #ac2925
.btn-danger:active,
.btn-danger.active,
.open > .dropdown-toggle.btn-danger
color: #fff
background-color: #c9302c
border-color: #ac2925
.btn-danger:active:hover,
.btn-danger.active:hover,
.open > .dropdown-toggle.btn-danger:hover,
.btn-danger:active:focus,
.btn-danger.active:focus,
.open > .dropdown-toggle.btn-danger:focus,
.btn-danger:active.focus,
.btn-danger.active.focus,
.open > .dropdown-toggle.btn-danger.focus
color: #fff
background-color: #ac2925
border-color: #761c19
.btn-danger:active,
.btn-danger.active,
.open > .dropdown-toggle.btn-danger
background-image: none
.btn-danger.disabled:hover,
.btn-danger[disabled]:hover,
fieldset[disabled] .btn-danger:hover,
.btn-danger.disabled:focus,
.btn-danger[disabled]:focus,
fieldset[disabled] .btn-danger:focus,
.btn-danger.disabled.focus,
.btn-danger[disabled].focus,
fieldset[disabled] .btn-danger.focus
background-color: #d9534f
border-color: #d43f3a
.btn-danger .badge
color: #d9534f
background-color: #fff
.btn-link
font-weight: normal
color: #337ab7
border-radius: 0
.btn-link,
.btn-link:active,
.btn-link.active,
.btn-link[disabled],
fieldset[disabled] .btn-link
background-color: transparent
-webkit-box-shadow: none
box-shadow: none
.btn-link,
.btn-link:hover,
.btn-link:focus,
.btn-link:active
border-color: transparent
.btn-link:hover,
.btn-link:focus
color: #23527c
text-decoration: underline
background-color: transparent
.btn-link[disabled]:hover,
fieldset[disabled] .btn-link:hover,
.btn-link[disabled]:focus,
fieldset[disabled] .btn-link:focus
color: #777
text-decoration: none
.btn-lg,
.btn-group-lg > .btn
padding: 10px 16px
font-size: 18px
line-height: 1.3333333
border-radius: 6px
.btn-sm,
.btn-group-sm > .btn
padding: 5px 10px
font-size: 12px
line-height: 1.5
border-radius: 3px
.btn-xs,
.btn-group-xs > .btn
padding: 1px 5px
font-size: 12px
line-height: 1.5
border-radius: 3px
.btn-block
display: block
width: 100%
.btn-block + .btn-block
margin-top: 5px
input[type="submit"].btn-block,
input[type="reset"].btn-block,
input[type="button"].btn-block
width: 100%

11
imports/util/csv.js Normal file
View File

@@ -0,0 +1,11 @@
let fs = require("fs");
let csv = require('csv-parse');
let result = function(csvFilePath, callback) {
let parser = csv(Assets.getText(csvFilePath), {delimiter: '\t'}, function(error, data) {
if(error) callback(error);
else callback(null, data);
});
};
export default result;

31
imports/util/de.combo.import.styl vendored Normal file
View File

@@ -0,0 +1,31 @@
.comboList {
z-index: 1000;
max-height: 160px;
overflow-y: auto;
-moz-box-shadow: 0 2px 3px #ccc;
-webkit-box-shadow: 0 2px 3px #ccc;
box-shadow: 0 2px 3px #ccc;
border: 1px solid #d1d1d1;
list-style-type: none;
padding: 0;
margin: 0;
display: none;
background: white;
li {
display: block;
padding: 5px 10px;
margin: 0;
text-indent: 0
background: white;
}
li.selected {
background-color: #ffe184 !important;
}
li[role='node'] {
font-weight: 800;
}
li[role='leaf'] {
padding-left: 2em;
}
}

455
imports/util/de.combo.js Normal file
View File

@@ -0,0 +1,455 @@
"use strict";
//
// Takes a input form element and a hidden form element (to store the selected id in) along with an array of objects, to build a dropdown select control that allows the user to type part of the selection to filter the list.
// Modified for meteor.
//
// @param options: See Combo.DEFAULTS below.
//
//
(function($) {
let Combo = function($input, $hidden, options) {
let _this = this;
this.focusCounter = 0;
this.$input = $input;
this.$hidden = $hidden;
this.options = $.extend({}, Combo.DEFAULTS, options);
this.$selected = null;
this.$listContainer = $('<div/>', {style: 'position: relative; height: 0;'});
this.$list = $('<ul/>', {role: 'menu', class: this.options.listClass});
//Ensure that if the hidden field exists and changes, that the hidden field's id matches the text in the input field. If not then the hidden id field was changed manually and externally and the text field should be updated.
if(this.$hidden) {
this.$hidden.on('change', hiddenInputChanged);
}
function hiddenInputChanged() {
let id = _this.$hidden.val();
let $li = _this.$list.children("[role!='node']");
for(let i = 0; i < $li.length; i++) {
let $next = $($li[i]);
if($next.data('model').id == id) {
if(_this.$input.val() != $next.text())
_this.$input.val($next.text());
}
}
}
//this.$list.appendTo($input.parent());
this.$list.appendTo(this.$listContainer);
//this.$listContainer.appendTo($input.parent());
this.$listContainer.prependTo(document.body); //Place the container at the top of the page with no height.
//Setup the list to highlight the item the user is hovering over, to select the item the user clicks, and to remove the hover styling when the list closes due to a selection being made.
this.$list
.on('mousemove', 'li', function() {
// _this.$list.find(_this.options.selectionClass).removeClass(_this.options.selectionClass);
let $this = $(this);
//Skip nodes.
while($this && $this.attr('role') == 'node') {
$this = $this.next();
}
//If we could find a non-node element then highlight it.
if($this) $this.addClass(_this.options.selectionClass).siblings().removeClass(_this.options.selectionClass);
})
.on('mousedown', 'li', function() {
let $this = $(this);
//Skip nodes.
while($this && $this.attr('role') == 'node') {
$this = $this.next();
}
//If we could find a non-node element then highlight it.
if($this) _this.select($this);
})
.on('mouseup', 'li', function() {
//Remove the selection highlighting.
_this.$list.children().removeClass(_this.options.selectionClass);
//Hide the list.
_this.hide();
});
//Setup the input field to handle opening the list when it receives focus, and close it when it loses focus.
this.$input
.on('focus', $.proxy(_this.focus, _this))
.on('blur', $.proxy(_this.blur, _this));
// this.$listContainer
// .on('focus', $.proxy(_this.focus, _this, "list container"))
// .on('blur', $.proxy(_this.blur, _this, "list container"));
// this.$list
// .on('focus', $.proxy(_this.focus, _this, "list"))
// .on('blur', $.proxy(_this.blur, _this, "list"));
//Handle key events on the input field. Up/down arrows should change the selection in the list. Enter should select an item and close the list. Tab and escape should hide the list before moving to the next focusable element on the page.
this.$input.on('input keydown', function(event) {
switch(event.keyCode) {
case 38: { //Up
let visibles = _this.$list.find('li.visible[role!="node"]');
let selected = visibles.index(visibles.filter('.' + _this.options.selectionClass)) || 0;
_this.highlight(selected - 1);
event.preventDefault();
break;
}
case 40: { //Down
let visibles = _this.$list.find('li.visible[role!="node"]');
let selected = visibles.index(visibles.filter('li.selected')) || 0;
_this.highlight(selected + 1);
event.preventDefault();
break;
}
case 13: //Enter
if(_this.$list.is(':visible')) {
_this.select(_this.$list.find('li.selected'));
event.preventDefault();
}
break;
case 9: //Tab
_this.select(_this.$list.find('li.selected'));
break;
case 27: //Esc
if(_this.$input.hasClass('open')) {
_this.hide();
//Try to stop any default behavior from occurring.
if(event.stopPropagation) event.stopPropagation();
else event.cancelBubble = true; //IE 6-8
event.preventDefault();
return false;
}
else {
return true;
}
default:
_this.filter();
_this.highlight(0);
break;
}
});
//
// Adds one or more elements to the control.
// data: The item or array of items to add. This will be the root tree elements if groupFunctions is provided.
function add(data) {
let groupFunctions = _this.options.groupFunctions;
let getClasses = _this.options.getClasses;
let addOne = function(data, parent) {
let text = _this.options.textAttr ? ($.isFunction(_this.options.textAttr) ? _this.options.textAttr(data) : data[_this.options.textAttr]) : data;
let li = $("<li" + (parent ? " role='leaf'" : "") + (getClasses ? " class='" + getClasses(data) + "'" : "") + ">" + text + "</li>");
li.appendTo(_this.$list);
li.data('model', data);
if(parent) li.data('parent-li', parent);
};
let addOneGroup = function(data, text, children) {
let li = $("<li role='node'>" + text + "</li>");
li.appendTo(_this.$list);
li.data('model', data);
for(let childIndex = 0; childIndex < children.length; childIndex++) {
addOne(children[childIndex], li);
}
};
let addOneBranch = function(data) {
let parents = $.isFunction(groupFunctions.groupParents) ? groupFunctions.groupParents(data) : data;
//Since there may be one or more parents identified for each data element passed to us...
if(Array.isArray(parents)) {
for(let parentIndex = 0; parentIndex < parents.length; parentIndex++) {
addOneGroup(parents[parentIndex], groupFunctions.parentText(parents[parentIndex]), groupFunctions.children(parents[parentIndex]));
}
}
else {
addOneGroup(parents, groupFunctions.parentText(parents), groupFunctions.children(parents));
}
};
if(groupFunctions instanceof Object && $.isFunction(groupFunctions.children) && $.isFunction(groupFunctions.parentText)) {
if(Array.isArray(data)) {
for(let dataIndex = 0; dataIndex < data.length; dataIndex++) {
addOneBranch(data[dataIndex]);
}
}
else {
addOneBranch(data);
}
}
else {
if(Array.isArray(data)) {
for(let dataIndex = 0; dataIndex < data.length; dataIndex++) {
addOne(data[dataIndex]);
}
}
else {
addOne(data);
}
}
//Filter the set of elements so that only those matching the text in the input field are marked as visible.
_this.filter();
}
Tracker.autorun(function() {
this.$list.empty();
if(options.cursor) {
//Add the initial set of data.
add(options.cursor.fetch());
}
else if(options.set) {
for(let i = 0; i < options.set.length; i++) {
add(options.set[i]);
}
}
}.bind(this));
//Check the hidden input field for an ID, and setup the selection based in it if there is one.
if(this.$hidden && _this.$hidden.val()) {
hiddenInputChanged();
}
//TODO: Should probably check to ensure comparator is a function? Not that it will help much if the function is not written correctly. Stupid Javascript!
if(this.options.selection && this.options.comparator) {
Tracker.autorun(function() {
let selectedData = this.options.selection.get();
if(selectedData) {
let listItems = this.$list.children();
let found = false;
for(let i = 0; !found && i < listItems.length; i++) {
if(this.options.comparator($(listItems[i]).data('model'), selectedData)) {
found = true;
this.select($(listItems[i]));
}
}
}
}.bind(this));
}
};
Combo.DEFAULTS = {
cursor: undefined, //A meteor Cursor used to populate the values displayed in the combo.
set: [], //An array of values displayed in the combo. This must be specified if cursor is not specified.
selection: undefined, //A meteor ReactiveVar whose value will be set to the current selection.
comparator: function(a, b) {return a === b;}, //A function that takes two collection objects and compares them for equality. If the combo shows users for example, this comparator would compare one user id to another. Required for the combo to set the selection if the view changes it externally relative to this combo.
textAttr: undefined, //The attribute of the data elements to use for the name. This can also be a function that takes the data object and returns the text.
idAttr: 'id', //The attribute of the data elements to use for the ID. This can also be a function that takes the data obejct and returns the ID.
// groupFunctions: The object containing three functions: 'groupParent', 'parentText', 'children'.
// groupParents(data) will take a data element and return the objects that best represents the parents of the children (for a multi layer tree, this would be the node just before the leaf nodes).
// parentText(parent) will be passed the group parent and the data object that generated it, and will return the text that represents the path to that parent.
// children(parent) will be passed the group parent (returned by groupParents()), and will return an array of children or leaf nodes for the tree.
groupFunctions: undefined,
filter: true, //Whether to filter the list as the user types.
effects: 'fade',
duration: '200',
listClass: 'de.combo-list',
selectionClass: 'selected', //The class to use for the selected element in the dropdown list.
getClasses: undefined //An optional function that will return a string to use in the list item's class attribute to style the list item for a given model data. The function will be passed the data object for the list item.
};
Combo.prototype.select = function($li) {
if($li.length == 0) {
if(this.$input.val() != '') {
this.$input.val("");
if(this.$hidden) this.$hidden.val(undefined).change();
this.filter();
//Note: Don't trigger the select event - for some reason it causes the dropdown to reopen and the control to retain focus when clicking out of the widget.
}
}
else {
if(!this.$list.has($li) || !$li.is('li.visible')) return;
//No need to change selection if the selection has not changed.
if(this.$input.val() != $li.text()) {
this.$input.val($li.text()); //Save the selected text into the text input.
if(this.$hidden) {
this.$hidden.val($li.data('model')[this.options.idAttr]);
this.$hidden.change();
} //Save the ID into the hidden form input if it exists.
this.hide();
this.filter();
//this.trigger('select', $li);
//Set the reactive var for the selection if one is provided and the selection has changed relative to the model.
if(this.options.selection && this.options.selection.get() != $li.data('model')) {
this.options.selection.set($li.data('model'));
}
}
}
};
Combo.prototype.escapeRegex = function(s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
};
// Removes all filtering. This is used to clear the filtering when first opening the combo when there is a value in the field. This is desirable because when we have an exact match, but are opening the combo, we most often want to select a new value.
Combo.prototype.clearFilter = function() {
console.log("CLearing Filter");
//Show all list elements.
this.$list.find('li').addClass('visible').show();
//Hide any node list elements.
this.$list.find('li[role="node"]').removeClass('visible').hide();
};
//Filters the list items by marking those that match the text in the text field as having the class 'visible'.
Combo.prototype.filter = function() {
try {
let search = this.$input.val();
let _this = this;
search = search ? search : "";
search = search.toLowerCase().trim();
//Show all list elements.
this.$list.find('li').addClass('visible').show();
//Hide any node list elements.
this.$list.find('li[role="node"]').removeClass('visible').hide();
if(this.options.filter) {
//Hide non-node elements (leaf nodes) that don't match.
let li = this.$list.children();
let searches = search && search.length > 0 ? search.split(/\s+/) : undefined;
let regexs = searches ? [] : undefined;
if(searches) {
for(let i = 0; i < searches.length; i++) {
regexs.push(new RegExp("\\b" + this.escapeRegex(searches[i])));
}
}
//Iterate over the list elements:
// hide all list items that are nodes;
// show all list items that are not nodes and whose text matches the input value;
// show all node list items associated with visible child list items (they occur after the parent, so the parent will be hidden first, then made visible).
for(let i = 0; i < li.length; i++) {
let $next = $(li[i]);
let node = $next.attr('role') == 'node';
//let hidden = node || $next.text().toLowerCase().indexOf(search) < 0;
let text = $next.text().toLowerCase();
let match = true;
if(!node && searches) {
for(let i = 0; match && i < regexs.length; i++) {
match = regexs[i].test(text)
}
}
//let match = text.match(/\bxxx/gi);
let hidden = node || !match;
if(hidden) $next.removeClass('visible').hide();
//If this isn't hidden and we have a tree with grouping, then turn on the group (parent) associated with this option.
if(!hidden && _this.options.groupFunctions) {
let parent = $next.data('parent-li');
if(!parent.hasClass('visible')) parent.addClass('visible').show();
}
}
//If we hid all elements then hide the whole list.
if(this.$list.find('li.visible').length == 0) this.hide();
}
} catch(e) {
console.log(e);
}
};
Combo.prototype.focus = function() {
this.show();
this.$input.select();
};
Combo.prototype.blur = function() {
this.hide();
this.select(this.$list.find('li.selected'));
};
Combo.prototype.show = function() {
// Make sure we don't repeatedly try to show the combo.
if(!this.isShowing) {
let position = this.$input.offset();
this.isShowing = true;
// Position the list relative to the field. Note that we place the combo at the top of the page (in the body tag) to avoid overflow not showing and to ensure the page scrolls if needed.
this.$list.css({position: 'absolute', top: position.top + this.$input.outerHeight(), left: position.left, width: this.$input.outerWidth()});
this.clearFilter();
if(!this.$list.is(':visible') && this.$list.find('li.visible').length > 0) {
let fns = {default: 'show', fade: 'fadeIn', slide: 'slideDown'};
let fn = fns[this.options.effects];
this.trigger('show');
this.$input.addClass('open');
this.$list[fn](this.options.duration, $.proxy(this.trigger, this, 'shown'));
}
}
};
Combo.prototype.hide = function() {
if(this.isShowing) {
let fns = {default: 'hide', fade: 'fadeOut', slide: 'slideUp'};
let fn = fns[this.options.effects];
this.isShowing = false;
this.trigger('hide');
this.$input.removeClass('open');
this.$list[fn](this.options.duration, $.proxy(this.trigger, this, 'hidden'));
}
};
// goDown: true/false - defaults to true - indicating whether the highlighting should go up or down if the requested item is a node. Nodes cannot be highlighted or selected.
Combo.prototype.highlight = function(index) {
let _this = this;
this.show();
setTimeout(function() {
let visibles = _this.$list.find('li.visible[role!="node"]');
let oldSelected = _this.$list.find('li.' + _this.options.selectionClass).removeClass(_this.options.selectionClass);
let oldSelectedIndex = visibles.index(oldSelected);
if(visibles.length > 0) {
let selectedIndex = (visibles.length + index) % visibles.length;
let selected = visibles.eq(selectedIndex);
let top = selected.position().top;
if(selected.attr('role') != 'node') selected.addClass(_this.options.selectionClass);
if(selectedIndex < oldSelectedIndex && top < 0)
_this.$list.scrollTop(_this.$list.scrollTop() + top);
if(selectedIndex > oldSelectedIndex && top + selected.outerHeight() > _this.$list.outerHeight())
_this.$list.scrollTop(_this.$list.scrollTop() + selected.outerHeight() + 2 * (top - _this.$list.outerHeight()));
}
});
};
Combo.prototype.trigger = function(event) {
let params = Array.prototype.slice.call(arguments, 1);
let args = [event + '.de.combo'];
args.push(params);
if(this.$select) this.$select.trigger.apply(this.$select, args);
this.$input.trigger.apply(this.$input, args);
};
$.fn.buildCombo = function(options) {
for(let index = 0; index < this.length; index++) {
let $next = $(this[index]);
let nextCombo = new Combo($next, $next.siblings('input[type=hidden]').first(), options);
$next.data("de.combo", nextCombo);
}
};
$.fn.getCombo = function() {
if(this.length > 0) {
return $(this[0]).data('de.combo');
}
};
})(jQuery);

508
imports/util/normalize.css vendored Normal file
View File

@@ -0,0 +1,508 @@
/*! normalize.css 2012-03-11T12:53 UTC - http://github.com/necolas/normalize.css */
/* =============================================================================
HTML5 display definitions
========================================================================== */
/*
* Corrects block display not defined in IE6/7/8/9 & FF3
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
nav,
section,
summary {
display: block;
}
/*
* Corrects inline-block display not defined in IE6/7/8/9 & FF3
*/
audio,
canvas,
video {
display: inline-block;
*display: inline;
*zoom: 1;
}
/*
* Prevents modern browsers from displaying 'audio' without controls
* Remove excess height in iOS5 devices
*/
audio:not([controls]) {
display: none;
height: 0;
}
/*
* Addresses styling for 'hidden' attribute not present in IE7/8/9, FF3, S4
* Known issue: no IE6 support
*/
[hidden] {
display: none;
}
/* =============================================================================
Base
========================================================================== */
/*
* 1. Corrects text resizing oddly in IE6/7 when body font-size is set using em units
* http://clagnut.com/blog/348/#c790
* 2. Prevents iOS text size adjust after orientation change, without disabling user zoom
* www.456bereastreet.com/archive/201012/controlling_text_size_in_safari_for_ios_without_disabling_user_zoom/
*/
html {
font-size: 100%; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
-ms-text-size-adjust: 100%; /* 2 */
}
/*
* Addresses font-family inconsistency between 'textarea' and other form elements.
*/
html,
button,
input,
select,
textarea {
font-family: sans-serif;
}
/*
* Addresses margins handled incorrectly in IE6/7
*/
body {
margin: 0;
}
/* =============================================================================
Links
========================================================================== */
/*
* Addresses outline displayed oddly in Chrome
*/
a:focus {
outline: thin dotted;
}
/*
* Improves readability when focused and also mouse hovered in all browsers
* people.opera.com/patrickl/experiments/keyboard/test
*/
a:hover,
a:active {
outline: 0;
}
/* =============================================================================
Typography
========================================================================== */
/*
* Addresses font sizes and margins set differently in IE6/7
* Addresses font sizes within 'section' and 'article' in FF4+, Chrome, S5
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
h2 {
font-size: 1.5em;
margin: 0.83em 0;
}
h3 {
font-size: 1.17em;
margin: 1em 0;
}
h4 {
font-size: 1em;
margin: 1.33em 0;
}
h5 {
font-size: 0.83em;
margin: 1.67em 0;
}
h6 {
font-size: 0.75em;
margin: 2.33em 0;
}
/*
* Addresses styling not present in IE7/8/9, S5, Chrome
*/
abbr[title] {
border-bottom: 1px dotted;
}
/*
* Addresses style set to 'bolder' in FF3+, S4/5, Chrome
*/
b,
strong {
font-weight: bold;
}
blockquote {
margin: 1em 40px;
}
/*
* Addresses styling not present in S5, Chrome
*/
dfn {
font-style: italic;
}
/*
* Addresses styling not present in IE6/7/8/9
*/
mark {
background: #ff0;
color: #000;
}
/*
* Addresses margins set differently in IE6/7
*/
p,
pre {
margin: 1em 0;
}
/*
* Corrects font family set oddly in IE6, S4/5, Chrome
* en.wikipedia.org/wiki/User:Davidgothberg/Test59
*/
pre,
code,
kbd,
samp {
font-family: monospace, serif;
_font-family: 'courier new', monospace;
font-size: 1em;
}
/*
* Improves readability of pre-formatted text in all browsers
*/
pre {
white-space: pre;
white-space: pre-wrap;
word-wrap: break-word;
}
/*
* 1. Addresses CSS quotes not supported in IE6/7
* 2. Addresses quote property not supported in S4
*/
/* 1 */
q {
quotes: none;
}
/* 2 */
q:before,
q:after {
content: '';
content: none;
}
small {
font-size: 75%;
}
/*
* Prevents sub and sup affecting line-height in all browsers
* gist.github.com/413930
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* =============================================================================
Lists
========================================================================== */
/*
* Addresses margins set differently in IE6/7
*/
dl,
menu,
ol,
ul {
margin: 1em 0;
}
dd {
margin: 0 0 0 40px;
}
/*
* Addresses paddings set differently in IE6/7
*/
menu,
ol,
ul {
padding: 0 0 0 40px;
}
/*
* Corrects list images handled incorrectly in IE7
*/
nav ul,
nav ol {
list-style: none;
list-style-image: none;
}
/* =============================================================================
Embedded content
========================================================================== */
/*
* 1. Removes border when inside 'a' element in IE6/7/8/9, FF3
* 2. Improves image quality when scaled in IE7
* code.flickr.com/blog/2008/11/12/on-ui-quality-the-little-things-client-side-image-resizing/
*/
img {
border: 0; /* 1 */
-ms-interpolation-mode: bicubic; /* 2 */
}
/*
* Corrects overflow displayed oddly in IE9
*/
svg:not(:root) {
overflow: hidden;
}
/* =============================================================================
Figures
========================================================================== */
/*
* Addresses margin not present in IE6/7/8/9, S5, O11
*/
figure {
margin: 0;
-webkit-margin-before: 0;
-webkit-margin-after: 0;
-webkit-margin-start: 0;
-webkit-margin-end: 0;
}
/* =============================================================================
Forms
========================================================================== */
/*
* Corrects margin displayed oddly in IE6/7
*/
form {
margin: 0;
}
/*
* Define consistent border, margin, and padding
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/*
* 1. Corrects color not being inherited in IE6/7/8/9
* 2. Corrects text not wrapping in FF3
* 3. Corrects alignment displayed oddly in IE6/7
*/
legend {
border: 0; /* 1 */
padding: 0;
white-space: normal; /* 2 */
*margin-left: -7px; /* 3 */
}
/*
* 1. Corrects font size not being inherited in all browsers
* 2. Addresses margins set differently in IE6/7, FF3+, S5, Chrome
* 3. Improves appearance and consistency in all browsers
*/
button,
input,
select,
textarea {
font-size: 100%; /* 1 */
margin: 0; /* 2 */
vertical-align: baseline; /* 3 */
*vertical-align: middle; /* 3 */
}
/*
* Addresses FF3/4 setting line-height on 'input' using !important in the UA stylesheet
*/
button,
input {
line-height: normal; /* 1 */
}
/*
* 1. Improves usability and consistency of cursor style between image-type 'input' and others
* 2. Corrects inability to style clickable 'input' types in iOS
* 3. Removes inner spacing in IE7 without affecting normal text inputs
* Known issue: inner spacing remains in IE6
*/
button,
input[type="button"],
input[type="reset"],
input[type="submit"] {
cursor: pointer; /* 1 */
-webkit-appearance: button; /* 2 */
*overflow: visible; /* 3 */
}
/*
* Re-set default cursor for disabled elements
*/
button[disabled],
input[disabled] {
cursor: default;
}
/*
* 1. Addresses box sizing set to content-box in IE8/9
* 2. Removes excess padding in IE8/9
* 3. Removes excess padding in IE7
Known issue: excess padding remains in IE6
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
*height: 13px; /* 3 */
*width: 13px; /* 3 */
}
/*
* 1. Addresses appearance set to searchfield in S5, Chrome
* 2. Addresses box-sizing set to border-box in S5, 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;
}
/*
* Removes inner padding and search cancel button in S5, Chrome on OS X
*/
input[type="search"]::-webkit-search-decoration,
input[type="search"]::-webkit-search-cancel-button {
-webkit-appearance: none;
}
/*
* Removes inner padding and border in FF3+
* www.sitepen.com/blog/2008/05/14/the-devils-in-the-details-fixing-dojos-toolbar-buttons/
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/*
* 1. Removes default vertical scrollbar in IE6/7/8/9
* 2. Improves readability and alignment in all browsers
*/
textarea {
overflow: auto; /* 1 */
vertical-align: top; /* 2 */
}
/* =============================================================================
Tables
========================================================================== */
/*
* Remove most spacing between table cells
*/
table {
border-collapse: collapse;
border-spacing: 0;
}

View File

@@ -0,0 +1,108 @@
// https://tc39.github.io/ecma262/#sec-array.prototype.includes
if(!Array.prototype.includes) {
Object.defineProperty(Array.prototype, 'includes', {
value: function(searchElement, fromIndex) {
// 1. Let O be ? ToObject(this value).
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
let o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
let len = o.length >>> 0;
// 3. If len is 0, return false.
if (len === 0) {
return false;
}
// 4. Let n be ? ToInteger(fromIndex).
// (If fromIndex is undefined, this step produces the value 0.)
let n = fromIndex | 0;
// 5. If n ≥ 0, then
// a. Let k be n.
// 6. Else n < 0,
// a. Let k be len + n.
// b. If k < 0, let k be 0.
let k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
// 7. Repeat, while k < len
while (k < len) {
// a. Let elementK be the result of ? Get(O, ! ToString(k)).
// b. If SameValueZero(searchElement, elementK) is true, return true.
// c. Increase k by 1.
// NOTE: === provides the correct "SameValueZero" comparison needed here.
if (o[k] === searchElement) {
return true;
}
k++;
}
// 8. Return false
return false;
}
});
}
//http://stackoverflow.com/questions/5306680/move-an-array-element-from-one-array-position-to-another
if(!Array.prototype.move) {
Array.prototype.move = function (old_index, new_index) {
if (new_index >= this.length) {
let k = new_index - this.length;
while ((k--) + 1) {
this.push(undefined);
}
}
this.splice(new_index, 0, this.splice(old_index, 1)[0]);
return this; // for testing purposes
};
}
//My own implementation to work around Javascript's shitty naming and support for collection operations.
if(!Array.prototype.remove) {
Array.prototype.remove = function(item) {
let index = this.indexOf(item);
if(index != -1) {
this.splice(index, 1);
return true;
}
return false;
}
}
//My own implementation to work around Javascript's shitty naming and support for collection operations.
// index is optional
if(!Array.prototype.add) {
Array.prototype.add = function(item, index) {
if(index == undefined || isNaN(index) || index >= this.length) return this.push(item);
else return this.splice(index, 0, item);
}
}
//My own implementation to work around Javascript's shitty naming and support for collection operations.
// Sorts a contiguous section of the array.
// Index is the index of the first element to be sorted (inclusive).
// Length is the number of elements of the array to be sorted (must be >= 2). If the length + index is greater than the array length then it will be adjusted to the end of the array.
// All other invalid inputs will result in no sorting action taken and no error.
if(!Array.prototype.partialSort) {
Array.prototype.partialSort = function(index, length, compareFunction) {
if(index >= 0 && length >= 2 && index <= (this.length - 2)) {
//Adjust the length so it doesn't over-run the array. This is the only error correction we will perform.
if(index + length > this.length) length = this.length - index;
//Shallow copy of the data in the segment to be sorted.
let sorted = this.slice(index, length + index);
sorted.sort(compareFunction);
//Put the sorted array elements back into the array.
for(let i = index, j = 0; j < length; i++, j++) {
this[i] = sorted[j];
}
}
}
}

View File

@@ -0,0 +1,19 @@
/**
* Get the parent template instance.
* @param {Number} [levels] How many levels to go up. Default is 1
* @returns {Blaze.TemplateInstance}
*/
Blaze.TemplateInstance.prototype.parentTemplate = Blaze.TemplateInstance.prototype.parentInstance = function(levels) {
let view = this.view;
levels = (typeof levels === "undefined") ? 1 : levels;
while(view) {
//if(view.name.substring(0, 9) === "Template." && !(levels--)) {
if(view.template && !(levels--)) {
//return view.templateInstance();
return view.templateInstance();
}
view = view.parentView;
}
};

View File

@@ -0,0 +1,71 @@
//
// Add a method to get a timezone adjusted date for an input field that is a date picker.
// Use $('input[name="date"]').val(new Date().toDateInputValue()) to set the date of the input field.
//
Date.prototype.toDateInputValue = (function() {
let local = new Date(this);
local.setMinutes(this.getMinutes() - this.getTimezoneOffset());
return local.toJSON().slice(0,10);
});
Date.prototype.getWeek = function() {
let dowOffset = 1; // I am fixing this to indicate that the first day of the week is always Monday (weeks end on Sunday), for this application. This was a parameter in the original code.
//dowOffset = typeof(dowOffset) == 'number' ? dowOffset : 0; //default dowOffset to zero - This should check to ensure that dowOffset is between 0..6
let newYear = new Date(this.getFullYear(),0,1);
let day = newYear.getDay() - dowOffset; //the day of week the year begins on
day = (day >= 0 ? day : day + 7);
// The number of days from the beginning of the year to this.day
let daynum = Math.floor((this.getTime() - newYear.getTime() - (this.getTimezoneOffset()-newYear.getTimezoneOffset())*60000)/86400000) + 1;
let weeknum;
// I have removed the mid-week starting cutoff detection because in this app we always want to start with week #1 (never have a week zero).
//if(day < 4) { //if the year starts before the middle of a week
weeknum = Math.floor((daynum + day - 1) / 7) + 1;
// I have turned off the detection of whether the last days of the year belong to this year's last week or next year's first week. This gets too confusing and does not result in any additional usefulness.
//if(weeknum > 52) {
// nYear = new Date(this.getFullYear() + 1, 0, 1);
// nday = nYear.getDay() - dowOffset;
// nday = nday >= 0 ? nday : nday + 7;
// // if the next year starts before the middle of the week, it is week #1 of that year
// weeknum = nday < 4 ? 1 : 53;
//}
//}
//else {
// weeknum = Math.floor((daynum+day-1)/7);
//}
return weeknum;
};
Date.prototype.getUTCWeek = function() {
let dowOffset = 1; // I am fixing this to indicate that the first day of the week is always Monday (weeks end on Sunday), for this application. This was a parameter in the original code.
//dowOffset = typeof(dowOffset) == 'number' ? dowOffset : 0; //default dowOffset to zero - This should check to ensure that dowOffset is between 0..6
let newYear = new Date(this.getUTCFullYear(),0,1);
let day = newYear.getDay() - dowOffset; //the day of week the year begins on
day = (day >= 0 ? day : day + 7);
// The number of days from the beginning of the year to this.day
let daynum = Math.floor((this.getTime() - newYear.getTime() - (this.getTimezoneOffset() - newYear.getTimezoneOffset())*60000)/86400000) + 1;
let weeknum;
// I have removed the mid-week starting cutoff detection because in this app we always want to start with week #1 (never have a week zero).
//if(day < 4) { //if the year starts before the middle of a week
weeknum = Math.floor((daynum + day - 1) / 7) + 1;
// I have turned off the detection of whether the last days of the year belong to this year's last week or next year's first week. This gets too confusing and does not result in any additional usefulness.
//if(weeknum > 52) {
// nYear = new Date(this.getFullYear() + 1, 0, 1);
// nday = nYear.getDay() - dowOffset;
// nday = nday >= 0 ? nday : nday + 7;
// // if the next year starts before the middle of the week, it is week #1 of that year
// weeknum = nday < 4 ? 1 : 53;
//}
//}
//else {
// weeknum = Math.floor((daynum+day-1)/7);
//}
return weeknum;
};

View File

@@ -0,0 +1,3 @@
RegExp.escape = function(s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
};

View File

@@ -0,0 +1,515 @@
/**
* Copyright Marc J. Schmidt. See the LICENSE file at the top-level
* directory of this distribution and at
* https://github.com/marcj/css-element-queries/blob/master/LICENSE.
*/
;
(function (root, factory) {
if (typeof define === "function" && define.amd) {
define(['./ResizeSensor.js'], factory);
} else if (typeof exports === "object") {
module.exports = factory(require('./ResizeSensor.js'));
} else {
root.ElementQueries = factory(root.ResizeSensor);
}
}(this, function (ResizeSensor) {
/**
*
* @type {Function}
* @constructor
*/
var ElementQueries = function() {
var trackingActive = false;
var elements = [];
/**
*
* @param element
* @returns {Number}
*/
function getEmSize(element) {
if (!element) {
element = document.documentElement;
}
var fontSize = window.getComputedStyle(element, null).fontSize;
return parseFloat(fontSize) || 16;
}
/**
*
* @copyright https://github.com/Mr0grog/element-query/blob/master/LICENSE
*
* @param {HTMLElement} element
* @param {*} value
* @returns {*}
*/
function convertToPx(element, value) {
var numbers = value.split(/\d/);
var units = numbers[numbers.length-1];
value = parseFloat(value);
switch (units) {
case "px":
return value;
case "em":
return value * getEmSize(element);
case "rem":
return value * getEmSize();
// Viewport units!
// According to http://quirksmode.org/mobile/tableViewport.html
// documentElement.clientWidth/Height gets us the most reliable info
case "vw":
return value * document.documentElement.clientWidth / 100;
case "vh":
return value * document.documentElement.clientHeight / 100;
case "vmin":
case "vmax":
var vw = document.documentElement.clientWidth / 100;
var vh = document.documentElement.clientHeight / 100;
var chooser = Math[units === "vmin" ? "min" : "max"];
return value * chooser(vw, vh);
default:
return value;
// for now, not supporting physical units (since they are just a set number of px)
// or ex/ch (getting accurate measurements is hard)
}
}
/**
*
* @param {HTMLElement} element
* @constructor
*/
function SetupInformation(element) {
this.element = element;
this.options = {};
var key, option, width = 0, height = 0, value, actualValue, attrValues, attrValue, attrName;
/**
* @param {Object} option {mode: 'min|max', property: 'width|height', value: '123px'}
*/
this.addOption = function(option) {
var idx = [option.mode, option.property, option.value].join(',');
this.options[idx] = option;
};
var attributes = ['min-width', 'min-height', 'max-width', 'max-height'];
/**
* Extracts the computed width/height and sets to min/max- attribute.
*/
this.call = function() {
// extract current dimensions
width = this.element.offsetWidth;
height = this.element.offsetHeight;
attrValues = {};
for (key in this.options) {
if (!this.options.hasOwnProperty(key)){
continue;
}
option = this.options[key];
value = convertToPx(this.element, option.value);
actualValue = option.property == 'width' ? width : height;
attrName = option.mode + '-' + option.property;
attrValue = '';
if (option.mode == 'min' && actualValue >= value) {
attrValue += option.value;
}
if (option.mode == 'max' && actualValue <= value) {
attrValue += option.value;
}
if (!attrValues[attrName]) attrValues[attrName] = '';
if (attrValue && -1 === (' '+attrValues[attrName]+' ').indexOf(' ' + attrValue + ' ')) {
attrValues[attrName] += ' ' + attrValue;
}
}
for (var k in attributes) {
if(!attributes.hasOwnProperty(k)) continue;
if (attrValues[attributes[k]]) {
this.element.setAttribute(attributes[k], attrValues[attributes[k]].substr(1));
} else {
this.element.removeAttribute(attributes[k]);
}
}
};
}
/**
* @param {HTMLElement} element
* @param {Object} options
*/
function setupElement(element, options) {
if (element.elementQueriesSetupInformation) {
element.elementQueriesSetupInformation.addOption(options);
} else {
element.elementQueriesSetupInformation = new SetupInformation(element);
element.elementQueriesSetupInformation.addOption(options);
element.elementQueriesSensor = new ResizeSensor(element, function() {
element.elementQueriesSetupInformation.call();
});
}
element.elementQueriesSetupInformation.call();
if (trackingActive && elements.indexOf(element) < 0) {
elements.push(element);
}
}
/**
* @param {String} selector
* @param {String} mode min|max
* @param {String} property width|height
* @param {String} value
*/
var allQueries = {};
function queueQuery(selector, mode, property, value) {
if (typeof(allQueries[mode]) == 'undefined') allQueries[mode] = {};
if (typeof(allQueries[mode][property]) == 'undefined') allQueries[mode][property] = {};
if (typeof(allQueries[mode][property][value]) == 'undefined') allQueries[mode][property][value] = selector;
else allQueries[mode][property][value] += ','+selector;
}
function getQuery() {
var query;
if (document.querySelectorAll) query = document.querySelectorAll.bind(document);
if (!query && 'undefined' !== typeof $$) query = $$;
if (!query && 'undefined' !== typeof jQuery) query = jQuery;
if (!query) {
throw 'No document.querySelectorAll, jQuery or Mootools\'s $$ found.';
}
return query;
}
/**
* Start the magic. Go through all collected rules (readRules()) and attach the resize-listener.
*/
function findElementQueriesElements() {
var query = getQuery();
for (var mode in allQueries) if (allQueries.hasOwnProperty(mode)) {
for (var property in allQueries[mode]) if (allQueries[mode].hasOwnProperty(property)) {
for (var value in allQueries[mode][property]) if (allQueries[mode][property].hasOwnProperty(value)) {
var elements = query(allQueries[mode][property][value]);
for (var i = 0, j = elements.length; i < j; i++) {
setupElement(elements[i], {
mode: mode,
property: property,
value: value
});
}
}
}
}
}
/**
*
* @param {HTMLElement} element
*/
function attachResponsiveImage(element) {
var children = [];
var rules = [];
var sources = [];
var defaultImageId = 0;
var lastActiveImage = -1;
var loadedImages = [];
for (var i in element.children) {
if(!element.children.hasOwnProperty(i)) continue;
if (element.children[i].tagName && element.children[i].tagName.toLowerCase() === 'img') {
children.push(element.children[i]);
var minWidth = element.children[i].getAttribute('min-width') || element.children[i].getAttribute('data-min-width');
//var minHeight = element.children[i].getAttribute('min-height') || element.children[i].getAttribute('data-min-height');
var src = element.children[i].getAttribute('data-src') || element.children[i].getAttribute('url');
sources.push(src);
var rule = {
minWidth: minWidth
};
rules.push(rule);
if (!minWidth) {
defaultImageId = children.length - 1;
element.children[i].style.display = 'block';
} else {
element.children[i].style.display = 'none';
}
}
}
lastActiveImage = defaultImageId;
function check() {
var imageToDisplay = false, i;
for (i in children){
if(!children.hasOwnProperty(i)) continue;
if (rules[i].minWidth) {
if (element.offsetWidth > rules[i].minWidth) {
imageToDisplay = i;
}
}
}
if (!imageToDisplay) {
//no rule matched, show default
imageToDisplay = defaultImageId;
}
if (lastActiveImage != imageToDisplay) {
//image change
if (!loadedImages[imageToDisplay]){
//image has not been loaded yet, we need to load the image first in memory to prevent flash of
//no content
var image = new Image();
image.onload = function() {
children[imageToDisplay].src = sources[imageToDisplay];
children[lastActiveImage].style.display = 'none';
children[imageToDisplay].style.display = 'block';
loadedImages[imageToDisplay] = true;
lastActiveImage = imageToDisplay;
};
image.src = sources[imageToDisplay];
} else {
children[lastActiveImage].style.display = 'none';
children[imageToDisplay].style.display = 'block';
lastActiveImage = imageToDisplay;
}
} else {
//make sure for initial check call the .src is set correctly
children[imageToDisplay].src = sources[imageToDisplay];
}
}
element.resizeSensor = new ResizeSensor(element, check);
check();
if (trackingActive) {
elements.push(element);
}
}
function findResponsiveImages(){
var query = getQuery();
var elements = query('[data-responsive-image],[responsive-image]');
for (var i = 0, j = elements.length; i < j; i++) {
attachResponsiveImage(elements[i]);
}
}
var regex = /,?[\s\t]*([^,\n]*?)((?:\[[\s\t]*?(?:min|max)-(?:width|height)[\s\t]*?[~$\^]?=[\s\t]*?"[^"]*?"[\s\t]*?])+)([^,\n\s\{]*)/mgi;
var attrRegex = /\[[\s\t]*?(min|max)-(width|height)[\s\t]*?[~$\^]?=[\s\t]*?"([^"]*?)"[\s\t]*?]/mgi;
/**
* @param {String} css
*/
function extractQuery(css) {
var match;
var smatch;
css = css.replace(/'/g, '"');
while (null !== (match = regex.exec(css))) {
smatch = match[1] + match[3];
attrs = match[2];
while (null !== (attrMatch = attrRegex.exec(attrs))) {
queueQuery(smatch, attrMatch[1], attrMatch[2], attrMatch[3]);
}
}
}
/**
* @param {CssRule[]|String} rules
*/
function readRules(rules) {
var selector = '';
if (!rules) {
return;
}
if ('string' === typeof rules) {
rules = rules.toLowerCase();
if (-1 !== rules.indexOf('min-width') || -1 !== rules.indexOf('max-width')) {
extractQuery(rules);
}
} else {
for (var i = 0, j = rules.length; i < j; i++) {
if (1 === rules[i].type) {
selector = rules[i].selectorText || rules[i].cssText;
if (-1 !== selector.indexOf('min-height') || -1 !== selector.indexOf('max-height')) {
extractQuery(selector);
}else if(-1 !== selector.indexOf('min-width') || -1 !== selector.indexOf('max-width')) {
extractQuery(selector);
}
} else if (4 === rules[i].type) {
readRules(rules[i].cssRules || rules[i].rules);
}
}
}
}
var defaultCssInjected = false;
/**
* Searches all css rules and setups the event listener to all elements with element query rules..
*
* @param {Boolean} withTracking allows and requires you to use detach, since we store internally all used elements
* (no garbage collection possible if you don not call .detach() first)
*/
this.init = function(withTracking) {
trackingActive = typeof withTracking === 'undefined' ? false : withTracking;
for (var i = 0, j = document.styleSheets.length; i < j; i++) {
try {
readRules(document.styleSheets[i].cssRules || document.styleSheets[i].rules || document.styleSheets[i].cssText);
} catch(e) {
if (e.name !== 'SecurityError') {
throw e;
}
}
}
if (!defaultCssInjected) {
var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = '[responsive-image] > img, [data-responsive-image] {overflow: hidden; padding: 0; } [responsive-image] > img, [data-responsive-image] > img { width: 100%;}';
document.getElementsByTagName('head')[0].appendChild(style);
defaultCssInjected = true;
}
findElementQueriesElements();
findResponsiveImages();
};
/**
*
* @param {Boolean} withTracking allows and requires you to use detach, since we store internally all used elements
* (no garbage collection possible if you don not call .detach() first)
*/
this.update = function(withTracking) {
this.init(withTracking);
};
this.detach = function() {
if (!this.withTracking) {
throw 'withTracking is not enabled. We can not detach elements since we don not store it.' +
'Use ElementQueries.withTracking = true; before domready or call ElementQueryes.update(true).';
}
var element;
while (element = elements.pop()) {
ElementQueries.detach(element);
}
elements = [];
};
};
/**
*
* @param {Boolean} withTracking allows and requires you to use detach, since we store internally all used elements
* (no garbage collection possible if you don not call .detach() first)
*/
ElementQueries.update = function(withTracking) {
ElementQueries.instance.update(withTracking);
};
/**
* Removes all sensor and elementquery information from the element.
*
* @param {HTMLElement} element
*/
ElementQueries.detach = function(element) {
if (element.elementQueriesSetupInformation) {
//element queries
element.elementQueriesSensor.detach();
delete element.elementQueriesSetupInformation;
delete element.elementQueriesSensor;
} else if (element.resizeSensor) {
//responsive image
element.resizeSensor.detach();
delete element.resizeSensor;
} else {
//console.log('detached already', element);
}
};
ElementQueries.withTracking = false;
ElementQueries.init = function() {
if (!ElementQueries.instance) {
ElementQueries.instance = new ElementQueries();
}
ElementQueries.instance.init(ElementQueries.withTracking);
};
var domLoaded = function (callback) {
/* Internet Explorer */
/*@cc_on
@if (@_win32 || @_win64)
document.write('<script id="ieScriptLoad" defer src="//:"><\/script>');
document.getElementById('ieScriptLoad').onreadystatechange = function() {
if (this.readyState == 'complete') {
callback();
}
};
@end @*/
/* Mozilla, Chrome, Opera */
if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', callback, false);
}
/* Safari, iCab, Konqueror */
else if (/KHTML|WebKit|iCab/i.test(navigator.userAgent)) {
var DOMLoadTimer = setInterval(function () {
if (/loaded|complete/i.test(document.readyState)) {
callback();
clearInterval(DOMLoadTimer);
}
}, 10);
}
/* Other web browsers */
else window.onload = callback;
};
ElementQueries.listen = function() {
domLoaded(ElementQueries.init);
};
// make available to common module loader
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = ElementQueries;
}
else {
window.ElementQueries = ElementQueries;
ElementQueries.listen();
}
return ElementQueries;
}));

View File

@@ -0,0 +1,227 @@
/**
* Copyright Marc J. Schmidt. See the LICENSE file at the top-level
* directory of this distribution and at
* https://github.com/marcj/css-element-queries/blob/master/LICENSE.
*/
;
(function (root, factory) {
if (typeof define === "function" && define.amd) {
define(factory);
} else if (typeof exports === "object") {
module.exports = factory();
} else {
root.ResizeSensor = factory();
}
}(this, function () {
//Make sure it does not throw in a SSR (Server Side Rendering) situation
if (typeof window === "undefined") {
return null;
}
// Only used for the dirty checking, so the event callback count is limted to max 1 call per fps per sensor.
// In combination with the event based resize sensor this saves cpu time, because the sensor is too fast and
// would generate too many unnecessary events.
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
function (fn) {
return window.setTimeout(fn, 20);
};
/**
* Iterate over each of the provided element(s).
*
* @param {HTMLElement|HTMLElement[]} elements
* @param {Function} callback
*/
function forEachElement(elements, callback){
var elementsType = Object.prototype.toString.call(elements);
var isCollectionTyped = ('[object Array]' === elementsType
|| ('[object NodeList]' === elementsType)
|| ('[object HTMLCollection]' === elementsType)
|| ('[object Object]' === elementsType)
|| ('undefined' !== typeof jQuery && elements instanceof jQuery) //jquery
|| ('undefined' !== typeof Elements && elements instanceof Elements) //mootools
);
var i = 0, j = elements.length;
if (isCollectionTyped) {
for (; i < j; i++) {
callback(elements[i]);
}
} else {
callback(elements);
}
}
/**
* Class for dimension change detection.
*
* @param {Element|Element[]|Elements|jQuery} element
* @param {Function} callback
*
* @constructor
*/
var ResizeSensor = function(element, callback) {
/**
*
* @constructor
*/
function EventQueue() {
var q = [];
this.add = function(ev) {
q.push(ev);
};
var i, j;
this.call = function() {
for (i = 0, j = q.length; i < j; i++) {
q[i].call();
}
};
this.remove = function(ev) {
var newQueue = [];
for(i = 0, j = q.length; i < j; i++) {
if(q[i] !== ev) newQueue.push(q[i]);
}
q = newQueue;
}
this.length = function() {
return q.length;
}
}
/**
* @param {HTMLElement} element
* @param {String} prop
* @returns {String|Number}
*/
function getComputedStyle(element, prop) {
if (element.currentStyle) {
return element.currentStyle[prop];
} else if (window.getComputedStyle) {
return window.getComputedStyle(element, null).getPropertyValue(prop);
} else {
return element.style[prop];
}
}
/**
*
* @param {HTMLElement} element
* @param {Function} resized
*/
function attachResizeEvent(element, resized) {
if (!element.resizedAttached) {
element.resizedAttached = new EventQueue();
element.resizedAttached.add(resized);
} else if (element.resizedAttached) {
element.resizedAttached.add(resized);
return;
}
element.resizeSensor = document.createElement('div');
element.resizeSensor.className = 'resize-sensor';
var style = 'position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: hidden; z-index: -1; visibility: hidden;';
var styleChild = 'position: absolute; left: 0; top: 0; transition: 0s;';
element.resizeSensor.style.cssText = style;
element.resizeSensor.innerHTML =
'<div class="resize-sensor-expand" style="' + style + '">' +
'<div style="' + styleChild + '"></div>' +
'</div>' +
'<div class="resize-sensor-shrink" style="' + style + '">' +
'<div style="' + styleChild + ' width: 200%; height: 200%"></div>' +
'</div>';
element.appendChild(element.resizeSensor);
if (getComputedStyle(element, 'position') == 'static') {
element.style.position = 'relative';
}
var expand = element.resizeSensor.childNodes[0];
var expandChild = expand.childNodes[0];
var shrink = element.resizeSensor.childNodes[1];
var dirty, rafId, newWidth, newHeight;
var lastWidth = element.offsetWidth;
var lastHeight = element.offsetHeight;
var reset = function() {
expandChild.style.width = '100000px';
expandChild.style.height = '100000px';
expand.scrollLeft = 100000;
expand.scrollTop = 100000;
shrink.scrollLeft = 100000;
shrink.scrollTop = 100000;
};
reset();
var onResized = function() {
rafId = 0;
if (!dirty) return;
lastWidth = newWidth;
lastHeight = newHeight;
if (element.resizedAttached) {
element.resizedAttached.call();
}
};
var onScroll = function() {
newWidth = element.offsetWidth;
newHeight = element.offsetHeight;
dirty = newWidth != lastWidth || newHeight != lastHeight;
if (dirty && !rafId) {
rafId = requestAnimationFrame(onResized);
}
reset();
};
var addEvent = function(el, name, cb) {
if (el.attachEvent) {
el.attachEvent('on' + name, cb);
} else {
el.addEventListener(name, cb);
}
};
addEvent(expand, 'scroll', onScroll);
addEvent(shrink, 'scroll', onScroll);
}
forEachElement(element, function(elem){
attachResizeEvent(elem, callback);
});
this.detach = function(ev) {
ResizeSensor.detach(element, ev);
};
};
ResizeSensor.detach = function(element, ev) {
forEachElement(element, function(elem){
if(elem.resizedAttached && typeof ev == "function"){
elem.resizedAttached.remove(ev);
if(elem.resizedAttached.length()) return;
}
if (elem.resizeSensor) {
if (elem.contains(elem.resizeSensor)) {
elem.removeChild(elem.resizeSensor);
}
delete elem.resizeSensor;
delete elem.resizedAttached;
}
});
};
return ResizeSensor;
}));

View File

@@ -0,0 +1,7 @@
var scrollBarWidth = getScrollBarWidth();
function getScrollBarWidth() {
let $outer = $('<div>').css({visibility: 'hidden', width: 100, overflow: 'scroll'}).appendTo('body'),
widthWithScroll = $('<div>').css({width: '100%'}).appendTo($outer).outerWidth();
$outer.remove();
return 100 - widthWithScroll;
}

484
imports/util/select2/select2.css vendored Normal file
View File

@@ -0,0 +1,484 @@
.select2-container {
box-sizing: border-box;
display: inline-block;
margin: 0;
position: relative;
vertical-align: middle; }
.select2-container .select2-selection--single {
box-sizing: border-box;
cursor: pointer;
display: block;
height: 28px;
user-select: none;
-webkit-user-select: none; }
.select2-container .select2-selection--single .select2-selection__rendered {
display: block;
padding-left: 8px;
padding-right: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap; }
.select2-container .select2-selection--single .select2-selection__clear {
position: relative; }
.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered {
padding-right: 8px;
padding-left: 20px; }
.select2-container .select2-selection--multiple {
box-sizing: border-box;
cursor: pointer;
display: block;
min-height: 32px;
user-select: none;
-webkit-user-select: none; }
.select2-container .select2-selection--multiple .select2-selection__rendered {
display: inline-block;
overflow: hidden;
padding-left: 8px;
text-overflow: ellipsis;
white-space: nowrap; }
.select2-container .select2-search--inline {
float: left; }
.select2-container .select2-search--inline .select2-search__field {
box-sizing: border-box;
border: none;
font-size: 100%;
margin-top: 5px;
padding: 0; }
.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button {
-webkit-appearance: none; }
.select2-dropdown {
background-color: white;
border: 1px solid #aaa;
border-radius: 4px;
box-sizing: border-box;
display: block;
position: absolute;
left: -100000px;
width: 100%;
z-index: 1051; }
.select2-results {
display: block; }
.select2-results__options {
list-style: none;
margin: 0;
padding: 0; }
.select2-results__option {
padding: 6px;
user-select: none;
-webkit-user-select: none; }
.select2-results__option[aria-selected] {
cursor: pointer; }
.select2-container--open .select2-dropdown {
left: 0; }
.select2-container--open .select2-dropdown--above {
border-bottom: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0; }
.select2-container--open .select2-dropdown--below {
border-top: none;
border-top-left-radius: 0;
border-top-right-radius: 0; }
.select2-search--dropdown {
display: block;
padding: 4px; }
.select2-search--dropdown .select2-search__field {
padding: 4px;
width: 100%;
box-sizing: border-box; }
.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button {
-webkit-appearance: none; }
.select2-search--dropdown.select2-search--hide {
display: none; }
.select2-close-mask {
border: 0;
margin: 0;
padding: 0;
display: block;
position: fixed;
left: 0;
top: 0;
min-height: 100%;
min-width: 100%;
height: auto;
width: auto;
opacity: 0;
z-index: 99;
background-color: #fff;
filter: alpha(opacity=0); }
.select2-hidden-accessible {
border: 0 !important;
clip: rect(0 0 0 0) !important;
height: 1px !important;
margin: -1px !important;
overflow: hidden !important;
padding: 0 !important;
position: absolute !important;
width: 1px !important; }
.select2-container--default .select2-selection--single {
background-color: #fff;
border: 1px solid #aaa;
border-radius: 4px; }
.select2-container--default .select2-selection--single .select2-selection__rendered {
color: #444;
line-height: 28px; }
.select2-container--default .select2-selection--single .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold; }
.select2-container--default .select2-selection--single .select2-selection__placeholder {
color: #999; }
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: 26px;
position: absolute;
top: 1px;
right: 1px;
width: 20px; }
.select2-container--default .select2-selection--single .select2-selection__arrow b {
border-color: #888 transparent transparent transparent;
border-style: solid;
border-width: 5px 4px 0 4px;
height: 0;
left: 50%;
margin-left: -4px;
margin-top: -2px;
position: absolute;
top: 50%;
width: 0; }
.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear {
float: left; }
.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow {
left: 1px;
right: auto; }
.select2-container--default.select2-container--disabled .select2-selection--single {
background-color: #eee;
cursor: default; }
.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear {
display: none; }
.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
border-color: transparent transparent #888 transparent;
border-width: 0 4px 5px 4px; }
.select2-container--default .select2-selection--multiple {
background-color: white;
border: 1px solid #aaa;
border-radius: 4px;
cursor: text; }
.select2-container--default .select2-selection--multiple .select2-selection__rendered {
box-sizing: border-box;
list-style: none;
margin: 0;
padding: 0 5px;
width: 100%; }
.select2-container--default .select2-selection--multiple .select2-selection__rendered li {
list-style: none; }
.select2-container--default .select2-selection--multiple .select2-selection__placeholder {
color: #999;
margin-top: 5px;
float: left; }
.select2-container--default .select2-selection--multiple .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold;
margin-top: 5px;
margin-right: 10px; }
.select2-container--default .select2-selection--multiple .select2-selection__choice {
background-color: #e4e4e4;
border: 1px solid #aaa;
border-radius: 4px;
cursor: default;
float: left;
margin-right: 5px;
margin-top: 5px;
padding: 0 5px; }
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
color: #999;
cursor: pointer;
display: inline-block;
font-weight: bold;
margin-right: 2px; }
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
color: #333; }
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline {
float: right; }
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
margin-left: 5px;
margin-right: auto; }
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
margin-left: 2px;
margin-right: auto; }
.select2-container--default.select2-container--focus .select2-selection--multiple {
border: solid black 1px;
outline: 0; }
.select2-container--default.select2-container--disabled .select2-selection--multiple {
background-color: #eee;
cursor: default; }
.select2-container--default.select2-container--disabled .select2-selection__choice__remove {
display: none; }
.select2-container--default.select2-container--open.select2-container--above .select2-selection--single, .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple {
border-top-left-radius: 0;
border-top-right-radius: 0; }
.select2-container--default.select2-container--open.select2-container--below .select2-selection--single, .select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0; }
.select2-container--default .select2-search--dropdown .select2-search__field {
border: 1px solid #aaa; }
.select2-container--default .select2-search--inline .select2-search__field {
background: transparent;
border: none;
outline: 0;
box-shadow: none;
-webkit-appearance: textfield; }
.select2-container--default .select2-results > .select2-results__options {
max-height: 200px;
overflow-y: auto; }
.select2-container--default .select2-results__option[role=group] {
padding: 0; }
.select2-container--default .select2-results__option[aria-disabled=true] {
color: #999; }
.select2-container--default .select2-results__option[aria-selected=true] {
background-color: #ddd; }
.select2-container--default .select2-results__option .select2-results__option {
padding-left: 1em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__group {
padding-left: 0; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option {
margin-left: -1em;
padding-left: 2em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -2em;
padding-left: 3em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -3em;
padding-left: 4em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -4em;
padding-left: 5em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -5em;
padding-left: 6em; }
.select2-container--default .select2-results__option--highlighted[aria-selected] {
background-color: #5897fb;
color: white; }
.select2-container--default .select2-results__group {
cursor: default;
display: block;
padding: 6px; }
.select2-container--classic .select2-selection--single {
background-color: #f7f7f7;
border: 1px solid #aaa;
border-radius: 4px;
outline: 0;
background-image: -webkit-linear-gradient(top, white 50%, #eeeeee 100%);
background-image: -o-linear-gradient(top, white 50%, #eeeeee 100%);
background-image: linear-gradient(to bottom, white 50%, #eeeeee 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); }
.select2-container--classic .select2-selection--single:focus {
border: 1px solid #5897fb; }
.select2-container--classic .select2-selection--single .select2-selection__rendered {
color: #444;
line-height: 28px; }
.select2-container--classic .select2-selection--single .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold;
margin-right: 10px; }
.select2-container--classic .select2-selection--single .select2-selection__placeholder {
color: #999; }
.select2-container--classic .select2-selection--single .select2-selection__arrow {
background-color: #ddd;
border: none;
border-left: 1px solid #aaa;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
height: 26px;
position: absolute;
top: 1px;
right: 1px;
width: 20px;
background-image: -webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
background-image: -o-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
background-image: linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0); }
.select2-container--classic .select2-selection--single .select2-selection__arrow b {
border-color: #888 transparent transparent transparent;
border-style: solid;
border-width: 5px 4px 0 4px;
height: 0;
left: 50%;
margin-left: -4px;
margin-top: -2px;
position: absolute;
top: 50%;
width: 0; }
.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear {
float: left; }
.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow {
border: none;
border-right: 1px solid #aaa;
border-radius: 0;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
left: 1px;
right: auto; }
.select2-container--classic.select2-container--open .select2-selection--single {
border: 1px solid #5897fb; }
.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow {
background: transparent;
border: none; }
.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b {
border-color: transparent transparent #888 transparent;
border-width: 0 4px 5px 4px; }
.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single {
border-top: none;
border-top-left-radius: 0;
border-top-right-radius: 0;
background-image: -webkit-linear-gradient(top, white 0%, #eeeeee 50%);
background-image: -o-linear-gradient(top, white 0%, #eeeeee 50%);
background-image: linear-gradient(to bottom, white 0%, #eeeeee 50%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); }
.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single {
border-bottom: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
background-image: -webkit-linear-gradient(top, #eeeeee 50%, white 100%);
background-image: -o-linear-gradient(top, #eeeeee 50%, white 100%);
background-image: linear-gradient(to bottom, #eeeeee 50%, white 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0); }
.select2-container--classic .select2-selection--multiple {
background-color: white;
border: 1px solid #aaa;
border-radius: 4px;
cursor: text;
outline: 0; }
.select2-container--classic .select2-selection--multiple:focus {
border: 1px solid #5897fb; }
.select2-container--classic .select2-selection--multiple .select2-selection__rendered {
list-style: none;
margin: 0;
padding: 0 5px; }
.select2-container--classic .select2-selection--multiple .select2-selection__clear {
display: none; }
.select2-container--classic .select2-selection--multiple .select2-selection__choice {
background-color: #e4e4e4;
border: 1px solid #aaa;
border-radius: 4px;
cursor: default;
float: left;
margin-right: 5px;
margin-top: 5px;
padding: 0 5px; }
.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove {
color: #888;
cursor: pointer;
display: inline-block;
font-weight: bold;
margin-right: 2px; }
.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover {
color: #555; }
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
float: right; }
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
margin-left: 5px;
margin-right: auto; }
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
margin-left: 2px;
margin-right: auto; }
.select2-container--classic.select2-container--open .select2-selection--multiple {
border: 1px solid #5897fb; }
.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple {
border-top: none;
border-top-left-radius: 0;
border-top-right-radius: 0; }
.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple {
border-bottom: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0; }
.select2-container--classic .select2-search--dropdown .select2-search__field {
border: 1px solid #aaa;
outline: 0; }
.select2-container--classic .select2-search--inline .select2-search__field {
outline: 0;
box-shadow: none; }
.select2-container--classic .select2-dropdown {
background-color: white;
border: 1px solid transparent; }
.select2-container--classic .select2-dropdown--above {
border-bottom: none; }
.select2-container--classic .select2-dropdown--below {
border-top: none; }
.select2-container--classic .select2-results > .select2-results__options {
max-height: 200px;
overflow-y: auto; }
.select2-container--classic .select2-results__option[role=group] {
padding: 0; }
.select2-container--classic .select2-results__option[aria-disabled=true] {
color: grey; }
.select2-container--classic .select2-results__option--highlighted[aria-selected] {
background-color: #3875d7;
color: white; }
.select2-container--classic .select2-results__group {
cursor: default;
display: block;
padding: 6px; }
.select2-container--classic.select2-container--open .select2-dropdown {
border-color: #5897fb; }

6436
imports/util/select2/select2.full.js vendored Normal file

File diff suppressed because it is too large Load Diff

5725
imports/util/select2/select2.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,314 @@
/**
* selectize.default.css (v0.12.0) - Default Theme
* Copyright (c) 20132015 Brian Reavis & contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
* @author Brian Reavis <brian@thirdroute.com>
*/
.selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder
visibility visible !important
background #f2f2f2 !important
background rgba(0, 0, 0, 0.06) !important
border 0 none !important
-webkit-box-shadow inset 0 0 12px 4px #ffffff
box-shadow inset 0 0 12px 4px #ffffff
.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after
content '!'
visibility hidden
.selectize-control.plugin-drag_drop .ui-sortable-helper
-webkit-box-shadow 0 2px 5px rgba(0, 0, 0, 0.2)
box-shadow 0 2px 5px rgba(0, 0, 0, 0.2)
.selectize-dropdown-header
position relative
padding 5px 8px
border-bottom 1px solid #d0d0d0
background #f8f8f8
-webkit-border-radius 3px 3px 0 0
-moz-border-radius 3px 3px 0 0
border-radius 3px 3px 0 0
.selectize-dropdown-header-close
position absolute
right 8px
top 50%
color #303030
opacity 0.4
margin-top -12px
line-height 20px
font-size 20px !important
.selectize-dropdown-header-close:hover
color #000000
.selectize-dropdown.plugin-optgroup_columns .optgroup
border-right 1px solid #f2f2f2
border-top 0 none
float left
-webkit-box-sizing border-box
-moz-box-sizing border-box
box-sizing border-box
.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child
border-right 0 none
.selectize-dropdown.plugin-optgroup_columns .optgroup:before
display none
.selectize-dropdown.plugin-optgroup_columns .optgroup-header
border-top 0 none
.selectize-control.plugin-remove_button [data-value]
position relative
padding-right 24px !important
.selectize-control.plugin-remove_button [data-value] .remove
z-index 1
/* fixes ie bug (see #392) */
position absolute
top 0
right 0
bottom 0
width 17px
text-align center
font-weight bold
font-size 12px
color inherit
text-decoration none
vertical-align middle
display inline-block
padding 2px 0 0 0
border-left 1px solid #0073bb
-webkit-border-radius 0 2px 2px 0
-moz-border-radius 0 2px 2px 0
border-radius 0 2px 2px 0
-webkit-box-sizing border-box
-moz-box-sizing border-box
box-sizing border-box
.selectize-control.plugin-remove_button [data-value] .remove:hover
background rgba(0, 0, 0, 0.05)
.selectize-control.plugin-remove_button [data-value].active .remove
border-left-color #00578d
.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover
background none
.selectize-control.plugin-remove_button .disabled [data-value] .remove
border-left-color #aaaaaa
.selectize-control
position relative
.selectize-dropdown, .selectize-input, .selectize-input input
color #303030
font-family inherit
font-size 13px
line-height 18px
-webkit-font-smoothing inherit
.selectize-input, .selectize-control.single .selectize-input.input-active
background #ffffff
cursor text
display inline-block
.selectize-input
border 1px solid #d0d0d0
padding 8px 8px
display inline-block
width 100%
overflow hidden
position relative
z-index 1
-webkit-box-sizing border-box
-moz-box-sizing border-box
box-sizing border-box
-webkit-box-shadow inset 0 1px 1px rgba(0, 0, 0, 0.1)
box-shadow inset 0 1px 1px rgba(0, 0, 0, 0.1)
-webkit-border-radius 3px
-moz-border-radius 3px
border-radius 3px
.selectize-control.multi .selectize-input.has-items
padding 5px 8px 2px
.selectize-input.full
background-color #ffffff
.selectize-input.disabled, .selectize-input.disabled *
cursor default !important
.selectize-input.focus
-webkit-box-shadow inset 0 1px 2px rgba(0, 0, 0, 0.15)
box-shadow inset 0 1px 2px rgba(0, 0, 0, 0.15)
.selectize-input.dropdown-active
-webkit-border-radius 3px 3px 0 0
-moz-border-radius 3px 3px 0 0
border-radius 3px 3px 0 0
.selectize-input > *
vertical-align baseline
display -moz-inline-stack
display inline-block
zoom 1
*display inline .selectize-control.multi .selectize-input > div
cursor pointer
margin 0 3px 3px 0
padding 2px 6px
background #1da7ee
color #ffffff
border 1px solid #0073bb
.selectize-control.multi .selectize-input > div.active
background #92c836
color #ffffff
border 1px solid #00578d
.selectize-control.multi .selectize-input.disabled > div, .selectize-control.multi .selectize-input.disabled > div.active
color #ffffff
background #d2d2d2
border 1px solid #aaaaaa
.selectize-input > input
display inline-block !important
padding 0 !important
min-height 0 !important
max-height none !important
max-width 100% !important
margin 0 1px !important
text-indent 0 !important
border 0 none !important
background none !important
line-height inherit !important
-webkit-user-select auto !important
-webkit-box-shadow none !important
box-shadow none !important
.selectize-input > input::-ms-clear
display none
.selectize-input > input:focus
outline none !important
.selectize-input::after
content ' '
display block
clear left
.selectize-input.dropdown-active::before
content ' '
display block
position absolute
background #f0f0f0
height 1px
bottom 0
left 0
right 0
.selectize-dropdown
position absolute
z-index 10
border 1px solid #d0d0d0
background #ffffff
margin -1px 0 0 0
border-top 0 none
-webkit-box-sizing border-box
-moz-box-sizing border-box
box-sizing border-box
-webkit-box-shadow 0 1px 3px rgba(0, 0, 0, 0.1)
box-shadow 0 1px 3px rgba(0, 0, 0, 0.1)
-webkit-border-radius 0 0 3px 3px
-moz-border-radius 0 0 3px 3px
border-radius 0 0 3px 3px
.selectize-dropdown [data-selectable]
cursor pointer
overflow hidden
.selectize-dropdown [data-selectable] .highlight
background rgba(125, 168, 208, 0.2)
-webkit-border-radius 1px
-moz-border-radius 1px
border-radius 1px
.selectize-dropdown [data-selectable], .selectize-dropdown .optgroup-header
padding 5px 8px
.selectize-dropdown .optgroup:first-child .optgroup-header
border-top 0 none
.selectize-dropdown .optgroup-header
color #303030
background #ffffff
cursor default
.selectize-dropdown .active
background-color #f5fafd
color #495c68
.selectize-dropdown .active.create
color #495c68
.selectize-dropdown .create
color rgba(48, 48, 48, 0.5)
.selectize-dropdown-content
overflow-y auto
overflow-x hidden
max-height 200px
.selectize-control.single .selectize-input, .selectize-control.single .selectize-input input
cursor pointer
.selectize-control.single .selectize-input.input-active, .selectize-control.single .selectize-input.input-active input
cursor text
.selectize-control.single .selectize-input:after
content ' '
display block
position absolute
top 50%
right 15px
margin-top -3px
width 0
height 0
border-style solid
border-width 5px 5px 0 5px
border-color #808080 transparent transparent transparent
.selectize-control.single .selectize-input.dropdown-active:after
margin-top -4px
border-width 0 5px 5px 5px
border-color transparent transparent #808080 transparent
.selectize-control.rtl.single .selectize-input:after
left 15px
right auto
.selectize-control.rtl .selectize-input > input
margin 0 4px 0 -2px !important
.selectize-control .selectize-input.disabled
opacity 0.5
background-color #fafafa
.selectize-control.multi .selectize-input.has-items
padding-left 5px
padding-right 5px
.selectize-control.multi .selectize-input.disabled [data-value]
color #999
text-shadow none
background none
-webkit-box-shadow none
box-shadow none
.selectize-control.multi .selectize-input.disabled [data-value], .selectize-control.multi .selectize-input.disabled [data-value] .remove
border-color #e6e6e6
.selectize-control.multi .selectize-input.disabled [data-value] .remove
background none
.selectize-control.multi .selectize-input [data-value]
text-shadow 0 1px 0 rgba(0, 51, 83, 0.3)
-webkit-border-radius 3px
-moz-border-radius 3px
border-radius 3px
background-color #1b9dec
background-image -moz-linear-gradient(top, rgb(29, 167, 238, 1), rgb(23, 142, 233, 1))
background-image -webkit-gradient(linear, 0 0, 0 100%, from(rgb(29, 167, 238, 1)), to(rgb(23, 142, 233, 1)))
background-image -webkit-linear-gradient(top, rgb(29, 167, 238, 1), rgb(23, 142, 233, 1))
background-image -o-linear-gradient(top, rgb(29, 167, 238, 1), rgb(23, 142, 233, 1))
background-image linear-gradient(to bottom, rgb(29, 167, 238, 1), rgb(23, 142, 233, 1))
background-repeat repeat-x
filter unquote("progid:DXImageTransform.Microsoft.gradient('#ff1da7ee', '#ff178ee9', 0)")
-webkit-box-shadow 0 1px 0 rgba(0, 0, 0, 0.2), inset 0 1px rgba(255, 255, 255, 0.03)
box-shadow 0 1px 0 rgba(0, 0, 0, 0.2), inset 0 1px rgba(255, 255, 255, 0.03)
.selectize-control.multi .selectize-input [data-value].active
background-color #0085d4
background-image -moz-linear-gradient(top, rgb(0, 143, 216, 1), rgb(0, 117, 207, 1))
background-image -webkit-gradient(linear, 0 0, 0 100%, from(rgb(0, 143, 216, 1)), to(rgb(0, 117, 207, 1)))
background-image -webkit-linear-gradient(top, rgb(0, 143, 216, 1), rgb(0, 117, 207, 1))
background-image -o-linear-gradient(top, rgb(0, 143, 216, 1), rgb(0, 117, 207, 1))
background-image linear-gradient(to bottom, rgb(0, 143, 216, 1), rgb(0, 117, 207, 1))
background-repeat repeat-x
filter unquote("progid:DXImageTransform.Microsoft.gradient('#ff008fd8', '#ff0075cf', 0)")
.selectize-control.single .selectize-input
-webkit-box-shadow 0 1px 0 rgba(0, 0, 0, 0.05), inset 0 1px 0 rgba(255, 255, 255, 0.8)
box-shadow 0 1px 0 rgba(0, 0, 0, 0.05), inset 0 1px 0 rgba(255, 255, 255, 0.8)
background-color #f9f9f9
background-image -moz-linear-gradient(top, rgb(254, 254, 254, 1), rgb(242, 242, 242, 1))
background-image -webkit-gradient(linear, 0 0, 0 100%, from(rgb(254, 254, 254, 1)), to(rgb(242, 242, 242, 1)))
background-image -webkit-linear-gradient(top, rgb(254, 254, 254, 1), rgb(242, 242, 242, 1))
background-image -o-linear-gradient(top, rgb(254, 254, 254, 1), rgb(242, 242, 242, 1))
background-image linear-gradient(to bottom, rgb(254, 254, 254, 1), rgb(242, 242, 242, 1))
background-repeat repeat-x
filter unquote("progid:DXImageTransform.Microsoft.gradient('#fffefefe', '#fff2f2f2', 0)")
.selectize-control.single .selectize-input, .selectize-dropdown.single
border-color #b8b8b8
.selectize-dropdown .optgroup-header
padding-top 7px
font-weight bold
font-size 0.85em
.selectize-dropdown .optgroup
border-top 1px solid #f0f0f0
.selectize-dropdown .optgroup:first-child
border-top 0 none

View File

@@ -0,0 +1,57 @@
<template name="Selectize">
<div class="selectize-control selectize {{#if multiple}}multi{{else}}single{{/if}} {{#if removeButton}}plugin-remove_button{{/if}} {{#if loading}}loading{{/if}}">
<select name={{name}} multiple={{multiple}} id={{id}} data-schema-key={{dataSchemaKey}}>
<option value=""></option>
{{#each getItems}}
<option value={{value}} selected={{selected}}>{{label}}</option>
{{/each}}
</select>
<div class="selectize-input items not-full has-options has-items {{#if disabled}}disabled{{/if}} {{#if open}}focus input-active dropdown-active{{/if}}">
{{#if getPlaceholder}}
<div class="selectize-placeholder {{#if open}}hidden{{/if}}">
{{getPlaceholder}}
</div>
{{/if}}
{{#if inputPosition -1}}
<input type="text" autocomplete="off" tabindex="" class="js-selectizeInput">
{{/if}}
{{#each getItemsSelected}}
<div data-value={{value}} class="item">
{{label}}
{{#if removeButton}}
<a href="#" class="remove" tabindex="-1" title="Remove">×</a>
{{/if}}
</div>
{{#if inputPosition @index}}
<input type="text" autocomplete="off" tabindex="" class="js-selectizeInput">
{{/if}}
{{/each}}
</div>
<div class="selectize-dropdown js-selectize-dropdown {{#unless open}}hidden{{/unless}}">
<div class="selectize-dropdown-content">
{{#each getItemsUnselected}}
<div data-value={{value}} data-index={{@index}} data-selectable class="option {{#if activeOption @index}}active{{/if}} {{isPlaceholder}}">
{{label}}
</div>
{{/each}}
{{#if create}}
{{#if getSearchText}}
<div data-selectable="" data-index='create' class="create {{#if activeOption 'create'}}active{{/if}}">
{{#if createText}}
{{{createText}}}
{{else}}
Add
{{/if}}
<strong>{{getSearchText}}</strong>
</div>
{{/if}}
{{/if}}
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,38 @@
.selectize.selectize-control
position relative
.hidden
display none
select
display none
.selectize-input
input
width 4px
opacity 1
position relative
left 0px
div.selectize-placeholder
color #bbb
background none
border none
&.input-active
div.selectize-placeholder
display none
.selectize-dropdown
width 100% !important
top 36px
&.loading:after
content ""
width 16px
height 16px
//background '/packages/vazco_selectize/img/loading.gif' no-repeat
background 'public/images/loading.gif' no-repeat
position absolute
top 10px
right 10px
z-index 1000
&.multi
.selectize-input
height 36px
.uniPlaceholder
color #bbb

View File

@@ -0,0 +1,696 @@
import './selectize.html';
/* Meteor need globals */
/* eslint strict: 0 */
/* jshint strict: false */
UniSelectize = function (options, template) {
this.items = new ReactiveVar([]);
this.itemsSelected = new ReactiveVar([]);
this.itemsUnselected = new ReactiveVar([]);
this.open = new ReactiveVar(false);
this.loading = new ReactiveVar(false);
this.searchText = new ReactiveVar();
this.activeOption = new ReactiveVar(-1);
this.inputPosition = new ReactiveVar(-1);
this.optionsMethodParams = new ReactiveVar();
this.create = options.create;
this.template = template;
this.multiple = options.multiple;
this.sortMethod = _.isUndefined(options.sortMethod) ? 'label' : options.sortMethod;
this.placeholder = options.placeholder;
this.removeButton = options.removeButton !== false;
this.createMethod = options.createMethod;
this.optionsMethod = options.optionsMethod;
this.optionsPlaceholder = options.optionsPlaceholder;
};
UniSelectize.prototype.triggerChangeEvent = function() {
var self = this;
Meteor.defer(function () {
$(self.template.find('select')).change();
});
};
UniSelectize.prototype.setItems = function (items, value) {
if (!_.isArray(items)) {
console.warn('invalid options format');
}
var values = value && (_.isArray(value) ? value : [value]);
items = _.filter(items, function (item) {
if (!item.value || !item.label) {
console.info('invalid option', item);
return false;
}
return true;
});
var itemValues = items.map(function (item) {
return item.value;
});
_.each(values, function (val) {
if (!_.contains(itemValues, val) && val) {
items.push({
value: val,
label: val
});
}
});
_.each(items, function (item) {
if (_.contains(values, item.value)) {
item.selected = true;
}
});
this.items.set(items);
};
UniSelectize.prototype.addItems = function (newItems, value) {
if (!_.isArray(newItems)) {
console.warn('invalid options format');
}
var values = value && (_.isArray(value) ? value : [value]);
var items = this.items.get();
var itemsValues = items.map(function (item) {
return item.value;
});
_.each(newItems, function (newItem) {
if (!newItem.value || !newItem.label) {
console.info('invalid option', newItem);
return;
}
if (!_.contains(itemsValues, newItem.value)) {
var item = {
value: newItem.value,
label: newItem.label,
selected: newItem.selected
};
if (_.contains(values, newItem.value)) {
item.selected = true;
}
items.push(item);
} else if (typeof newItem.selected !== 'undefined') {
var item = _.find(items, function (item) {
return item.value === newItem.value;
});
item.selected = newItem.selected;
}
});
this.items.set(items);
};
UniSelectize.prototype.removeUnusedItems = function (newItems) {
if (!_.isArray(newItems)) {
console.warn('invalid options format');
}
var items = this.items.get();
var newItemsValues = newItems.map(function (item) {
return item.value;
});
items = _.filter(items, function (item) {
return _.contains(newItemsValues, item.value) || item.selected;
});
this.items.set(items);
};
UniSelectize.prototype.itemsAutorun = function () {
var items = this.items.get();
var itemsSelected = [];
var itemsUnselected = [];
_.each(items, function (item) {
if (item.selected) {
itemsSelected.push(item);
} else {
itemsUnselected.push(item);
}
});
if (this.sortMethod) {
itemsSelected = _.sortBy(itemsSelected, this.sortMethod);
itemsUnselected = _.sortBy(itemsUnselected, this.sortMethod);
}
var itemsSelectedPrev = this.itemsSelected.get();
if (!_.isEqual(itemsSelectedPrev, itemsSelected)) {
this.itemsSelected.set(itemsSelected);
}
if (this.placeholder && this.optionsPlaceholder) {
itemsUnselected.unshift({
value: '',
label: _.isString(this.optionsPlaceholder) ? this.optionsPlaceholder: this.placeholder
});
}
this.itemsUnselected.set(itemsUnselected);
};
UniSelectize.prototype.itemsSelectedAutorun = function () {
var itemsSelected = this.template.uniSelectize.itemsSelected.get();
this.template.uniSelectize.inputPosition.set(itemsSelected.length - 1);
};
UniSelectize.prototype.inputFocus = function () {
var self = this;
Meteor.defer(function () {
var $input = $(self.template.find('input'));
$input.focus();
});
};
UniSelectize.prototype.selectItem = function (value) {
var items = this.items.get();
var multiple = this.multiple;
_.each(items, function (item) {
if (value === '') {
item.selected = false;
} else if (item.value === value) {
item.selected = true;
} else if (!multiple) {
item.selected = false;
}
});
this.setItems(items);
this.triggerChangeEvent();
};
UniSelectize.prototype.unselectItem = function (value, reset) {
var items = this.items.get();
_.each(items, function (item) {
if (item.value === value || reset) {
item.selected = false;
}
});
this.setItems(items);
this.triggerChangeEvent()
};
UniSelectize.prototype.removeItemBeforeInput = function () {
var items = this.itemsSelected.get();
var inputPosition = this.inputPosition.get();
var itemToRemove;
_.each(items, function (item, index) {
if (index === inputPosition) {
itemToRemove = item;
}
});
if (itemToRemove) {
this.unselectItem(itemToRemove.value, false);
}
};
UniSelectize.prototype.removeItemAfterInput = function () {
var items = this.itemsSelected.get();
var inputPosition = this.inputPosition.get();
var itemToRemove;
_.each(items, function (item, index) {
if (index === inputPosition + 1) {
itemToRemove = item;
}
});
if (itemToRemove) {
this.unselectItem(itemToRemove.value, false);
}
};
UniSelectize.prototype.selectActiveItem = function () {
var itemsUnselected = this.getItemsUnselectedFiltered();
var activeOption = this.activeOption.get();
var itemToSelect = itemsUnselected && itemsUnselected[activeOption];
if (activeOption === itemsUnselected.length && this.create) {
this.createItem();
return;
}
itemToSelect && this.selectItem(itemToSelect.value);
if (this.multiple) {
this.open.set(true);
this.inputFocus();
} else {
this.open.set(false);
}
};
UniSelectize.prototype.insertItem = function (item) {
var items = this.items.get();
if (!_.find(items, function (obj) {
if (obj.value === item.value) {
obj.selected = item.selected;
return true;
}
return false;
})) {
items.push(item);
}
this.setItems(items);
this.triggerChangeEvent();
};
UniSelectize.prototype.createItem = function () {
var self = this;
var template = this.template;
var searchText = this.searchText.get();
if (!searchText) {
return false;
}
var item = {
label: searchText,
value: searchText,
selected: true
};
if (template.uniSelectize.createMethod) {
Meteor.call(template.uniSelectize.createMethod, searchText, searchText, function (error, value) {
if (error) {
console.error('universe selectize create method error:', error);
return;
}
Meteor.defer(function () {
item.value = value || item.value;
self.insertItem(item);
});
});
} else {
this.insertItem(item);
}
if (this.multiple) {
this.inputFocus();
} else {
this.open.set(false);
}
};
UniSelectize.prototype.getItemsUnselectedFiltered = function () {
var items = this.itemsUnselected.get();
var searchText = this.searchText.get();
return _.filter(items, function (item) {
if (item.label && item.label.search(new RegExp(searchText, 'i')) !== -1) {
return true;
}
return false;
});
};
UniSelectize.prototype.checkDisabled = function () {
if (this.template.data.disabled) {
throw new Meteor.Error('This field is disabled');
}
};
UniSelectize.prototype.measureString = function (str, $parent) {
if (!str) {
return 0;
}
var $test = $('<test>').css({
position: 'absolute',
top: -99999,
left: -99999,
width: 'auto',
padding: 0,
whiteSpace: 'pre'
}).text(str).appendTo('body');
this.transferStyles($parent, $test, [
'letterSpacing',
'fontSize',
'fontFamily',
'fontWeight',
'textTransform'
]);
var width = $test.width();
$test.remove();
return width;
};
UniSelectize.prototype.transferStyles = function ($from, $to, properties) {
var i, n, styles = {};
if (properties) {
for (i = 0, n = properties.length; i < n; i++) {
styles[properties[i]] = $from.css(properties[i]);
}
} else {
styles = $from.css();
}
$to.css(styles);
};
UniSelectize.prototype.getOptionsFromMethod = function (values) {
var self = this;
var methodName = this.optionsMethod;
var searchText = this.searchText.get();
var params = this.optionsMethodParams.get();
if (!methodName) {
return false;
}
var searchVal = {
searchText: searchText,
values: values || [],
params: params || null
};
self.loading.set(true);
Meteor.call(methodName, searchVal, function (err, options) {
self.loading.set(false);
if (params) {
self.removeUnusedItems(options);
}
self.addItems(options, values);
});
};
Template.Selectize.onCreated(function () {
var template = this;
template.uniSelectize = new UniSelectize(template.data, template);
});
Template.Selectize.onRendered(function () {
var template = this;
template.autorun(function () {
var data = Template.currentData();
var value = data.value;
if (template.uniSelectize.optionsMethod) {
template.uniSelectize.getOptionsFromMethod(value);
} else {
var options = data.options;
template.uniSelectize.setItems(options, value);
}
});
template.autorun(function () {
template.uniSelectize.itemsAutorun();
});
template.autorun(function () {
template.uniSelectize.itemsSelectedAutorun();
});
template.autorun(function () {
var data = Template.currentData();
var methodParams = data.optionsMethodParams;
var params = _.isFunction(methodParams) ? methodParams() : methodParams;
template.uniSelectize.optionsMethodParams.set(params);
});
this.form = $(template.find('select')).parents('form');
this.form.bind('reset', function () {
template.uniSelectize.unselectItem(null, true);
});
});
Template.Selectize.onDestroyed(function () {
if (this.form) {
this.form.unbind('reset');
}
});
Template.Selectize.helpers({
multiple: function () {
var template = Template.instance();
return template.uniSelectize.multiple;
},
removeButton: function () {
var template = Template.instance();
return template.uniSelectize.multiple && template.uniSelectize.removeButton;
},
getItems: function () {
var template = Template.instance();
return template.uniSelectize.items.get();
},
getItemsSelected: function () {
var template = Template.instance();
return template.uniSelectize.itemsSelected.get();
},
getItemsUnselected: function () {
var template = Template.instance();
return template.uniSelectize.getItemsUnselectedFiltered();
},
getSearchText: function () {
var template = Template.instance();
return template.uniSelectize.searchText.get();
},
open: function () {
var template = Template.instance();
return template.uniSelectize.open.get();
},
loading: function () {
var template = Template.instance();
return template.uniSelectize.loading.get();
},
inputPosition: function (position) {
var template = Template.instance();
var inputPosition = template.uniSelectize.inputPosition.get();
return position === inputPosition;
},
activeOption: function (position) {
var template = Template.instance();
var activeOption = template.uniSelectize.activeOption.get();
var itemsUnselected = template.uniSelectize.getItemsUnselectedFiltered();
var createOption = template.uniSelectize.create;
if (activeOption === itemsUnselected.length && createOption) {
return position === 'create';
}
return position === activeOption;
},
getPlaceholder: function () {
var template = Template.instance();
var itemsSelected = template.uniSelectize.itemsSelected.get();
if (itemsSelected.length) {
return false;
}
return template.uniSelectize.placeholder;
},
isPlaceholder: function () {
return this.value === '' ? 'uniPlaceholder' : '';
}
});
Template.Selectize.events({
'click .selectize-input': function (e, template) {
template.uniSelectize.checkDisabled();
template.uniSelectize.inputFocus(template);
template.uniSelectize.getOptionsFromMethod();
},
'keydown input.js-selectizeInput': function (e, template) {
var uniSelectize = template.uniSelectize;
var itemsSelected = uniSelectize.itemsSelected.get();
var itemsUnselected = uniSelectize.getItemsUnselectedFiltered();
var inputPosition = uniSelectize.inputPosition.get();
var activeOption = uniSelectize.activeOption.get();
template.uniSelectize.checkDisabled();
var $input = $(e.target);
var width = template.uniSelectize.measureString($input.val(), $input) + 10;
$input.width(width);
switch (e.keyCode) {
case 8: // backspace
if ($input.val() === '') {
e.preventDefault();
uniSelectize.removeItemBeforeInput();
}
uniSelectize.open.set(true);
uniSelectize.inputFocus();
break;
case 46: // delete
if ($input.val() === '') {
uniSelectize.removeItemAfterInput();
}
uniSelectize.open.set(true);
uniSelectize.inputFocus();
break;
case 27: // escape
$input.blur();
break;
case 13: // enter
e.preventDefault();
if (activeOption === -1 && $input.val() === '') {
break;
}
if (itemsUnselected && itemsUnselected.length > 0) {
uniSelectize.selectActiveItem(template);
uniSelectize.searchText.set('');
$input.val('');
} else if (uniSelectize.create /*&& createOnBlur*/) {
uniSelectize.createItem();
uniSelectize.searchText.set('');
$input.val('');
}
break;
case 37: // left
if (!uniSelectize.multiple) {
break;
}
if (inputPosition > -1) {
uniSelectize.inputPosition.set(inputPosition - 1);
uniSelectize.inputFocus();
}
break;
case 39: // right
if (!uniSelectize.multiple) {
break;
}
if (inputPosition < itemsSelected.length - 1) {
uniSelectize.inputPosition.set(inputPosition + 1);
uniSelectize.inputFocus();
}
break;
case 38: // up
if (activeOption > -1) {
uniSelectize.activeOption.set(activeOption - 1);
}
break;
case 40: // down
if (activeOption < itemsUnselected.length - 1 ||
(activeOption < itemsUnselected.length && uniSelectize.create)) {
uniSelectize.activeOption.set(activeOption + 1);
}
break;
}
if (!template.uniSelectize.multiple && itemsSelected.length) {
return false;
}
},
'keyup input.js-selectizeInput': function (e, template) {
template.uniSelectize.checkDisabled();
var $el = $(e.target);
var value = $el.val();
template.uniSelectize.searchText.set(value);
template.uniSelectize.getOptionsFromMethod();
},
'focus input.js-selectizeInput': function (e, template) {
template.uniSelectize.checkDisabled();
template.uniSelectize.open.set(true);
Meteor.clearTimeout(template.uniSelectize.timeoutId);
},
'change input.js-selectizeInput': function(e, template) {
template.uniSelectize.checkDisabled();
// prevent non-autoform fields changes from submitting the form when autosave is enabled
e.preventDefault();
e.stopPropagation();
},
'blur input.js-selectizeInput': function (e, template) {
template.uniSelectize.checkDisabled();
template.uniSelectize.timeoutId = Meteor.setTimeout(function () {
template.uniSelectize.open.set(false);
}, 500);
},
'scroll .selectize-dropdown-content': function (e, template) {
Meteor.clearTimeout(template.uniSelectize.timeoutId);
template.uniSelectize.timeoutId = Meteor.setTimeout(function () {
template.uniSelectize.open.set(false);
}, 5000);
},
'click .selectize-dropdown-content > div:not(.create)': function (e, template) {
e.preventDefault();
template.uniSelectize.checkDisabled();
var $input = $(template.find('input'));
var itemsUnselected = template.uniSelectize.getItemsUnselectedFiltered();
var itemsUnselectedLength = itemsUnselected && itemsUnselected.length;
template.uniSelectize.selectItem(this.value);
template.uniSelectize.searchText.set('');
$input.val('');
if (template.uniSelectize.multiple && itemsUnselectedLength && this.value) {
template.uniSelectize.inputFocus();
} else {
template.uniSelectize.open.set(false);
}
},
'mouseenter .selectize-dropdown-content > div': function (e, template) {
var $el = $(e.target);
var elIndex = $el.attr('data-index');
var itemsUnselected = template.uniSelectize.getItemsUnselectedFiltered();
if (elIndex === 'create') {
elIndex = itemsUnselected.length;
} else {
elIndex = parseInt(elIndex);
}
template.uniSelectize.activeOption.set(elIndex);
},
'click .create': function (e, template) {
e.preventDefault();
template.uniSelectize.checkDisabled();
var $input = $(template.find('input'));
template.uniSelectize.createItem();
template.uniSelectize.searchText.set('');
$input.val('');
},
'click .remove': function (e, template) {
e.preventDefault();
template.uniSelectize.checkDisabled();
template.uniSelectize.unselectItem(this.value, false);
}
});

View File

288
imports/util/simplegrid.css Normal file
View File

@@ -0,0 +1,288 @@
/*
Simple Grid
Project Page - http://thisisdallas.github.com/Simple-Grid/
Author - Dallas Bass
Site - http://dallasbass.com
*/
[class*='grid'],
[class*='col-'],
[class*='mobile-'],
.grid:after {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
[class*='col-'] {
float: left;
min-height: 1px;
padding-right: 20px; /* column-space */
}
[class*='col-'] [class*='col-']:last-child {
padding-right: 0;
}
.grid {
width: 100%;
max-width: 1140px;
min-width: 748px; /* when using padded grid on ipad in portrait mode, width should be viewport-width - padding = (768 - 20) = 748. actually, it should be even smaller to allow for padding of grid containing element */
margin: 0 auto;
overflow: hidden;
}
.grid:after {
content: "";
display: table;
clear: both;
}
.grid-pad {
padding-top: 20px;
padding-left: 20px; /* grid-space to left */
padding-right: 0; /* grid-space to right: (grid-space-left - column-space) e.g. 20px-20px=0 */
}
.push-right {
float: right;
}
/* Content Columns */
.col-1-1 {
width: 100%;
}
.col-2-3, .col-8-12 {
width: 66.66%;
}
.col-1-2, .col-6-12 {
width: 50%;
}
.col-1-3, .col-4-12 {
width: 33.33%;
}
.col-1-4, .col-3-12 {
width: 25%;
}
.col-1-5 {
width: 20%;
}
.col-1-6, .col-2-12 {
width: 16.667%;
}
.col-1-7 {
width: 14.28%;
}
.col-1-8 {
width: 12.5%;
}
.col-1-9 {
width: 11.1%;
}
.col-1-10 {
width: 10%;
}
.col-1-11 {
width: 9.09%;
}
.col-1-12 {
width: 8.33%
}
/* Layout Columns */
.col-11-12 {
width: 91.66%
}
.col-10-12 {
width: 83.333%;
}
.col-9-12 {
width: 75%;
}
.col-5-12 {
width: 41.66%;
}
.col-7-12 {
width: 58.33%
}
/* Pushing blocks */
.push-2-3, .push-8-12 {
margin-left: 66.66%;
}
.push-1-2, .push-6-12 {
margin-left: 50%;
}
.push-1-3, .push-4-12 {
margin-left: 33.33%;
}
.push-1-4, .push-3-12 {
margin-left: 25%;
}
.push-1-5 {
margin-left: 20%;
}
.push-1-6, .push-2-12 {
margin-left: 16.667%;
}
.push-1-7 {
margin-left: 14.28%;
}
.push-1-8 {
margin-left: 12.5%;
}
.push-1-9 {
margin-left: 11.1%;
}
.push-1-10 {
margin-left: 10%;
}
.push-1-11 {
margin-left: 9.09%;
}
.push-1-12 {
margin-left: 8.33%
}
@media handheld, only screen and (max-width: 767px) {
.grid {
width: 100%;
min-width: 0;
margin-left: 0;
margin-right: 0;
padding-left: 20px; /* grid-space to left */
padding-right: 10px; /* grid-space to right: (grid-space-left - column-space) e.g. 20px-10px=10px */
}
[class*='col-'] {
width: auto;
float: none;
margin: 10px 0;
padding-left: 0;
padding-right: 10px; /* column-space */
}
[class*='col-'] [class*='col-'] {
padding-right: 0;
}
/* Mobile Layout */
[class*='mobile-col-'] {
float: left;
margin: 0 0 10px;
padding-left: 0;
padding-right: 10px; /* column-space */
padding-bottom: 0;
}
.mobile-col-1-1 {
width: 100%;
}
.mobile-col-2-3, .mobile-col-8-12 {
width: 66.66%;
}
.mobile-col-1-2, .mobile-col-6-12 {
width: 50%;
}
.mobile-col-1-3, .mobile-col-4-12 {
width: 33.33%;
}
.mobile-col-1-4, .mobile-col-3-12 {
width: 25%;
}
.mobile-col-1-5 {
width: 20%;
}
.mobile-col-1-6, .mobile-col-2-12 {
width: 16.667%;
}
.mobile-col-1-7 {
width: 14.28%;
}
.mobile-col-1-8 {
width: 12.5%;
}
.mobile-col-1-9 {
width: 11.1%;
}
.mobile-col-1-10 {
width: 10%;
}
.mobile-col-1-11 {
width: 9.09%;
}
.mobile-col-1-12 {
width: 8.33%
}
/* Layout Columns */
.mobile-col-11-12 {
width: 91.66%
}
.mobile-col-10-12 {
width: 83.333%;
}
.mobile-col-9-12 {
width: 75%;
}
.mobile-col-5-12 {
width: 41.66%;
}
.mobile-col-7-12 {
width: 58.33%
}
.hide-on-mobile {
display: none !important;
width: 0;
height: 0;
}
}

562
imports/util/validator.js Normal file
View File

@@ -0,0 +1,562 @@
/*!
* Validator v0.11.5 for Bootstrap 3, by @1000hz
* Copyright 2016 Cina Saffary
* Licensed under http://opensource.org/licenses/MIT
*
* https://github.com/1000hz/bootstrap-validator
*/
/*!
* Modified by Wynne Crisman 10/2016.
* Modifications Copyright 2016 Wynne Crisman
* Modifications licensed under http://opensource.org/licenses/MIT
*
* Added commenting and formatting to tabs - wtf spaces?! (side note: If you are coding in Notepad or vim, please seek a therapist's help immediately. Perhaps you haven't heard that there are free tools (Notepad++, Emacs) that do formatting for you, or you can go all out and use an actual integrated development environment like a professional developer who values their time might.)
* Added semicolons; While not strictly required, they help with readability (knowing, without thinking, where a line ends is really handy when you code all day) and are required by some pretty printing tools for JS.
* Removed code that used && as an if block - this is really hard to read, and will confuse even seasoned developers - there is no reason to do it (any performance improvement is due entirely to crappy VM implementations - there is no hard evidence this is worth the aggravation).
* Added an optional parameter to validate(..) - a callback function may be passed now. The callback will be provided with a boolean parameter 'isValid' indicating whether the form validated. This allows developers to easily force a validation and take an action on the result (submit the form?).
* Added documentation at the top of this file to provide basic usage information.
* Added highlighting on input-group elements in addition to form-group elements.
*/
/*
* USAGE INFO
*
* Getting Started:
* Create a form along with any fields in HTML '<form id='myform'>...</form>', then in your javascript block call: $('#myform').validator(); to initialize the validator.
* Your form elements may specify additional standard validation tags to help the validator know what to do (for example add 'required' to any form element to tell the validator which elements must have data).
* Form elements should be put into form-groups, placing related elements into a common group. Form groups are simply Div's with the class='form-group'.
* Useful Functions:
* $(form).validator() - Initializes the validator for a form. This does not return the validator instance, but it does attach it to the form element.
* $(form).data('bs.validator') - Gets the validator object for the given form. The validator object is stored as data attached to the form with the 'bs.validator' key.
* validate(fn) - Forces validation to occur and takes an optional callback which will be passed a flag (boolean) indicating the success of the validation (isValid).
* reset() - Resets the form's validation status. Clears all error information, without turning validation off.
* update() - Updates the collection of fields that require validation. Call this after making changes to the form, including initializing any form elements that may generate HTML (such as Select2).
*
* Notes:
* To handle decimal values "0.05", you need to add `step='0.01'` or similar to the input field.
* To get the validator to validate a field, you must add `required` to the property list: `<input type='text' required/>`
* I have modified this to not require a form-group or form-input classed container. If one is found, then the container will be used to mark for errors and success, otherwise the field element will be marked instead. Example: `<div class='form-group'><input type='text' required/></div>`
*/
+function ($) {
'use strict';
// VALIDATOR CLASS DEFINITION
// ==========================
//Gets the value of the HTML element.
function getValue($el) {
if($el.is('[type="checkbox"]')) {
return $el.prop('checked')
}
else if($el.is('[type="radio"]')) {
return !!$('[name="' + $el.attr('name') + '"]:checked').length
}
else {
return $el.val();
}
}
//Gets the actual element to perform the validation on. This returns the input element if the input element isn't a surrogate for a hidden form element.
//Some widget libraries (such as Select2) hide the actual element that holds the data, and use custom graphical elements for the control and display logic.
//We need the actual element that holds the value, and not the display elements which fire the events we are interested in (focusout, etc).
function getActualWidget($el) {
if($el.hasClass('select2-search__field')) { //Handle Select2 multi-select controls.
//Select2 creates a structure of span elements whose parent span has the .select2 class and whose sibling is a hidden select element that contains the actual selection to be validated.
return $el.parents('.select2').siblings('select');
}
else {
return $el;
}
}
//The opposite of getActualWidget($el). Gets the visible surrogate widget for the actual hidden form widget.
//The actual hidden widget holds the value and acts like a form element when submitting the form, while the surrogate has the display and functionality desired by the view.
function getSurrogate($el) {
if($el.hasClass('select2-hidden-accessible')) { //Handle Select2 multi-select controls.
//Select2 creates a structure of span elements whose parent span has the .select2 class and whose sibling is a hidden select element that contains the actual selection to be validated.
return $el.siblings('.select2').find('.select2-selection');
}
else {
return $el;
}
}
var Validator = function(element, options) {
this.options = options;
this.validators = $.extend({}, Validator.VALIDATORS, options.custom);
this.$element = $(element);
this.$btn = $('button[type="submit"], input[type="submit"]')
.filter('[form="' + this.$element.attr('id') + '"]')
.add(this.$element.find('input[type="submit"], button[type="submit"]'));
this.update();
//Register for the events (uses a namespace for easy de-registration).
this.$element.on('input.bs.validator change.bs.validator focusout.bs.validator', $.proxy(this.onInput, this));
this.$element.on('submit.bs.validator', $.proxy(this.onSubmit, this));
this.$element.on('reset.bs.validator', $.proxy(this.reset, this));
//TODO: What is '[data-match]' ????
//This will find some kind of matching elements in the form and when the validation event is detected on the match target (retrieved from the data-match element's 'match' data), a validation event will also be fired on the data-match element.
this.$element.find('[data-match]').each(function() {
var $this = $(this);
var target = $this.data('match');
//Register an event handler on the match target element and if the match target element has a value, then fire the validator event on the data-match element also.
$(target).on('input.bs.validator', function(e) {
if(getValue($this)) $this.trigger('input.bs.validator');
});
});
//Force validation on any elements that start with values (ignore those that don't).
//Filter the HTML elements we will be validating to get the set that contains values (ignore those that don't have values), then trigger a 'focusout' event on those elements (forcing validation as we start the Validator up).
this.$inputs.filter(function() {
return getValue($(this))
}).trigger('focusout');
//Diable automatic native validation.
this.$element.attr('novalidate', true);
//Update the submit elements based on the current error state.
this.toggleSubmit();
};
Validator.VERSION = '0.11.5';
Validator.INPUT_SELECTOR = ':input:not([type="hidden"], [type="submit"], [type="reset"], button, .select2-search__field)'
Validator.SURROGATE_SELECTOR = '.select2 .select2-selection';
Validator.FOCUS_OFFSET = 20;
Validator.DEFAULTS = {
delay: 500,
html: false,
disable: true,
focus: true,
custom: {},
errors: {
match: 'Does not match',
minlength: 'Not long enough'
},
feedback: {
success: 'glyphicon-ok',
error: 'glyphicon-remove'
}
};
//The default set of validators to be used.
Validator.VALIDATORS = {
'native': function($el) {
var el = $el[0];
if(el.checkValidity) {
return !el.checkValidity() && !el.validity.valid && (el.validationMessage || "error!");
}
},
'match': function($el) {
var target = $el.data('match');
return $el.val() !== $(target).val() && Validator.DEFAULTS.errors.match;
},
'minlength': function($el) {
var minlength = $el.data('minlength');
return $el.val().length < minlength && Validator.DEFAULTS.errors.minlength;
}
};
//Collects a list of HTML elements that require validation using the INPUT_SELECTOR option supplied when the Validator was created, and the default data-validate attribute that can be supplied in the HTML tags.
Validator.prototype.update = function() {
this.$inputs = this.$element.find(Validator.INPUT_SELECTOR)
.add(this.$element.find('[data-validate="true"]'))
.not(this.$element.find('[data-validate="false"]'));
this.$surrogates = this.$inputs.filter('.select2-hidden-accessible').map(function(el) {
return getSurrogate($(this));
});
return this;
};
//An event handler that will perform validation on a HTML element when that element fires an event.
Validator.prototype.onInput = function(e) {
var self = this;
var $surrogate = $(e.target); //Wrapper the event target with a jquery object.
var $el = getActualWidget($surrogate); //Get the actual (hidden) form widget if there is one, otherwise $el will equal (===) $surrogate.
var deferErrors = e.type !== 'focusout';
//If the event target is not in the set of HTML elements that require validation, then ignore it.
if(!this.$inputs.is($el)) return;
//Validate the HTML element and update the submit button's state as necessary.
this.validateInput($el, $surrogate, deferErrors).done(function() {
self.toggleSubmit();
})
};
//Runs a complete validation. Returns this validator (does not return the result of the validation since the validation is performed in the future).
//@param fn An optional function that will be run after validating, and which will be passed a boolean indicating whether the form passed validation (isValid). [Added by Wynne Crisman 10/2016]
// Allows the user to validate and do something based on the form's validity without any complicated gyrations:
// $form.data('bs.validator').validate(function(isValid) { if(isValid) { /* Allow form submittal. */ }});
Validator.prototype.validate = function(fn) {
var self = this;
//Create a collection of promises by validating each input HTML element, then update the submit logic based on the presence of errors and set the focus to the first element with an error.
$.when(this.$inputs.map(function(el) {
var $el = $(this); //Wrapper the event target with a jquery object.
var $surrogate = getSurrogate($el); //Gets the surrogate widget used to manage the display and functionality. Will === $el if there isn't a surrogate.
return self.validateInput($el, $surrogate, false);
})).then(function() {
self.toggleSubmit();
self.focusError();
//Call the callback, passing whether the form is valid (boolean).
if(fn instanceof Function) fn(!self.isIncomplete() && !self.hasErrors());
});
return this;
};
//Validates the value of an HTML element and returns a promise.
Validator.prototype.validateInput = function($el, $surrogate, deferErrors) {
//var value = getValue($el);
var prevErrors = $el.data('bs.validator.errors'); //Get the errors from a previous run of the validator.
var errors;
if($el.is('[type="radio"]')) $el = this.$element.find('input[name="' + $el.attr('name') + '"]');
//Create a validator event indicating we are about to validate.
var e = $.Event('validate.bs.validator', {relatedTarget: $el[0]});
//Fire the event.
this.$element.trigger(e);
//If the event handlers flag that we shouldn't validate then stop here.
if(e.isDefaultPrevented()) return;
var self = this;
//Run the validators on our HTML element and handle any errors.
return this.runValidators($el).done(function(errors) {
//Save the errors by attaching them to the HTML element.
$el.data('bs.validator.errors', errors);
//If there were no errors then call clearErrors() to remove the error styling on the view, otherwise we do have errors and we either show them immediately (change styling & HTML) or defer the showing of them until later if told to defer.
!errors.length ? self.clearErrors($el) : (deferErrors ? self.defer($el, self.showErrors) : self.showErrors($el));
//If this is the first run of the validator for this element (prevErrors == undefined), or the previous errors are the same as the new errors (nothing changed), then fire an event notifying listeners of the change in error status.
if(!prevErrors || errors.toString() !== prevErrors.toString()) {
if(errors.length) {
e = $.Event('invalid.bs.validator', {relatedTarget: $el[0], detail: errors});
}
else {
e = $.Event('valid.bs.validator', {relatedTarget: $el[0], detail: prevErrors});
}
//Fire the event.
self.$element.trigger(e);
}
//Update the view's submit elements based on the error state of the form.
self.toggleSubmit();
//Fire an event on the form indicating the related element was validated (irregardless of whether it has errors or not).
self.$element.trigger($.Event('validated.bs.validator', {relatedTarget: $el[0]}));
})
};
//Returns a Promise where the result is the array of errors (error messages) found by the validator. The promise will be passed an empty array if there are no errors.
Validator.prototype.runValidators = function($el) {
var errors = [];
var deferred = $.Deferred();
//If using deferred validation then reject (avoid recursive calls?).
if($el.data('bs.validator.deferred')) $el.data('bs.validator.deferred').reject();
//Set that validation is deferred (avoid recursive calls?).
$el.data('bs.validator.deferred', deferred);
function getValidatorSpecificError(key) {
return $el.data(key + '-error');
}
function getValidityStateError() {
var validity = $el[0].validity;
return validity.typeMismatch ? $el.data('type-error')
: validity.patternMismatch ? $el.data('pattern-error')
: validity.stepMismatch ? $el.data('step-error')
: validity.rangeOverflow ? $el.data('max-error')
: validity.rangeUnderflow ? $el.data('min-error')
: validity.valueMissing ? $el.data('required-error')
: null;
}
function getGenericError() {
return $el.data('error');
}
function getErrorMessage(key) {
return getValidatorSpecificError(key)
|| getValidityStateError()
|| getGenericError();
}
//For each validator in the validator hashmap (key value pair), call the function f(key,validator) using this as the context (so it has access to 'this' from this method).
//See the VALIDATORS hashmap defined above (used as the default set of validator functions).
$.each(this.validators, $.proxy(function(key, validator) {
var error;
//If the HTML element has a value OR is required, AND there is data attached to the HTML element for the current validator OR the validator is 'native', AND the validator produces an error.
if((getValue($el) || $el.attr('required')) && ($el.data(key) || key == 'native') && (error = validator.call(this, $el))) {
//Allow generic or state or specific validator errors to trump the validation error.
error = getErrorMessage(key) || error;
//If the error is not in the list then add it. Use jquery.inArray instead of indexOf since indexOf does not exist in some IE versions.
if(!~$.inArray(error, errors)) errors.push(error);
}
}, this));
//Get errors from the server if a 'remote' URL is provided (allow it to check the element's value and supply additional errors), and then resolve the promise with the collected errors.
//If there are no errors AND the HTML element has a value AND the HTML element has 'remote' data associated, then make an AJAX call sometime in the future to pass the server the element name and value, otherwise resolve the promise passing the error list.
if(!errors.length && getValue($el) && $el.data('remote')) {
this.defer($el, function() {
var data = {};
//Assign elementName = elementValue to the data object.
data[$el.attr('name')] = getValue($el);
//Make an AJAX GET call to the URL stored in the element's 'remote' data, passing the object containing the element's name and its value. If there is an error in the AJAX call then add the error to the error list.
$.get($el.data('remote'), data)
.fail(function(jqXHR, textStatus, error) {
errors.push(getErrorMessage('remote') || error)
})
.always(function() {
deferred.resolve(errors)
})
})
}
else deferred.resolve(errors);
//Return the promise that will be passed the collected errors (array of strings) when the error checking is complete.
return deferred.promise();
};
//Changes the focus to the first element with an error.
Validator.prototype.focusError = function() {
if(!this.options.focus) return;
var $input = this.$element.find(".has-error:first"); // :input
if($input.length === 0) return;
//Find the input field if it is a input group or form group that has the error markings.
if($input.hasClass('form-group') || $input.hasClass('input-group')) {
$input = $input.find('input');
}
//If this is a select2 control then look for the child of a sibling that has the .select2-selection class and use it instead.
if($input.hasClass('select2-hidden-accessible')) {
$input = $input.parent().find('.select2-selection');
}
$('html, body').animate({scrollTop: $input.offset().top - Validator.FOCUS_OFFSET}, 250);
$input.focus();
};
//Alters the display to highlight errors in the form.
Validator.prototype.showErrors = function($el) {
var method = this.options.html ? 'html' : 'text';
var errors = $el.data('bs.validator.errors');
var $group = $el.closest('.form-group,.input-group');
var $block = $group.find('.help-block.with-errors');
var $feedback = $group.find('.form-control-feedback');
if(!errors.length) return;
errors = $('<ul/>')
.addClass('list-unstyled')
.append($.map(errors, function(error) {
return $('<li/>')[method](error)
}));
if($block.data('bs.validator.originalContent') === undefined)
$block.data('bs.validator.originalContent', $block.html());
$block.empty().append(errors);
if($group.length > 0)
//Add the 'has-error' and 'has-danger' classes to the grouping.
$group.addClass('has-error has-danger');
else
$el.addClass('has-error has-danger');
//If this is a select2 control then look for the child of a sibling that has the .select2-selection class and use it instead.
if($el.hasClass('select2-hidden-accessible')) {
$el.parent().find('.select2-selection').addClass('has-error has-danger');
}
//If the group has the 'has-feedback' class then remove any success markings and add error markings.
if($group.hasClass('has-feedback')) {
$feedback.removeClass(this.options.feedback.success);
$feedback.addClass(this.options.feedback.error);
$group.removeClass('has-success');
}
};
//Clears the display of all error information.
Validator.prototype.clearErrors = function($el) {
var $group = $el.closest('.form-group,.input-group');
var $block = $group.find('.help-block.with-errors');
var $feedback = $group.find('.form-control-feedback');
$block.html($block.data('bs.validator.originalContent'));
if($group.length > 0)
$group.removeClass('has-error has-danger has-success');
else
$el.removeClass('has-error has-danger has-success');
//Clean the sibling controls for select2.
$el.parent().find('.select2 .select2-selection').removeClass('has-error has-danger has-success');
$group.hasClass('has-feedback')
&& $feedback.removeClass(this.options.feedback.error)
&& $feedback.removeClass(this.options.feedback.success)
&& getValue($el)
&& $feedback.addClass(this.options.feedback.success)
&& $group.addClass('has-success');
};
//Returns whether the form has any errors.
//Warning: Calling this externally is dangerous because validation is asynchronous and this call will not wait for validation to finish.
// Better to call '$form.data('bs.validator').validate(function(isValid) {...})' instead to ensure validation is done.
Validator.prototype.hasErrors = function() {
function fieldErrors() {
return !!($(this).data('bs.validator.errors') || []).length;
}
return !!this.$inputs.filter(fieldErrors).length;
};
//Returns whether the form is not complete (appears this is due to a text input containing only spaces when a value is required).
//Warning: Calling this externally is dangerous because validation is asynchronous and this call will not wait for validation to finish.
// Better to call '$form.data('bs.validator').validate(function(isValid) {...})' instead to ensure validation is done.
Validator.prototype.isIncomplete = function() {
function fieldIncomplete() {
var value = getValue($(this));
return !(typeof value == "string" ? $.trim(value) : value);
}
return !!this.$inputs.filter('[required]').filter(fieldIncomplete).length;
};
//Prevents form submittal (by setting the preventDefault flag on the event) if the form is not valid or complete.
Validator.prototype.onSubmit = function(e) {
this.validate();
if(this.isIncomplete() || this.hasErrors()) e.preventDefault();
};
//Enables or disables the submit button based on the validation state of the form.
Validator.prototype.toggleSubmit = function() {
if(!this.options.disable) return;
this.$btn.toggleClass('disabled', this.isIncomplete() || this.hasErrors())
};
//Defers the callback function so that it runs sometime in the future. Only one callback may be deferred at any given time. How far into the future it will be deferred depends on the 'delay' option.
//Used internally to defer validation as the user interacts with the form such that we don't validated constantly (for every character typed).
Validator.prototype.defer = function($el, callback) {
//Wrapper the callback so it's 'this' variable will be the validator.
callback = $.proxy(callback, this, $el);
//If there isn't a delay then run the callback immediately.
if(!this.options.delay) return callback();
//Clear any callback already being delayed.
window.clearTimeout($el.data('bs.validator.timeout'));
//Delay the execution of the callback.
$el.data('bs.validator.timeout', window.setTimeout(callback, this.options.delay));
};
//Resets the form to its state, removing all validator modifications (but not removing the validator). Returns the validator reference.
Validator.prototype.reset = function() {
this.$element.find('.form-control-feedback').removeClass(this.options.feedback.error).removeClass(this.options.feedback.success);
this.$inputs
.removeData(['bs.validator.errors', 'bs.validator.deferred'])
.each(function() {
var $this = $(this);
var timeout = $this.data('bs.validator.timeout');
window.clearTimeout(timeout) && $this.removeData('bs.validator.timeout');
});
this.$element.find('.help-block.with-errors')
.each(function() {
var $this = $(this);
var originalContent = $this.data('bs.validator.originalContent');
$this
.removeData('bs.validator.originalContent')
.html(originalContent);
});
this.$btn.removeClass('disabled');
this.$element.find('.has-error, .has-danger, .has-success').removeClass('has-error has-danger has-success');
return this;
};
//Removes the validator completely and resets the state.
Validator.prototype.destroy = function() {
this.reset();
//Remove attributes, data, and event handlers from all elements.
this.$element
.removeAttr('novalidate')
.removeData('bs.validator')
.off('.bs.validator');
//Remove event handlers from input elements.
this.$inputs.off('.bs.validator');
this.options = null;
this.validators = null;
this.$element = null;
this.$btn = null;
return this;
};
// VALIDATOR PLUGIN DEFINITION
// ===========================
//Plugin constructor.
function Plugin(option) {
return this.each(function() {
var $this = $(this);
var options = $.extend({}, Validator.DEFAULTS, $this.data(), typeof option == 'object' && option);
var data = $this.data('bs.validator');
if(!data && option == 'destroy') return;
if(!data) $this.data('bs.validator', (data = new Validator(this, options)));
if(typeof option == 'string') data[option]();
});
}
var old = $.fn.validator;
//JQuery integration.
$.fn.validator = Plugin;
$.fn.validator.Constructor = Validator;
// VALIDATOR NO CONFLICT
// =====================
$.fn.validator.noConflict = function() {
$.fn.validator = old;
return this;
};
// VALIDATOR DATA-API
// ==================
$(window).on('load', function() {
$('form[data-toggle="validator"]').each(function() {
var $form = $(this);
Plugin.call($form, $form.data());
});
});
}(jQuery);

2227
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

21
package.json Normal file
View File

@@ -0,0 +1,21 @@
{
"name": "PetitTeton",
"private": true,
"scripts": {
"start": "meteor run",
"build": "npm install --product && meteor build --architecture os.linux.x86_64 --server-only ../"
},
"dependencies": {
"1.0": "^1.0.2",
"@babel/runtime": "^7.0.0-beta.38",
"csv-parse": "latest",
"dragula": "^3.7.2",
"jquery": "^3.1.1",
"jquery-mousewheel": "^3.1.13",
"malihu-custom-scrollbar-plugin": "latest",
"meteor-node-stubs": "^0.2.4",
"properties-reader": "0.0.15",
"simpl-schema": "latest",
"sweetalert2": "^6.3.8"
}
}

View File

@@ -0,0 +1,11 @@
// Import Tinytest from the tinytest Meteor package.
import { Tinytest } from "meteor/tinytest";
// Import and rename a variable exported by jquery-custom-scrollbar.js.
import { name as packageName } from "meteor/wcrisman:jquery-custom-scrollbar";
// Write your tests here!
// Here is an example.
Tinytest.add('jquery-custom-scrollbar - example', function (test) {
test.equal(packageName, "jquery-custom-scrollbar");
});

View File

@@ -0,0 +1,7 @@
import 'jquery-mousewheel';
import "malihu-custom-scrollbar-plugin";
// Write your package code here!
// Variables exported by this module can be imported by other packages and
// applications. See jquery-custom-scrollbar-tests.js for an example of importing.
export const name = 'jquery-custom-scrollbar';

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,221 @@
/*!
* jQuery Mousewheel 3.1.13
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license
* http://jquery.org/license
*/
(function (factory) {
if ( typeof define === 'function' && define.amd ) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node/CommonJS style for Browserify
module.exports = factory;
} else {
// Browser globals
factory(jQuery);
}
}(function ($) {
var toFix = ['wheel', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll'],
toBind = ( 'onwheel' in document || document.documentMode >= 9 ) ?
['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'],
slice = Array.prototype.slice,
nullLowestDeltaTimeout, lowestDelta;
if ( $.event.fixHooks ) {
for ( var i = toFix.length; i; ) {
$.event.fixHooks[ toFix[--i] ] = $.event.mouseHooks;
}
}
var special = $.event.special.mousewheel = {
version: '3.1.12',
setup: function() {
if ( this.addEventListener ) {
for ( var i = toBind.length; i; ) {
this.addEventListener( toBind[--i], handler, false );
}
} else {
this.onmousewheel = handler;
}
// Store the line height and page height for this particular element
$.data(this, 'mousewheel-line-height', special.getLineHeight(this));
$.data(this, 'mousewheel-page-height', special.getPageHeight(this));
},
teardown: function() {
if ( this.removeEventListener ) {
for ( var i = toBind.length; i; ) {
this.removeEventListener( toBind[--i], handler, false );
}
} else {
this.onmousewheel = null;
}
// Clean up the data we added to the element
$.removeData(this, 'mousewheel-line-height');
$.removeData(this, 'mousewheel-page-height');
},
getLineHeight: function(elem) {
var $elem = $(elem),
$parent = $elem['offsetParent' in $.fn ? 'offsetParent' : 'parent']();
if (!$parent.length) {
$parent = $('body');
}
return parseInt($parent.css('fontSize'), 10) || parseInt($elem.css('fontSize'), 10) || 16;
},
getPageHeight: function(elem) {
return $(elem).height();
},
settings: {
adjustOldDeltas: true, // see shouldAdjustOldDeltas() below
normalizeOffset: true // calls getBoundingClientRect for each event
}
};
$.fn.extend({
mousewheel: function(fn) {
return fn ? this.bind('mousewheel', fn) : this.trigger('mousewheel');
},
unmousewheel: function(fn) {
return this.unbind('mousewheel', fn);
}
});
function handler(event) {
var orgEvent = event || window.event,
args = slice.call(arguments, 1),
delta = 0,
deltaX = 0,
deltaY = 0,
absDelta = 0,
offsetX = 0,
offsetY = 0;
event = $.event.fix(orgEvent);
event.type = 'mousewheel';
// Old school scrollwheel delta
if ( 'detail' in orgEvent ) { deltaY = orgEvent.detail * -1; }
if ( 'wheelDelta' in orgEvent ) { deltaY = orgEvent.wheelDelta; }
if ( 'wheelDeltaY' in orgEvent ) { deltaY = orgEvent.wheelDeltaY; }
if ( 'wheelDeltaX' in orgEvent ) { deltaX = orgEvent.wheelDeltaX * -1; }
// Firefox < 17 horizontal scrolling related to DOMMouseScroll event
if ( 'axis' in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
deltaX = deltaY * -1;
deltaY = 0;
}
// Set delta to be deltaY or deltaX if deltaY is 0 for backwards compatabilitiy
delta = deltaY === 0 ? deltaX : deltaY;
// New school wheel delta (wheel event)
if ( 'deltaY' in orgEvent ) {
deltaY = orgEvent.deltaY * -1;
delta = deltaY;
}
if ( 'deltaX' in orgEvent ) {
deltaX = orgEvent.deltaX;
if ( deltaY === 0 ) { delta = deltaX * -1; }
}
// No change actually happened, no reason to go any further
if ( deltaY === 0 && deltaX === 0 ) { return; }
// Need to convert lines and pages to pixels if we aren't already in pixels
// There are three delta modes:
// * deltaMode 0 is by pixels, nothing to do
// * deltaMode 1 is by lines
// * deltaMode 2 is by pages
if ( orgEvent.deltaMode === 1 ) {
var lineHeight = $.data(this, 'mousewheel-line-height');
delta *= lineHeight;
deltaY *= lineHeight;
deltaX *= lineHeight;
} else if ( orgEvent.deltaMode === 2 ) {
var pageHeight = $.data(this, 'mousewheel-page-height');
delta *= pageHeight;
deltaY *= pageHeight;
deltaX *= pageHeight;
}
// Store lowest absolute delta to normalize the delta values
absDelta = Math.max( Math.abs(deltaY), Math.abs(deltaX) );
if ( !lowestDelta || absDelta < lowestDelta ) {
lowestDelta = absDelta;
// Adjust older deltas if necessary
if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) {
lowestDelta /= 40;
}
}
// Adjust older deltas if necessary
if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) {
// Divide all the things by 40!
delta /= 40;
deltaX /= 40;
deltaY /= 40;
}
// Get a whole, normalized value for the deltas
delta = Math[ delta >= 1 ? 'floor' : 'ceil' ](delta / lowestDelta);
deltaX = Math[ deltaX >= 1 ? 'floor' : 'ceil' ](deltaX / lowestDelta);
deltaY = Math[ deltaY >= 1 ? 'floor' : 'ceil' ](deltaY / lowestDelta);
// Normalise offsetX and offsetY properties
if ( special.settings.normalizeOffset && this.getBoundingClientRect ) {
var boundingRect = this.getBoundingClientRect();
offsetX = event.clientX - boundingRect.left;
offsetY = event.clientY - boundingRect.top;
}
// Add information to the event object
event.deltaX = deltaX;
event.deltaY = deltaY;
event.deltaFactor = lowestDelta;
event.offsetX = offsetX;
event.offsetY = offsetY;
// Go ahead and set deltaMode to 0 since we converted to pixels
// Although this is a little odd since we overwrite the deltaX/Y
// properties with normalized deltas.
event.deltaMode = 0;
// Add event and delta to the front of the arguments
args.unshift(event, delta, deltaX, deltaY);
// Clearout lowestDelta after sometime to better
// handle multiple device types that give different
// a different lowestDelta
// Ex: trackpad = 3 and mouse wheel = 120
if (nullLowestDeltaTimeout) { clearTimeout(nullLowestDeltaTimeout); }
nullLowestDeltaTimeout = setTimeout(nullLowestDelta, 200);
return ($.event.dispatch || $.event.handle).apply(this, args);
}
function nullLowestDelta() {
lowestDelta = null;
}
function shouldAdjustOldDeltas(orgEvent, absDelta) {
// If this is an older event and the delta is divisable by 120,
// then we are assuming that the browser is treating this as an
// older mouse wheel event and that we should divide the deltas
// by 40 to try and get a more usable deltaFactor.
// Side note, this actually impacts the reported scroll distance
// in older browsers and can cause scrolling to be slower than native.
// Turn this off by setting $.event.special.mousewheel.settings.adjustOldDeltas to false.
return special.settings.adjustOldDeltas && orgEvent.type === 'mousewheel' && absDelta % 120 === 0;
}
}));

View File

@@ -0,0 +1,29 @@
Package.describe({
name: 'wcrisman:jquery-custom-scrollbar',
version: '3.0.0',
// Brief, one-line summary of the package.
summary: 'Simple wrapper for the jquery-mousewheel & malihu-custom-scrollbar-plugin NPM packages. Users must install the version they want of these two NPM packages for this to work.',
// URL to the Git repository containing the source code for this package.
git: '',
// By default, Meteor will default to using README.md for documentation.
// To avoid submitting documentation, set this field to null.
documentation: 'README.md'
});
Package.onUse(function(api) {
api.versionsFrom('METEOR@0.9.0.1');
api.versionsFrom('1.5.2.2');
//TODO: Figure out how to reference the NPM modules instead of copying out of them into this local atmosphere package!
api.use('jquery');
//api.mainModule('jquery-custom-scrollbar.js');
api.addFiles('jquery.mousewheel.js', 'client');
api.addFiles('jquery.mCustomScrollbar.js', 'client');
api.addFiles('jquery.mCustomScrollbar.css', 'client');
});
//Npm.depends({
// "jquery-mousewheel": "^3.1.13",
// "malihu-custom-scrollbar-plugin": "^3.1.5"
//});

View File

@@ -0,0 +1,19 @@
{
"dependencies": [
[
"jquery",
"1.0.0"
],
[
"meteor",
"1.0.2"
],
[
"underscore",
"1.0.0"
]
],
"pluginDependencies": [],
"toolVersion": "meteor-tool@1.0.26",
"format": "1.0"
}

99
public/barChart.html Normal file
View File

@@ -0,0 +1,99 @@
<!DOCTYPE html>
<style>
.axis .domain {
display: none;
}
</style>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x0 = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.1);
var x1 = d3.scaleBand()
.padding(0.05);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
d3.csv("barChartData.csv", function(d, i, columns) {
for (var i = 1, n = columns.length; i < n; ++i) d[columns[i]] = +d[columns[i]];
return d;
}, function(error, data) {
if (error) throw error;
var keys = data.columns.slice(1);
x0.domain(data.map(function(d) { return d.State; }));
x1.domain(keys).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.max(data, function(d) { return d3.max(keys, function(key) { return d[key]; }); })]).nice();
let t = y.domain();
console.log(t);
g.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) { return "translate(" + x0(d.State) + ",0)"; })
.selectAll("rect")
.data(function(d) { return keys.map(function(key) { return {key: key, value: d[key]}; }); })
.enter().append("rect")
.attr("x", function(d) { return x1(d.key); })
.attr("y", function(d) { return y(d.value); })
.attr("width", x1.bandwidth())
.attr("height", function(d) { return height - y(d.value); })
.attr("fill", function(d) { return z(d.key); });
g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x0));
g.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y).ticks(null, "s"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks().pop()) + 0.5)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", "start")
.text("Population");
var legend = g.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("text-anchor", "end")
.selectAll("g")
.data(keys.slice().reverse())
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 19)
.attr("width", 19)
.attr("height", 19)
.attr("fill", z);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9.5)
.attr("dy", "0.32em")
.text(function(d) { return d; });
});
</script>

7
public/barChartData.csv Normal file
View File

@@ -0,0 +1,7 @@
State,Under 5 Years,5 to 13 Years,14 to 17 Years,18 to 24 Years,25 to 44 Years,45 to 64 Years,65 Years and Over
CA,2704659,4499890,2159981,3853788,10604510,8819342,4114496
TX,2027307,3277946,1420518,2454721,7017731,5656528,2472223
NY,1208495,2141490,1058031,1999120,5355235,5120254,2607672
FL,1140516,1938695,925060,1607297,4782119,4746856,3187797
IL,894368,1558919,725973,1311479,3596343,3239173,1575308
PA,737462,1345341,679201,1203944,3157759,3414001,1910571
1 State Under 5 Years 5 to 13 Years 14 to 17 Years 18 to 24 Years 25 to 44 Years 45 to 64 Years 65 Years and Over
2 CA 2704659 4499890 2159981 3853788 10604510 8819342 4114496
3 TX 2027307 3277946 1420518 2454721 7017731 5656528 2472223
4 NY 1208495 2141490 1058031 1999120 5355235 5120254 2607672
5 FL 1140516 1938695 925060 1607297 4782119 4746856 3187797
6 IL 894368 1558919 725973 1311479 3596343 3239173 1575308
7 PA 737462 1345341 679201 1203944 3157759 3414001 1910571

BIN
public/images/Header_v1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 614 KiB

BIN
public/images/Logo_v1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
public/images/loading.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

21
server/server.js Normal file
View File

@@ -0,0 +1,21 @@
import '/imports/util/polyfills/regex.js';
import '/imports/startup/server';
import '/imports/startup/both';
import '/imports/api';
import '/imports/startup/server/postStartup/version.js'; //Run this right after the api - relies on the API to upgrade the app database & data to the current version.
let PropertiesReader = require('properties-reader');
let props = PropertiesReader('./assets/app/release.properties');
if(props.get('email-settings')) {
process.env.MAIL_URL = props.get('email-settings');
// console.log("Loaded email settings from properties file.");
}
if (!process.env.MAIL_URL) {
process.env.MAIL_URL = Meteor.settings.MAIL_URL;
}
// console.log("Mail settings: " + process.env.MAIL_URL);
if(Meteor.log) Meteor.log.info("Server Started");