diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..94700a4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules +.idea +private diff --git a/.meteor/.finished-upgraders b/.meteor/.finished-upgraders new file mode 100644 index 0000000..910574c --- /dev/null +++ b/.meteor/.finished-upgraders @@ -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 diff --git a/.meteor/.gitignore b/.meteor/.gitignore new file mode 100644 index 0000000..4083037 --- /dev/null +++ b/.meteor/.gitignore @@ -0,0 +1 @@ +local diff --git a/.meteor/.id b/.meteor/.id new file mode 100644 index 0000000..9a3be4f --- /dev/null +++ b/.meteor/.id @@ -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 diff --git a/.meteor/packages b/.meteor/packages new file mode 100644 index 0000000..1f49c34 --- /dev/null +++ b/.meteor/packages @@ -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. diff --git a/.meteor/platforms b/.meteor/platforms new file mode 100644 index 0000000..efeba1b --- /dev/null +++ b/.meteor/platforms @@ -0,0 +1,2 @@ +server +browser diff --git a/.meteor/release b/.meteor/release new file mode 100644 index 0000000..d502dc0 --- /dev/null +++ b/.meteor/release @@ -0,0 +1 @@ +METEOR@1.6.1 diff --git a/.meteor/versions b/.meteor/versions new file mode 100644 index 0000000..0d16b67 --- /dev/null +++ b/.meteor/versions @@ -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 diff --git a/README IMPORT.md b/README IMPORT.md new file mode 100644 index 0000000..05fc23e --- /dev/null +++ b/README IMPORT.md @@ -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. \ No newline at end of file diff --git a/README.md b/README.md index 1420b2b..56f1282 100644 --- a/README.md +++ b/README.md @@ -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. \ No newline at end of file diff --git a/client/bootstrap.styl b/client/bootstrap.styl new file mode 100644 index 0000000..2fe409b --- /dev/null +++ b/client/bootstrap.styl @@ -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; + */ \ No newline at end of file diff --git a/client/client.js b/client/client.js new file mode 100644 index 0000000..0ebc439 --- /dev/null +++ b/client/client.js @@ -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. */ + // } + }); +}); \ No newline at end of file diff --git a/client/head.html b/client/head.html new file mode 100644 index 0000000..cc91107 --- /dev/null +++ b/client/head.html @@ -0,0 +1,6 @@ + + PT App + + + + \ No newline at end of file diff --git a/client/main.styl b/client/main.styl new file mode 100644 index 0000000..b9ca6bc --- /dev/null +++ b/client/main.styl @@ -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" \ No newline at end of file diff --git a/client/titatoggle-dist.css b/client/titatoggle-dist.css new file mode 100644 index 0000000..62be2b4 --- /dev/null +++ b/client/titatoggle-dist.css @@ -0,0 +1,1245 @@ +/******************************************************* +Variables +*******************************************************/ +/******************************************************* +Animation +*******************************************************/ +@-webkit-keyframes popIn { + 0% { + -webkit-transform: scale(1, 1); + transform: scale(1, 1); + } + 25% { + -webkit-transform: scale(1.2, 1); + transform: scale(1.2, 1); + } + 50% { + -webkit-transform: scale(1.4, 1); + transform: scale(1.4, 1); + } + 100% { + -webkit-transform: scale(1, 1); + transform: scale(1, 1); + } +} +@keyframes popIn { + 0% { + -webkit-transform: scale(1, 1); + transform: scale(1, 1); + } + 25% { + -webkit-transform: scale(1.2, 1); + transform: scale(1.2, 1); + } + 50% { + -webkit-transform: scale(1.4, 1); + transform: scale(1.4, 1); + } + 100% { + -webkit-transform: scale(1, 1); + transform: scale(1, 1); + } +} +@-webkit-keyframes popOut { + 0% { + -webkit-transform: scale(1, 1); + transform: scale(1, 1); + } + 25% { + -webkit-transform: scale(1.2, 1); + transform: scale(1.2, 1); + } + 50% { + -webkit-transform: scale(1.4, 1); + transform: scale(1.4, 1); + } + 100% { + -webkit-transform: scale(1, 1); + transform: scale(1, 1); + } +} +@keyframes popOut { + 0% { + -webkit-transform: scale(1, 1); + transform: scale(1, 1); + } + 25% { + -webkit-transform: scale(1.2, 1); + transform: scale(1.2, 1); + } + 50% { + -webkit-transform: scale(1.4, 1); + transform: scale(1.4, 1); + } + 100% { + -webkit-transform: scale(1, 1); + transform: scale(1, 1); + } +} +@-webkit-keyframes splashIn { + 0% { + -webkit-transform: scale(1); + transform: scale(1); + opacity: 1; + } + 25% { + -webkit-transform: scale(1.1); + transform: scale(1.1); + opacity: 0.8; + } + 50% { + -webkit-transform: scale(1.1); + transform: scale(1.1); + opacity: .9; + } + 100% { + -webkit-transform: scale(1); + transform: scale(1); + opacity: 1; + } +} +@keyframes splashIn { + 0% { + -webkit-transform: scale(1); + transform: scale(1); + opacity: 1; + } + 25% { + -webkit-transform: scale(1.1); + transform: scale(1.1); + opacity: 0.8; + } + 50% { + -webkit-transform: scale(1.1); + transform: scale(1.1); + opacity: .9; + } + 100% { + -webkit-transform: scale(1); + transform: scale(1); + opacity: 1; + } +} +@-webkit-keyframes splashOut { + 0% { + -webkit-transform: scale(1); + transform: scale(1); + opacity: 1; + } + 25% { + -webkit-transform: scale(1); + transform: scale(1); + opacity: 0.8; + } + 50% { + -webkit-transform: scale(1); + transform: scale(1); + opacity: .9; + } + 100% { + -webkit-transform: scale(0.5); + transform: scale(0.5); + opacity: 1; + } +} +@keyframes splashOut { + 0% { + -webkit-transform: scale(1); + transform: scale(1); + opacity: 1; + } + 25% { + -webkit-transform: scale(1); + transform: scale(1); + opacity: 0.8; + } + 50% { + -webkit-transform: scale(1); + transform: scale(1); + opacity: .9; + } + 100% { + -webkit-transform: scale(0.5); + transform: scale(0.5); + opacity: 1; + } +} +/******************************************************* +Main Slider basics +*******************************************************/ +.checkbox-toggle { + position: relative; +} +.checkbox-toggle input { + display: block; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: 0%; + height: 0%; + margin: 0 0; + cursor: pointer; + zoom: 1; + -webkit-opacity: 0; + -moz-opacity: 0; + opacity: 0; + filter: alpha(opacity=0); +} +.checkbox-toggle input + span { + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.checkbox-toggle input + span:before { + position: absolute; + left: 0px; + display: inline-block; +} +.checkbox-toggle input + span > h4 { + display: inline; +} +.form-horizontal [class^='checkbox'] input + span:after { + top: 7px; +} +/******************************************************* +Main Slider +*******************************************************/ +.checkbox-slider { + position: relative; +} +.checkbox-slider input { + display: block; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: 0%; + height: 0%; + margin: 0 0; + cursor: pointer; + zoom: 1; + -webkit-opacity: 0; + -moz-opacity: 0; + opacity: 0; + filter: alpha(opacity=0); +} +.checkbox-slider input + span { + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.checkbox-slider input + span:before { + position: absolute; + left: 0px; + display: inline-block; +} +.checkbox-slider input + span > h4 { + display: inline; +} +.checkbox-slider input + span { + padding-left: 40px; +} +.checkbox-slider input + span:before { + content: ""; + height: 20px; + width: 40px; + background: rgba(100, 100, 100, 0.2); + box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.8); + transition: background 0.2s ease-out; +} +.checkbox-slider input + span:after { + width: 20px; + height: 20px; + position: absolute; + left: 0px; + top: 0; + display: block; + background: #ffffff; + transition: margin-left 0.1s ease-in-out; + text-align: center; + font-weight: bold; + content: ""; +} +.checkbox-slider input:checked + span:after { + margin-left: 20px; + content: ""; +} +.checkbox-slider input:checked + span:before { + transition: background 0.2s ease-in; +} +/******************************************************* +Slider default +*******************************************************/ +.checkbox-slider--default { + position: relative; +} +.checkbox-slider--default input { + display: block; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: 0%; + height: 0%; + margin: 0 0; + cursor: pointer; + zoom: 1; + -webkit-opacity: 0; + -moz-opacity: 0; + opacity: 0; + filter: alpha(opacity=0); +} +.checkbox-slider--default input + span { + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.checkbox-slider--default input + span:before { + position: absolute; + left: 0px; + display: inline-block; +} +.checkbox-slider--default input + span > h4 { + display: inline; +} +.checkbox-slider--default input + span { + padding-left: 40px; +} +.checkbox-slider--default input + span:before { + content: ""; + height: 20px; + width: 40px; + background: rgba(100, 100, 100, 0.2); + box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.8); + transition: background 0.2s ease-out; +} +.checkbox-slider--default input + span:after { + width: 20px; + height: 20px; + position: absolute; + left: 0px; + top: 0; + display: block; + background: #ffffff; + transition: margin-left 0.1s ease-in-out; + text-align: center; + font-weight: bold; + content: ""; +} +.checkbox-slider--default input:checked + span:after { + margin-left: 20px; + content: ""; +} +.checkbox-slider--default input:checked + span:before { + transition: background 0.2s ease-in; +} +.checkbox-slider--default input + span:after { + background: #ffffff; + border: solid transparent 1px; + background-clip: content-box; +} +.checkbox-slider--default input:checked + span:after { + background: #5cb85c; + border: solid transparent 1px; + background-clip: content-box; +} +/******************************************************* +Slider default rounded +*******************************************************/ +.checkbox-slider--a-rounded { + position: relative; +} +.checkbox-slider--a-rounded input { + display: block; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: 0%; + height: 0%; + margin: 0 0; + cursor: pointer; + zoom: 1; + -webkit-opacity: 0; + -moz-opacity: 0; + opacity: 0; + filter: alpha(opacity=0); +} +.checkbox-slider--a-rounded input + span { + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.checkbox-slider--a-rounded input + span:before { + position: absolute; + left: 0px; + display: inline-block; +} +.checkbox-slider--a-rounded input + span > h4 { + display: inline; +} +.checkbox-slider--a-rounded input + span { + padding-left: 40px; +} +.checkbox-slider--a-rounded input + span:before { + content: ""; + height: 20px; + width: 40px; + background: rgba(100, 100, 100, 0.2); + box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.8); + transition: background 0.2s ease-out; +} +.checkbox-slider--a-rounded input + span:after { + width: 20px; + height: 20px; + position: absolute; + left: 0px; + top: 0; + display: block; + background: #ffffff; + transition: margin-left 0.1s ease-in-out; + text-align: center; + font-weight: bold; + content: ""; +} +.checkbox-slider--a-rounded input:checked + span:after { + margin-left: 20px; + content: ""; +} +.checkbox-slider--a-rounded input:checked + span:before { + transition: background 0.2s ease-in; +} +.checkbox-slider--a-rounded input + span:after { + background: #ffffff; + border: solid transparent 1px; + background-clip: content-box; +} +.checkbox-slider--a-rounded input:checked + span:after { + background: #5cb85c; + border: solid transparent 1px; + background-clip: content-box; +} +.checkbox-slider--a-rounded input + span:after, +.checkbox-slider--a-rounded input + span:before { + border-radius: 4px; +} +.checkbox-slider--a-rounded input + span:after, +.checkbox-slider--a-rounded input:checked + span:after { + border: solid transparent 2px; + background-clip: content-box; +} +/******************************************************* +Slider default rounded Sizes +*******************************************************/ +.checkbox-slider--a-rounded.checkbox-slider-sm input + span:before, +.checkbox-slider--a-rounded.checkbox-slider-sm input + span:after { + border-radius: 3px; +} +.checkbox-slider--a-rounded.checkbox-slider-md input + span:before, +.checkbox-slider--a-rounded.checkbox-slider-md input + span:after { + border-radius: 4px; +} +.checkbox-slider--a-rounded.checkbox-slider-lg input + span:before, +.checkbox-slider--a-rounded.checkbox-slider-lg input + span:after { + border-radius: 6px; +} +/******************************************************* +Slider A +*******************************************************/ +.checkbox-slider--a { + position: relative; +} +.checkbox-slider--a input { + display: block; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: 0%; + height: 0%; + margin: 0 0; + cursor: pointer; + zoom: 1; + -webkit-opacity: 0; + -moz-opacity: 0; + opacity: 0; + filter: alpha(opacity=0); +} +.checkbox-slider--a input + span { + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.checkbox-slider--a input + span:before { + position: absolute; + left: 0px; + display: inline-block; +} +.checkbox-slider--a input + span > h4 { + display: inline; +} +.checkbox-slider--a input + span { + padding-left: 40px; +} +.checkbox-slider--a input + span:before { + content: ""; + height: 20px; + width: 40px; + background: rgba(100, 100, 100, 0.2); + box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.8); + transition: background 0.2s ease-out; +} +.checkbox-slider--a input + span:after { + width: 20px; + height: 20px; + position: absolute; + left: 0px; + top: 0; + display: block; + background: #ffffff; + transition: margin-left 0.1s ease-in-out; + text-align: center; + font-weight: bold; + content: ""; +} +.checkbox-slider--a input:checked + span:after { + margin-left: 20px; + content: ""; +} +.checkbox-slider--a input:checked + span:before { + transition: background 0.2s ease-in; +} +.checkbox-slider--a input + span { + padding-left: 60px; +} +.checkbox-slider--a input + span:before { + content: ""; + width: 60px; +} +.checkbox-slider--a input + span:after { + width: 40px; + font-size: 10px; + color: #000000; + content: "Off"; + border: solid transparent 1px; + background-clip: content-box; +} +.checkbox-slider--a input:checked + span:after { + content: "On"; + color: #ffffff; + background: #5cb85c; + border: solid transparent 1px; + background-clip: content-box; +} +/******************************************************* +Slider A SIZES +*******************************************************/ +.checkbox-slider--a.checkbox-slider-sm input + span { + padding-left: 30px; +} +.checkbox-slider--a.checkbox-slider-sm input + span:before { + width: 30px; +} +.checkbox-slider--a.checkbox-slider-sm input + span:after { + width: 20px; + font-size: 5px; +} +.checkbox-slider--a.checkbox-slider-sm input:checked + span:after { + margin-left: 10px; +} +.checkbox-slider--a.checkbox-slider-md input + span { + padding-left: 90px; +} +.checkbox-slider--a.checkbox-slider-md input + span:before { + width: 90px; +} +.checkbox-slider--a.checkbox-slider-md input + span:after { + width: 60px; + font-size: 15px; +} +.checkbox-slider--a.checkbox-slider-md input:checked + span:after { + margin-left: 30px; +} +.checkbox-slider--a.checkbox-slider-lg input + span { + padding-left: 120px; +} +.checkbox-slider--a.checkbox-slider-lg input + span:before { + width: 120px; +} +.checkbox-slider--a.checkbox-slider-lg input + span:after { + width: 80px; + font-size: 20px; +} +.checkbox-slider--a.checkbox-slider-lg input:checked + span:after { + margin-left: 40px; +} +/******************************************************* +Slider B +*******************************************************/ +.checkbox-slider--b { + position: relative; +} +.checkbox-slider--b input { + display: block; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: 0%; + height: 0%; + margin: 0 0; + cursor: pointer; + zoom: 1; + -webkit-opacity: 0; + -moz-opacity: 0; + opacity: 0; + filter: alpha(opacity=0); +} +.checkbox-slider--b input + span { + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.checkbox-slider--b input + span:before { + position: absolute; + left: 0px; + display: inline-block; +} +.checkbox-slider--b input + span > h4 { + display: inline; +} +.checkbox-slider--b input + span { + padding-left: 40px; +} +.checkbox-slider--b input + span:before { + content: ""; + height: 20px; + width: 40px; + background: rgba(100, 100, 100, 0.2); + box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.8); + transition: background 0.2s ease-out; +} +.checkbox-slider--b input + span:after { + width: 20px; + height: 20px; + position: absolute; + left: 0px; + top: 0; + display: block; + background: #ffffff; + transition: margin-left 0.1s ease-in-out; + text-align: center; + font-weight: bold; + content: ""; +} +.checkbox-slider--b input:checked + span:after { + margin-left: 20px; + content: ""; +} +.checkbox-slider--b input:checked + span:before { + transition: background 0.2s ease-in; +} +.checkbox-slider--b input + span { + padding-left: 40px; +} +.checkbox-slider--b input + span:before { + border-radius: 20px; + width: 40px; +} +.checkbox-slider--b input + span:after { + background: #ffffff; + content: ""; + width: 20px; + border: solid transparent 2px; + background-clip: padding-box; + border-radius: 20px; +} +.checkbox-slider--b input:not(:checked) + span:after { + -webkit-animation: popOut ease-in 0.3s normal; + animation: popOut ease-in 0.3s normal; +} +.checkbox-slider--b input:checked + span:after { + content: ""; + margin-left: 20px; + border: solid transparent 2px; + background-clip: padding-box; + -webkit-animation: popIn ease-in 0.3s normal; + animation: popIn ease-in 0.3s normal; +} +.checkbox-slider--b input:checked + span:before { + background: #5cb85c; +} +/******************************************************* +Slider B Sizes +*******************************************************/ +.checkbox-slider--b.checkbox-slider-md input + span:before { + border-radius: 30px; +} +.checkbox-slider--b.checkbox-slider-md input + span:after { + border-radius: 30px; +} +.checkbox-slider--b.checkbox-slider-lg input + span:before { + border-radius: 40px; +} +.checkbox-slider--b.checkbox-slider-lg input + span:after { + border-radius: 40px; +} +/******************************************************* +Slider B-flat +*******************************************************/ +.checkbox-slider--b-flat { + position: relative; +} +.checkbox-slider--b-flat input { + display: block; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: 0%; + height: 0%; + margin: 0 0; + cursor: pointer; + zoom: 1; + -webkit-opacity: 0; + -moz-opacity: 0; + opacity: 0; + filter: alpha(opacity=0); +} +.checkbox-slider--b-flat input + span { + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.checkbox-slider--b-flat input + span:before { + position: absolute; + left: 0px; + display: inline-block; +} +.checkbox-slider--b-flat input + span > h4 { + display: inline; +} +.checkbox-slider--b-flat input + span { + padding-left: 40px; +} +.checkbox-slider--b-flat input + span:before { + content: ""; + height: 20px; + width: 40px; + background: rgba(100, 100, 100, 0.2); + box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.8); + transition: background 0.2s ease-out; +} +.checkbox-slider--b-flat input + span:after { + width: 20px; + height: 20px; + position: absolute; + left: 0px; + top: 0; + display: block; + background: #ffffff; + transition: margin-left 0.1s ease-in-out; + text-align: center; + font-weight: bold; + content: ""; +} +.checkbox-slider--b-flat input:checked + span:after { + margin-left: 20px; + content: ""; +} +.checkbox-slider--b-flat input:checked + span:before { + transition: background 0.2s ease-in; +} +.checkbox-slider--b-flat input + span { + padding-left: 40px; +} +.checkbox-slider--b-flat input + span:before { + border-radius: 20px; + width: 40px; +} +.checkbox-slider--b-flat input + span:after { + background: #ffffff; + content: ""; + width: 20px; + border: solid transparent 2px; + background-clip: padding-box; + border-radius: 20px; +} +.checkbox-slider--b-flat input:not(:checked) + span:after { + -webkit-animation: popOut ease-in 0.3s normal; + animation: popOut ease-in 0.3s normal; +} +.checkbox-slider--b-flat input:checked + span:after { + content: ""; + margin-left: 20px; + border: solid transparent 2px; + background-clip: padding-box; + -webkit-animation: popIn ease-in 0.3s normal; + animation: popIn ease-in 0.3s normal; +} +.checkbox-slider--b-flat input:checked + span:before { + background: #5cb85c; +} +.checkbox-slider--b-flat.checkbox-slider-md input + span:before { + border-radius: 30px; +} +.checkbox-slider--b-flat.checkbox-slider-md input + span:after { + border-radius: 30px; +} +.checkbox-slider--b-flat.checkbox-slider-lg input + span:before { + border-radius: 40px; +} +.checkbox-slider--b-flat.checkbox-slider-lg input + span:after { + border-radius: 40px; +} +.checkbox-slider--b-flat input + span:before { + box-shadow: none; +} +/******************************************************* +Slider C +*******************************************************/ +.checkbox-slider--c { + position: relative; +} +.checkbox-slider--c input { + display: block; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: 0%; + height: 0%; + margin: 0 0; + cursor: pointer; + zoom: 1; + -webkit-opacity: 0; + -moz-opacity: 0; + opacity: 0; + filter: alpha(opacity=0); +} +.checkbox-slider--c input + span { + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.checkbox-slider--c input + span:before { + position: absolute; + left: 0px; + display: inline-block; +} +.checkbox-slider--c input + span > h4 { + display: inline; +} +.checkbox-slider--c input + span { + padding-left: 40px; +} +.checkbox-slider--c input + span:before { + content: ""; + height: 20px; + width: 40px; + background: rgba(100, 100, 100, 0.2); + box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.8); + transition: background 0.2s ease-out; +} +.checkbox-slider--c input + span:after { + width: 20px; + height: 20px; + position: absolute; + left: 0px; + top: 0; + display: block; + background: #ffffff; + transition: margin-left 0.1s ease-in-out; + text-align: center; + font-weight: bold; + content: ""; +} +.checkbox-slider--c input:checked + span:after { + margin-left: 20px; + content: ""; +} +.checkbox-slider--c input:checked + span:before { + transition: background 0.2s ease-in; +} +.checkbox-slider--c input + span { + padding-left: 40px; +} +.checkbox-slider--c input + span:before { + height: 2px !important; + top: 10px; + box-shadow: none; + width: 40px; + background: #555555; +} +.checkbox-slider--c input + span:after { + box-shadow: none; + width: 20px; + border: solid #555555 2px; + border-radius: 20px; +} +.checkbox-slider--c input:checked + span:after { + background: #5cb85c; + margin-left: 20px; + border: solid #5cb85c 2px; + -webkit-animation: splashIn ease-in 0.3s normal; + animation: splashIn ease-in 0.3s normal; +} +.checkbox-slider--c input:checked + span:before { + background: #5cb85c; +} +/******************************************************* +Slider C Sizes +*******************************************************/ +.checkbox-slider--c.checkbox-slider-sm input + span:before { + top: 4px; +} +.checkbox-slider--c.checkbox-slider-md input + span:before { + top: 14px; +} +.checkbox-slider--c.checkbox-slider-md input + span:after { + width: 30px; + border-radius: 30px; +} +.checkbox-slider--c.checkbox-slider-lg input + span:before { + top: 19px; +} +.checkbox-slider--c.checkbox-slider-lg input + span:after { + width: 40px; + border-radius: 40px; +} +.form-horizontal [class*='checkbox-slider--c'].checkbox-slider-sm input + span:before { + top: 10px; +} +.form-horizontal [class*='checkbox-slider--c'].checkbox-slider-md input + span:before { + top: 20px; +} +.form-horizontal [class*='checkbox-slider--c'].checkbox-slider-lg input + span:before { + top: 25px; +} +/******************************************************* +Slider C-weight +*******************************************************/ +.checkbox-slider--c-weight { + position: relative; +} +.checkbox-slider--c-weight input { + display: block; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: 0%; + height: 0%; + margin: 0 0; + cursor: pointer; + zoom: 1; + -webkit-opacity: 0; + -moz-opacity: 0; + opacity: 0; + filter: alpha(opacity=0); +} +.checkbox-slider--c-weight input + span { + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.checkbox-slider--c-weight input + span:before { + position: absolute; + left: 0px; + display: inline-block; +} +.checkbox-slider--c-weight input + span > h4 { + display: inline; +} +.checkbox-slider--c-weight input + span { + padding-left: 40px; +} +.checkbox-slider--c-weight input + span:before { + content: ""; + height: 20px; + width: 40px; + background: rgba(100, 100, 100, 0.2); + box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.8); + transition: background 0.2s ease-out; +} +.checkbox-slider--c-weight input + span:after { + width: 20px; + height: 20px; + position: absolute; + left: 0px; + top: 0; + display: block; + background: #ffffff; + transition: margin-left 0.1s ease-in-out; + text-align: center; + font-weight: bold; + content: ""; +} +.checkbox-slider--c-weight input:checked + span:after { + margin-left: 20px; + content: ""; +} +.checkbox-slider--c-weight input:checked + span:before { + transition: background 0.2s ease-in; +} +.checkbox-slider--c-weight input + span { + padding-left: 40px; +} +.checkbox-slider--c-weight input + span:before { + height: 2px !important; + top: 10px; + box-shadow: none; + width: 40px; + background: #555555; +} +.checkbox-slider--c-weight input + span:after { + box-shadow: none; + width: 20px; + border: solid #555555 2px; + border-radius: 20px; +} +.checkbox-slider--c-weight input:checked + span:after { + background: #5cb85c; + margin-left: 20px; + border: solid #5cb85c 2px; + -webkit-animation: splashIn ease-in 0.3s normal; + animation: splashIn ease-in 0.3s normal; +} +.checkbox-slider--c-weight input:checked + span:before { + background: #5cb85c; +} +.checkbox-slider--c-weight.checkbox-slider-sm input + span:before { + top: 4px; +} +.checkbox-slider--c-weight.checkbox-slider-md input + span:before { + top: 14px; +} +.checkbox-slider--c-weight.checkbox-slider-md input + span:after { + width: 30px; + border-radius: 30px; +} +.checkbox-slider--c-weight.checkbox-slider-lg input + span:before { + top: 19px; +} +.checkbox-slider--c-weight.checkbox-slider-lg input + span:after { + width: 40px; + border-radius: 40px; +} +.checkbox-slider--c-weight input + span:before { + height: 1px !important; +} +.checkbox-slider--c-weight input:checked + span:before { + height: 2px !important; +} +.checkbox-slider--c-weight input:not(:checked) + span:after { + -webkit-transform: scale(0.7); + transform: scale(0.7); + left: -6px; +} +/****************************************************** +State Disabled +*******************************************************/ +.checkbox-slider--default input:disabled + span:after { + background: #777777; +} +.checkbox-slider--default input:disabled + span:before { + box-shadow: 0 0 0 black; +} +.checkbox-slider--default input:disabled + span { + color: #777777; +} +.checkbox-slider--a input:disabled + span:after { + background: #777777; + color: #ffffff; +} +.checkbox-slider--a input:disabled + span:before { + box-shadow: 0 0 0 black; +} +.checkbox-slider--a input:disabled + span { + color: #777777; +} +.checkbox-slider--b input:disabled + span:after { + border: solid transparent 2px; + border-radius: 40px; +} +.checkbox-slider--b input:disabled + span:before { + box-shadow: 0 0 0 black; +} +.checkbox-slider--b input:disabled + span { + color: #777777; +} +.checkbox-slider--c input:disabled:checked + span:after { + background: #777777; +} +.checkbox-slider--c input:disabled + span:after { + border-color: #777777; +} +.checkbox-slider--c input:disabled + span:before { + background: #777777; +} +.checkbox-slider--c input:disabled + span { + color: #777777; +} +/******************************************************* +Indicators +*******************************************************/ +input:checked + .indicator-success { + color: #5cb85c; +} +input:checked + .indicator-info { + color: #5bc0de; +} +input:checked + .indicator-warning { + color: #f0ad4e; +} +input:checked + .indicator-danger { + color: #d9534f; +} +/******************************************************* +Sizes +*******************************************************/ +.checkbox-slider-sm { + line-height: 10px; +} +.checkbox-slider-sm input + span { + padding-left: 20px; +} +.checkbox-slider-sm input + span:before { + width: 20px; +} +.checkbox-slider-sm input + span:after, +.checkbox-slider-sm input + span:before { + height: 10px; + line-height: 10px; +} +.checkbox-slider-sm input + span:after { + width: 10px; + vertical-align: middle; +} +.checkbox-slider-sm input:checked + span:after { + margin-left: 10px; +} +.checkbox-slider-md { + line-height: 30px; +} +.checkbox-slider-md input + span { + padding-left: 60px; +} +.checkbox-slider-md input + span:before { + width: 60px; +} +.checkbox-slider-md input + span:after, +.checkbox-slider-md input + span:before { + height: 30px; + line-height: 30px; +} +.checkbox-slider-md input + span:after { + width: 30px; + vertical-align: middle; +} +.checkbox-slider-md input:checked + span:after { + margin-left: 30px; +} +.checkbox-slider-lg { + line-height: 40px; +} +.checkbox-slider-lg input + span { + padding-left: 80px; +} +.checkbox-slider-lg input + span:before { + width: 80px; +} +.checkbox-slider-lg input + span:after, +.checkbox-slider-lg input + span:before { + height: 40px; + line-height: 40px; +} +.checkbox-slider-lg input + span:after { + width: 40px; + vertical-align: middle; +} +.checkbox-slider-lg input:checked + span:after { + margin-left: 40px; +} +/******************************************************* +Variations +*******************************************************/ +.checkbox-slider-info.checkbox-slider--default input:checked + span:after, +.checkbox-slider-info.checkbox-slider--a input:checked + span:after, +.checkbox-slider-info.checkbox-slider--c input:checked + span:after, +.checkbox-slider-info.checkbox-slider--c-weight input:checked + span:after { + background: #5bc0de; + background-clip: content-box; +} +.checkbox-slider-info.checkbox-slider--c input:checked + span:after, +.checkbox-slider-info.checkbox-slider--c-weight input:checked + span:after { + border-color: #5bc0de; +} +.checkbox-slider-info.checkbox-slider--b input:checked + span:before, +.checkbox-slider-info.checkbox-slider--b-flat input:checked + span:before, +.checkbox-slider-info.checkbox-slider--c input:checked + span:before, +.checkbox-slider-info.checkbox-slider--c-weight input:checked + span:before { + background: #5bc0de; +} +.checkbox-slider-warning.checkbox-slider--default input:checked + span:after, +.checkbox-slider-warning.checkbox-slider--a input:checked + span:after, +.checkbox-slider-warning.checkbox-slider--c input:checked + span:after, +.checkbox-slider-warning.checkbox-slider--c-weight input:checked + span:after { + background: #f0ad4e; + background-clip: content-box; +} +.checkbox-slider-warning.checkbox-slider--c input:checked + span:after, +.checkbox-slider-warning.checkbox-slider--c-weight input:checked + span:after { + border-color: #f0ad4e; +} +.checkbox-slider-warning.checkbox-slider--b input:checked + span:before, +.checkbox-slider-warning.checkbox-slider--b-flat input:checked + span:before, +.checkbox-slider-warning.checkbox-slider--c input:checked + span:before, +.checkbox-slider-warning.checkbox-slider--c-weight input:checked + span:before { + background: #f0ad4e; +} +.checkbox-slider-danger.checkbox-slider--default input:checked + span:after, +.checkbox-slider-danger.checkbox-slider--a input:checked + span:after, +.checkbox-slider-danger.checkbox-slider--c input:checked + span:after, +.checkbox-slider-danger.checkbox-slider--c-weight input:checked + span:after { + background: #d9534f; + background-clip: content-box; +} +.checkbox-slider-danger.checkbox-slider--c input:checked + span:after, +.checkbox-slider-danger.checkbox-slider--c-weight input:checked + span:after { + border-color: #d9534f; +} +.checkbox-slider-danger.checkbox-slider--b input:checked + span:before, +.checkbox-slider-danger.checkbox-slider--b-flat input:checked + span:before, +.checkbox-slider-danger.checkbox-slider--c input:checked + span:before, +.checkbox-slider-danger.checkbox-slider--c-weight input:checked + span:before { + background: #d9534f; +} diff --git a/imports/api/Roles.js b/imports/api/Roles.js new file mode 100644 index 0000000..e62b9fb --- /dev/null +++ b/imports/api/Roles.js @@ -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; \ No newline at end of file diff --git a/imports/api/User.js b/imports/api/User.js new file mode 100644 index 0000000..a405878 --- /dev/null +++ b/imports/api/User.js @@ -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; \ No newline at end of file diff --git a/imports/api/index.js b/imports/api/index.js new file mode 100644 index 0000000..a41436e --- /dev/null +++ b/imports/api/index.js @@ -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); + } + } +} \ No newline at end of file diff --git a/imports/startup/both/accounts.js b/imports/startup/both/accounts.js new file mode 100644 index 0000000..2fdb446 --- /dev/null +++ b/imports/startup/both/accounts.js @@ -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' +//}); diff --git a/imports/startup/both/index.js b/imports/startup/both/index.js new file mode 100644 index 0000000..c6b312f --- /dev/null +++ b/imports/startup/both/index.js @@ -0,0 +1 @@ +import './accounts.js'; \ No newline at end of file diff --git a/imports/startup/client/index.js b/imports/startup/client/index.js new file mode 100644 index 0000000..147b8a8 --- /dev/null +++ b/imports/startup/client/index.js @@ -0,0 +1 @@ +import './routes.js'; \ No newline at end of file diff --git a/imports/startup/client/routes.js b/imports/startup/client/routes.js new file mode 100644 index 0000000..1539771 --- /dev/null +++ b/imports/startup/client/routes.js @@ -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"}); + } +}); diff --git a/imports/startup/server/email.js b/imports/startup/server/email.js new file mode 100644 index 0000000..b8ab653 --- /dev/null +++ b/imports/startup/server/email.js @@ -0,0 +1,12 @@ + +Accounts.emailTemplates.from = "Do Not Reply "; +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; +// }; \ No newline at end of file diff --git a/imports/startup/server/index.js b/imports/startup/server/index.js new file mode 100644 index 0000000..8bf6f7e --- /dev/null +++ b/imports/startup/server/index.js @@ -0,0 +1,2 @@ +import "./email.js" +import "./../../util/polyfills/date.js" \ No newline at end of file diff --git a/imports/startup/server/postStartup/version.js b/imports/startup/server/postStartup/version.js new file mode 100644 index 0000000..118eff7 --- /dev/null +++ b/imports/startup/server/postStartup/version.js @@ -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); +} \ No newline at end of file diff --git a/imports/ui/AdminHome.html b/imports/ui/AdminHome.html new file mode 100644 index 0000000..3bb836c --- /dev/null +++ b/imports/ui/AdminHome.html @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/imports/ui/AdminHome.import.styl b/imports/ui/AdminHome.import.styl new file mode 100644 index 0000000..2c4c1dc --- /dev/null +++ b/imports/ui/AdminHome.import.styl @@ -0,0 +1,2 @@ +#adminHome + margin: 20px 40px \ No newline at end of file diff --git a/imports/ui/AdminHome.js b/imports/ui/AdminHome.js new file mode 100644 index 0000000..4020bc1 --- /dev/null +++ b/imports/ui/AdminHome.js @@ -0,0 +1,2 @@ + +import './AdminHome.html'; \ No newline at end of file diff --git a/imports/ui/Home.html b/imports/ui/Home.html new file mode 100644 index 0000000..00a4f22 --- /dev/null +++ b/imports/ui/Home.html @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/imports/ui/Home.import.styl b/imports/ui/Home.import.styl new file mode 100644 index 0000000..0e9a5ec --- /dev/null +++ b/imports/ui/Home.import.styl @@ -0,0 +1,3 @@ +#homePage + display: block + min-height: 300px \ No newline at end of file diff --git a/imports/ui/Home.js b/imports/ui/Home.js new file mode 100644 index 0000000..ba3b14c --- /dev/null +++ b/imports/ui/Home.js @@ -0,0 +1,2 @@ + +import './Home.html'; \ No newline at end of file diff --git a/imports/ui/UserManagement.html b/imports/ui/UserManagement.html new file mode 100644 index 0000000..403205d --- /dev/null +++ b/imports/ui/UserManagement.html @@ -0,0 +1,76 @@ + + + + + + + \ No newline at end of file diff --git a/imports/ui/UserManagement.import.styl b/imports/ui/UserManagement.import.styl new file mode 100644 index 0000000..09e604b --- /dev/null +++ b/imports/ui/UserManagement.import.styl @@ -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 \ No newline at end of file diff --git a/imports/ui/UserManagement.js b/imports/ui/UserManagement.js new file mode 100644 index 0000000..f0b53e9 --- /dev/null +++ b/imports/ui/UserManagement.js @@ -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] : ''; + } +}); \ No newline at end of file diff --git a/imports/ui/accounts/accounts.html b/imports/ui/accounts/accounts.html new file mode 100644 index 0000000..86cf640 --- /dev/null +++ b/imports/ui/accounts/accounts.html @@ -0,0 +1,58 @@ + + + diff --git a/imports/ui/accounts/accounts.js b/imports/ui/accounts/accounts.js new file mode 100644 index 0000000..6ee7f20 --- /dev/null +++ b/imports/ui/accounts/accounts.js @@ -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'); \ No newline at end of file diff --git a/imports/ui/helpers.js b/imports/ui/helpers.js new file mode 100644 index 0000000..40f6bfc --- /dev/null +++ b/imports/ui/helpers.js @@ -0,0 +1,8 @@ + +// General use helpers - available to all views. + +UI.registerHelper('currentUserName', function() { + if(Meteor.user()){ + return Meteor.user().emails[0].address; + } +}); \ No newline at end of file diff --git a/imports/ui/layouts/Admin.html b/imports/ui/layouts/Admin.html new file mode 100644 index 0000000..0f4e6f8 --- /dev/null +++ b/imports/ui/layouts/Admin.html @@ -0,0 +1,32 @@ + \ No newline at end of file diff --git a/imports/ui/layouts/Admin.import.styl b/imports/ui/layouts/Admin.import.styl new file mode 100644 index 0000000..baf606d --- /dev/null +++ b/imports/ui/layouts/Admin.import.styl @@ -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 \ No newline at end of file diff --git a/imports/ui/layouts/Admin.js b/imports/ui/layouts/Admin.js new file mode 100644 index 0000000..8f498ce --- /dev/null +++ b/imports/ui/layouts/Admin.js @@ -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')); + } +}); \ No newline at end of file diff --git a/imports/ui/layouts/Login.html b/imports/ui/layouts/Login.html new file mode 100644 index 0000000..4016b5c --- /dev/null +++ b/imports/ui/layouts/Login.html @@ -0,0 +1,9 @@ + diff --git a/imports/ui/layouts/Login.import.styl b/imports/ui/layouts/Login.import.styl new file mode 100644 index 0000000..720f650 --- /dev/null +++ b/imports/ui/layouts/Login.import.styl @@ -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 \ No newline at end of file diff --git a/imports/ui/layouts/Login.js b/imports/ui/layouts/Login.js new file mode 100644 index 0000000..95cb56d --- /dev/null +++ b/imports/ui/layouts/Login.js @@ -0,0 +1 @@ +import './Login.html'; \ No newline at end of file diff --git a/imports/ui/layouts/Public.html b/imports/ui/layouts/Public.html new file mode 100644 index 0000000..94a79c1 --- /dev/null +++ b/imports/ui/layouts/Public.html @@ -0,0 +1,29 @@ + \ No newline at end of file diff --git a/imports/ui/layouts/Public.import.styl b/imports/ui/layouts/Public.import.styl new file mode 100644 index 0000000..739d566 --- /dev/null +++ b/imports/ui/layouts/Public.import.styl @@ -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 \ No newline at end of file diff --git a/imports/ui/layouts/Public.js b/imports/ui/layouts/Public.js new file mode 100644 index 0000000..65a7e30 --- /dev/null +++ b/imports/ui/layouts/Public.js @@ -0,0 +1,6 @@ +import { Template } from 'meteor/templating'; +import './Public.html'; + +Template.Public.events({ + +}); \ No newline at end of file diff --git a/imports/ui/styles/buttons.import.styl b/imports/ui/styles/buttons.import.styl new file mode 100644 index 0000000..95f4f78 --- /dev/null +++ b/imports/ui/styles/buttons.import.styl @@ -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 \ No newline at end of file diff --git a/imports/ui/styles/effects.import.styl b/imports/ui/styles/effects.import.styl new file mode 100644 index 0000000..407a90f --- /dev/null +++ b/imports/ui/styles/effects.import.styl @@ -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%) diff --git a/imports/ui/styles/forms.import.styl b/imports/ui/styles/forms.import.styl new file mode 100644 index 0000000..3f28c31 --- /dev/null +++ b/imports/ui/styles/forms.import.styl @@ -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; \ No newline at end of file diff --git a/imports/ui/styles/maxHeightLayout.import.styl b/imports/ui/styles/maxHeightLayout.import.styl new file mode 100644 index 0000000..51d6904 --- /dev/null +++ b/imports/ui/styles/maxHeightLayout.import.styl @@ -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------ +
+
+

+ Some content. +

+

+ More content... +

+
+
+
+ Test bar. +
+
+
+
+
+ */ +.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; +} \ No newline at end of file diff --git a/imports/ui/styles/tabs.import.styl b/imports/ui/styles/tabs.import.styl new file mode 100644 index 0000000..ed2818e --- /dev/null +++ b/imports/ui/styles/tabs.import.styl @@ -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: " " \ No newline at end of file diff --git a/imports/util/bootstrap-like-btn.import.styl b/imports/util/bootstrap-like-btn.import.styl new file mode 100644 index 0000000..7340983 --- /dev/null +++ b/imports/util/bootstrap-like-btn.import.styl @@ -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% + diff --git a/imports/util/csv.js b/imports/util/csv.js new file mode 100644 index 0000000..b9fc455 --- /dev/null +++ b/imports/util/csv.js @@ -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; \ No newline at end of file diff --git a/imports/util/de.combo.import.styl b/imports/util/de.combo.import.styl new file mode 100644 index 0000000..0337035 --- /dev/null +++ b/imports/util/de.combo.import.styl @@ -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; + } +} \ No newline at end of file diff --git a/imports/util/de.combo.js b/imports/util/de.combo.js new file mode 100644 index 0000000..b46645d --- /dev/null +++ b/imports/util/de.combo.js @@ -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 = $('
', {style: 'position: relative; height: 0;'}); + this.$list = $('