From d392a540e7426e83affb81910728ff087f6a6af8 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 6 May 2013 03:38:29 -0400 Subject: [PATCH] Initial Commit --- .gitignore | 2 + app/controllers/account.js | 6 + app/controllers/clients.js | 127 + app/controllers/home.js | 10 + app/controllers/login.js | 21 + app/controllers/messages.js | 79 + app/controllers/schedule.js | 26 + app/controllers/users.js | 25 + app/controllers/workorders.js | 284 ++ app/model/client.js | 25 + app/model/counter.js | 12 + app/model/user.js | 24 + app/model/workorder.js | 27 + app/views/error.jade | 18 + app/views/index.jade | 50 + app/views/login.jade | 15 + config/auth.js | 36 + config/calendar.js | 96 + config/config.js | 46 + config/express.js | 40 + config/passport.js | 55 + config/piler.js | 38 + config/routes.js | 59 + mongo-upgrade.js | 322 +++ package.json | 24 + public/css/biomed.less | 77 + public/css/biomed/breadcrumbs.less | 30 + public/css/biomed/buttons.less | 7 + public/css/biomed/datepicker.less | 292 ++ public/css/biomed/mixins.less | 23 + public/css/biomed/navbar.less | 78 + public/css/biomed/scaffolding.less | 118 + public/css/biomed/select2.less | 523 ++++ public/css/biomed/sprites.less | 6 + public/css/biomed/tables.less | 30 + public/css/biomed/toolbelt.less | 61 + public/css/biomed/type.less | 14 + public/css/biomed/variables.less | 46 + public/css/biomed/widgets.less | 269 ++ public/css/bootstrap/accordion.less | 34 + public/css/bootstrap/alerts.less | 79 + public/css/bootstrap/breadcrumbs.less | 24 + public/css/bootstrap/button-groups.less | 229 ++ public/css/bootstrap/buttons.less | 228 ++ public/css/bootstrap/carousel.less | 158 ++ public/css/bootstrap/close.less | 32 + public/css/bootstrap/code.less | 61 + .../css/bootstrap/component-animations.less | 22 + public/css/bootstrap/dropdowns.less | 237 ++ public/css/bootstrap/forms.less | 690 +++++ public/css/bootstrap/grid.less | 21 + public/css/bootstrap/hero-unit.less | 25 + public/css/bootstrap/labels-badges.less | 84 + public/css/bootstrap/layouts.less | 16 + public/css/bootstrap/media.less | 55 + public/css/bootstrap/mixins.less | 702 +++++ public/css/bootstrap/modals.less | 95 + public/css/bootstrap/navbar.less | 497 ++++ public/css/bootstrap/navs.less | 409 +++ public/css/bootstrap/pager.less | 43 + public/css/bootstrap/pagination.less | 123 + public/css/bootstrap/popovers.less | 133 + public/css/bootstrap/progress-bars.less | 122 + public/css/bootstrap/reset.less | 216 ++ .../css/bootstrap/responsive-1200px-min.less | 28 + .../css/bootstrap/responsive-767px-max.less | 193 ++ .../css/bootstrap/responsive-768px-979px.less | 19 + public/css/bootstrap/responsive-navbar.less | 189 ++ .../css/bootstrap/responsive-utilities.less | 59 + public/css/bootstrap/responsive.less | 48 + public/css/bootstrap/scaffolding.less | 53 + public/css/bootstrap/sprites.less | 197 ++ public/css/bootstrap/tables.less | 244 ++ public/css/bootstrap/thumbnails.less | 53 + public/css/bootstrap/tooltip.less | 70 + public/css/bootstrap/type.less | 247 ++ public/css/bootstrap/utilities.less | 30 + public/css/bootstrap/variables.less | 301 +++ public/css/bootstrap/wells.less | 29 + public/css/old/bio-header.less | 97 + public/css/old/bio-nav.less | 42 + public/css/old/biomed.less | 43 + public/css/old/breadcrumbs.less | 43 + public/css/old/buttons.less | 221 ++ public/css/old/datepicker.less | 180 ++ public/css/old/dropdowns.less | 237 ++ public/css/old/forms.less | 639 +++++ public/css/old/grid.less | 21 + public/css/old/layouts.less | 16 + public/css/old/mixins.less | 704 +++++ public/css/old/navs.less | 409 +++ public/css/old/pagination.less | 104 + public/css/old/progress-bars.less | 129 + public/css/old/reset.less | 216 ++ public/css/old/scaffolding.less | 156 ++ public/css/old/sprites.less | 197 ++ public/css/old/tables.less | 257 ++ public/css/old/timepicker.less | 144 + public/css/old/type.less | 247 ++ public/css/old/variables.less | 311 +++ public/css/old/widgets.less | 247 ++ public/fonts/Vegur-Bold.otf | Bin 0 -> 15956 bytes public/fonts/Vegur-Light.otf | Bin 0 -> 15300 bytes public/fonts/Vegur-Regular.otf | Bin 0 -> 15248 bytes public/img/bodyBg.png | Bin 0 -> 941 bytes public/img/breadcrumb.png | Bin 0 -> 1118 bytes public/img/glyphicons-halflings-white.png | Bin 0 -> 8777 bytes public/img/glyphicons-halflings.png | Bin 0 -> 12799 bytes public/img/loader.gif | Bin 0 -> 1442 bytes public/img/logo.svg | 298 ++ public/js/app.js | 74 + public/js/controllers.js | 533 ++++ public/js/directives.js | 423 +++ public/js/filters.js | 16 + public/js/lib/angular-ui.js | 0 public/js/lib/bootstrap-datepicker.js | 1046 +++++++ public/js/lib/dialog.js | 369 +++ public/js/lib/moment.js | 1400 ++++++++++ public/js/lib/select2.js | 2407 +++++++++++++++++ public/js/services.js | 114 + public/partials/clients/add.html | 175 ++ public/partials/clients/edit.html | 288 ++ public/partials/clients/index.html | 41 + public/partials/messages.html | 75 + public/partials/schedule/index.html | 34 + public/partials/schedule/pms.html | 54 + public/partials/techPicker.html | 20 + public/partials/workorders/add.html | 171 ++ public/partials/workorders/edit.html | 207 ++ public/partials/workorders/index.html | 52 + server.js | 44 + templates/calendarDescription.tmpl | 22 + test.js | 21 + users.json | 332 +++ 134 files changed, 22012 insertions(+) create mode 100644 .gitignore create mode 100644 app/controllers/account.js create mode 100644 app/controllers/clients.js create mode 100644 app/controllers/home.js create mode 100644 app/controllers/login.js create mode 100644 app/controllers/messages.js create mode 100644 app/controllers/schedule.js create mode 100644 app/controllers/users.js create mode 100644 app/controllers/workorders.js create mode 100644 app/model/client.js create mode 100644 app/model/counter.js create mode 100644 app/model/user.js create mode 100644 app/model/workorder.js create mode 100644 app/views/error.jade create mode 100644 app/views/index.jade create mode 100644 app/views/login.jade create mode 100644 config/auth.js create mode 100644 config/calendar.js create mode 100644 config/config.js create mode 100644 config/express.js create mode 100644 config/passport.js create mode 100644 config/piler.js create mode 100644 config/routes.js create mode 100644 mongo-upgrade.js create mode 100644 package.json create mode 100644 public/css/biomed.less create mode 100644 public/css/biomed/breadcrumbs.less create mode 100644 public/css/biomed/buttons.less create mode 100644 public/css/biomed/datepicker.less create mode 100644 public/css/biomed/mixins.less create mode 100644 public/css/biomed/navbar.less create mode 100644 public/css/biomed/scaffolding.less create mode 100644 public/css/biomed/select2.less create mode 100644 public/css/biomed/sprites.less create mode 100644 public/css/biomed/tables.less create mode 100644 public/css/biomed/toolbelt.less create mode 100644 public/css/biomed/type.less create mode 100644 public/css/biomed/variables.less create mode 100644 public/css/biomed/widgets.less create mode 100644 public/css/bootstrap/accordion.less create mode 100644 public/css/bootstrap/alerts.less create mode 100644 public/css/bootstrap/breadcrumbs.less create mode 100644 public/css/bootstrap/button-groups.less create mode 100644 public/css/bootstrap/buttons.less create mode 100644 public/css/bootstrap/carousel.less create mode 100644 public/css/bootstrap/close.less create mode 100644 public/css/bootstrap/code.less create mode 100644 public/css/bootstrap/component-animations.less create mode 100644 public/css/bootstrap/dropdowns.less create mode 100644 public/css/bootstrap/forms.less create mode 100644 public/css/bootstrap/grid.less create mode 100644 public/css/bootstrap/hero-unit.less create mode 100644 public/css/bootstrap/labels-badges.less create mode 100644 public/css/bootstrap/layouts.less create mode 100644 public/css/bootstrap/media.less create mode 100644 public/css/bootstrap/mixins.less create mode 100644 public/css/bootstrap/modals.less create mode 100644 public/css/bootstrap/navbar.less create mode 100644 public/css/bootstrap/navs.less create mode 100644 public/css/bootstrap/pager.less create mode 100644 public/css/bootstrap/pagination.less create mode 100644 public/css/bootstrap/popovers.less create mode 100644 public/css/bootstrap/progress-bars.less create mode 100644 public/css/bootstrap/reset.less create mode 100644 public/css/bootstrap/responsive-1200px-min.less create mode 100644 public/css/bootstrap/responsive-767px-max.less create mode 100644 public/css/bootstrap/responsive-768px-979px.less create mode 100644 public/css/bootstrap/responsive-navbar.less create mode 100644 public/css/bootstrap/responsive-utilities.less create mode 100644 public/css/bootstrap/responsive.less create mode 100644 public/css/bootstrap/scaffolding.less create mode 100644 public/css/bootstrap/sprites.less create mode 100644 public/css/bootstrap/tables.less create mode 100644 public/css/bootstrap/thumbnails.less create mode 100644 public/css/bootstrap/tooltip.less create mode 100644 public/css/bootstrap/type.less create mode 100644 public/css/bootstrap/utilities.less create mode 100644 public/css/bootstrap/variables.less create mode 100644 public/css/bootstrap/wells.less create mode 100644 public/css/old/bio-header.less create mode 100644 public/css/old/bio-nav.less create mode 100644 public/css/old/biomed.less create mode 100644 public/css/old/breadcrumbs.less create mode 100644 public/css/old/buttons.less create mode 100644 public/css/old/datepicker.less create mode 100644 public/css/old/dropdowns.less create mode 100644 public/css/old/forms.less create mode 100644 public/css/old/grid.less create mode 100644 public/css/old/layouts.less create mode 100644 public/css/old/mixins.less create mode 100644 public/css/old/navs.less create mode 100644 public/css/old/pagination.less create mode 100644 public/css/old/progress-bars.less create mode 100644 public/css/old/reset.less create mode 100644 public/css/old/scaffolding.less create mode 100644 public/css/old/sprites.less create mode 100644 public/css/old/tables.less create mode 100644 public/css/old/timepicker.less create mode 100644 public/css/old/type.less create mode 100644 public/css/old/variables.less create mode 100644 public/css/old/widgets.less create mode 100644 public/fonts/Vegur-Bold.otf create mode 100644 public/fonts/Vegur-Light.otf create mode 100644 public/fonts/Vegur-Regular.otf create mode 100644 public/img/bodyBg.png create mode 100644 public/img/breadcrumb.png create mode 100644 public/img/glyphicons-halflings-white.png create mode 100644 public/img/glyphicons-halflings.png create mode 100644 public/img/loader.gif create mode 100644 public/img/logo.svg create mode 100644 public/js/app.js create mode 100644 public/js/controllers.js create mode 100644 public/js/directives.js create mode 100644 public/js/filters.js create mode 100644 public/js/lib/angular-ui.js create mode 100644 public/js/lib/bootstrap-datepicker.js create mode 100644 public/js/lib/dialog.js create mode 100644 public/js/lib/moment.js create mode 100644 public/js/lib/select2.js create mode 100644 public/js/services.js create mode 100644 public/partials/clients/add.html create mode 100644 public/partials/clients/edit.html create mode 100644 public/partials/clients/index.html create mode 100644 public/partials/messages.html create mode 100644 public/partials/schedule/index.html create mode 100644 public/partials/schedule/pms.html create mode 100644 public/partials/techPicker.html create mode 100644 public/partials/workorders/add.html create mode 100644 public/partials/workorders/edit.html create mode 100644 public/partials/workorders/index.html create mode 100644 server.js create mode 100644 templates/calendarDescription.tmpl create mode 100644 test.js create mode 100644 users.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..16769d8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +node diff --git a/app/controllers/account.js b/app/controllers/account.js new file mode 100644 index 0000000..8f66462 --- /dev/null +++ b/app/controllers/account.js @@ -0,0 +1,6 @@ + +var mongoose = require('mongoose'); + +exports.profile = function(req, res) { + res.json(req.user); +}; diff --git a/app/controllers/clients.js b/app/controllers/clients.js new file mode 100644 index 0000000..cfcb5a5 --- /dev/null +++ b/app/controllers/clients.js @@ -0,0 +1,127 @@ + +var mongoose = require('mongoose'), + Client = mongoose.model('Client'), + Workorder = mongoose.model('Workorder'); + +var frequencies = ["annual","semi","quarterly","sterilizer","tg","ert","rae","medgas","imaging","neptune","anesthesia"]; + +exports.index = function(req, res) { + var query = Client.find({ deleted: false }) + .select('name identifier') + .slice('contacts', 1) + .sort('name') + .exec(function(err, results) { + if (err) { + res.json(500, err); + } else { + res.json(results); + } + }); +} + +exports.get = function(req, res, next) { + var id = req.param('client_id'); + + Client.findById(id) + .exec(function(err, client) { + if (err) return next(err); + if (!client) return next(new Error('Failed to load client ' + id)); + + res.json(client); + }); +} + +exports.frequencies = function(req, res, next) { + var query = Client.find({ deleted: false }) + .select('name identifier frequencies') + .slice('contacts', 1) + .sort('name') + .exec(function(err, results) { + if (err) { + res.json(500, err); + } else { + res.json(results); + } + }); +}; + +exports.workorders = function(req, res, next) { + var id = req.param('client_id'); + Workorder.find({ client: id, deleted: false }) + .populate({path: 'techs', select: 'name'}) + .sort('-scheduling.start') + .exec(function(err, workorders) { + if (err) return next(err); + if (!workorders) return next(new Error('Failed to load workorders ' + id)); + + res.json(workorders); + }); +}; + +exports.create = function(req, res, next) { + console.log(req.body); + + var client = new Client({ + name: req.body.name, + identifier: req.body.identifier, + contacts: req.body.contacts, + address: req.body.address, + frequencies: {} + }); + + var freq = {}; + + for (key in frequencies) { + client.frequencies[frequencies[key]] = [false, false, false, false, false, false, false, false, false, false, false, false]; + } + + return client.save(function(err) { + if (!err) { + console.log("saved"); + } else { + console.log("error"); + } + + return res.json(client); + }) +}; + +exports.update = function(req, res, next) { + var id = req.param('client_id'); + + return Client.findById(id, function(err, client) { + client.name = req.body.name; + client.identifier = req.body.identifier; + client.contacts = req.body.contacts; + client.address = req.body.address; + client.frequencies = req.body.frequencies; + + return client.save(function(err) { + if (!err) { + console.log("updated"); + } else { + console.log("error"); + } + + return res.json(client); + }); + }); +}; + +exports.destroy = function(req, res, next) { + var id = req.param('client_id'); + + return Client.findById(id, function(err, client) { + client.deleted = true; + + return client.save(function(err) { + if (!err) { + console.log("deleted"); + } else { + console.log("error"); + } + + return res.json(client); + }) + }); +}; \ No newline at end of file diff --git a/app/controllers/home.js b/app/controllers/home.js new file mode 100644 index 0000000..103c778 --- /dev/null +++ b/app/controllers/home.js @@ -0,0 +1,10 @@ +module.exports = function(piler) { + return { + index: function(req, res) { + res.render("index.jade", { + js: piler.js.renderTags(), + css: piler.css.renderTags() + }); + } + }; +}; \ No newline at end of file diff --git a/app/controllers/login.js b/app/controllers/login.js new file mode 100644 index 0000000..0171ca0 --- /dev/null +++ b/app/controllers/login.js @@ -0,0 +1,21 @@ +module.exports = function(piler) { + return { + login: function(req, res) { + res.render("login.jade", { + js: piler.js.renderTags(), + css: piler.css.renderTags() + }); + }, + + error: function(req, res) { + res.render("error.jade", { + js: piler.js.renderTags(), + css: piler.css.renderTags() + }); + }, + logout: function(req, res) { + req.logout(); + res.redirect('/'); + } + }; +} \ No newline at end of file diff --git a/app/controllers/messages.js b/app/controllers/messages.js new file mode 100644 index 0000000..3b06555 --- /dev/null +++ b/app/controllers/messages.js @@ -0,0 +1,79 @@ + +var mongoose = require('mongoose'), + email = require('emailjs'), + sprintf = require('sprintf').sprintf, + User = mongoose.model('User'); + +module.exports = function(config) { + var server = email.server.connect({ + user: config.email.user, + password: config.email.password, + host: 'smtp.gmail.com', + ssl: true + }); + + return { + send: function(req, res) { + console.log(req.body); + + var userId = req.body.user; + if (!userId) { + return res.json(404, null); + } + + console.log("Sending message"); + User.findById(userId, function(err, user) { + if (err) return res.json(500, err); + + server.send({ + text: generateMessage(user, req.body), + from: config.email.user, + to: generateToLine(user), + subject: 'Message from portal' + }, function(err, message) { + console.log(err || message); + if (err) { + res.json(500, err); + } else { + res.json(null); + } + }); + }); + } + }; +} + +function generateToLine(user) { + return user.name.first + " " + user.name.last + " <" + user.email + ">"; +} + +function generateMessage(user, message) { + var template = + "Message For: %(user)s\n" + + "\n" + + "Name: %(name)s\n" + + "Company: %(company)s\n" + + "Phone: %(phone)s\n" + + "Extension: %(extension)s\n" + + "\n" + + "%(messages)s\n" + + "%(notes)s\n"; + + var resources = { + user: user.name.first + " " + user.name.last, + name: message.name || '', + company: message.company || '', + phone: message.phone || '', + extension: message.extension || '', + messages: '', + notes: message.notes || '' + }; + + message.messages.forEach(function(msg) { + if (msg.checked) { + resources.messages += msg.message + "\n"; + } + }); + + return sprintf(template, resources); +} diff --git a/app/controllers/schedule.js b/app/controllers/schedule.js new file mode 100644 index 0000000..475f502 --- /dev/null +++ b/app/controllers/schedule.js @@ -0,0 +1,26 @@ + +var mongoose = require('mongoose'), + moment = require('moment'), + Workorder = mongoose.model('Workorder'); + +exports.index = function(req, res) { + var date = moment(req.query.date); + var start = date.clone().startOf('day').toDate(); + var end = date.clone().endOf('day').toDate(); + + Workorder + .find({ + deleted: false, + 'scheduling.start': { '$gte': start, '$lt': end } + }) + .populate('techs', 'name') + .populate('client', 'name identifier address') + .select('scheduling techs client') + .exec(function(err, results) { + if (err) { + res.json(500, err); + } else { + res.json(results); + } + }); +}; diff --git a/app/controllers/users.js b/app/controllers/users.js new file mode 100644 index 0000000..3f996c7 --- /dev/null +++ b/app/controllers/users.js @@ -0,0 +1,25 @@ + +var mongoose = require('mongoose'), + User = mongoose.model('User'); + +exports.index = function(req, res) { + var criteria = { deleted: false }; + + if (req.query.group) { + criteria.groups = req.query.group; + } + + if (req.query.perms) { + criteria.perms = req.query.perms; + } + + var query = User.find(criteria) + .select('name groups') + .exec(function(err, results) { + if (err) { + res.json(500, err); + } else { + res.json(results); + } + }); +}; diff --git a/app/controllers/workorders.js b/app/controllers/workorders.js new file mode 100644 index 0000000..7f4d928 --- /dev/null +++ b/app/controllers/workorders.js @@ -0,0 +1,284 @@ + +var mongoose = require('mongoose'), + moment = require('moment'), + async = require('async'), + sprintf = require('sprintf').sprintf, + Client = mongoose.model('Client'), + Workorder = mongoose.model('Workorder'), + Counter = mongoose.model('Counter'), + User = mongoose.model('User'); + +module.exports = function(calendar) { + return { + index: function(req, res) { + + var start = moment(req.query.start).toDate(); + var end = moment(req.query.end).add('days', 1).toDate(); + + Workorder + .find({ + deleted: false, + 'scheduling.start': { '$gte': start, '$lt': end } + }) + .populate('client', 'name identifier address') + .populate('techs', 'name') + .sort('-scheduling.start client.name') + .exec(function(err, results) { + if (err) { + res.json(500, err); + } else { + res.json(results); + } + }); + }, + + get: function(req, res, next) { + var id = req.param('workorder_id'); + + Workorder.findById(id) + .populate('client', 'name identifier') + .populate('techs', 'name') + .exec(function(err, workorder) { + if (err) return next(err); + if (!workorder) return next(new Error('Failed to load workorder ' + id)); + + res.json(workorder); + }); + }, + + create: function(req, res, next) { + var workorder = new Workorder({ + client: req.body.client, + createdOn: new Date(), + createdBy: req.user, + reason: req.body.reason, + remarks: req.body.remarks || "", + status: req.body.status, + scheduling: req.body.scheduling, + techs: req.body.techs + }); + + var client; + var techs; + var jsonResult; + + async.waterfall([ + function(callback) { + console.log("Get next workorder id."); + Counter.collection.findAndModify( + { name: 'workorder' }, + [], + { $inc: { seq: 1 } }, + { 'new': true, upsert: true }, + function(err, result) { + workorder.biomedId = result.seq - 1; + callback(err); + }); + }, + function(callback) { + console.log("Get Client"); + Client.findById(req.body.client, function(err, result) { + client = result; + callback(err); + }); + }, + function(callback) { + console.log('Get Techs'); + User.find({ + '_id': { $in: workorder.techs } + }, + function(err, result) { + console.log(result); + techs = result; + callback(err); + }); + }, + function(callback) { + console.log("Create Calendar Event") + + calendar.scheduleEvent({ + summary: generateSummary(client), + description: generateDescription(client, workorder), + location: generateLocation(client), + start: workorder.scheduling.start, + end: workorder.scheduling.end, + attendees: generateAttendees(techs) + }, function(err, result) { + workorder.calendarId = result.id; + callback(err); + }); + }, + function(callback) { + console.log("Save Workorder"); + workorder.save(function(err, result) { callback(err, result); }); + }, + function(result, callback) { + console.log("Update Client") + jsonResult = result; + + Client.findByIdAndUpdate(req.body.client, { $push: { 'workorders': result.id } }, + function(err, ignored) { callback(err, result) }); + } + ], + function(err, result) { + if (!err) { + res.json(jsonResult); + } else { + console.log(err); + throw err; + } + }); + }, + + update: function(req, res, next) { + var id = req.param('workorder_id'); + + var workorder; + var client; + var techs; + + async.waterfall([ + function(callback) { + console.log("Get Workorder"); + Workorder.findById(id, function(err, result) { + workorder = result; + + workorder.reason = req.body.reason; + workorder.remarks = req.body.remarks; + workorder.scheduling = req.body.scheduling; + workorder.status = req.body.status; + workorder.techs = req.body.techs.map(function(t) { return t._id; }); + + callback(err); + }); + }, + function(callback) { + console.log("Get Client"); + Client.findById(workorder.client, function(err, result) { + client = result; + callback(err); + }); + }, + function(callback) { + console.log('Get Techs'); + User.find({ + '_id': { $in: workorder.techs } + }, + function(err, result) { + console.log(result); + techs = result; + callback(err); + }); + }, + function(callback) { + console.log("Update Calendar Event") + + calendar.updateEvent({ + summary: generateSummary(client), + description: generateDescription(client, workorder), + location: generateLocation(client), + start: workorder.scheduling.start, + end: workorder.scheduling.end, + attendees: generateAttendees(techs), + eventId: workorder.calendarId + }, function(err, result) { + callback(err); + }); + }, + function(callback) { + workorder.save(function(err) { + callback(err); + }) + } + ], + function(err) { + if (!err) { + console.log('updated'); + } else { + console.log('error'); + console.log(err); + } + + res.json(workorder); + }); + }, + + destroy: function(req, res, next) { + var id = req.param('workorder_id'); + + return Workorder.findById(id, function(err, workorder) { + workorder.deleted = true; + + return workorder.save(function(err) { + if (!err) { + console.log("deleted"); + } else { + console.log("error"); + } + + return res.json(workorder); + }) + }); + } + }; +}; + + +function generateSummary(client) { + return client.name + ' (' + client.identifier + ')'; +} + +function generateLocation(client) { + return sprintf("%(street1)s %(street2)s %(city)s, %(state)s. %(zip)s", client.address); +} + +function generateDescription(client, workorder) { + var template = + "Workorder ID:\n" + + " %(biomedId)s\n" + + "\n" + + "Customer:\n" + + " %(name)s (%(identifier)s)\n" + + "\n" + + "Phone:\n" + + " %(phone)s\n" + + "\n" + + "Address:\n" + + " %(street1)s\n" + + " %(street2)s\n" + + " %(city)s, %(state)s. %(zip)s\n" + + "\n" + + "Reason:\n" + + " %(reason)s\n" + + "\n" + + "Status:\n" + + " %(status)s\n" + + "\n" + + "Remarks:\n" + + " %(remarks)s\n"; + + var resources = { + biomedId: workorder.biomedId || '', + name: client.name || '', + identifier: client.identifier || '', + street1: client.address.street1 || '', + street2: client.address.street2 || '', + city: client.address.city || '', + state: client.address.state || '', + zip: client.address.zip || '', + reason: workorder.reason || '', + status: workorder.status || '', + remarks: workorder.remarks || '', + phone: '' + }; + + if (client.contacts[0]) { + resources.phone = client.contacts[0].phone || ''; + } + + return sprintf(template, resources); +} + +function generateAttendees(techs) { + return techs.map(function(t) { return t.email; }); +} \ No newline at end of file diff --git a/app/model/client.js b/app/model/client.js new file mode 100644 index 0000000..d1d508e --- /dev/null +++ b/app/model/client.js @@ -0,0 +1,25 @@ +var mongoose = require('mongoose'), + Schema = mongoose.Schema, + ObjectId = Schema.ObjectId; + +var clientSchema = new Schema({ + name: String, + identifier: String, + address: { + street1: String, + street2: String, + city: String, + state: String, + zip: String + }, + contacts: [{ + name: String, + phone: String, + email: String + }], + frequencies: {}, + workorders: [{ type: ObjectId, ref: 'Workorder' }], + deleted: { type: Boolean, default: false } +}); + +module.exports = mongoose.model('Client', clientSchema); \ No newline at end of file diff --git a/app/model/counter.js b/app/model/counter.js new file mode 100644 index 0000000..67d7c45 --- /dev/null +++ b/app/model/counter.js @@ -0,0 +1,12 @@ +var mongoose = require('mongoose'), + Schema = mongoose.Schema, + ObjectId = Schema.ObjectId; + +var counterSchema = new Schema({ + name: String, + seq: { type: Number, default: 0 } +}); + +counterSchema.index({ field: 1, model: 1 }, { unique: true, required: true, index: -1 }); + +module.exports = mongoose.model('Counter', counterSchema); \ No newline at end of file diff --git a/app/model/user.js b/app/model/user.js new file mode 100644 index 0000000..de373c1 --- /dev/null +++ b/app/model/user.js @@ -0,0 +1,24 @@ +var mongoose = require('mongoose'), + Schema = mongoose.Schema, + ObjectId = Schema.ObjectId; + +var userSchema = new Schema({ + name: { + first: String, + last: String + }, + email: String, + picture: String, + refreshToken: String, + accessToken: String, + + groups: [String], + perms: [String], + deleted: { type: Boolean, default: false } +}); + +userSchema.methods.hasPermission = function(perm) { + return this.perms.indexOf(perm) != -1; +} + +var User = module.exports = mongoose.model('User', userSchema); diff --git a/app/model/workorder.js b/app/model/workorder.js new file mode 100644 index 0000000..157ff1e --- /dev/null +++ b/app/model/workorder.js @@ -0,0 +1,27 @@ +var mongoose = require('mongoose') + Schema = mongoose.Schema, + ObjectId = Schema.ObjectId; + +var workorderSchema = new Schema({ + biomedId: Number, + client: { type: ObjectId, ref: 'Client' }, + createdOn: Date, + createdBy: { type: ObjectId, ref: 'User' }, + reason: String, + remarks: String, + status: String, + scheduling: { + start: Date, + end: Date + }, + calendarId: String, + techs: [{ type: ObjectId, ref: 'User' }], + history: [{ + oldValues: {}, + newValues: {}, + modifiedBy: { type: ObjectId, ref: 'User' } + }], + deleted: { type: Boolean, default: false } +}); + +module.exports = mongoose.model('Workorder', workorderSchema); \ No newline at end of file diff --git a/app/views/error.jade b/app/views/error.jade new file mode 100644 index 0000000..bfcb230 --- /dev/null +++ b/app/views/error.jade @@ -0,0 +1,18 @@ +doctype 5 +html(lang="en") + head + title Atlantic Biomedical - Login + !{css} + body.login-page + .container-fluid + #loginbox + .brand-container + .brand Atlantic Biomedical + form + .alert.alert-error + | You are not authorized to access this site. + | Please contact your site administrator to request access. + p + | Please login with your Google Apps account to continue. + .form-actions + a.btn.btn-danger(href='/auth') Login with Google Apps \ No newline at end of file diff --git a/app/views/index.jade b/app/views/index.jade new file mode 100644 index 0000000..6d9cd57 --- /dev/null +++ b/app/views/index.jade @@ -0,0 +1,50 @@ +doctype 5 +html(lang="en", ng-app="biomed", ng-controller="biomed.PageCtrl") + head + title Atlantic Biomedical + !{css} + body + error-panel + .navbar + .navbar-inner + a.brand(href='/', target='_self') Atlantic Biomedical + progress-panel + ul.nav.pull-right(ng-controller='biomed.AccountCtrl') + li + a(href='http://atlanticbiomedical.com/ticket/', target='_blank') + i.icon-fire + | Tickets + li + a(ng-click="openDialog()") + i.icon-envelope + | Messages + li + a(href='/logout', target='_self') + i.icon-share-alt + | Logout + li(ng-show='account.name') + a(href='#') + i.icon-user + | {{account.name.first}} {{account.name.last}} + li.photo(ng-show='account.picture', ns-show='account.picture') + img(ng-src='{{account.picture}}?sz=50') + .navbar.navbar-secondary(bio-navbar) + .navbar-inner + ul.nav + li(data-match-route='/schedule.*') + a(href='/schedule') + i.icon-calendar + | Schedule + + li(data-match-route='/client.*') + a(href='/clients') + i.icon-briefcase + | Clients + + li(data-match-route='/workorder.*') + a(href='/workorders') + i.icon-wrench + | Workorders + .container-fluid + ng-view + !{js} \ No newline at end of file diff --git a/app/views/login.jade b/app/views/login.jade new file mode 100644 index 0000000..4b44ed8 --- /dev/null +++ b/app/views/login.jade @@ -0,0 +1,15 @@ +doctype 5 +html(lang="en") + head + title Atlantic Biomedical - Login + !{css} + body.login-page + .container-fluid + #loginbox + .brand-container + .brand Atlantic Biomedical + form + p + | Please login with your Google Apps account to continue. + .form-actions + a.btn.btn-danger(href='/auth') Login with Google Apps \ No newline at end of file diff --git a/config/auth.js b/config/auth.js new file mode 100644 index 0000000..5e535aa --- /dev/null +++ b/config/auth.js @@ -0,0 +1,36 @@ +module.exports = function(app, passport) { + app.get('/auth', passport.authenticate('google', { + accessType: 'offline', + scope: [ + 'https://www.googleapis.com/auth/userinfo.profile', + 'https://www.googleapis.com/auth/userinfo.email', + 'https://www.googleapis.com/auth/calendar' + ]})); + + app.get('/auth/callback', function(req, res, next) { + passport.authenticate('google', function(err, user, info) { + if (err) { return next(err); } + if (!user) { return res.redirect('/login/error'); } + + req.logIn(user, function(err) { + if (err) { return next(err); } + return res.redirect('/'); + }); + })(req, res, next); + }); + + return { + requiresUiLogin: function(req, res, next) { + if (!req.isAuthenticated()) { + return res.redirect('/login'); + } + next(); + }, + requiresApiAccess: function(req, res, next) { + if (!req.isAuthenticated()) { + return res.send(403); + } + next(); + } + }; +}; \ No newline at end of file diff --git a/config/calendar.js b/config/calendar.js new file mode 100644 index 0000000..e73625e --- /dev/null +++ b/config/calendar.js @@ -0,0 +1,96 @@ +var googleapis = require('googleapis'), + sprintf = require('sprintf'), + OAuth2Client = googleapis.OAuth2Client; + + +module.exports = function(config) { + + var oauth2Client = new OAuth2Client( + config.auth.clientId, config.auth.clientSecret, config.auth.callback); + + oauth2Client.credentials = { + access_token: config.auth.accessToken, + refresh_token: config.auth.refreshToken + }; + + var opts = { baseDiscoveryUrl: 'https://www.googleapis.com/discovery/v1/apis/' }; + + + return { + scheduleEvent: function(event, callback) { + console.log("schedule event"); + + api(function(client, callback) { + + var resource = buildResource(event); + + var request = client.calendar.events.insert({ + calendarId: 'primary', + resource: resource + }); + + request.withAuthClient(oauth2Client).execute(function(err, result) { + callback(err, result); + }); + }, callback); + }, + + updateEvent: function(event, callback) { + api(function(client, callback) { + + var resource = buildResource(event); + + var request = client.calendar.events.update({ + calendarId: 'primary', + eventId: event.eventId, + resource: resource + }); + + request.withAuthClient(oauth2Client).execute(function(err, result) { + callback(err, result); + }); + }, callback); + } + }; + + function buildResource(event) { + var resource = { + summary: event.summary, + description: event.description, + location: event.location, + start: { + dateTime: event.start + }, + end: { + dateTime: event.end + }, + attendees: [] + }; + + event.attendees.forEach(function(attendee) { + resource.attendees.push({ + email: attendee + }); + }) + + return resource; + } + + function api(workorder, callback) { + googleapis + .discover('calendar', 'v3') + .execute(function(err, client) { + if (err) return callback(err); + + workorder(client, function(err, result) { + if (oauth2Client.credentials.access_token != config.auth.accessToken) { + console.log("Updating access token"); + config.auth.accessToken = oauth2Client.credentials.access_token; + } + + callback(err, result); + }); + }); + } + +}; diff --git a/config/config.js b/config/config.js new file mode 100644 index 0000000..b8af1ad --- /dev/null +++ b/config/config.js @@ -0,0 +1,46 @@ +module.exports = { + development: { + root: require('path').normalize(__dirname + '/..'), + debug: true, + database: 'mongodb://biomed.akira.gs/biomed_devel2', + auth: { + clientId: '223145213165.apps.googleusercontent.com', + clientSecret: '8MRNar9E_pRTOGTQonPzYOW_', + callback: 'http://devel.portal.atlanticbiomedical.com/auth/callback', + accessToken: 'ya29.AHES6ZR-vUVEh7CZzsEeGFSHqFfXtU1-LHyEAidi0CKhDGQ', + refreshToken: '1/exRXjTaGNlWEo-HZZWyn4NTwJ4TY3wKb-_npce21c50', + }, + email: { + user: 'api@atlanticbiomedical.com', + password: 'success4' + }, + mysql: { + host: 'biomed.akira.gs', + user: 'biomed_prod', + password: 'wUw3RB8rrXX4HwKj', + database: 'biomed_prod', + } + }, + prod: { + root: require('path').normalize(__dirname + '/..'), + debug: true, + database: 'mongodb://localhost/biomed_prod', + auth: { + clientId: '333768673996-8epedo3je5h59n4l97v4dv8nofs7qnee.apps.googleusercontent.com', + clientSecret: 'afu9KhKxckWJ3Tk6uxzp9Pg6', + callback: 'http://portal.atlanticbiomedical.com/auth/callback', + accessToken: 'ya29.AHES6ZT1Sj1vpgidR2I_ksLdlV_VeZUjkitnZ01cP6VRrknjUEVbuw', + refreshToken: '1/XQW9P9FNYm6jikTsV8HOIuPAo1APYhwTH5CLhq9263g' + }, + email: { + user: 'api@atlanticbiomedical.com', + password: 'success4', + }, + mysql: { + host: 'localhost', + user: 'biomed_prod', + password: 'wUw3RB8rrXX4HwKj', + database: 'biomed_prod' + } + } +}; diff --git a/config/express.js b/config/express.js new file mode 100644 index 0000000..8cfb24b --- /dev/null +++ b/config/express.js @@ -0,0 +1,40 @@ +var express = require('express'); + +module.exports = function(app, config, passport, piler) { + app.set('showStackError', true); + + app.use(express.static(config.root + '/public')) + app.use(express.logger('dev')); + app.set('views', config.root + '/app/views'); + app.set('view engine', 'jade'); + + app.configure(function() { + // cookieParser should be above session + app.use(express.cookieParser()); + + // bodyParser should be above methodOverride + app.use(express.bodyParser()); + app.use(express.methodOverride()); + + app.use(express.session({ + secret: 'atlantic_biomed_server_secret' + })); + + // use passport session + app.use(passport.initialize()); + app.use(passport.session()); + + // use piler for asset management + piler.bind(); + + app.use(express.favicon()); + + // routes should be last + app.use(app.router); + }); + +// app.configure('development', function() { +// // enable live update in development mode. +// piler.liveUpdate(); +// }); +} \ No newline at end of file diff --git a/config/passport.js b/config/passport.js new file mode 100644 index 0000000..c925735 --- /dev/null +++ b/config/passport.js @@ -0,0 +1,55 @@ +var mongoose = require('mongoose') + GoogleStrategy = require('passport-google-oauth').OAuth2Strategy, + User = mongoose.model('User'); + +module.exports = function(passport, config) { + passport.serializeUser(function(user, done) { + done(null, user._id); + }); + + passport.deserializeUser(function(id, done) { + User.findById(id, function(err, user) { + done(err, user); + }); + }); + + passport.use(new GoogleStrategy({ + clientID: config.auth.clientId, + clientSecret: config.auth.clientSecret, + callbackURL: config.auth.callback + }, + function(accessToken, refreshToken, profile, done) { + console.log(profile); + console.log(accessToken); + console.log(refreshToken); + + profile = profile._json; + User.findOne({ email: profile.email.toLowerCase() }, function(err, user) { + if (err) { return done(err); } + if (!user || !user.hasPermission("system.login")) { + return done(null, false, { message: "You are not authorized to access this portal." }); + } + + user.accessToken = accessToken; + + if (refreshToken) { + user.refreshToken = refreshToken; + } + if (profile.given_name) { + user.name.first = profile.given_name; + } + if (profile.family_name) { + user.name.last = profile.family_name; + } + if (profile.picture) { + user.picture = profile.picture; + } + + user.save(function(err) { + if (err) console.log(err); + + return done(err, user); + }); + }); + })); +} \ No newline at end of file diff --git a/config/piler.js b/config/piler.js new file mode 100644 index 0000000..f55d968 --- /dev/null +++ b/config/piler.js @@ -0,0 +1,38 @@ +var pile = require('piler'); + +module.exports = function(app, server, io, config) { + var js = pile.createJSManager(); + var css = pile.createCSSManager(); + + var root = config.root + "/public"; + + return { + bind: function() { + js.bind(app, server); + css.bind(app, server); + }, + + liveUpdate: function() { + js.liveUpdate(css, io); + }, + + addCssUrl: function(url) { + css.addUrl(url); + }, + + addCssFile: function(path) { + css.addFile(root + path); + }, + + addJsUrl: function(url) { + js.addUrl(url); + }, + + addJsFile: function(path) { + js.addFile(root + path); + }, + + js: js, + css: css + }; +}; diff --git a/config/routes.js b/config/routes.js new file mode 100644 index 0000000..dd1e359 --- /dev/null +++ b/config/routes.js @@ -0,0 +1,59 @@ + +module.exports = function(app, auth, piler, calendar, config) { + + piler.addCssUrl("//fonts.googleapis.com/css?family=Open+Sans:400,300"); + piler.addCssFile("/css/biomed.less"); + + piler.addJsUrl("//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"); + piler.addJsUrl("//ajax.googleapis.com/ajax/libs/angularjs/1.1.3/angular.js"); + piler.addJsUrl("//ajax.googleapis.com/ajax/libs/angularjs/1.1.3/angular-resource.js"); + piler.addJsUrl("http://d3js.org/d3.v2.js"); + piler.addJsFile("/js/lib/moment.js"); + piler.addJsFile("/js/lib/bootstrap-datepicker.js"); + piler.addJsFile("/js/lib/dialog.js"); + piler.addJsFile("/js/lib/select2.js"); + piler.addJsFile("/js/app.js"); + piler.addJsFile("/js/controllers.js"); + piler.addJsFile("/js/directives.js"); + piler.addJsFile("/js/filters.js"); + piler.addJsFile("/js/services.js"); + + app.all('/api/*', auth.requiresApiAccess); + + var clients = require('../app/controllers/clients'); + app.get('/api/clients', clients.index); + app.get('/api/clients/frequencies', clients.frequencies); + app.get('/api/clients/:client_id', clients.get); + app.get('/api/clients/:client_id/workorders', clients.workorders); + app.post('/api/clients', clients.create); + app.post('/api/clients/:client_id', clients.update); + app.del('/api/clients/:client_id', clients.destroy); + + var workorders = require('../app/controllers/workorders')(calendar); + app.get('/api/workorders', workorders.index); + app.get('/api/workorders/:workorder_id', workorders.get); + app.post('/api/workorders', workorders.create); + app.post('/api/workorders/:workorder_id', workorders.update); + app.del('/api/workorders/:workorder_id', workorders.destroy); + + var schedule = require('../app/controllers/schedule'); + app.get('/api/schedule', schedule.index); + + var users = require('../app/controllers/users'); + app.get('/api/users', users.index); + + var account = require('../app/controllers/account'); + app.get('/api/account', account.profile); + + var messages = require('../app/controllers/messages')(config); + app.post('/api/messages/send', messages.send); + + var login = require('../app/controllers/login')(piler); + app.get('/login', login.login); + app.get('/login/error', login.error); + app.get('/logout', login.logout); + + var home = require('../app/controllers/home')(piler); + app.get('/', auth.requiresUiLogin, home.index); + app.get('*', auth.requiresUiLogin, home.index); +}; diff --git a/mongo-upgrade.js b/mongo-upgrade.js new file mode 100644 index 0000000..94fb2fa --- /dev/null +++ b/mongo-upgrade.js @@ -0,0 +1,322 @@ +var env = process.env.NODE_ENV || 'development', + config = require('./config/config')[env], + fs = require('fs'), + async = require('async'), + mongoose = require('mongoose'); + +mongoose.connect(config.database); + +var modelPath = __dirname + '/app/model' +fs.readdirSync(modelPath).forEach(function (file) { + require(modelPath + '/' + file) +}) + +var mysql = require('mysql'); +var pool = mysql.createPool(config.mysql); + +var Client = mongoose.model('Client'); +var User = mongoose.model('User'); +var Workorder = mongoose.model('Workorder'); +var Counter = mongoose.model('Counter'); + +mongoose.connection.on('error', function(err) { + console.log(err); +}); + +mongoose.connection.on('disconnected', function(msg) { + console.log("Disconnected"); +}); + +var frequencies = ["annual","semi","quarterly","sterilizer","tg","ert","rae","medgas","imaging","neptune","anesthesia"]; +var months = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"]; + +var systemUser; + +var userMap = {}; +var clientMap = {}; +var workorderMap = {}; + +var maxWorkorderId = 0; + +async.series([ + importUsers, + createSystemUser, + processQuery('SELECT * FROM user', processUser), + processQuery('SELECT * FROM client', processClient), + processQuery('SELECT * FROM workorder', processWorkorder), + processQuery('SELECT * FROM workorder_tech', processWorkorderTech), + updateCounter, +], +function(err, result) { + if (err) throw err; + + console.log("Import Complete"); + + mongoose.connection.close(function(dberr) { + if (dberr) { + console.log("Error disconnecting from mongo: "); + console.log(dberr); + } else { + process.exit(); + } + }); +}); + +function importUsers(callback) { + fs.readFile(__dirname + '/users.json', 'utf8', function(err, data) { + if (err) { + return callback(err); + } + + var data = JSON.parse(data); + + async.each(data, function(user, userCallback) { + var id = user.id; + delete user.id; + + console.log("Adding user: " + user.name.first + " " + user.name.last); + + new User(user).save(function(err, result) { + if (id) { + if (!Array.isArray(id)) id = [id]; + + id.forEach(function(biomedid) { + userMap[biomedid] = result.id; + }); + } + + userCallback(err); + }); + + }, callback); + }); +} + +function createSystemUser(callback) { + new User({ + name: { + first: 'System', + last: 'User' + }, + email: 'system@atlanticbiomedical.com', + }).save(function(err, result) { + systemUser = result.id; + callback(err, result); + }); +} + +function processUser(row, callback) { + if (userMap[row.id]) { + console.log("Found preconfigured user: " + row.first_name + " " + row.last_name); + return callback(null); + } + + var user = { + name: { + first: row.first_name, + last: row.last_name + }, + email: row.email || 'unknown@atlanticbiomedical.com', + perms: [], + groups: [], + deleted: true + }; + + if (row.user_type_id === 1) { + user.groups.push("all"); + } + + console.log("Adding deleted user: " + user.name.first + " " + user.name.last); + + new User(user).save(function(err, result) { + userMap[row.id] = result.id; + callback(err, result); + }); +} + +function processClient(row, callback) { + var client = new Client({ + name: row.client_name, + identifier: row.client_identification, + address: { + street1: row.address || null, + street2: row.address_2 || null, + city: row.city || null, + state: row.state || null, + zip: row.zip || null + }, + contacts:[{ + name: row.attn || undefined, + phone: row.phone || undefined, + email: row.email || undefined + }] + }); + + if (row.secondary_attn || row.secondary_phone || row.secondary_email) { + client.contacts.push({ + name: row.secondary_attn || undefined, + phone: row.secondary_phone || undefined, + email: row.secondary_email || undefined + }); + } + + client.frequencies = {}; + + if (row.frequency && row.frequency.toString()) { + var str = row.frequency.toString(); + + var frq = client.frequencies['legacy'] = [false, false, false, false, false, false, false, false, false, false, false, false]; + for (m in months) { + if (str.indexOf(months[m]) !== -1) { + frq[m] = true; + } + } + } + + for (key in frequencies) { + var frequency = frequencies[key]; + var data = row["frequency_" + frequency]; + var frq = client.frequencies[frequency] = [false, false, false, false, false, false, false, false, false, false, false, false]; + + if (data && data.toString()) { + var str = data.toString(); + for (m in months) { + if (str.indexOf(months[m]) !== -1) { + frq[m] = true; + } + } + } + } + + client.save(function (err, result) { + clientMap[row.id] = result.id; + callback(err, result); + }); +} + +function processWorkorder(row, callback) { + var jobDate = row.job_date; + + if (!jobDate) return callback(null, null); + + var jobStart = buildJobTime(jobDate, row.job_start); + var jobEnd = buildJobTime(jobDate, row.job_end); + + var workorder = new Workorder({ + biomedId: row.id, + client: clientMap[row.client_id], + createdOn: new Date(Date.parse(row.job_scheduled_date)), + createdBy: systemUser, + reason: mapReason(row.reason), + remarks: row.remarks, + status: mapStatus(jobStart), + scheduling: { + start: jobStart, + end: jobEnd + }, + calendarId: row.google_event_id, + }); + + workorder.save(function(err, result) { + if (err) return callback(err); + + workorderMap[row.id] = result.id; + + maxWorkorderId = Math.max(maxWorkorderId, row.id); + + Client.findByIdAndUpdate(clientMap[row.client_id], { $push: { 'workorders': result.id } }, callback); + }); +} + +function processWorkorderTech(row, callback) { + var workorderId = workorderMap[row.workorder_id]; + var userId = userMap[row.user_id]; + + if (workorderId && userId) { + Workorder.findByIdAndUpdate(workorderId, { $push: { 'techs': userId }}, callback); + } else { + console.log("Unable to map workorder: " + row.workorder_id + " -> " + row.user_id); + callback(null); + } +} + +function updateCounter(callback) { + new Counter({ + name: 'workorder', + seq: maxWorkorderId + }).save(callback); +} + +function processQuery(sql, processor) { + return function(callback) { + pool.getConnection(function(err, connection) { + if (err) return console.log(err); + + var calls = []; + + console.log('Executing query "' + sql + '"') + + connection.query(sql, function(err, rows) { + if (err) return console.log(err); + + console.log("Found " + rows.length + " rows"); + + rows.forEach(function(row) { + calls.push(function(callback) { + processor(row, callback); + }); + }); + + async.parallel(calls, function(err, result) { + if (err) return callback(err); + + console.log("Finished processing query"); + callback(null); + }); + }); + }); + } +} + +function buildJobTime(date, time) { + return new Date(Date.parse(date + ' ' + parseJobTime(time))); +} + +function parseJobTime(str) { + if (str.length == 3) { + return '0' + str.substr(0, 1) + ':' + str.substr(1, 2) + ':00'; + } else { + return str.substr(0, 2) + ':' + str.substr(2, 2) + ':00'; + } +} + +var reasons = { + "18": "Add New Equipment", + "24": "As Directed", + "7": "Autoclave Repair", + "21": "Calibration", + "9": "Delivery", + "10": "Diagnose Problem", + "19": "Install Parts", + "22": "Off", + "23": "PM Reschedule", + "17": "Preventive Maintenance", + "6": "Printer Failure", + "20": "Repair" +}; + +function mapReason(reason) { + if (reasons[reason]) { + return reasons[reason]; + } else { + return reason; + } +} + +function mapStatus(jobDate) { + if (jobDate < new Date(2012, 3, 10)) { + return 'invoiced'; + } else { + return 'scheduled'; + } +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..2c2970d --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "biomed-portal", + "version": "0.0.1", + "author": "Dobie Wollert", + "description": "Atlantic Biomedical Portal", + "private": true, + "dependencies": { + "express": "", + "mongoose": "", + "mongoose-pureautoinc": "", + "passport": "", + "passport-google-oauth": "", + "piler": "", + "less": "", + "socket.io": "", + "jade": "", + "googleapis": "", + "sprintf": "", + "emailjs": "" + }, + "devDependencies": { + "supervisor": "" + } +} \ No newline at end of file diff --git a/public/css/biomed.less b/public/css/biomed.less new file mode 100644 index 0000000..4fc9dc3 --- /dev/null +++ b/public/css/biomed.less @@ -0,0 +1,77 @@ +/*! + * Bootstrap v2.3.1 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */ + +// Core variables and mixins +@import "bootstrap/variables.less"; +@import "biomed/variables.less"; +@import "bootstrap/mixins.less"; +@import "biomed/mixins.less"; + +// CSS Reset +@import "bootstrap/reset.less"; + +// Grid system and page structure +@import "bootstrap/scaffolding.less"; +@import "biomed/scaffolding.less"; +@import "bootstrap/grid.less"; +@import "bootstrap/layouts.less"; + +// Base CSS +@import "bootstrap/type.less"; +@import "biomed/type.less"; +@import "bootstrap/code.less"; +@import "bootstrap/forms.less"; +@import "bootstrap/tables.less"; +@import "biomed/tables.less"; + +// Components: common +@import "bootstrap/sprites.less"; +@import "biomed/sprites.less"; +@import "bootstrap/dropdowns.less"; +@import "bootstrap/wells.less"; +@import "bootstrap/component-animations.less"; +@import "bootstrap/close.less"; + +// Components: Buttons & Alerts +@import "bootstrap/buttons.less"; +@import "biomed/buttons.less"; +@import "bootstrap/button-groups.less"; +@import "bootstrap/alerts.less"; + +// Components: Nav +@import "bootstrap/navs.less"; +@import "bootstrap/navbar.less"; +@import "biomed/navbar.less"; +@import "bootstrap/breadcrumbs.less"; +@import "biomed/breadcrumbs.less"; +@import "bootstrap/pagination.less"; +@import "bootstrap/pager.less"; + +// Components: Popovers +@import "bootstrap/modals.less"; +@import "bootstrap/tooltip.less"; +@import "bootstrap/popovers.less"; + +// Components: Misc +@import "bootstrap/thumbnails.less"; +@import "bootstrap/media.less"; +@import "bootstrap/labels-badges.less"; +@import "bootstrap/progress-bars.less"; +@import "bootstrap/accordion.less"; +@import "bootstrap/carousel.less"; +@import "bootstrap/hero-unit.less"; +@import "biomed/toolbelt.less"; +@import "biomed/datepicker.less"; + +@import "biomed/widgets.less"; +@import "biomed/select2.less"; + +// Utility classes +@import "bootstrap/utilities.less"; // Has to be last to override when necessary diff --git a/public/css/biomed/breadcrumbs.less b/public/css/biomed/breadcrumbs.less new file mode 100644 index 0000000..5f2c2a2 --- /dev/null +++ b/public/css/biomed/breadcrumbs.less @@ -0,0 +1,30 @@ + +.breadcrumb { + background-color: #ffffff; + border-bottom: 1px solid #e3ebed; + margin: 0 -20px @baseLineHeight -20px; + padding: 8px 0; + font-size: 11px; + + a { + color: #666666; + padding: 0 20px 0 10px; + + [class^="icon-"], + [class*=" icon-"] { + margin: 0 5px; + opacity: .6; + } + } + + a:hover { + color: #333333; + text-decoration: none; + + [class^="icon-"], + [class*=" icon-"] { + opacity: 1; + } + } +} + diff --git a/public/css/biomed/buttons.less b/public/css/biomed/buttons.less new file mode 100644 index 0000000..d078171 --- /dev/null +++ b/public/css/biomed/buttons.less @@ -0,0 +1,7 @@ + +.btn { + background-image: none; + text-shadow: none; + border: 0; + padding: 5px 13px; +} diff --git a/public/css/biomed/datepicker.less b/public/css/biomed/datepicker.less new file mode 100644 index 0000000..61fc5bd --- /dev/null +++ b/public/css/biomed/datepicker.less @@ -0,0 +1,292 @@ +.datepicker { + padding: 4px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + direction: ltr; + /*.dow { + border-top: 1px solid #ddd !important; + }*/ + +} +.datepicker-inline { + width: 220px; +} +.datepicker.datepicker-rtl { + direction: rtl; +} +.datepicker.datepicker-rtl table tr td span { + float: right; +} +.datepicker-dropdown { + top: 0; + left: 0; +} +.datepicker-dropdown:before { + content: ''; + display: inline-block; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-bottom-color: rgba(0, 0, 0, 0.2); + position: absolute; + top: -7px; + left: 6px; +} +.datepicker-dropdown:after { + content: ''; + display: inline-block; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid #ffffff; + position: absolute; + top: -6px; + left: 7px; +} +.datepicker > div { + display: none; +} +.datepicker.days div.datepicker-days { + display: block; +} +.datepicker.months div.datepicker-months { + display: block; +} +.datepicker.years div.datepicker-years { + display: block; +} +.datepicker table { + margin: 0; +} +.datepicker td, +.datepicker th { + text-align: center; + width: 20px; + height: 20px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + border: none; +} +.table-striped .datepicker table tr td, +.table-striped .datepicker table tr th { + background-color: transparent; +} +.datepicker table tr td.day:hover { + background: #eeeeee; + cursor: pointer; +} +.datepicker table tr td.old, +.datepicker table tr td.new { + color: #999999; +} +.datepicker table tr td.disabled, +.datepicker table tr td.disabled:hover { + background: none; + color: #999999; + cursor: default; +} +.datepicker table tr td.today, +.datepicker table tr td.today:hover, +.datepicker table tr td.today.disabled, +.datepicker table tr td.today.disabled:hover { + background-color: #fde19a; + background-image: -moz-linear-gradient(top, #fdd49a, #fdf59a); + background-image: -ms-linear-gradient(top, #fdd49a, #fdf59a); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fdd49a), to(#fdf59a)); + background-image: -webkit-linear-gradient(top, #fdd49a, #fdf59a); + background-image: -o-linear-gradient(top, #fdd49a, #fdf59a); + background-image: linear-gradient(top, #fdd49a, #fdf59a); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0); + border-color: #fdf59a #fdf59a #fbed50; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + color: #000 !important; +} +.datepicker table tr td.today:hover, +.datepicker table tr td.today:hover:hover, +.datepicker table tr td.today.disabled:hover, +.datepicker table tr td.today.disabled:hover:hover, +.datepicker table tr td.today:active, +.datepicker table tr td.today:hover:active, +.datepicker table tr td.today.disabled:active, +.datepicker table tr td.today.disabled:hover:active, +.datepicker table tr td.today.active, +.datepicker table tr td.today:hover.active, +.datepicker table tr td.today.disabled.active, +.datepicker table tr td.today.disabled:hover.active, +.datepicker table tr td.today.disabled, +.datepicker table tr td.today:hover.disabled, +.datepicker table tr td.today.disabled.disabled, +.datepicker table tr td.today.disabled:hover.disabled, +.datepicker table tr td.today[disabled], +.datepicker table tr td.today:hover[disabled], +.datepicker table tr td.today.disabled[disabled], +.datepicker table tr td.today.disabled:hover[disabled] { + background-color: #fdf59a; +} +.datepicker table tr td.today:active, +.datepicker table tr td.today:hover:active, +.datepicker table tr td.today.disabled:active, +.datepicker table tr td.today.disabled:hover:active, +.datepicker table tr td.today.active, +.datepicker table tr td.today:hover.active, +.datepicker table tr td.today.disabled.active, +.datepicker table tr td.today.disabled:hover.active { + background-color: #fbf069 \9; +} +.datepicker table tr td.active, +.datepicker table tr td.active:hover, +.datepicker table tr td.active.disabled, +.datepicker table tr td.active.disabled:hover { + background-color: #006dcc; + background-image: -moz-linear-gradient(top, #0088cc, #0044cc); + background-image: -ms-linear-gradient(top, #0088cc, #0044cc); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); + background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); + background-image: -o-linear-gradient(top, #0088cc, #0044cc); + background-image: linear-gradient(top, #0088cc, #0044cc); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0); + border-color: #0044cc #0044cc #002a80; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + color: #fff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.datepicker table tr td.active:hover, +.datepicker table tr td.active:hover:hover, +.datepicker table tr td.active.disabled:hover, +.datepicker table tr td.active.disabled:hover:hover, +.datepicker table tr td.active:active, +.datepicker table tr td.active:hover:active, +.datepicker table tr td.active.disabled:active, +.datepicker table tr td.active.disabled:hover:active, +.datepicker table tr td.active.active, +.datepicker table tr td.active:hover.active, +.datepicker table tr td.active.disabled.active, +.datepicker table tr td.active.disabled:hover.active, +.datepicker table tr td.active.disabled, +.datepicker table tr td.active:hover.disabled, +.datepicker table tr td.active.disabled.disabled, +.datepicker table tr td.active.disabled:hover.disabled, +.datepicker table tr td.active[disabled], +.datepicker table tr td.active:hover[disabled], +.datepicker table tr td.active.disabled[disabled], +.datepicker table tr td.active.disabled:hover[disabled] { + background-color: #0044cc; +} +.datepicker table tr td.active:active, +.datepicker table tr td.active:hover:active, +.datepicker table tr td.active.disabled:active, +.datepicker table tr td.active.disabled:hover:active, +.datepicker table tr td.active.active, +.datepicker table tr td.active:hover.active, +.datepicker table tr td.active.disabled.active, +.datepicker table tr td.active.disabled:hover.active { + background-color: #003399 \9; +} +.datepicker table tr td span { + display: block; + width: 23%; + height: 54px; + line-height: 54px; + float: left; + margin: 1%; + cursor: pointer; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.datepicker table tr td span:hover { + background: #eeeeee; +} +.datepicker table tr td span.disabled, +.datepicker table tr td span.disabled:hover { + background: none; + color: #999999; + cursor: default; +} +.datepicker table tr td span.active, +.datepicker table tr td span.active:hover, +.datepicker table tr td span.active.disabled, +.datepicker table tr td span.active.disabled:hover { + background-color: #006dcc; + background-image: -moz-linear-gradient(top, #0088cc, #0044cc); + background-image: -ms-linear-gradient(top, #0088cc, #0044cc); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); + background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); + background-image: -o-linear-gradient(top, #0088cc, #0044cc); + background-image: linear-gradient(top, #0088cc, #0044cc); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0); + border-color: #0044cc #0044cc #002a80; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + color: #fff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.datepicker table tr td span.active:hover, +.datepicker table tr td span.active:hover:hover, +.datepicker table tr td span.active.disabled:hover, +.datepicker table tr td span.active.disabled:hover:hover, +.datepicker table tr td span.active:active, +.datepicker table tr td span.active:hover:active, +.datepicker table tr td span.active.disabled:active, +.datepicker table tr td span.active.disabled:hover:active, +.datepicker table tr td span.active.active, +.datepicker table tr td span.active:hover.active, +.datepicker table tr td span.active.disabled.active, +.datepicker table tr td span.active.disabled:hover.active, +.datepicker table tr td span.active.disabled, +.datepicker table tr td span.active:hover.disabled, +.datepicker table tr td span.active.disabled.disabled, +.datepicker table tr td span.active.disabled:hover.disabled, +.datepicker table tr td span.active[disabled], +.datepicker table tr td span.active:hover[disabled], +.datepicker table tr td span.active.disabled[disabled], +.datepicker table tr td span.active.disabled:hover[disabled] { + background-color: #0044cc; +} +.datepicker table tr td span.active:active, +.datepicker table tr td span.active:hover:active, +.datepicker table tr td span.active.disabled:active, +.datepicker table tr td span.active.disabled:hover:active, +.datepicker table tr td span.active.active, +.datepicker table tr td span.active:hover.active, +.datepicker table tr td span.active.disabled.active, +.datepicker table tr td span.active.disabled:hover.active { + background-color: #003399 \9; +} +.datepicker table tr td span.old { + color: #999999; +} +.datepicker th.switch { + width: 145px; +} +.datepicker thead tr:first-child th, +.datepicker tfoot tr:first-child th { + cursor: pointer; +} +.datepicker thead tr:first-child th:hover, +.datepicker tfoot tr:first-child th:hover { + background: #eeeeee; +} +.datepicker .cw { + font-size: 10px; + width: 12px; + padding: 0 2px 0 5px; + vertical-align: middle; +} +.datepicker thead tr:first-child th.cw { + cursor: default; + background-color: transparent; +} +.input-append.date .add-on i, +.input-prepend.date .add-on i { + display: block; + cursor: pointer; + width: 16px; + height: 16px; +} \ No newline at end of file diff --git a/public/css/biomed/mixins.less b/public/css/biomed/mixins.less new file mode 100644 index 0000000..ec41b47 --- /dev/null +++ b/public/css/biomed/mixins.less @@ -0,0 +1,23 @@ +// Toolbelt vertical align +// ------------------------- +// Vertically center elements in the toolbelt. +// Example: an element has a height of 30px, so write out `.toolbeltVerticalAlign(30px);` to calculate the appropriate top margin. +.toolbeltVerticalAlign(@elementHeight) { + margin-top: (@toolbeltHeight - @elementHeight) / 2; +} + +// Button backgrounds +// ------------------ +.buttonBackground(@startColor, @endColor, @textColor: #fff, @textShadow: 0 -1px 0 rgba(0,0,0,.25)) { + background-color: @startColor; + color: @textColor; + + &:hover, &:focus, &:active, &.active, &.disabled, &[disabled] { + background-color: lighten(@startColor, 10%); + } + + &:active, + &.active { + background-color: darken(@startColor, 10%); + } +} \ No newline at end of file diff --git a/public/css/biomed/navbar.less b/public/css/biomed/navbar.less new file mode 100644 index 0000000..3d1b479 --- /dev/null +++ b/public/css/biomed/navbar.less @@ -0,0 +1,78 @@ + +.navbar { + margin-bottom: 0; +} + +.navbar-inner { + background: @navbarBackground; + border: none; + padding: 0; +} + +.navbar .nav.pull-right > li { + border-left: 1px solid #363e48; +} + +.navbar .nav > li > a { + text-shadow: none; + font-size: 11px; + font-weight: bold; +} + +// Active nav items +.navbar .nav > .active > a, +.navbar .nav > .active > a:hover, +.navbar .nav > .active > a:focus { + .box-shadow(none); +} + +.navbar .brand { + background: url('/img/logo.svg') 0 0 no-repeat; + background-size: contain; + + height: 50px; + width: 316.1290322580645px; + padding: 0; + margin: 0; + + line-height: 600px; + overflow: hidden; +} + +.navbar-secondary { + + .navbar-inner { + background-color: @navbarSecondaryBackground; + min-height: auto; + } + + .nav > li { + border-right: 1px solid #41bedd; + } + + .nav > li > a { + padding: 10px; + + color: @navbarSecondaryLinkColor; + &:hover, + &:focus { + color: @navbarSecondaryLinkColorHover; + } + } + + // Hover/focus + .nav > li > a:focus, + .nav > li > a:hover { + background-color: @navbarSecondaryLinkBackgroundHover; + color: @navbarSecondaryLinkColorHover; + text-decoration: none; + } + + // Active nav items + .nav > .active > a, + .nav > .active > a:hover, + .nav > .active > a:focus { + color: @navbarSecondaryLinkColorActive; + background-color: @navbarSecondaryLinkBackgroundActive; + } +} \ No newline at end of file diff --git a/public/css/biomed/scaffolding.less b/public/css/biomed/scaffolding.less new file mode 100644 index 0000000..0daf671 --- /dev/null +++ b/public/css/biomed/scaffolding.less @@ -0,0 +1,118 @@ + +body { + background-image: url('/img/bodyBg.png'); +} + +::-webkit-scrollbar { + height: 8px; + overflow: visible; + width: 8px; + background: #fff; +} +::-webkit-scrollbar-button { + display: none; + height:0; + width: 0; +} +::-webkit-scrollbar-track { + -moz-background-clip: border; + -webkit-background-clip: border; + background-clip: border-box; + border-width: 0 0 0 4px; + border: solid transparent; +} +::-webkit-scrollbar-track:hover { + background-color:rgba(0,0,0,.05); + -moz-box-shadow: inset 1px 0 0 rgba(0,0,0,.1); + -webkit-box-shadow: inset 1px 0 0 rgba(0,0,0,.1); + box-shadow: inset 1px 0 0 rgba(0,0,0,.1); +} +::-webkit-scrollbar-track:active { + background-color:rgba(0,0,0,.05); + -moz-box-shadow: inset 1px 0 0 rgba(0,0,0,.14), inset -1px 0 0 rgba(0,0,0,.07); + -webkit-box-shadow: inset 1px 0 0 rgba(0,0,0,.14), inset -1px 0 0 rgba(0,0,0,.07); + box-shadow: inset 1px 0 0 rgba(0,0,0,.14), inset -1px 0 0 rgba(0,0,0,.07); +} +::-webkit-scrollbar-track:horizontal { + border-width: 4px 0 0; +} +::-webkit-scrollbar-track:horizontal:hover { + -moz-box-shadow: inset 0 1px 0 rgba(0,0,0,.1); + -webkit-box-shadow: inset 0 1px 0 rgba(0,0,0,.1); + box-shadow: inset 0 1px 0 rgba(0,0,0,.1); +} +::-webkit-scrollbar-track:horizontal:active { + -moz-box-shadow: inset 0 1px 0 rgba(0,0,0,.14), inset 0 -1px 0 rgba(0,0,0,.07); + -webkit-box-shadow: inset 0 1px 0 rgba(0,0,0,.14), inset 0 -1px 0 rgba(0,0,0,.07); + box-shadow: inset 0 1px 0 rgba(0,0,0,.14), inset 0 -1px 0 rgba(0,0,0,.07); +} +::-webkit-scrollbar-thumb { + -moz-background-clip: border; + -webkit-background-clip: border; + background-clip: border-box; + background-color: rgba(0,0,0,.2); + /*border-width: 1px 1px 1px 6px; + border: solid transparent;*/ + -moz-box-shadow: inset 1px 1px 0 rgba(0,0,0,.1),inset 0 -1px 0 rgba(0,0,0,.07); + -webkit-box-shadow: inset 1px 1px 0 rgba(0,0,0,.1),inset 0 -1px 0 rgba(0,0,0,.07); + box-shadow: inset 1px 1px 0 rgba(0,0,0,.1),inset 0 -1px 0 rgba(0,0,0,.07); + min-height: 28px; + padding: 100px 0 0; +} +::-webkit-scrollbar-thumb:hover { + background-color:rgba(0,0,0,.4); + -moz-box-shadow: inset 1px 1px 1px rgba(0,0,0,.25); + -webkit-box-shadow: inset 1px 1px 1px rgba(0,0,0,.25); + box-shadow: inset 1px 1px 1px rgba(0,0,0,.25); +} +::-webkit-scrollbar-thumb:active { + background-color:rgba(0,0,0,0.5); + -moz-box-shadow: inset 1px 1px 3px rgba(0,0,0,0.35); + -webkit-box-shadow: inset 1px 1px 3px rgba(0,0,0,0.35); + box-shadow: inset 1px 1px 3px rgba(0,0,0,0.35); +} +::-webkit-scrollbar-thumb:horizontal { + border-width: 6px 1px 1px; + -moz-box-shadow: inset 1px 1px 0 rgba(0,0,0,.1), inset -1px 0 0 rgba(0,0,0,.07); + -webkit-box-shadow: inset 1px 1px 0 rgba(0,0,0,.1), inset -1px 0 0 rgba(0,0,0,.07); + box-shadow: inset 1px 1px 0 rgba(0,0,0,.1), inset -1px 0 0 rgba(0,0,0,.07); + padding: 0 0 0 100px; +} +::-webkit-scrollbar-corner { + background: transparent; +} + +body::-webkit-scrollbar-track-piece { + -moz-background-clip: border; + -webkit-background-clip: border; + background-clip: border-box; + background-color: #f5f5f5; + /*border-width: 0 0 0 3px; + border: solid #fff;*/ + -moz-box-shadow: inset 1px 0 0 rgba(0,0,0,.14), inset -1px 0 0 rgba(0,0,0,.07); + -webkit-box-shadow: inset 1px 0 0 rgba(0,0,0,.14), inset -1px 0 0 rgba(0,0,0,.07); + box-shadow: inset 1px 0 0 rgba(0,0,0,.14), inset -1px 0 0 rgba(0,0,0,.07); +} +body::-webkit-scrollbar-track-piece:horizontal { + border-width:3px 0 0; + -moz-box-shadow: inset 0 1px 0 rgba(0,0,0,.14), inset 0 -1px 0 rgba(0,0,0,.07); + -webkit-box-shadow: inset 0 1px 0 rgba(0,0,0,.14), inset 0 -1px 0 rgba(0,0,0,.07); + box-shadow: inset 0 1px 0 rgba(0,0,0,.14), inset 0 -1px 0 rgba(0,0,0,.07); +} +body::-webkit-scrollbar-thumb { + border-width: 1px 1px 1px 5px; +} +body::-webkit-scrollbar-thumb:horizontal { + border-width: 5px 1px 1px; +} +body::-webkit-scrollbar-corner { + -moz-background-clip: border; + -webkit-background-clip: border; + background-clip: border-box; + background-color: #f5f5f5; + border-width: 3px 0 0 3px; + border: solid #fff; + -moz-box-shadow: inset 1px 1px 0 rgba(0,0,0,.14); + -webkit-box-shadow: inset 1px 1px 0 rgba(0,0,0,.14); + box-shadow: inset 1px 1px 0 rgba(0,0,0,.14); +} \ No newline at end of file diff --git a/public/css/biomed/select2.less b/public/css/biomed/select2.less new file mode 100644 index 0000000..ae6e6a9 --- /dev/null +++ b/public/css/biomed/select2.less @@ -0,0 +1,523 @@ +/* +Version: 3.2 Timestamp: Mon Sep 10 10:38:04 PDT 2012 +*/ +.select2-container { + position: relative; + display: inline-block; + /* inline-block for ie7 */ + zoom: 1; + *display: inline; + vertical-align: top; +} + +.select2-container, +.select2-drop, +.select2-search, +.select2-search input{ + /* + Force border-box so that % widths fit the parent + container without overlap because of margin/padding. + + More Info : http://www.quirksmode.org/css/box.html + */ + -moz-box-sizing: border-box; /* firefox */ + -ms-box-sizing: border-box; /* ie */ + -webkit-box-sizing: border-box; /* webkit */ + -khtml-box-sizing: border-box; /* konqueror */ + box-sizing: border-box; /* css3 */ +} + +.select2-container .select2-choice { + background-color: #fff; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.5, white)); + background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 50%); + background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 50%); + background-image: -o-linear-gradient(bottom, #eeeeee 0%, #ffffff 50%); + background-image: -ms-linear-gradient(top, #eeeeee 0%, #ffffff 50%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#ffffff', GradientType = 0); + background-image: linear-gradient(top, #eeeeee 0%, #ffffff 50%); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #aaa; + display: block; + overflow: hidden; + white-space: nowrap; + position: relative; + height: 26px; + line-height: 26px; + padding: 0 0 0 8px; + color: #444; + text-decoration: none; +} + +.select2-container.select2-drop-above .select2-choice +{ + border-bottom-color: #aaa; + -webkit-border-radius:0px 0px 4px 4px; + -moz-border-radius:0px 0px 4px 4px; + border-radius:0px 0px 4px 4px; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.9, white)); + background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 90%); + background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 90%); + background-image: -o-linear-gradient(bottom, #eeeeee 0%, white 90%); + background-image: -ms-linear-gradient(top, #eeeeee 0%,#ffffff 90%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff',GradientType=0 ); + background-image: linear-gradient(top, #eeeeee 0%,#ffffff 90%); +} + +.select2-container .select2-choice span { + margin-right: 26px; + display: block; + overflow: hidden; + white-space: nowrap; + -o-text-overflow: ellipsis; + -ms-text-overflow: ellipsis; + text-overflow: ellipsis; +} + +.select2-container .select2-choice abbr { + display: block; + position: absolute; + right: 26px; + top: 8px; + width: 12px; + height: 12px; + font-size: 1px; + background: url('/img/select2.png') right top no-repeat; + cursor: pointer; + text-decoration: none; + border:0; + outline: 0; +} +.select2-container .select2-choice abbr:hover { + background-position: right -11px; + cursor: pointer; +} + +.select2-drop { + background: #fff; + color: #000; + border: 1px solid #aaa; + border-top: 0; + position: absolute; + top: 100%; + -webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15); + -moz-box-shadow: 0 4px 5px rgba(0, 0, 0, .15); + -o-box-shadow: 0 4px 5px rgba(0, 0, 0, .15); + box-shadow: 0 4px 5px rgba(0, 0, 0, .15); + z-index: 9999; + width:100%; + margin-top:-1px; + + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} + +.select2-drop.select2-drop-above { + -webkit-border-radius: 4px 4px 0px 0px; + -moz-border-radius: 4px 4px 0px 0px; + border-radius: 4px 4px 0px 0px; + margin-top:1px; + border-top: 1px solid #aaa; + border-bottom: 0; + + -webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); + -moz-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); + -o-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); + box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); +} + +.select2-container .select2-choice div { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box; + background: #ccc; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee)); + background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%); + background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%); + background-image: -o-linear-gradient(bottom, #ccc 0%, #eee 60%); + background-image: -ms-linear-gradient(top, #cccccc 0%, #eeeeee 60%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#cccccc', endColorstr = '#eeeeee', GradientType = 0); + background-image: linear-gradient(top, #cccccc 0%, #eeeeee 60%); + border-left: 1px solid #aaa; + position: absolute; + right: 0; + top: 0; + display: block; + height: 100%; + width: 18px; +} + +.select2-container .select2-choice div b { + background: url('/img/select2.png') no-repeat 0 1px; + display: block; + width: 100%; + height: 100%; +} + +.select2-search { + display: inline-block; + white-space: nowrap; + z-index: 10000; + min-height: 26px; + width: 100%; + margin: 0; + padding-left: 4px; + padding-right: 4px; +} + +.select2-search-hidden { + display: block; + position: absolute; + left: -10000px; +} + +.select2-search input { + background: #fff url('/img/select2.png') no-repeat 100% -22px; background: url('/img/select2.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee)); + background: url('/img/select2.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('/img/select2.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('/img/select2.png') no-repeat 100% -22px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%); + background: url('/img/select2.png') no-repeat 100% -22px, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%); + background: url('/img/select2.png') no-repeat 100% -22px, linear-gradient(top, #ffffff 85%, #eeeeee 99%); + padding: 4px 20px 4px 5px; + outline: 0; + border: 1px solid #aaa; + font-family: sans-serif; + font-size: 1em; + width:100%; + margin:0; + height:auto !important; + min-height: 26px; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + border-radius: 0; + -moz-border-radius: 0; + -webkit-border-radius: 0; +} + +.select2-drop.select2-drop-above .select2-search input +{ + margin-top:4px; +} + +.select2-search input.select2-active { + background: #fff url('spinner.gif') no-repeat 100%; + background: url('spinner.gif') no-repeat 100%, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee)); + background: url('spinner.gif') no-repeat 100%, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('spinner.gif') no-repeat 100%, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('spinner.gif') no-repeat 100%, -o-linear-gradient(bottom, white 85%, #eeeeee 99%); + background: url('spinner.gif') no-repeat 100%, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%); + background: url('spinner.gif') no-repeat 100%, linear-gradient(top, #ffffff 85%, #eeeeee 99%); +} + + +.select2-container-active .select2-choice, +.select2-container-active .select2-choices { + -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3); + -moz-box-shadow : 0 0 5px rgba(0,0,0,.3); + -o-box-shadow : 0 0 5px rgba(0,0,0,.3); + box-shadow : 0 0 5px rgba(0,0,0,.3); + border: 1px solid #5897fb; + outline: none; +} + +.select2-dropdown-open .select2-choice { + border: 1px solid #aaa; + border-bottom-color: transparent; + -webkit-box-shadow: 0 1px 0 #fff inset; + -moz-box-shadow : 0 1px 0 #fff inset; + -o-box-shadow : 0 1px 0 #fff inset; + box-shadow : 0 1px 0 #fff inset; + background-color: #eee; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, white), color-stop(0.5, #eeeeee)); + background-image: -webkit-linear-gradient(center bottom, white 0%, #eeeeee 50%); + background-image: -moz-linear-gradient(center bottom, white 0%, #eeeeee 50%); + background-image: -o-linear-gradient(bottom, white 0%, #eeeeee 50%); + background-image: -ms-linear-gradient(top, #ffffff 0%,#eeeeee 50%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 ); + background-image: linear-gradient(top, #ffffff 0%,#eeeeee 50%); + -webkit-border-bottom-left-radius : 0; + -webkit-border-bottom-right-radius: 0; + -moz-border-radius-bottomleft : 0; + -moz-border-radius-bottomright: 0; + border-bottom-left-radius : 0; + border-bottom-right-radius: 0; +} + +.select2-dropdown-open .select2-choice div { + background: transparent; + border-left: none; +} +.select2-dropdown-open .select2-choice div b { + background-position: -18px 1px; +} + +/* results */ +.select2-results { + margin: 4px 4px 4px 0; + padding: 0 0 0 4px; + position: relative; + overflow-x: hidden; + overflow-y: auto; + max-height: 200px; +} + +.select2-results ul.select2-result-sub { + margin: 0 0 0 0; +} + +.select2-results ul.select2-result-sub > li .select2-result-label { padding-left: 20px } +.select2-results ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 40px } +.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 60px } +.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 80px } +.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 100px } +.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 110px } +.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 120px } + +.select2-results li { + list-style: none; + display: list-item; +} + +.select2-results li.select2-result-with-children > .select2-result-label { + font-weight: bold; +} + +.select2-results .select2-result-label { + padding: 3px 7px 4px; + margin: 0; + cursor: pointer; +} + +.select2-results .select2-highlighted { + background: #3875d7; + color: #fff; +} +.select2-results li em { + background: #feffde; + font-style: normal; +} +.select2-results .select2-highlighted em { + background: transparent; +} +.select2-results .select2-no-results, +.select2-results .select2-searching, +.select2-results .select2-selection-limit { + background: #f4f4f4; + display: list-item; +} + +/* +disabled look for already selected choices in the results dropdown +.select2-results .select2-disabled.select2-highlighted { + color: #666; + background: #f4f4f4; + display: list-item; + cursor: default; +} +.select2-results .select2-disabled { + background: #f4f4f4; + display: list-item; + cursor: default; +} +*/ +.select2-results .select2-disabled { + display: none; +} + +.select2-more-results.select2-active { + background: #f4f4f4 url('spinner.gif') no-repeat 100%; +} + +.select2-more-results { + background: #f4f4f4; + display: list-item; +} + +/* disabled styles */ + +.select2-container.select2-container-disabled .select2-choice { + background-color: #f4f4f4; + background-image: none; + border: 1px solid #ddd; + cursor: default; +} + +.select2-container.select2-container-disabled .select2-choice div { + background-color: #f4f4f4; + background-image: none; + border-left: 0; +} + + +/* multiselect */ + +.select2-container-multi .select2-choices { + background-color: #fff; + background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); + background-image: -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%); + background-image: -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%); + background-image: -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%); + background-image: -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%); + background-image: linear-gradient(top, #eeeeee 1%, #ffffff 15%); + border: 1px solid #aaa; + margin: 0; + padding: 0; + cursor: text; + overflow: hidden; + height: auto !important; + height: 1%; + position: relative; +} + +.select2-container-multi .select2-choices { + min-height: 26px; +} + +.select2-container-multi.select2-container-active .select2-choices { + -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3); + -moz-box-shadow : 0 0 5px rgba(0,0,0,.3); + -o-box-shadow : 0 0 5px rgba(0,0,0,.3); + box-shadow : 0 0 5px rgba(0,0,0,.3); + border: 1px solid #5897fb; + outline: none; +} +.select2-container-multi .select2-choices li { + float: left; + list-style: none; +} +.select2-container-multi .select2-choices .select2-search-field { + white-space: nowrap; + margin: 0; + padding: 0; +} + +.select2-container-multi .select2-choices .select2-search-field input { + color: #666; + background: transparent !important; + font-family: sans-serif; + font-size: 100%; + height: 15px; + padding: 5px; + margin: 1px 0; + outline: 0; + border: 0; + -webkit-box-shadow: none; + -moz-box-shadow : none; + -o-box-shadow : none; + box-shadow : none; +} + +.select2-container-multi .select2-choices .select2-search-field input.select2-active { + background: #fff url('spinner.gif') no-repeat 100% !important; +} + +.select2-default { + color: #999 !important; +} + +.select2-container-multi .select2-choices .select2-search-choice { + -webkit-border-radius: 3px; + -moz-border-radius : 3px; + border-radius : 3px; + -moz-background-clip : padding; + -webkit-background-clip: padding-box; + background-clip : padding-box; + background-color: #e4e4e4; + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#eeeeee', GradientType=0 ); + background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee)); + background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: -ms-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + -webkit-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); + -moz-box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); + box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); + color: #333; + border: 1px solid #aaaaaa; + line-height: 13px; + padding: 3px 5px 3px 18px; + margin: 3px 0 3px 5px; + position: relative; + cursor: default; +} +.select2-container-multi .select2-choices .select2-search-choice span { + cursor: default; +} +.select2-container-multi .select2-choices .select2-search-choice-focus { + background: #d4d4d4; +} + +.select2-search-choice-close { + display: block; + position: absolute; + right: 3px; + top: 4px; + width: 12px; + height: 13px; + font-size: 1px; + background: url('/img/select2.png') right top no-repeat; + outline: none; +} + +.select2-container-multi .select2-search-choice-close { + left: 3px; +} + + +.select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close:hover { + background-position: right -11px; +} +.select2-container-multi .select2-choices .select2-search-choice-focus .select2-search-choice-close { + background-position: right -11px; +} + +/* disabled styles */ + +.select2-container-multi.select2-container-disabled .select2-choices{ + background-color: #f4f4f4; + background-image: none; + border: 1px solid #ddd; + cursor: default; +} + +.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice { + background-image: none; + background-color: #f4f4f4; + border: 1px solid #ddd; + padding: 3px 5px 3px 5px; +} + +.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close { + display: none; +} +/* end multiselect */ + +.select2-result-selectable .select2-match, +.select2-result-unselectable .select2-result-selectable .select2-match { text-decoration: underline; } +.select2-result-unselectable .select2-match { text-decoration: none; } + +.select2-offscreen { position: absolute; left: -10000px; } + +/* Retina-ize icons */ + +@media only screen and (-webkit-min-device-pixel-ratio: 1.5) { + .select2-search input, .select2-search-choice-close, .select2-container .select2-choice abbr, .select2-container .select2-choice div b { + background-image: url('/ing/select2x2.png') !important; + background-repeat: no-repeat !important; + background-size: 60px 40px !important; + } + .select2-search input { + background-position: 100% -21px !important; + } +} diff --git a/public/css/biomed/sprites.less b/public/css/biomed/sprites.less new file mode 100644 index 0000000..ae7f598 --- /dev/null +++ b/public/css/biomed/sprites.less @@ -0,0 +1,6 @@ +/* White icons with optional class, or on hover/focus/active states of certain elements */ +.icon-white, +.navbar .nav a > [class^="icon-"], +.navbar .nav a > [class*=" icon-"] { + background-image: url("@{iconWhiteSpritePath}"); +} \ No newline at end of file diff --git a/public/css/biomed/tables.less b/public/css/biomed/tables.less new file mode 100644 index 0000000..7915c60 --- /dev/null +++ b/public/css/biomed/tables.less @@ -0,0 +1,30 @@ + +.table { + th { + background-color: #efefef; + } + + caption + thead tr:first-child th, + colgroup + thead tr:first-child th, + thead:first-child tr:first-child th { + border-top: 1px solid @tableBorder; + } + + th:first-child { + border-left: 1px solid @tableBorder; + } + + th:last-child { + border-right: 1px solid @tableBorder; + } + + td.table-loading { + text-align: center; + padding: 30px; + } + + td.table-message { + text-align: center; + font-style: italic; + } +} \ No newline at end of file diff --git a/public/css/biomed/toolbelt.less b/public/css/biomed/toolbelt.less new file mode 100644 index 0000000..9f0e051 --- /dev/null +++ b/public/css/biomed/toolbelt.less @@ -0,0 +1,61 @@ +.toolbelt { + overflow: visible; + margin-bottom: @baseLineHeight / 2; + min-height: @toolbeltHeight; +} + +.toolbelt .btn, +.toolbelt .btn-group { + .toolbeltVerticalAlign(30px); // Vertically center in navbar +} +.toolbelt .btn-group .btn, +.toolbelt .input-prepend .btn, +.toolbelt .input-append .btn, +.toolbelt .input-prepend .btn-group, +.toolbelt .input-append .btn-group { + margin-top: 0; // then undo the margin here so we don't accidentally double it +} + +// Plain text in toolbelt +// ------------------------- +.toolbelt-text { + margin-bottom: 0; + padding: 0 5px 0 10px; + line-height: @toolbeltHeight; + color: @toolbeltText; +} + + +.toolbelt .pull-right { + float: right; +} + +// Navbar forms +// ------------------------- +.toolbelt { + input, + select, + .radio, + .checkbox { + .toolbeltVerticalAlign(30px); // Vertically center in navbar + } + input, + select, + .btn { + display: inline-block; + margin-bottom: 0; + } + input[type="image"], + input[type="checkbox"], + input[type="radio"] { + margin-top: 3px; + } + .input-append, + .input-prepend { + margin-bottom: 0px; + white-space: nowrap; // preven two items from separating within a .navbar-form that has .pull-left + input { + margin-top: 0; // remove the margin on top since it's on the parent + } + } +} \ No newline at end of file diff --git a/public/css/biomed/type.less b/public/css/biomed/type.less new file mode 100644 index 0000000..242ac7c --- /dev/null +++ b/public/css/biomed/type.less @@ -0,0 +1,14 @@ +@font-face { + font-family: "Vegur-Bold"; + src: url("/fonts/Vegur-Bold.otf"); +} + +@font-face { + font-family: "Vegur-Light"; + src: url("/fonts/Vegur-Light.otf"); +} + +@font-face { + font-family: "Vegur-Regular"; + src: url("/fonts/Vegur-Regular.otf"); +} diff --git a/public/css/biomed/variables.less b/public/css/biomed/variables.less new file mode 100644 index 0000000..1089388 --- /dev/null +++ b/public/css/biomed/variables.less @@ -0,0 +1,46 @@ +@baseBorderRadius: 0; + +// Typography +// ------------------------- +@headingsFontFamily: "Vegur-Light", Helvetica, Arial, sans-serif; +@headingsFontWeight: normal; + + +// Navbar +// ------------------------- +@navbarHeight: 50px; +@navbarBackground: #2e363f; +@navbarLinkColor: #999999; +@navbarLinkColorHover: #ffffff; +@navbarLinkLinkColorActive: @navbarLinkColorHover; +@navbarLinkBackgroundHover: #232a32; + +@navbarSecondaryBackground: #49cced; +@navbarSecondaryText: #ffffff; + +@navbarSecondaryText: #ffffff; +@navbarSecondaryLinkColor: #ffffff; +@navbarSecondaryLinkColorHover: @white; +@navbarSecondaryLinkColorActive: @navbarSecondaryLinkColorHover; +@navbarSecondaryLinkBackgroundHover: #41bedd; +@navbarSecondaryLinkBackgroundActive: @navbarSecondaryLinkBackgroundHover; + +// Buttons +// ------------------------- +@btnBackground: #dadada; +@btnPrimaryBackground: #006dcc; +@btnInfoBackground: #49afcd; +@btnSuccessBackground: #5bb75b; +@btnWarningBackground: #faa732; +@btnDangerBackground: #da4f49; +@btnInverseBackground: #363636; + +// Toolbelt +// ------------------------- +@toolbeltHeight: 30px; +@toolbeltText: #999999; + +// Sprite icons path +// ------------------------- +@iconSpritePath: "/img/glyphicons-halflings.png"; +@iconWhiteSpritePath: "/img/glyphicons-halflings-white.png"; \ No newline at end of file diff --git a/public/css/biomed/widgets.less b/public/css/biomed/widgets.less new file mode 100644 index 0000000..d62400f --- /dev/null +++ b/public/css/biomed/widgets.less @@ -0,0 +1,269 @@ + +.biomed-table { + .table; + .table-striped; + .table-condensed; +} + + +#loginbox { + width: 32%; + margin-left: auto; + margin-right: auto; + background-color: #fff; + + form { + background: #fff; + } +} + +body.login-page { + background-color: #2E363F; + background-image: none; + margin-top: 10%; +} + +#loginbox .brand-container { + background-color: #49CCED; +} + +#loginbox .brand { + background: url('/img/logo.svg') 0 0 no-repeat; + background-size: contain; + + height: 50px; + width: 316.1290322580645px; + padding: 0; + margin: 0; + + line-height: 600px; + overflow: hidden; +} + +#loginbox .alert { + margin-bottom: 0; + white-space: pre; +} + +#loginbox form p { + margin: 20px +} + +.loader { + width: 32px; height: 32px; display: inline-block; + background: transparent url(/img/loader.gif) no-repeat; + -webkit-animation: 'animation' 1s steps(10, end) infinite; + -moz-animation: 'animation' 1s steps(10, end) infinite; +} + +@-webkit-keyframes animation { from { background-position: 0 0; } to { background-position: 100% 0; } } +@-moz-keyframes animation { from { background-position: 0 0; } to { background-position: 100% 0; } } + +header { + margin-bottom: 20px; + + h1 { + margin: 0; + line-height: 30px; + } + + .lead { + font-size: 11px; + line-height: 20px; + font-weight: 500; + color: #999999; + margin: 0; + } +} + +.form .form-section { + padding: 15px 0; + border-bottom: 1px solid #eee; + .makeRow; +} + +.form .form-section:first-child { + padding: 0 0 15px 0; +} + +.form .form-preview { +} + +.form .section-label { + .makeColumn(2); + .muted; +} + +.form .section-container { + .makeColumn(10); +} + +.form .form-editor { + .form-horizontal; + + background-color: #f9f9f9; + border: 1px solid #cdcdcd; + padding-top: 20px; + + .form-actions { + margin-bottom: 0; + } +} + +.form a.disabled { + color: #999; + + &:hover { + color: #999; + text-decoration: none; + } +} + +.frequency { + width: auto; + + .name { + text-align: right; + } + + th { + background: transparent; + border: 0 !important; + text-align: center; + } + + td { + text-align: center; + } + + a { + padding: 20px; + corsor: pointer; + } + + i { + opacity: 0.7; + } + + .false { + .icon { + .icon-remove; + } + } + + .true { + background-color: #DFF0D8; + + .icon { + .icon-ok; + } + } +} + + + +.axis path, +.axis line { + fill: none; + stroke: #000; + shape-rendering: crispEdges; +} + +.x.axis line { + stroke: lightgrey; + } + +.x.axis .minor { + stroke-opacity: .5; +} + +.highlighter line { + fill: none; + stroke: red; + stroke-opacity: .5; + shape-rendering: crispEdges; +} + +.highlighter rect { + fill: red; + fill-opacity: .1; +} + +.fade { + fill-opacity: .1; +} + +.axis text { + font-size: 10px; +} + + +.techpicker { + position: relative; + margin: 20px; + + .hour-labels { + position: relative; + margin-left: 150px; + height: @baseLineHeight; + font-size: 11px; + } + + .hour-labels .hour-label { + position: absolute; + text-align: left; + margin-right: 5px; + } + + .schedule { + position: relative; + } + + .hour-markers { + position: absolute; + margin-left: 150px; + top: 0; + right: 0; + bottom: 0; + left: 0; + border: 1px solid #aaaaaa; + } + + .hour-markers .marker { + position: absolute; + height: 100%; + border-right: 1px solid #dddddd; + } + + .hour-markers .half-hour-marker { + height: 100%; + width: 50%; + border-right: 1px dashed #dddddd; + } + + .tech { + padding: 1px 0; + .clearfix(); + } + + .tech-label { + float: left; + } + + .enteries { + margin-left: 150px; + position: relative; + } + + .entry { + position: absolute; + height: 20px; + box-sizing: border-box; + border-right: 2px solid white; + padding-left: 5px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + cursor: pointer; + } +} diff --git a/public/css/bootstrap/accordion.less b/public/css/bootstrap/accordion.less new file mode 100644 index 0000000..d63523b --- /dev/null +++ b/public/css/bootstrap/accordion.less @@ -0,0 +1,34 @@ +// +// Accordion +// -------------------------------------------------- + + +// Parent container +.accordion { + margin-bottom: @baseLineHeight; +} + +// Group == heading + body +.accordion-group { + margin-bottom: 2px; + border: 1px solid #e5e5e5; + .border-radius(@baseBorderRadius); +} +.accordion-heading { + border-bottom: 0; +} +.accordion-heading .accordion-toggle { + display: block; + padding: 8px 15px; +} + +// General toggle styles +.accordion-toggle { + cursor: pointer; +} + +// Inner needs the styles because you can't animate properly with any styles on the element +.accordion-inner { + padding: 9px 15px; + border-top: 1px solid #e5e5e5; +} diff --git a/public/css/bootstrap/alerts.less b/public/css/bootstrap/alerts.less new file mode 100644 index 0000000..0116b19 --- /dev/null +++ b/public/css/bootstrap/alerts.less @@ -0,0 +1,79 @@ +// +// Alerts +// -------------------------------------------------- + + +// Base styles +// ------------------------- + +.alert { + padding: 8px 35px 8px 14px; + margin-bottom: @baseLineHeight; + text-shadow: 0 1px 0 rgba(255,255,255,.5); + background-color: @warningBackground; + border: 1px solid @warningBorder; + .border-radius(@baseBorderRadius); +} +.alert, +.alert h4 { + // Specified for the h4 to prevent conflicts of changing @headingsColor + color: @warningText; +} +.alert h4 { + margin: 0; +} + +// Adjust close link position +.alert .close { + position: relative; + top: -2px; + right: -21px; + line-height: @baseLineHeight; +} + + +// Alternate styles +// ------------------------- + +.alert-success { + background-color: @successBackground; + border-color: @successBorder; + color: @successText; +} +.alert-success h4 { + color: @successText; +} +.alert-danger, +.alert-error { + background-color: @errorBackground; + border-color: @errorBorder; + color: @errorText; +} +.alert-danger h4, +.alert-error h4 { + color: @errorText; +} +.alert-info { + background-color: @infoBackground; + border-color: @infoBorder; + color: @infoText; +} +.alert-info h4 { + color: @infoText; +} + + +// Block alerts +// ------------------------- + +.alert-block { + padding-top: 14px; + padding-bottom: 14px; +} +.alert-block > p, +.alert-block > ul { + margin-bottom: 0; +} +.alert-block p + p { + margin-top: 5px; +} diff --git a/public/css/bootstrap/breadcrumbs.less b/public/css/bootstrap/breadcrumbs.less new file mode 100644 index 0000000..f753df6 --- /dev/null +++ b/public/css/bootstrap/breadcrumbs.less @@ -0,0 +1,24 @@ +// +// Breadcrumbs +// -------------------------------------------------- + + +.breadcrumb { + padding: 8px 15px; + margin: 0 0 @baseLineHeight; + list-style: none; + background-color: #f5f5f5; + .border-radius(@baseBorderRadius); + > li { + display: inline-block; + .ie7-inline-block(); + text-shadow: 0 1px 0 @white; + > .divider { + padding: 0 5px; + color: #ccc; + } + } + > .active { + color: @grayLight; + } +} diff --git a/public/css/bootstrap/button-groups.less b/public/css/bootstrap/button-groups.less new file mode 100644 index 0000000..55cdc60 --- /dev/null +++ b/public/css/bootstrap/button-groups.less @@ -0,0 +1,229 @@ +// +// Button groups +// -------------------------------------------------- + + +// Make the div behave like a button +.btn-group { + position: relative; + display: inline-block; + .ie7-inline-block(); + font-size: 0; // remove as part 1 of font-size inline-block hack + vertical-align: middle; // match .btn alignment given font-size hack above + white-space: nowrap; // prevent buttons from wrapping when in tight spaces (e.g., the table on the tests page) + .ie7-restore-left-whitespace(); +} + +// Space out series of button groups +.btn-group + .btn-group { + margin-left: 5px; +} + +// Optional: Group multiple button groups together for a toolbar +.btn-toolbar { + font-size: 0; // Hack to remove whitespace that results from using inline-block + margin-top: @baseLineHeight / 2; + margin-bottom: @baseLineHeight / 2; + > .btn + .btn, + > .btn-group + .btn, + > .btn + .btn-group { + margin-left: 5px; + } +} + +// Float them, remove border radius, then re-add to first and last elements +.btn-group > .btn { + position: relative; + .border-radius(0); +} +.btn-group > .btn + .btn { + margin-left: -1px; +} +.btn-group > .btn, +.btn-group > .dropdown-menu, +.btn-group > .popover { + font-size: @baseFontSize; // redeclare as part 2 of font-size inline-block hack +} + +// Reset fonts for other sizes +.btn-group > .btn-mini { + font-size: @fontSizeMini; +} +.btn-group > .btn-small { + font-size: @fontSizeSmall; +} +.btn-group > .btn-large { + font-size: @fontSizeLarge; +} + +// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match +.btn-group > .btn:first-child { + margin-left: 0; + .border-top-left-radius(@baseBorderRadius); + .border-bottom-left-radius(@baseBorderRadius); +} +// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it +.btn-group > .btn:last-child, +.btn-group > .dropdown-toggle { + .border-top-right-radius(@baseBorderRadius); + .border-bottom-right-radius(@baseBorderRadius); +} +// Reset corners for large buttons +.btn-group > .btn.large:first-child { + margin-left: 0; + .border-top-left-radius(@borderRadiusLarge); + .border-bottom-left-radius(@borderRadiusLarge); +} +.btn-group > .btn.large:last-child, +.btn-group > .large.dropdown-toggle { + .border-top-right-radius(@borderRadiusLarge); + .border-bottom-right-radius(@borderRadiusLarge); +} + +// On hover/focus/active, bring the proper btn to front +.btn-group > .btn:hover, +.btn-group > .btn:focus, +.btn-group > .btn:active, +.btn-group > .btn.active { + z-index: 2; +} + +// On active and open, don't show outline +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} + + + +// Split button dropdowns +// ---------------------- + +// Give the line between buttons some depth +.btn-group > .btn + .dropdown-toggle { + padding-left: 8px; + padding-right: 8px; + .box-shadow(~"inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)"); + *padding-top: 5px; + *padding-bottom: 5px; +} +.btn-group > .btn-mini + .dropdown-toggle { + padding-left: 5px; + padding-right: 5px; + *padding-top: 2px; + *padding-bottom: 2px; +} +.btn-group > .btn-small + .dropdown-toggle { + *padding-top: 5px; + *padding-bottom: 4px; +} +.btn-group > .btn-large + .dropdown-toggle { + padding-left: 12px; + padding-right: 12px; + *padding-top: 7px; + *padding-bottom: 7px; +} + +.btn-group.open { + + // The clickable button for toggling the menu + // Remove the gradient and set the same inset shadow as the :active state + .dropdown-toggle { + background-image: none; + .box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)"); + } + + // Keep the hover's background when dropdown is open + .btn.dropdown-toggle { + background-color: @btnBackgroundHighlight; + } + .btn-primary.dropdown-toggle { + background-color: @btnPrimaryBackgroundHighlight; + } + .btn-warning.dropdown-toggle { + background-color: @btnWarningBackgroundHighlight; + } + .btn-danger.dropdown-toggle { + background-color: @btnDangerBackgroundHighlight; + } + .btn-success.dropdown-toggle { + background-color: @btnSuccessBackgroundHighlight; + } + .btn-info.dropdown-toggle { + background-color: @btnInfoBackgroundHighlight; + } + .btn-inverse.dropdown-toggle { + background-color: @btnInverseBackgroundHighlight; + } +} + + +// Reposition the caret +.btn .caret { + margin-top: 8px; + margin-left: 0; +} +// Carets in other button sizes +.btn-large .caret { + margin-top: 6px; +} +.btn-large .caret { + border-left-width: 5px; + border-right-width: 5px; + border-top-width: 5px; +} +.btn-mini .caret, +.btn-small .caret { + margin-top: 8px; +} +// Upside down carets for .dropup +.dropup .btn-large .caret { + border-bottom-width: 5px; +} + + + +// Account for other colors +.btn-primary, +.btn-warning, +.btn-danger, +.btn-info, +.btn-success, +.btn-inverse { + .caret { + border-top-color: @white; + border-bottom-color: @white; + } +} + + + +// Vertical button groups +// ---------------------- + +.btn-group-vertical { + display: inline-block; // makes buttons only take up the width they need + .ie7-inline-block(); +} +.btn-group-vertical > .btn { + display: block; + float: none; + max-width: 100%; + .border-radius(0); +} +.btn-group-vertical > .btn + .btn { + margin-left: 0; + margin-top: -1px; +} +.btn-group-vertical > .btn:first-child { + .border-radius(@baseBorderRadius @baseBorderRadius 0 0); +} +.btn-group-vertical > .btn:last-child { + .border-radius(0 0 @baseBorderRadius @baseBorderRadius); +} +.btn-group-vertical > .btn-large:first-child { + .border-radius(@borderRadiusLarge @borderRadiusLarge 0 0); +} +.btn-group-vertical > .btn-large:last-child { + .border-radius(0 0 @borderRadiusLarge @borderRadiusLarge); +} diff --git a/public/css/bootstrap/buttons.less b/public/css/bootstrap/buttons.less new file mode 100644 index 0000000..4cd4d86 --- /dev/null +++ b/public/css/bootstrap/buttons.less @@ -0,0 +1,228 @@ +// +// Buttons +// -------------------------------------------------- + + +// Base styles +// -------------------------------------------------- + +// Core +.btn { + display: inline-block; + .ie7-inline-block(); + padding: 4px 12px; + margin-bottom: 0; // For input.btn + font-size: @baseFontSize; + line-height: @baseLineHeight; + text-align: center; + vertical-align: middle; + cursor: pointer; + .buttonBackground(@btnBackground, @btnBackgroundHighlight, @grayDark, 0 1px 1px rgba(255,255,255,.75)); + border: 1px solid @btnBorder; + *border: 0; // Remove the border to prevent IE7's black border on input:focus + border-bottom-color: darken(@btnBorder, 10%); + .border-radius(@baseBorderRadius); + .ie7-restore-left-whitespace(); // Give IE7 some love + .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)"); + + // Hover/focus state + &:hover, + &:focus { + color: @grayDark; + text-decoration: none; + background-position: 0 -15px; + + // transition is only when going to hover/focus, otherwise the background + // behind the gradient (there for IE<=9 fallback) gets mismatched + .transition(background-position .1s linear); + } + + // Focus state for keyboard and accessibility + &:focus { + .tab-focus(); + } + + // Active state + &.active, + &:active { + background-image: none; + outline: 0; + .box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)"); + } + + // Disabled state + &.disabled, + &[disabled] { + cursor: default; + background-image: none; + .opacity(65); + .box-shadow(none); + } + +} + + + +// Button Sizes +// -------------------------------------------------- + +// Large +.btn-large { + padding: @paddingLarge; + font-size: @fontSizeLarge; + .border-radius(@borderRadiusLarge); +} +.btn-large [class^="icon-"], +.btn-large [class*=" icon-"] { + margin-top: 4px; +} + +// Small +.btn-small { + padding: @paddingSmall; + font-size: @fontSizeSmall; + .border-radius(@borderRadiusSmall); +} +.btn-small [class^="icon-"], +.btn-small [class*=" icon-"] { + margin-top: 0; +} +.btn-mini [class^="icon-"], +.btn-mini [class*=" icon-"] { + margin-top: -1px; +} + +// Mini +.btn-mini { + padding: @paddingMini; + font-size: @fontSizeMini; + .border-radius(@borderRadiusSmall); +} + + +// Block button +// ------------------------- + +.btn-block { + display: block; + width: 100%; + padding-left: 0; + padding-right: 0; + .box-sizing(border-box); +} + +// Vertically space out multiple block buttons +.btn-block + .btn-block { + margin-top: 5px; +} + +// Specificity overrides +input[type="submit"], +input[type="reset"], +input[type="button"] { + &.btn-block { + width: 100%; + } +} + + + +// Alternate buttons +// -------------------------------------------------- + +// Provide *some* extra contrast for those who can get it +.btn-primary.active, +.btn-warning.active, +.btn-danger.active, +.btn-success.active, +.btn-info.active, +.btn-inverse.active { + color: rgba(255,255,255,.75); +} + +// Set the backgrounds +// ------------------------- +.btn-primary { + .buttonBackground(@btnPrimaryBackground, @btnPrimaryBackgroundHighlight); +} +// Warning appears are orange +.btn-warning { + .buttonBackground(@btnWarningBackground, @btnWarningBackgroundHighlight); +} +// Danger and error appear as red +.btn-danger { + .buttonBackground(@btnDangerBackground, @btnDangerBackgroundHighlight); +} +// Success appears as green +.btn-success { + .buttonBackground(@btnSuccessBackground, @btnSuccessBackgroundHighlight); +} +// Info appears as a neutral blue +.btn-info { + .buttonBackground(@btnInfoBackground, @btnInfoBackgroundHighlight); +} +// Inverse appears as dark gray +.btn-inverse { + .buttonBackground(@btnInverseBackground, @btnInverseBackgroundHighlight); +} + + +// Cross-browser Jank +// -------------------------------------------------- + +button.btn, +input[type="submit"].btn { + + // Firefox 3.6 only I believe + &::-moz-focus-inner { + padding: 0; + border: 0; + } + + // IE7 has some default padding on button controls + *padding-top: 3px; + *padding-bottom: 3px; + + &.btn-large { + *padding-top: 7px; + *padding-bottom: 7px; + } + &.btn-small { + *padding-top: 3px; + *padding-bottom: 3px; + } + &.btn-mini { + *padding-top: 1px; + *padding-bottom: 1px; + } +} + + +// Link buttons +// -------------------------------------------------- + +// Make a button look and behave like a link +.btn-link, +.btn-link:active, +.btn-link[disabled] { + background-color: transparent; + background-image: none; + .box-shadow(none); +} +.btn-link { + border-color: transparent; + cursor: pointer; + color: @linkColor; + .border-radius(0); +} +.btn-link:hover, +.btn-link:focus { + color: @linkColorHover; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +.btn-link[disabled]:focus { + color: @grayDark; + text-decoration: none; +} diff --git a/public/css/bootstrap/carousel.less b/public/css/bootstrap/carousel.less new file mode 100644 index 0000000..55bc050 --- /dev/null +++ b/public/css/bootstrap/carousel.less @@ -0,0 +1,158 @@ +// +// Carousel +// -------------------------------------------------- + + +.carousel { + position: relative; + margin-bottom: @baseLineHeight; + line-height: 1; +} + +.carousel-inner { + overflow: hidden; + width: 100%; + position: relative; +} + +.carousel-inner { + + > .item { + display: none; + position: relative; + .transition(.6s ease-in-out left); + + // Account for jankitude on images + > img, + > a > img { + display: block; + line-height: 1; + } + } + + > .active, + > .next, + > .prev { display: block; } + + > .active { + left: 0; + } + + > .next, + > .prev { + position: absolute; + top: 0; + width: 100%; + } + + > .next { + left: 100%; + } + > .prev { + left: -100%; + } + > .next.left, + > .prev.right { + left: 0; + } + + > .active.left { + left: -100%; + } + > .active.right { + left: 100%; + } + +} + +// Left/right controls for nav +// --------------------------- + +.carousel-control { + position: absolute; + top: 40%; + left: 15px; + width: 40px; + height: 40px; + margin-top: -20px; + font-size: 60px; + font-weight: 100; + line-height: 30px; + color: @white; + text-align: center; + background: @grayDarker; + border: 3px solid @white; + .border-radius(23px); + .opacity(50); + + // we can't have this transition here + // because webkit cancels the carousel + // animation if you trip this while + // in the middle of another animation + // ;_; + // .transition(opacity .2s linear); + + // Reposition the right one + &.right { + left: auto; + right: 15px; + } + + // Hover/focus state + &:hover, + &:focus { + color: @white; + text-decoration: none; + .opacity(90); + } +} + +// Carousel indicator pips +// ----------------------------- +.carousel-indicators { + position: absolute; + top: 15px; + right: 15px; + z-index: 5; + margin: 0; + list-style: none; + + li { + display: block; + float: left; + width: 10px; + height: 10px; + margin-left: 5px; + text-indent: -999px; + background-color: #ccc; + background-color: rgba(255,255,255,.25); + border-radius: 5px; + } + .active { + background-color: #fff; + } +} + +// Caption for text below images +// ----------------------------- + +.carousel-caption { + position: absolute; + left: 0; + right: 0; + bottom: 0; + padding: 15px; + background: @grayDark; + background: rgba(0,0,0,.75); +} +.carousel-caption h4, +.carousel-caption p { + color: @white; + line-height: @baseLineHeight; +} +.carousel-caption h4 { + margin: 0 0 5px; +} +.carousel-caption p { + margin-bottom: 0; +} diff --git a/public/css/bootstrap/close.less b/public/css/bootstrap/close.less new file mode 100644 index 0000000..4c626bd --- /dev/null +++ b/public/css/bootstrap/close.less @@ -0,0 +1,32 @@ +// +// Close icons +// -------------------------------------------------- + + +.close { + float: right; + font-size: 20px; + font-weight: bold; + line-height: @baseLineHeight; + color: @black; + text-shadow: 0 1px 0 rgba(255,255,255,1); + .opacity(20); + &:hover, + &:focus { + color: @black; + text-decoration: none; + cursor: pointer; + .opacity(40); + } +} + +// Additional properties for button version +// iOS requires the button element instead of an anchor tag. +// If you want the anchor version, it requires `href="#"`. +button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; +} \ No newline at end of file diff --git a/public/css/bootstrap/code.less b/public/css/bootstrap/code.less new file mode 100644 index 0000000..266a926 --- /dev/null +++ b/public/css/bootstrap/code.less @@ -0,0 +1,61 @@ +// +// Code (inline and blocK) +// -------------------------------------------------- + + +// Inline and block code styles +code, +pre { + padding: 0 3px 2px; + #font > #family > .monospace; + font-size: @baseFontSize - 2; + color: @grayDark; + .border-radius(3px); +} + +// Inline code +code { + padding: 2px 4px; + color: #d14; + background-color: #f7f7f9; + border: 1px solid #e1e1e8; + white-space: nowrap; +} + +// Blocks of code +pre { + display: block; + padding: (@baseLineHeight - 1) / 2; + margin: 0 0 @baseLineHeight / 2; + font-size: @baseFontSize - 1; // 14px to 13px + line-height: @baseLineHeight; + word-break: break-all; + word-wrap: break-word; + white-space: pre; + white-space: pre-wrap; + background-color: #f5f5f5; + border: 1px solid #ccc; // fallback for IE7-8 + border: 1px solid rgba(0,0,0,.15); + .border-radius(@baseBorderRadius); + + // Make prettyprint styles more spaced out for readability + &.prettyprint { + margin-bottom: @baseLineHeight; + } + + // Account for some code outputs that place code tags in pre tags + code { + padding: 0; + color: inherit; + white-space: pre; + white-space: pre-wrap; + background-color: transparent; + border: 0; + } +} + +// Enable scrollable blocks of code +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} \ No newline at end of file diff --git a/public/css/bootstrap/component-animations.less b/public/css/bootstrap/component-animations.less new file mode 100644 index 0000000..d614263 --- /dev/null +++ b/public/css/bootstrap/component-animations.less @@ -0,0 +1,22 @@ +// +// Component animations +// -------------------------------------------------- + + +.fade { + opacity: 0; + .transition(opacity .15s linear); + &.in { + opacity: 1; + } +} + +.collapse { + position: relative; + height: 0; + overflow: hidden; + .transition(height .35s ease); + &.in { + height: auto; + } +} diff --git a/public/css/bootstrap/dropdowns.less b/public/css/bootstrap/dropdowns.less new file mode 100644 index 0000000..bbfe3fd --- /dev/null +++ b/public/css/bootstrap/dropdowns.less @@ -0,0 +1,237 @@ +// +// Dropdown menus +// -------------------------------------------------- + + +// Use the .menu class on any
  • element within the topbar or ul.tabs and you'll get some superfancy dropdowns +.dropup, +.dropdown { + position: relative; +} +.dropdown-toggle { + // The caret makes the toggle a bit too tall in IE7 + *margin-bottom: -3px; +} +.dropdown-toggle:active, +.open .dropdown-toggle { + outline: 0; +} + +// Dropdown arrow/caret +// -------------------- +.caret { + display: inline-block; + width: 0; + height: 0; + vertical-align: top; + border-top: 4px solid @black; + border-right: 4px solid transparent; + border-left: 4px solid transparent; + content: ""; +} + +// Place the caret +.dropdown .caret { + margin-top: 8px; + margin-left: 2px; +} + +// The dropdown menu (ul) +// ---------------------- +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: @zindexDropdown; + display: none; // none by default, but block on "open" of the menu + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; // override default ul + list-style: none; + background-color: @dropdownBackground; + border: 1px solid #ccc; // Fallback for IE7-8 + border: 1px solid @dropdownBorder; + *border-right-width: 2px; + *border-bottom-width: 2px; + .border-radius(6px); + .box-shadow(0 5px 10px rgba(0,0,0,.2)); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; + + // Aligns the dropdown menu to right + &.pull-right { + right: 0; + left: auto; + } + + // Dividers (basically an hr) within the dropdown + .divider { + .nav-divider(@dropdownDividerTop, @dropdownDividerBottom); + } + + // Links within the dropdown menu + > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: @baseLineHeight; + color: @dropdownLinkColor; + white-space: nowrap; + } +} + +// Hover/Focus state +// ----------- +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus, +.dropdown-submenu:hover > a, +.dropdown-submenu:focus > a { + text-decoration: none; + color: @dropdownLinkColorHover; + #gradient > .vertical(@dropdownLinkBackgroundHover, darken(@dropdownLinkBackgroundHover, 5%)); +} + +// Active state +// ------------ +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: @dropdownLinkColorActive; + text-decoration: none; + outline: 0; + #gradient > .vertical(@dropdownLinkBackgroundActive, darken(@dropdownLinkBackgroundActive, 5%)); +} + +// Disabled state +// -------------- +// Gray out text and ensure the hover/focus state remains gray +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: @grayLight; +} +// Nuke hover/focus effects +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + background-color: transparent; + background-image: none; // Remove CSS gradient + .reset-filter(); + cursor: default; +} + +// Open state for the dropdown +// --------------------------- +.open { + // IE7's z-index only goes to the nearest positioned ancestor, which would + // make the menu appear below buttons that appeared later on the page + *z-index: @zindexDropdown; + + & > .dropdown-menu { + display: block; + } +} + +// Right aligned dropdowns +// --------------------------- +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} + +// Allow for dropdowns to go bottom up (aka, dropup-menu) +// ------------------------------------------------------ +// Just add .dropup after the standard .dropdown class and you're set, bro. +// TODO: abstract this so that the navbar fixed styles are not placed here? +.dropup, +.navbar-fixed-bottom .dropdown { + // Reverse the caret + .caret { + border-top: 0; + border-bottom: 4px solid @black; + content: ""; + } + // Different positioning for bottom up menu + .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 1px; + } +} + +// Sub menus +// --------------------------- +.dropdown-submenu { + position: relative; +} +// Default dropdowns +.dropdown-submenu > .dropdown-menu { + top: 0; + left: 100%; + margin-top: -6px; + margin-left: -1px; + .border-radius(0 6px 6px 6px); +} +.dropdown-submenu:hover > .dropdown-menu { + display: block; +} + +// Dropups +.dropup .dropdown-submenu > .dropdown-menu { + top: auto; + bottom: 0; + margin-top: 0; + margin-bottom: -2px; + .border-radius(5px 5px 5px 0); +} + +// Caret to indicate there is a submenu +.dropdown-submenu > a:after { + display: block; + content: " "; + float: right; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; + border-width: 5px 0 5px 5px; + border-left-color: darken(@dropdownBackground, 20%); + margin-top: 5px; + margin-right: -10px; +} +.dropdown-submenu:hover > a:after { + border-left-color: @dropdownLinkColorHover; +} + +// Left aligned submenus +.dropdown-submenu.pull-left { + // Undo the float + // Yes, this is awkward since .pull-left adds a float, but it sticks to our conventions elsewhere. + float: none; + + // Positioning the submenu + > .dropdown-menu { + left: -100%; + margin-left: 10px; + .border-radius(6px 0 6px 6px); + } +} + +// Tweak nav headers +// ----------------- +// Increase padding from 15px to 20px on sides +.dropdown .dropdown-menu .nav-header { + padding-left: 20px; + padding-right: 20px; +} + +// Typeahead +// --------- +.typeahead { + z-index: 1051; + margin-top: 2px; // give it some space to breathe + .border-radius(@baseBorderRadius); +} diff --git a/public/css/bootstrap/forms.less b/public/css/bootstrap/forms.less new file mode 100644 index 0000000..06767bd --- /dev/null +++ b/public/css/bootstrap/forms.less @@ -0,0 +1,690 @@ +// +// Forms +// -------------------------------------------------- + + +// GENERAL STYLES +// -------------- + +// Make all forms have space below them +form { + margin: 0 0 @baseLineHeight; +} + +fieldset { + padding: 0; + margin: 0; + border: 0; +} + +// Groups of fields with labels on top (legends) +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: @baseLineHeight; + font-size: @baseFontSize * 1.5; + line-height: @baseLineHeight * 2; + color: @grayDark; + border: 0; + border-bottom: 1px solid #e5e5e5; + + // Small + small { + font-size: @baseLineHeight * .75; + color: @grayLight; + } +} + +// Set font for forms +label, +input, +button, +select, +textarea { + #font > .shorthand(@baseFontSize,normal,@baseLineHeight); // Set size, weight, line-height here +} +input, +button, +select, +textarea { + font-family: @baseFontFamily; // And only set font-family here for those that need it (note the missing label element) +} + +// Identify controls by their labels +label { + display: block; + margin-bottom: 5px; +} + +// Form controls +// ------------------------- + +// Shared size and type resets +select, +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + display: inline-block; + height: @baseLineHeight; + padding: 4px 6px; + margin-bottom: @baseLineHeight / 2; + font-size: @baseFontSize; + line-height: @baseLineHeight; + color: @gray; + .border-radius(@inputBorderRadius); + vertical-align: middle; +} + +// Reset appearance properties for textual inputs and textarea +// Declare width for legacy (can't be on input[type=*] selectors or it's too specific) +input, +textarea, +.uneditable-input { + width: 206px; // plus 12px padding and 2px border +} +// Reset height since textareas have rows +textarea { + height: auto; +} +// Everything else +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + background-color: @inputBackground; + border: 1px solid @inputBorder; + .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); + .transition(~"border linear .2s, box-shadow linear .2s"); + + // Focus state + &:focus { + border-color: rgba(82,168,236,.8); + outline: 0; + outline: thin dotted \9; /* IE6-9 */ + .box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6)"); + } +} + +// Position radios and checkboxes better +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + *margin-top: 0; /* IE7 */ + margin-top: 1px \9; /* IE8-9 */ + line-height: normal; +} + +// Reset width of input images, buttons, radios, checkboxes +input[type="file"], +input[type="image"], +input[type="submit"], +input[type="reset"], +input[type="button"], +input[type="radio"], +input[type="checkbox"] { + width: auto; // Override of generic input selector +} + +// Set the height of select and file controls to match text inputs +select, +input[type="file"] { + height: @inputHeight; /* In IE7, the height of the select element cannot be changed by height, only font-size */ + *margin-top: 4px; /* For IE7, add top margin to align select with labels */ + line-height: @inputHeight; +} + +// Make select elements obey height by applying a border +select { + width: 220px; // default input width + 10px of padding that doesn't get applied + border: 1px solid @inputBorder; + background-color: @inputBackground; // Chrome on Linux and Mobile Safari need background-color +} + +// Make multiple select elements height not fixed +select[multiple], +select[size] { + height: auto; +} + +// Focus for select, file, radio, and checkbox +select:focus, +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + .tab-focus(); +} + + +// Uneditable inputs +// ------------------------- + +// Make uneditable inputs look inactive +.uneditable-input, +.uneditable-textarea { + color: @grayLight; + background-color: darken(@inputBackground, 1%); + border-color: @inputBorder; + .box-shadow(inset 0 1px 2px rgba(0,0,0,.025)); + cursor: not-allowed; +} + +// For text that needs to appear as an input but should not be an input +.uneditable-input { + overflow: hidden; // prevent text from wrapping, but still cut it off like an input does + white-space: nowrap; +} + +// Make uneditable textareas behave like a textarea +.uneditable-textarea { + width: auto; + height: auto; +} + + +// Placeholder +// ------------------------- + +// Placeholder text gets special styles because when browsers invalidate entire lines if it doesn't understand a selector +input, +textarea { + .placeholder(); +} + + +// CHECKBOXES & RADIOS +// ------------------- + +// Indent the labels to position radios/checkboxes as hanging +.radio, +.checkbox { + min-height: @baseLineHeight; // clear the floating input if there is no label text + padding-left: 20px; +} +.radio input[type="radio"], +.checkbox input[type="checkbox"] { + float: left; + margin-left: -20px; +} + +// Move the options list down to align with labels +.controls > .radio:first-child, +.controls > .checkbox:first-child { + padding-top: 5px; // has to be padding because margin collaspes +} + +// Radios and checkboxes on same line +// TODO v3: Convert .inline to .control-inline +.radio.inline, +.checkbox.inline { + display: inline-block; + padding-top: 5px; + margin-bottom: 0; + vertical-align: middle; +} +.radio.inline + .radio.inline, +.checkbox.inline + .checkbox.inline { + margin-left: 10px; // space out consecutive inline controls +} + + + +// INPUT SIZES +// ----------- + +// General classes for quick sizes +.input-mini { width: 60px; } +.input-small { width: 90px; } +.input-medium { width: 150px; } +.input-large { width: 210px; } +.input-xlarge { width: 270px; } +.input-xxlarge { width: 530px; } + +// Grid style input sizes +input[class*="span"], +select[class*="span"], +textarea[class*="span"], +.uneditable-input[class*="span"], +// Redeclare since the fluid row class is more specific +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"] { + float: none; + margin-left: 0; +} +// Ensure input-prepend/append never wraps +.input-append input[class*="span"], +.input-append .uneditable-input[class*="span"], +.input-prepend input[class*="span"], +.input-prepend .uneditable-input[class*="span"], +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"], +.row-fluid .input-prepend [class*="span"], +.row-fluid .input-append [class*="span"] { + display: inline-block; +} + + + +// GRID SIZING FOR INPUTS +// ---------------------- + +// Grid sizes +#grid > .input(@gridColumnWidth, @gridGutterWidth); + +// Control row for multiple inputs per line +.controls-row { + .clearfix(); // Clear the float from controls +} + +// Float to collapse white-space for proper grid alignment +.controls-row [class*="span"], +// Redeclare the fluid grid collapse since we undo the float for inputs +.row-fluid .controls-row [class*="span"] { + float: left; +} +// Explicity set top padding on all checkboxes/radios, not just first-child +.controls-row .checkbox[class*="span"], +.controls-row .radio[class*="span"] { + padding-top: 5px; +} + + + + +// DISABLED STATE +// -------------- + +// Disabled and read-only inputs +input[disabled], +select[disabled], +textarea[disabled], +input[readonly], +select[readonly], +textarea[readonly] { + cursor: not-allowed; + background-color: @inputDisabledBackground; +} +// Explicitly reset the colors here +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"][readonly], +input[type="checkbox"][readonly] { + background-color: transparent; +} + + + + +// FORM FIELD FEEDBACK STATES +// -------------------------- + +// Warning +.control-group.warning { + .formFieldState(@warningText, @warningText, @warningBackground); +} +// Error +.control-group.error { + .formFieldState(@errorText, @errorText, @errorBackground); +} +// Success +.control-group.success { + .formFieldState(@successText, @successText, @successBackground); +} +// Success +.control-group.info { + .formFieldState(@infoText, @infoText, @infoBackground); +} + +// HTML5 invalid states +// Shares styles with the .control-group.error above +input:focus:invalid, +textarea:focus:invalid, +select:focus:invalid { + color: #b94a48; + border-color: #ee5f5b; + &:focus { + border-color: darken(#ee5f5b, 10%); + @shadow: 0 0 6px lighten(#ee5f5b, 20%); + .box-shadow(@shadow); + } +} + + + +// FORM ACTIONS +// ------------ + +.form-actions { + padding: (@baseLineHeight - 1) 20px @baseLineHeight; + margin-top: @baseLineHeight; + margin-bottom: @baseLineHeight; + background-color: @formActionsBackground; + border-top: 1px solid #e5e5e5; + .clearfix(); // Adding clearfix to allow for .pull-right button containers +} + + + +// HELP TEXT +// --------- + +.help-block, +.help-inline { + color: lighten(@textColor, 15%); // lighten the text some for contrast +} + +.help-block { + display: block; // account for any element using help-block + margin-bottom: @baseLineHeight / 2; +} + +.help-inline { + display: inline-block; + .ie7-inline-block(); + vertical-align: middle; + padding-left: 5px; +} + + + +// INPUT GROUPS +// ------------ + +// Allow us to put symbols and text within the input field for a cleaner look +.input-append, +.input-prepend { + display: inline-block; + margin-bottom: @baseLineHeight / 2; + vertical-align: middle; + font-size: 0; // white space collapse hack + white-space: nowrap; // Prevent span and input from separating + + // Reset the white space collapse hack + input, + select, + .uneditable-input, + .dropdown-menu, + .popover { + font-size: @baseFontSize; + } + + input, + select, + .uneditable-input { + position: relative; // placed here by default so that on :focus we can place the input above the .add-on for full border and box-shadow goodness + margin-bottom: 0; // prevent bottom margin from screwing up alignment in stacked forms + *margin-left: 0; + vertical-align: top; + .border-radius(0 @inputBorderRadius @inputBorderRadius 0); + // Make input on top when focused so blue border and shadow always show + &:focus { + z-index: 2; + } + } + .add-on { + display: inline-block; + width: auto; + height: @baseLineHeight; + min-width: 16px; + padding: 4px 5px; + font-size: @baseFontSize; + font-weight: normal; + line-height: @baseLineHeight; + text-align: center; + text-shadow: 0 1px 0 @white; + background-color: @grayLighter; + border: 1px solid #ccc; + } + .add-on, + .btn, + .btn-group > .dropdown-toggle { + vertical-align: top; + .border-radius(0); + } + .active { + background-color: lighten(@green, 30); + border-color: @green; + } +} + +.input-prepend { + .add-on, + .btn { + margin-right: -1px; + } + .add-on:first-child, + .btn:first-child { + // FYI, `.btn:first-child` accounts for a button group that's prepended + .border-radius(@inputBorderRadius 0 0 @inputBorderRadius); + } +} + +.input-append { + input, + select, + .uneditable-input { + .border-radius(@inputBorderRadius 0 0 @inputBorderRadius); + + .btn-group .btn:last-child { + .border-radius(0 @inputBorderRadius @inputBorderRadius 0); + } + } + .add-on, + .btn, + .btn-group { + margin-left: -1px; + } + .add-on:last-child, + .btn:last-child, + .btn-group:last-child > .dropdown-toggle { + .border-radius(0 @inputBorderRadius @inputBorderRadius 0); + } +} + +// Remove all border-radius for inputs with both prepend and append +.input-prepend.input-append { + input, + select, + .uneditable-input { + .border-radius(0); + + .btn-group .btn { + .border-radius(0 @inputBorderRadius @inputBorderRadius 0); + } + } + .add-on:first-child, + .btn:first-child { + margin-right: -1px; + .border-radius(@inputBorderRadius 0 0 @inputBorderRadius); + } + .add-on:last-child, + .btn:last-child { + margin-left: -1px; + .border-radius(0 @inputBorderRadius @inputBorderRadius 0); + } + .btn-group:first-child { + margin-left: 0; + } +} + + + + +// SEARCH FORM +// ----------- + +input.search-query { + padding-right: 14px; + padding-right: 4px \9; + padding-left: 14px; + padding-left: 4px \9; /* IE7-8 doesn't have border-radius, so don't indent the padding */ + margin-bottom: 0; // Remove the default margin on all inputs + .border-radius(15px); +} + +/* Allow for input prepend/append in search forms */ +.form-search .input-append .search-query, +.form-search .input-prepend .search-query { + .border-radius(0); // Override due to specificity +} +.form-search .input-append .search-query { + .border-radius(14px 0 0 14px); +} +.form-search .input-append .btn { + .border-radius(0 14px 14px 0); +} +.form-search .input-prepend .search-query { + .border-radius(0 14px 14px 0); +} +.form-search .input-prepend .btn { + .border-radius(14px 0 0 14px); +} + + + + +// HORIZONTAL & VERTICAL FORMS +// --------------------------- + +// Common properties +// ----------------- + +.form-search, +.form-inline, +.form-horizontal { + input, + textarea, + select, + .help-inline, + .uneditable-input, + .input-prepend, + .input-append { + display: inline-block; + .ie7-inline-block(); + margin-bottom: 0; + vertical-align: middle; + } + // Re-hide hidden elements due to specifity + .hide { + display: none; + } +} +.form-search label, +.form-inline label, +.form-search .btn-group, +.form-inline .btn-group { + display: inline-block; +} +// Remove margin for input-prepend/-append +.form-search .input-append, +.form-inline .input-append, +.form-search .input-prepend, +.form-inline .input-prepend { + margin-bottom: 0; +} +// Inline checkbox/radio labels (remove padding on left) +.form-search .radio, +.form-search .checkbox, +.form-inline .radio, +.form-inline .checkbox { + padding-left: 0; + margin-bottom: 0; + vertical-align: middle; +} +// Remove float and margin, set to inline-block +.form-search .radio input[type="radio"], +.form-search .checkbox input[type="checkbox"], +.form-inline .radio input[type="radio"], +.form-inline .checkbox input[type="checkbox"] { + float: left; + margin-right: 3px; + margin-left: 0; +} + + +// Margin to space out fieldsets +.control-group { + margin-bottom: @baseLineHeight / 2; +} + +// Legend collapses margin, so next element is responsible for spacing +legend + .control-group { + margin-top: @baseLineHeight; + -webkit-margin-top-collapse: separate; +} + +// Horizontal-specific styles +// -------------------------- + +.form-horizontal { + // Increase spacing between groups + .control-group { + margin-bottom: @baseLineHeight; + .clearfix(); + } + // Float the labels left + .control-label { + float: left; + width: @horizontalComponentOffset - 20; + padding-top: 5px; + text-align: right; + } + // Move over all input controls and content + .controls { + // Super jank IE7 fix to ensure the inputs in .input-append and input-prepend + // don't inherit the margin of the parent, in this case .controls + *display: inline-block; + *padding-left: 20px; + margin-left: @horizontalComponentOffset; + *margin-left: 0; + &:first-child { + *padding-left: @horizontalComponentOffset; + } + } + // Remove bottom margin on block level help text since that's accounted for on .control-group + .help-block { + margin-bottom: 0; + } + // And apply it only to .help-block instances that follow a form control + input, + select, + textarea, + .uneditable-input, + .input-prepend, + .input-append { + + .help-block { + margin-top: @baseLineHeight / 2; + } + } + // Move over buttons in .form-actions to align with .controls + .form-actions { + padding-left: @horizontalComponentOffset; + } +} diff --git a/public/css/bootstrap/grid.less b/public/css/bootstrap/grid.less new file mode 100644 index 0000000..750d203 --- /dev/null +++ b/public/css/bootstrap/grid.less @@ -0,0 +1,21 @@ +// +// Grid system +// -------------------------------------------------- + + +// Fixed (940px) +#grid > .core(@gridColumnWidth, @gridGutterWidth); + +// Fluid (940px) +#grid > .fluid(@fluidGridColumnWidth, @fluidGridGutterWidth); + +// Reset utility classes due to specificity +[class*="span"].hide, +.row-fluid [class*="span"].hide { + display: none; +} + +[class*="span"].pull-right, +.row-fluid [class*="span"].pull-right { + float: right; +} diff --git a/public/css/bootstrap/hero-unit.less b/public/css/bootstrap/hero-unit.less new file mode 100644 index 0000000..763d86a --- /dev/null +++ b/public/css/bootstrap/hero-unit.less @@ -0,0 +1,25 @@ +// +// Hero unit +// -------------------------------------------------- + + +.hero-unit { + padding: 60px; + margin-bottom: 30px; + font-size: 18px; + font-weight: 200; + line-height: @baseLineHeight * 1.5; + color: @heroUnitLeadColor; + background-color: @heroUnitBackground; + .border-radius(6px); + h1 { + margin-bottom: 0; + font-size: 60px; + line-height: 1; + color: @heroUnitHeadingColor; + letter-spacing: -1px; + } + li { + line-height: @baseLineHeight * 1.5; // Reset since we specify in type.less + } +} diff --git a/public/css/bootstrap/labels-badges.less b/public/css/bootstrap/labels-badges.less new file mode 100644 index 0000000..bc321fe --- /dev/null +++ b/public/css/bootstrap/labels-badges.less @@ -0,0 +1,84 @@ +// +// Labels and badges +// -------------------------------------------------- + + +// Base classes +.label, +.badge { + display: inline-block; + padding: 2px 4px; + font-size: @baseFontSize * .846; + font-weight: bold; + line-height: 14px; // ensure proper line-height if floated + color: @white; + vertical-align: baseline; + white-space: nowrap; + text-shadow: 0 -1px 0 rgba(0,0,0,.25); + background-color: @grayLight; +} +// Set unique padding and border-radii +.label { + .border-radius(3px); +} +.badge { + padding-left: 9px; + padding-right: 9px; + .border-radius(9px); +} + +// Empty labels/badges collapse +.label, +.badge { + &:empty { + display: none; + } +} + +// Hover/focus state, but only for links +a { + &.label:hover, + &.label:focus, + &.badge:hover, + &.badge:focus { + color: @white; + text-decoration: none; + cursor: pointer; + } +} + +// Colors +// Only give background-color difference to links (and to simplify, we don't qualifty with `a` but [href] attribute) +.label, +.badge { + // Important (red) + &-important { background-color: @errorText; } + &-important[href] { background-color: darken(@errorText, 10%); } + // Warnings (orange) + &-warning { background-color: @orange; } + &-warning[href] { background-color: darken(@orange, 10%); } + // Success (green) + &-success { background-color: @successText; } + &-success[href] { background-color: darken(@successText, 10%); } + // Info (turquoise) + &-info { background-color: @infoText; } + &-info[href] { background-color: darken(@infoText, 10%); } + // Inverse (black) + &-inverse { background-color: @grayDark; } + &-inverse[href] { background-color: darken(@grayDark, 10%); } +} + +// Quick fix for labels/badges in buttons +.btn { + .label, + .badge { + position: relative; + top: -1px; + } +} +.btn-mini { + .label, + .badge { + top: 0; + } +} diff --git a/public/css/bootstrap/layouts.less b/public/css/bootstrap/layouts.less new file mode 100644 index 0000000..24a2062 --- /dev/null +++ b/public/css/bootstrap/layouts.less @@ -0,0 +1,16 @@ +// +// Layouts +// -------------------------------------------------- + + +// Container (centered, fixed-width layouts) +.container { + .container-fixed(); +} + +// Fluid layouts (left aligned, with sidebar, min- & max-width content) +.container-fluid { + padding-right: @gridGutterWidth; + padding-left: @gridGutterWidth; + .clearfix(); +} \ No newline at end of file diff --git a/public/css/bootstrap/media.less b/public/css/bootstrap/media.less new file mode 100644 index 0000000..e461e44 --- /dev/null +++ b/public/css/bootstrap/media.less @@ -0,0 +1,55 @@ +// Media objects +// Source: http://stubbornella.org/content/?p=497 +// -------------------------------------------------- + + +// Common styles +// ------------------------- + +// Clear the floats +.media, +.media-body { + overflow: hidden; + *overflow: visible; + zoom: 1; +} + +// Proper spacing between instances of .media +.media, +.media .media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} + +// For images and videos, set to block +.media-object { + display: block; +} + +// Reset margins on headings for tighter default spacing +.media-heading { + margin: 0 0 5px; +} + + +// Media image alignment +// ------------------------- + +.media > .pull-left { + margin-right: 10px; +} +.media > .pull-right { + margin-left: 10px; +} + + +// Media list variation +// ------------------------- + +// Undo default ul/ol styles +.media-list { + margin-left: 0; + list-style: none; +} diff --git a/public/css/bootstrap/mixins.less b/public/css/bootstrap/mixins.less new file mode 100644 index 0000000..79d8892 --- /dev/null +++ b/public/css/bootstrap/mixins.less @@ -0,0 +1,702 @@ +// +// Mixins +// -------------------------------------------------- + + +// UTILITY MIXINS +// -------------------------------------------------- + +// Clearfix +// -------- +// For clearing floats like a boss h5bp.com/q +.clearfix { + *zoom: 1; + &:before, + &:after { + display: table; + content: ""; + // Fixes Opera/contenteditable bug: + // http://nicolasgallagher.com/micro-clearfix-hack/#comment-36952 + line-height: 0; + } + &:after { + clear: both; + } +} + +// Webkit-style focus +// ------------------ +.tab-focus() { + // Default + outline: thin dotted #333; + // Webkit + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +// Center-align a block level element +// ---------------------------------- +.center-block() { + display: block; + margin-left: auto; + margin-right: auto; +} + +// IE7 inline-block +// ---------------- +.ie7-inline-block() { + *display: inline; /* IE7 inline-block hack */ + *zoom: 1; +} + +// IE7 likes to collapse whitespace on either side of the inline-block elements. +// Ems because we're attempting to match the width of a space character. Left +// version is for form buttons, which typically come after other elements, and +// right version is for icons, which come before. Applying both is ok, but it will +// mean that space between those elements will be .6em (~2 space characters) in IE7, +// instead of the 1 space in other browsers. +.ie7-restore-left-whitespace() { + *margin-left: .3em; + + &:first-child { + *margin-left: 0; + } +} + +.ie7-restore-right-whitespace() { + *margin-right: .3em; +} + +// Sizing shortcuts +// ------------------------- +.size(@height, @width) { + width: @width; + height: @height; +} +.square(@size) { + .size(@size, @size); +} + +// Placeholder text +// ------------------------- +.placeholder(@color: @placeholderText) { + &:-moz-placeholder { + color: @color; + } + &:-ms-input-placeholder { + color: @color; + } + &::-webkit-input-placeholder { + color: @color; + } +} + +// Text overflow +// ------------------------- +// Requires inline-block or block for proper styling +.text-overflow() { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +// CSS image replacement +// ------------------------- +// Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757 +.hide-text { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + + +// FONTS +// -------------------------------------------------- + +#font { + #family { + .serif() { + font-family: @serifFontFamily; + } + .sans-serif() { + font-family: @sansFontFamily; + } + .monospace() { + font-family: @monoFontFamily; + } + } + .shorthand(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) { + font-size: @size; + font-weight: @weight; + line-height: @lineHeight; + } + .serif(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) { + #font > #family > .serif; + #font > .shorthand(@size, @weight, @lineHeight); + } + .sans-serif(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) { + #font > #family > .sans-serif; + #font > .shorthand(@size, @weight, @lineHeight); + } + .monospace(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) { + #font > #family > .monospace; + #font > .shorthand(@size, @weight, @lineHeight); + } +} + + +// FORMS +// -------------------------------------------------- + +// Block level inputs +.input-block-level { + display: block; + width: 100%; + min-height: @inputHeight; // Make inputs at least the height of their button counterpart (base line-height + padding + border) + .box-sizing(border-box); // Makes inputs behave like true block-level elements +} + + + +// Mixin for form field states +.formFieldState(@textColor: #555, @borderColor: #ccc, @backgroundColor: #f5f5f5) { + // Set the text color + .control-label, + .help-block, + .help-inline { + color: @textColor; + } + // Style inputs accordingly + .checkbox, + .radio, + input, + select, + textarea { + color: @textColor; + } + input, + select, + textarea { + border-color: @borderColor; + .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work + &:focus { + border-color: darken(@borderColor, 10%); + @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@borderColor, 20%); + .box-shadow(@shadow); + } + } + // Give a small background color for input-prepend/-append + .input-prepend .add-on, + .input-append .add-on { + color: @textColor; + background-color: @backgroundColor; + border-color: @textColor; + } +} + + + +// CSS3 PROPERTIES +// -------------------------------------------------- + +// Border Radius +.border-radius(@radius) { + -webkit-border-radius: @radius; + -moz-border-radius: @radius; + border-radius: @radius; +} + +// Single Corner Border Radius +.border-top-left-radius(@radius) { + -webkit-border-top-left-radius: @radius; + -moz-border-radius-topleft: @radius; + border-top-left-radius: @radius; +} +.border-top-right-radius(@radius) { + -webkit-border-top-right-radius: @radius; + -moz-border-radius-topright: @radius; + border-top-right-radius: @radius; +} +.border-bottom-right-radius(@radius) { + -webkit-border-bottom-right-radius: @radius; + -moz-border-radius-bottomright: @radius; + border-bottom-right-radius: @radius; +} +.border-bottom-left-radius(@radius) { + -webkit-border-bottom-left-radius: @radius; + -moz-border-radius-bottomleft: @radius; + border-bottom-left-radius: @radius; +} + +// Single Side Border Radius +.border-top-radius(@radius) { + .border-top-right-radius(@radius); + .border-top-left-radius(@radius); +} +.border-right-radius(@radius) { + .border-top-right-radius(@radius); + .border-bottom-right-radius(@radius); +} +.border-bottom-radius(@radius) { + .border-bottom-right-radius(@radius); + .border-bottom-left-radius(@radius); +} +.border-left-radius(@radius) { + .border-top-left-radius(@radius); + .border-bottom-left-radius(@radius); +} + +// Drop shadows +.box-shadow(@shadow) { + -webkit-box-shadow: @shadow; + -moz-box-shadow: @shadow; + box-shadow: @shadow; +} + +// Transitions +.transition(@transition) { + -webkit-transition: @transition; + -moz-transition: @transition; + -o-transition: @transition; + transition: @transition; +} +.transition-delay(@transition-delay) { + -webkit-transition-delay: @transition-delay; + -moz-transition-delay: @transition-delay; + -o-transition-delay: @transition-delay; + transition-delay: @transition-delay; +} +.transition-duration(@transition-duration) { + -webkit-transition-duration: @transition-duration; + -moz-transition-duration: @transition-duration; + -o-transition-duration: @transition-duration; + transition-duration: @transition-duration; +} + +// Transformations +.rotate(@degrees) { + -webkit-transform: rotate(@degrees); + -moz-transform: rotate(@degrees); + -ms-transform: rotate(@degrees); + -o-transform: rotate(@degrees); + transform: rotate(@degrees); +} +.scale(@ratio) { + -webkit-transform: scale(@ratio); + -moz-transform: scale(@ratio); + -ms-transform: scale(@ratio); + -o-transform: scale(@ratio); + transform: scale(@ratio); +} +.translate(@x, @y) { + -webkit-transform: translate(@x, @y); + -moz-transform: translate(@x, @y); + -ms-transform: translate(@x, @y); + -o-transform: translate(@x, @y); + transform: translate(@x, @y); +} +.skew(@x, @y) { + -webkit-transform: skew(@x, @y); + -moz-transform: skew(@x, @y); + -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twitter/bootstrap/issues/4885 + -o-transform: skew(@x, @y); + transform: skew(@x, @y); + -webkit-backface-visibility: hidden; // See https://github.com/twitter/bootstrap/issues/5319 +} +.translate3d(@x, @y, @z) { + -webkit-transform: translate3d(@x, @y, @z); + -moz-transform: translate3d(@x, @y, @z); + -o-transform: translate3d(@x, @y, @z); + transform: translate3d(@x, @y, @z); +} + +// Backface visibility +// Prevent browsers from flickering when using CSS 3D transforms. +// Default value is `visible`, but can be changed to `hidden +// See git pull https://github.com/dannykeane/bootstrap.git backface-visibility for examples +.backface-visibility(@visibility){ + -webkit-backface-visibility: @visibility; + -moz-backface-visibility: @visibility; + backface-visibility: @visibility; +} + +// Background clipping +// Heads up: FF 3.6 and under need "padding" instead of "padding-box" +.background-clip(@clip) { + -webkit-background-clip: @clip; + -moz-background-clip: @clip; + background-clip: @clip; +} + +// Background sizing +.background-size(@size) { + -webkit-background-size: @size; + -moz-background-size: @size; + -o-background-size: @size; + background-size: @size; +} + + +// Box sizing +.box-sizing(@boxmodel) { + -webkit-box-sizing: @boxmodel; + -moz-box-sizing: @boxmodel; + box-sizing: @boxmodel; +} + +// User select +// For selecting text on the page +.user-select(@select) { + -webkit-user-select: @select; + -moz-user-select: @select; + -ms-user-select: @select; + -o-user-select: @select; + user-select: @select; +} + +// Resize anything +.resizable(@direction) { + resize: @direction; // Options: horizontal, vertical, both + overflow: auto; // Safari fix +} + +// CSS3 Content Columns +.content-columns(@columnCount, @columnGap: @gridGutterWidth) { + -webkit-column-count: @columnCount; + -moz-column-count: @columnCount; + column-count: @columnCount; + -webkit-column-gap: @columnGap; + -moz-column-gap: @columnGap; + column-gap: @columnGap; +} + +// Optional hyphenation +.hyphens(@mode: auto) { + word-wrap: break-word; + -webkit-hyphens: @mode; + -moz-hyphens: @mode; + -ms-hyphens: @mode; + -o-hyphens: @mode; + hyphens: @mode; +} + +// Opacity +.opacity(@opacity) { + opacity: @opacity / 100; + filter: ~"alpha(opacity=@{opacity})"; +} + + + +// BACKGROUNDS +// -------------------------------------------------- + +// Add an alphatransparency value to any background or border color (via Elyse Holladay) +#translucent { + .background(@color: @white, @alpha: 1) { + background-color: hsla(hue(@color), saturation(@color), lightness(@color), @alpha); + } + .border(@color: @white, @alpha: 1) { + border-color: hsla(hue(@color), saturation(@color), lightness(@color), @alpha); + .background-clip(padding-box); + } +} + +// Gradient Bar Colors for buttons and alerts +.gradientBar(@primaryColor, @secondaryColor, @textColor: #fff, @textShadow: 0 -1px 0 rgba(0,0,0,.25)) { + color: @textColor; + text-shadow: @textShadow; + #gradient > .vertical(@primaryColor, @secondaryColor); + border-color: @secondaryColor @secondaryColor darken(@secondaryColor, 15%); + border-color: rgba(0,0,0,.1) rgba(0,0,0,.1) fadein(rgba(0,0,0,.1), 15%); +} + +// Gradients +#gradient { + .horizontal(@startColor: #555, @endColor: #333) { + background-color: @endColor; + background-image: -moz-linear-gradient(left, @startColor, @endColor); // FF 3.6+ + background-image: -webkit-gradient(linear, 0 0, 100% 0, from(@startColor), to(@endColor)); // Safari 4+, Chrome 2+ + background-image: -webkit-linear-gradient(left, @startColor, @endColor); // Safari 5.1+, Chrome 10+ + background-image: -o-linear-gradient(left, @startColor, @endColor); // Opera 11.10 + background-image: linear-gradient(to right, @startColor, @endColor); // Standard, IE10 + background-repeat: repeat-x; + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)",argb(@startColor),argb(@endColor))); // IE9 and down + } + .vertical(@startColor: #555, @endColor: #333) { + background-color: mix(@startColor, @endColor, 60%); + background-image: -moz-linear-gradient(top, @startColor, @endColor); // FF 3.6+ + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), to(@endColor)); // Safari 4+, Chrome 2+ + background-image: -webkit-linear-gradient(top, @startColor, @endColor); // Safari 5.1+, Chrome 10+ + background-image: -o-linear-gradient(top, @startColor, @endColor); // Opera 11.10 + background-image: linear-gradient(to bottom, @startColor, @endColor); // Standard, IE10 + background-repeat: repeat-x; + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@startColor),argb(@endColor))); // IE9 and down + } + .directional(@startColor: #555, @endColor: #333, @deg: 45deg) { + background-color: @endColor; + background-repeat: repeat-x; + background-image: -moz-linear-gradient(@deg, @startColor, @endColor); // FF 3.6+ + background-image: -webkit-linear-gradient(@deg, @startColor, @endColor); // Safari 5.1+, Chrome 10+ + background-image: -o-linear-gradient(@deg, @startColor, @endColor); // Opera 11.10 + background-image: linear-gradient(@deg, @startColor, @endColor); // Standard, IE10 + } + .horizontal-three-colors(@startColor: #00b3ee, @midColor: #7a43b6, @colorStop: 50%, @endColor: #c3325f) { + background-color: mix(@midColor, @endColor, 80%); + background-image: -webkit-gradient(left, linear, 0 0, 0 100%, from(@startColor), color-stop(@colorStop, @midColor), to(@endColor)); + background-image: -webkit-linear-gradient(left, @startColor, @midColor @colorStop, @endColor); + background-image: -moz-linear-gradient(left, @startColor, @midColor @colorStop, @endColor); + background-image: -o-linear-gradient(left, @startColor, @midColor @colorStop, @endColor); + background-image: linear-gradient(to right, @startColor, @midColor @colorStop, @endColor); + background-repeat: no-repeat; + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@startColor),argb(@endColor))); // IE9 and down, gets no color-stop at all for proper fallback + } + + .vertical-three-colors(@startColor: #00b3ee, @midColor: #7a43b6, @colorStop: 50%, @endColor: #c3325f) { + background-color: mix(@midColor, @endColor, 80%); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), color-stop(@colorStop, @midColor), to(@endColor)); + background-image: -webkit-linear-gradient(@startColor, @midColor @colorStop, @endColor); + background-image: -moz-linear-gradient(top, @startColor, @midColor @colorStop, @endColor); + background-image: -o-linear-gradient(@startColor, @midColor @colorStop, @endColor); + background-image: linear-gradient(@startColor, @midColor @colorStop, @endColor); + background-repeat: no-repeat; + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@startColor),argb(@endColor))); // IE9 and down, gets no color-stop at all for proper fallback + } + .radial(@innerColor: #555, @outerColor: #333) { + background-color: @outerColor; + background-image: -webkit-gradient(radial, center center, 0, center center, 460, from(@innerColor), to(@outerColor)); + background-image: -webkit-radial-gradient(circle, @innerColor, @outerColor); + background-image: -moz-radial-gradient(circle, @innerColor, @outerColor); + background-image: -o-radial-gradient(circle, @innerColor, @outerColor); + background-repeat: no-repeat; + } + .striped(@color: #555, @angle: 45deg) { + background-color: @color; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(.25, rgba(255,255,255,.15)), color-stop(.25, transparent), color-stop(.5, transparent), color-stop(.5, rgba(255,255,255,.15)), color-stop(.75, rgba(255,255,255,.15)), color-stop(.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); + } +} +// Reset filters for IE +.reset-filter() { + filter: e(%("progid:DXImageTransform.Microsoft.gradient(enabled = false)")); +} + + + +// COMPONENT MIXINS +// -------------------------------------------------- + +// Horizontal dividers +// ------------------------- +// Dividers (basically an hr) within dropdowns and nav lists +.nav-divider(@top: #e5e5e5, @bottom: @white) { + // IE7 needs a set width since we gave a height. Restricting just + // to IE7 to keep the 1px left/right space in other browsers. + // It is unclear where IE is getting the extra space that we need + // to negative-margin away, but so it goes. + *width: 100%; + height: 1px; + margin: ((@baseLineHeight / 2) - 1) 1px; // 8px 1px + *margin: -5px 0 5px; + overflow: hidden; + background-color: @top; + border-bottom: 1px solid @bottom; +} + +// Button backgrounds +// ------------------ +.buttonBackground(@startColor, @endColor, @textColor: #fff, @textShadow: 0 -1px 0 rgba(0,0,0,.25)) { + // gradientBar will set the background to a pleasing blend of these, to support IE<=9 + .gradientBar(@startColor, @endColor, @textColor, @textShadow); + *background-color: @endColor; /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + .reset-filter(); + + // in these cases the gradient won't cover the background, so we override + &:hover, &:focus, &:active, &.active, &.disabled, &[disabled] { + color: @textColor; + background-color: @endColor; + *background-color: darken(@endColor, 5%); + } + + // IE 7 + 8 can't handle box-shadow to show active, so we darken a bit ourselves + &:active, + &.active { + background-color: darken(@endColor, 10%) e("\9"); + } +} + +// Navbar vertical align +// ------------------------- +// Vertically center elements in the navbar. +// Example: an element has a height of 30px, so write out `.navbarVerticalAlign(30px);` to calculate the appropriate top margin. +.navbarVerticalAlign(@elementHeight) { + margin-top: (@navbarHeight - @elementHeight) / 2; +} + + + +// Grid System +// ----------- + +// Centered container element +.container-fixed() { + margin-right: auto; + margin-left: auto; + .clearfix(); +} + +// Table columns +.tableColumns(@columnSpan: 1) { + float: none; // undo default grid column styles + width: ((@gridColumnWidth) * @columnSpan) + (@gridGutterWidth * (@columnSpan - 1)) - 16; // 16 is total padding on left and right of table cells + margin-left: 0; // undo default grid column styles +} + +// Make a Grid +// Use .makeRow and .makeColumn to assign semantic layouts grid system behavior +.makeRow() { + margin-left: @gridGutterWidth * -1; + .clearfix(); +} +.makeColumn(@columns: 1, @offset: 0) { + float: left; + margin-left: (@gridColumnWidth * @offset) + (@gridGutterWidth * (@offset - 1)) + (@gridGutterWidth * 2); + width: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns - 1)); +} + +// The Grid +#grid { + + .core (@gridColumnWidth, @gridGutterWidth) { + + .spanX (@index) when (@index > 0) { + .span@{index} { .span(@index); } + .spanX(@index - 1); + } + .spanX (0) {} + + .offsetX (@index) when (@index > 0) { + .offset@{index} { .offset(@index); } + .offsetX(@index - 1); + } + .offsetX (0) {} + + .offset (@columns) { + margin-left: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns + 1)); + } + + .span (@columns) { + width: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns - 1)); + } + + .row { + margin-left: @gridGutterWidth * -1; + .clearfix(); + } + + [class*="span"] { + float: left; + min-height: 1px; // prevent collapsing columns + margin-left: @gridGutterWidth; + } + + // Set the container width, and override it for fixed navbars in media queries + .container, + .navbar-static-top .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { .span(@gridColumns); } + + // generate .spanX and .offsetX + .spanX (@gridColumns); + .offsetX (@gridColumns); + + } + + .fluid (@fluidGridColumnWidth, @fluidGridGutterWidth) { + + .spanX (@index) when (@index > 0) { + .span@{index} { .span(@index); } + .spanX(@index - 1); + } + .spanX (0) {} + + .offsetX (@index) when (@index > 0) { + .offset@{index} { .offset(@index); } + .offset@{index}:first-child { .offsetFirstChild(@index); } + .offsetX(@index - 1); + } + .offsetX (0) {} + + .offset (@columns) { + margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) + (@fluidGridGutterWidth*2); + *margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) - (.5 / @gridRowWidth * 100 * 1%) + (@fluidGridGutterWidth*2) - (.5 / @gridRowWidth * 100 * 1%); + } + + .offsetFirstChild (@columns) { + margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) + (@fluidGridGutterWidth); + *margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) - (.5 / @gridRowWidth * 100 * 1%) + @fluidGridGutterWidth - (.5 / @gridRowWidth * 100 * 1%); + } + + .span (@columns) { + width: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)); + *width: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) - (.5 / @gridRowWidth * 100 * 1%); + } + + .row-fluid { + width: 100%; + .clearfix(); + [class*="span"] { + .input-block-level(); + float: left; + margin-left: @fluidGridGutterWidth; + *margin-left: @fluidGridGutterWidth - (.5 / @gridRowWidth * 100 * 1%); + } + [class*="span"]:first-child { + margin-left: 0; + } + + // Space grid-sized controls properly if multiple per line + .controls-row [class*="span"] + [class*="span"] { + margin-left: @fluidGridGutterWidth; + } + + // generate .spanX and .offsetX + .spanX (@gridColumns); + .offsetX (@gridColumns); + } + + } + + .input(@gridColumnWidth, @gridGutterWidth) { + + .spanX (@index) when (@index > 0) { + input.span@{index}, textarea.span@{index}, .uneditable-input.span@{index} { .span(@index); } + .spanX(@index - 1); + } + .spanX (0) {} + + .span(@columns) { + width: ((@gridColumnWidth) * @columns) + (@gridGutterWidth * (@columns - 1)) - 14; + } + + input, + textarea, + .uneditable-input { + margin-left: 0; // override margin-left from core grid system + } + + // Space grid-sized controls properly if multiple per line + .controls-row [class*="span"] + [class*="span"] { + margin-left: @gridGutterWidth; + } + + // generate .spanX + .spanX (@gridColumns); + + } +} diff --git a/public/css/bootstrap/modals.less b/public/css/bootstrap/modals.less new file mode 100644 index 0000000..8e272d4 --- /dev/null +++ b/public/css/bootstrap/modals.less @@ -0,0 +1,95 @@ +// +// Modals +// -------------------------------------------------- + +// Background +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: @zindexModalBackdrop; + background-color: @black; + // Fade for backdrop + &.fade { opacity: 0; } +} + +.modal-backdrop, +.modal-backdrop.fade.in { + .opacity(80); +} + +// Base modal +.modal { + position: fixed; + top: 10%; + left: 50%; + z-index: @zindexModal; + width: 560px; + margin-left: -280px; + background-color: @white; + border: 1px solid #999; + border: 1px solid rgba(0,0,0,.3); + *border: 1px solid #999; /* IE6-7 */ + .border-radius(6px); + .box-shadow(0 3px 7px rgba(0,0,0,0.3)); + .background-clip(padding-box); + // Remove focus outline from opened modal + outline: none; + + &.fade { + .transition(e('opacity .3s linear, top .3s ease-out')); + top: -25%; + } + &.fade.in { top: 10%; } +} +.modal-header { + padding: 9px 15px; + border-bottom: 1px solid #eee; + // Close icon + .close { margin-top: 2px; } + // Heading + h3 { + margin: 0; + line-height: 30px; + } +} + +// Body (where all modal content resides) +.modal-body { + position: relative; + overflow-y: auto; + max-height: 400px; + padding: 15px; +} +// Remove bottom margin if need be +.modal-form { + margin-bottom: 0; +} + +// Footer (for actions) +.modal-footer { + padding: 14px 15px 15px; + margin-bottom: 0; + text-align: right; // right align buttons + background-color: #f5f5f5; + border-top: 1px solid #ddd; + .border-radius(0 0 6px 6px); + .box-shadow(inset 0 1px 0 @white); + .clearfix(); // clear it in case folks use .pull-* classes on buttons + + // Properly space out buttons + .btn + .btn { + margin-left: 5px; + margin-bottom: 0; // account for input[type="submit"] which gets the bottom margin like all other inputs + } + // but override that for button groups + .btn-group .btn + .btn { + margin-left: -1px; + } + // and override it for block buttons as well + .btn-block + .btn-block { + margin-left: 0; + } +} diff --git a/public/css/bootstrap/navbar.less b/public/css/bootstrap/navbar.less new file mode 100644 index 0000000..93d09bc --- /dev/null +++ b/public/css/bootstrap/navbar.less @@ -0,0 +1,497 @@ +// +// Navbars (Redux) +// -------------------------------------------------- + + +// COMMON STYLES +// ------------- + +// Base class and wrapper +.navbar { + overflow: visible; + margin-bottom: @baseLineHeight; + + // Fix for IE7's bad z-indexing so dropdowns don't appear below content that follows the navbar + *position: relative; + *z-index: 2; +} + +// Inner for background effects +// Gradient is applied to its own element because overflow visible is not honored by IE when filter is present +.navbar-inner { + min-height: @navbarHeight; + padding-left: 20px; + padding-right: 20px; + #gradient > .vertical(@navbarBackgroundHighlight, @navbarBackground); + border: 1px solid @navbarBorder; + .border-radius(@baseBorderRadius); + .box-shadow(0 1px 4px rgba(0,0,0,.065)); + + // Prevent floats from breaking the navbar + .clearfix(); +} + +// Set width to auto for default container +// We then reset it for fixed navbars in the #gridSystem mixin +.navbar .container { + width: auto; +} + +// Override the default collapsed state +.nav-collapse.collapse { + height: auto; + overflow: visible; +} + + +// Brand: website or project name +// ------------------------- +.navbar .brand { + float: left; + display: block; + // Vertically center the text given @navbarHeight + padding: ((@navbarHeight - @baseLineHeight) / 2) 20px ((@navbarHeight - @baseLineHeight) / 2); + margin-left: -20px; // negative indent to left-align the text down the page + font-size: 20px; + font-weight: 200; + color: @navbarBrandColor; + text-shadow: 0 1px 0 @navbarBackgroundHighlight; + &:hover, + &:focus { + text-decoration: none; + } +} + +// Plain text in topbar +// ------------------------- +.navbar-text { + margin-bottom: 0; + line-height: @navbarHeight; + color: @navbarText; +} + +// Janky solution for now to account for links outside the .nav +// ------------------------- +.navbar-link { + color: @navbarLinkColor; + &:hover, + &:focus { + color: @navbarLinkColorHover; + } +} + +// Dividers in navbar +// ------------------------- +.navbar .divider-vertical { + height: @navbarHeight; + margin: 0 9px; + border-left: 1px solid @navbarBackground; + border-right: 1px solid @navbarBackgroundHighlight; +} + +// Buttons in navbar +// ------------------------- +.navbar .btn, +.navbar .btn-group { + .navbarVerticalAlign(30px); // Vertically center in navbar +} +.navbar .btn-group .btn, +.navbar .input-prepend .btn, +.navbar .input-append .btn, +.navbar .input-prepend .btn-group, +.navbar .input-append .btn-group { + margin-top: 0; // then undo the margin here so we don't accidentally double it +} + +// Navbar forms +// ------------------------- +.navbar-form { + margin-bottom: 0; // remove default bottom margin + .clearfix(); + input, + select, + .radio, + .checkbox { + .navbarVerticalAlign(30px); // Vertically center in navbar + } + input, + select, + .btn { + display: inline-block; + margin-bottom: 0; + } + input[type="image"], + input[type="checkbox"], + input[type="radio"] { + margin-top: 3px; + } + .input-append, + .input-prepend { + margin-top: 5px; + white-space: nowrap; // preven two items from separating within a .navbar-form that has .pull-left + input { + margin-top: 0; // remove the margin on top since it's on the parent + } + } +} + +// Navbar search +// ------------------------- +.navbar-search { + position: relative; + float: left; + .navbarVerticalAlign(30px); // Vertically center in navbar + margin-bottom: 0; + .search-query { + margin-bottom: 0; + padding: 4px 14px; + #font > .sans-serif(13px, normal, 1); + .border-radius(15px); // redeclare because of specificity of the type attribute + } +} + + + +// Static navbar +// ------------------------- + +.navbar-static-top { + position: static; + margin-bottom: 0; // remove 18px margin for default navbar + .navbar-inner { + .border-radius(0); + } +} + + + +// Fixed navbar +// ------------------------- + +// Shared (top/bottom) styles +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: @zindexFixedNavbar; + margin-bottom: 0; // remove 18px margin for default navbar +} +.navbar-fixed-top .navbar-inner, +.navbar-static-top .navbar-inner { + border-width: 0 0 1px; +} +.navbar-fixed-bottom .navbar-inner { + border-width: 1px 0 0; +} +.navbar-fixed-top .navbar-inner, +.navbar-fixed-bottom .navbar-inner { + padding-left: 0; + padding-right: 0; + .border-radius(0); +} + +// Reset container width +// Required here as we reset the width earlier on and the grid mixins don't override early enough +.navbar-static-top .container, +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + #grid > .core > .span(@gridColumns); +} + +// Fixed to top +.navbar-fixed-top { + top: 0; +} +.navbar-fixed-top, +.navbar-static-top { + .navbar-inner { + .box-shadow(~"0 1px 10px rgba(0,0,0,.1)"); + } +} + +// Fixed to bottom +.navbar-fixed-bottom { + bottom: 0; + .navbar-inner { + .box-shadow(~"0 -1px 10px rgba(0,0,0,.1)"); + } +} + + + +// NAVIGATION +// ---------- + +.navbar .nav { + position: relative; + left: 0; + display: block; + float: left; + margin: 0 10px 0 0; +} +.navbar .nav.pull-right { + float: right; // redeclare due to specificity + margin-right: 0; // remove margin on float right nav +} +.navbar .nav > li { + float: left; +} + +// Links +.navbar .nav > li > a { + float: none; + // Vertically center the text given @navbarHeight + padding: ((@navbarHeight - @baseLineHeight) / 2) 15px ((@navbarHeight - @baseLineHeight) / 2); + color: @navbarLinkColor; + text-decoration: none; + text-shadow: 0 1px 0 @navbarBackgroundHighlight; +} +.navbar .nav .dropdown-toggle .caret { + margin-top: 8px; +} + +// Hover/focus +.navbar .nav > li > a:focus, +.navbar .nav > li > a:hover { + background-color: @navbarLinkBackgroundHover; // "transparent" is default to differentiate :hover/:focus from .active + color: @navbarLinkColorHover; + text-decoration: none; +} + +// Active nav items +.navbar .nav > .active > a, +.navbar .nav > .active > a:hover, +.navbar .nav > .active > a:focus { + color: @navbarLinkColorActive; + text-decoration: none; + background-color: @navbarLinkBackgroundActive; + .box-shadow(inset 0 3px 8px rgba(0,0,0,.125)); +} + +// Navbar button for toggling navbar items in responsive layouts +// These definitions need to come after '.navbar .btn' +.navbar .btn-navbar { + display: none; + float: right; + padding: 7px 10px; + margin-left: 5px; + margin-right: 5px; + .buttonBackground(darken(@navbarBackgroundHighlight, 5%), darken(@navbarBackground, 5%)); + .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075)"); +} +.navbar .btn-navbar .icon-bar { + display: block; + width: 18px; + height: 2px; + background-color: #f5f5f5; + .border-radius(1px); + .box-shadow(0 1px 0 rgba(0,0,0,.25)); +} +.btn-navbar .icon-bar + .icon-bar { + margin-top: 3px; +} + + + +// Dropdown menus +// -------------- + +// Menu position and menu carets +.navbar .nav > li > .dropdown-menu { + &:before { + content: ''; + display: inline-block; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-bottom-color: @dropdownBorder; + position: absolute; + top: -7px; + left: 9px; + } + &:after { + content: ''; + display: inline-block; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid @dropdownBackground; + position: absolute; + top: -6px; + left: 10px; + } +} +// Menu position and menu caret support for dropups via extra dropup class +.navbar-fixed-bottom .nav > li > .dropdown-menu { + &:before { + border-top: 7px solid #ccc; + border-top-color: @dropdownBorder; + border-bottom: 0; + bottom: -7px; + top: auto; + } + &:after { + border-top: 6px solid @dropdownBackground; + border-bottom: 0; + bottom: -6px; + top: auto; + } +} + +// Caret should match text color on hover/focus +.navbar .nav li.dropdown > a:hover .caret, +.navbar .nav li.dropdown > a:focus .caret { + border-top-color: @navbarLinkColorHover; + border-bottom-color: @navbarLinkColorHover; +} + +// Remove background color from open dropdown +.navbar .nav li.dropdown.open > .dropdown-toggle, +.navbar .nav li.dropdown.active > .dropdown-toggle, +.navbar .nav li.dropdown.open.active > .dropdown-toggle { + background-color: @navbarLinkBackgroundActive; + color: @navbarLinkColorActive; +} +.navbar .nav li.dropdown > .dropdown-toggle .caret { + border-top-color: @navbarLinkColor; + border-bottom-color: @navbarLinkColor; +} +.navbar .nav li.dropdown.open > .dropdown-toggle .caret, +.navbar .nav li.dropdown.active > .dropdown-toggle .caret, +.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret { + border-top-color: @navbarLinkColorActive; + border-bottom-color: @navbarLinkColorActive; +} + +// Right aligned menus need alt position +.navbar .pull-right > li > .dropdown-menu, +.navbar .nav > li > .dropdown-menu.pull-right { + left: auto; + right: 0; + &:before { + left: auto; + right: 12px; + } + &:after { + left: auto; + right: 13px; + } + .dropdown-menu { + left: auto; + right: 100%; + margin-left: 0; + margin-right: -1px; + .border-radius(6px 0 6px 6px); + } +} + + +// Inverted navbar +// ------------------------- + +.navbar-inverse { + + .navbar-inner { + #gradient > .vertical(@navbarInverseBackgroundHighlight, @navbarInverseBackground); + border-color: @navbarInverseBorder; + } + + .brand, + .nav > li > a { + color: @navbarInverseLinkColor; + text-shadow: 0 -1px 0 rgba(0,0,0,.25); + &:hover, + &:focus { + color: @navbarInverseLinkColorHover; + } + } + + .brand { + color: @navbarInverseBrandColor; + } + + .navbar-text { + color: @navbarInverseText; + } + + .nav > li > a:focus, + .nav > li > a:hover { + background-color: @navbarInverseLinkBackgroundHover; + color: @navbarInverseLinkColorHover; + } + + .nav .active > a, + .nav .active > a:hover, + .nav .active > a:focus { + color: @navbarInverseLinkColorActive; + background-color: @navbarInverseLinkBackgroundActive; + } + + // Inline text links + .navbar-link { + color: @navbarInverseLinkColor; + &:hover, + &:focus { + color: @navbarInverseLinkColorHover; + } + } + + // Dividers in navbar + .divider-vertical { + border-left-color: @navbarInverseBackground; + border-right-color: @navbarInverseBackgroundHighlight; + } + + // Dropdowns + .nav li.dropdown.open > .dropdown-toggle, + .nav li.dropdown.active > .dropdown-toggle, + .nav li.dropdown.open.active > .dropdown-toggle { + background-color: @navbarInverseLinkBackgroundActive; + color: @navbarInverseLinkColorActive; + } + .nav li.dropdown > a:hover .caret, + .nav li.dropdown > a:focus .caret { + border-top-color: @navbarInverseLinkColorActive; + border-bottom-color: @navbarInverseLinkColorActive; + } + .nav li.dropdown > .dropdown-toggle .caret { + border-top-color: @navbarInverseLinkColor; + border-bottom-color: @navbarInverseLinkColor; + } + .nav li.dropdown.open > .dropdown-toggle .caret, + .nav li.dropdown.active > .dropdown-toggle .caret, + .nav li.dropdown.open.active > .dropdown-toggle .caret { + border-top-color: @navbarInverseLinkColorActive; + border-bottom-color: @navbarInverseLinkColorActive; + } + + // Navbar search + .navbar-search { + .search-query { + color: @white; + background-color: @navbarInverseSearchBackground; + border-color: @navbarInverseSearchBorder; + .box-shadow(~"inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15)"); + .transition(none); + .placeholder(@navbarInverseSearchPlaceholderColor); + + // Focus states (we use .focused since IE7-8 and down doesn't support :focus) + &:focus, + &.focused { + padding: 5px 15px; + color: @grayDark; + text-shadow: 0 1px 0 @white; + background-color: @navbarInverseSearchBackgroundFocus; + border: 0; + .box-shadow(0 0 3px rgba(0,0,0,.15)); + outline: 0; + } + } + } + + // Navbar collapse button + .btn-navbar { + .buttonBackground(darken(@navbarInverseBackgroundHighlight, 5%), darken(@navbarInverseBackground, 5%)); + } + +} diff --git a/public/css/bootstrap/navs.less b/public/css/bootstrap/navs.less new file mode 100644 index 0000000..01cd805 --- /dev/null +++ b/public/css/bootstrap/navs.less @@ -0,0 +1,409 @@ +// +// Navs +// -------------------------------------------------- + + +// BASE CLASS +// ---------- + +.nav { + margin-left: 0; + margin-bottom: @baseLineHeight; + list-style: none; +} + +// Make links block level +.nav > li > a { + display: block; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: @grayLighter; +} + +// Prevent IE8 from misplacing imgs +// See https://github.com/h5bp/html5-boilerplate/issues/984#issuecomment-3985989 +.nav > li > a > img { + max-width: none; +} + +// Redeclare pull classes because of specifity +.nav > .pull-right { + float: right; +} + +// Nav headers (for dropdowns and lists) +.nav-header { + display: block; + padding: 3px 15px; + font-size: 11px; + font-weight: bold; + line-height: @baseLineHeight; + color: @grayLight; + text-shadow: 0 1px 0 rgba(255,255,255,.5); + text-transform: uppercase; +} +// Space them out when they follow another list item (link) +.nav li + .nav-header { + margin-top: 9px; +} + + + +// NAV LIST +// -------- + +.nav-list { + padding-left: 15px; + padding-right: 15px; + margin-bottom: 0; +} +.nav-list > li > a, +.nav-list .nav-header { + margin-left: -15px; + margin-right: -15px; + text-shadow: 0 1px 0 rgba(255,255,255,.5); +} +.nav-list > li > a { + padding: 3px 15px; +} +.nav-list > .active > a, +.nav-list > .active > a:hover, +.nav-list > .active > a:focus { + color: @white; + text-shadow: 0 -1px 0 rgba(0,0,0,.2); + background-color: @linkColor; +} +.nav-list [class^="icon-"], +.nav-list [class*=" icon-"] { + margin-right: 2px; +} +// Dividers (basically an hr) within the dropdown +.nav-list .divider { + .nav-divider(); +} + + + +// TABS AND PILLS +// ------------- + +// Common styles +.nav-tabs, +.nav-pills { + .clearfix(); +} +.nav-tabs > li, +.nav-pills > li { + float: left; +} +.nav-tabs > li > a, +.nav-pills > li > a { + padding-right: 12px; + padding-left: 12px; + margin-right: 2px; + line-height: 14px; // keeps the overall height an even number +} + +// TABS +// ---- + +// Give the tabs something to sit on +.nav-tabs { + border-bottom: 1px solid #ddd; +} +// Make the list-items overlay the bottom border +.nav-tabs > li { + margin-bottom: -1px; +} +// Actual tabs (as links) +.nav-tabs > li > a { + padding-top: 8px; + padding-bottom: 8px; + line-height: @baseLineHeight; + border: 1px solid transparent; + .border-radius(4px 4px 0 0); + &:hover, + &:focus { + border-color: @grayLighter @grayLighter #ddd; + } +} +// Active state, and it's :hover/:focus to override normal :hover/:focus +.nav-tabs > .active > a, +.nav-tabs > .active > a:hover, +.nav-tabs > .active > a:focus { + color: @gray; + background-color: @bodyBackground; + border: 1px solid #ddd; + border-bottom-color: transparent; + cursor: default; +} + + +// PILLS +// ----- + +// Links rendered as pills +.nav-pills > li > a { + padding-top: 8px; + padding-bottom: 8px; + margin-top: 2px; + margin-bottom: 2px; + .border-radius(5px); +} + +// Active state +.nav-pills > .active > a, +.nav-pills > .active > a:hover, +.nav-pills > .active > a:focus { + color: @white; + background-color: @linkColor; +} + + + +// STACKED NAV +// ----------- + +// Stacked tabs and pills +.nav-stacked > li { + float: none; +} +.nav-stacked > li > a { + margin-right: 0; // no need for the gap between nav items +} + +// Tabs +.nav-tabs.nav-stacked { + border-bottom: 0; +} +.nav-tabs.nav-stacked > li > a { + border: 1px solid #ddd; + .border-radius(0); +} +.nav-tabs.nav-stacked > li:first-child > a { + .border-top-radius(4px); +} +.nav-tabs.nav-stacked > li:last-child > a { + .border-bottom-radius(4px); +} +.nav-tabs.nav-stacked > li > a:hover, +.nav-tabs.nav-stacked > li > a:focus { + border-color: #ddd; + z-index: 2; +} + +// Pills +.nav-pills.nav-stacked > li > a { + margin-bottom: 3px; +} +.nav-pills.nav-stacked > li:last-child > a { + margin-bottom: 1px; // decrease margin to match sizing of stacked tabs +} + + + +// DROPDOWNS +// --------- + +.nav-tabs .dropdown-menu { + .border-radius(0 0 6px 6px); // remove the top rounded corners here since there is a hard edge above the menu +} +.nav-pills .dropdown-menu { + .border-radius(6px); // make rounded corners match the pills +} + +// Default dropdown links +// ------------------------- +// Make carets use linkColor to start +.nav .dropdown-toggle .caret { + border-top-color: @linkColor; + border-bottom-color: @linkColor; + margin-top: 6px; +} +.nav .dropdown-toggle:hover .caret, +.nav .dropdown-toggle:focus .caret { + border-top-color: @linkColorHover; + border-bottom-color: @linkColorHover; +} +/* move down carets for tabs */ +.nav-tabs .dropdown-toggle .caret { + margin-top: 8px; +} + +// Active dropdown links +// ------------------------- +.nav .active .dropdown-toggle .caret { + border-top-color: #fff; + border-bottom-color: #fff; +} +.nav-tabs .active .dropdown-toggle .caret { + border-top-color: @gray; + border-bottom-color: @gray; +} + +// Active:hover/:focus dropdown links +// ------------------------- +.nav > .dropdown.active > a:hover, +.nav > .dropdown.active > a:focus { + cursor: pointer; +} + +// Open dropdowns +// ------------------------- +.nav-tabs .open .dropdown-toggle, +.nav-pills .open .dropdown-toggle, +.nav > li.dropdown.open.active > a:hover, +.nav > li.dropdown.open.active > a:focus { + color: @white; + background-color: @grayLight; + border-color: @grayLight; +} +.nav li.dropdown.open .caret, +.nav li.dropdown.open.active .caret, +.nav li.dropdown.open a:hover .caret, +.nav li.dropdown.open a:focus .caret { + border-top-color: @white; + border-bottom-color: @white; + .opacity(100); +} + +// Dropdowns in stacked tabs +.tabs-stacked .open > a:hover, +.tabs-stacked .open > a:focus { + border-color: @grayLight; +} + + + +// TABBABLE +// -------- + + +// COMMON STYLES +// ------------- + +// Clear any floats +.tabbable { + .clearfix(); +} +.tab-content { + overflow: auto; // prevent content from running below tabs +} + +// Remove border on bottom, left, right +.tabs-below > .nav-tabs, +.tabs-right > .nav-tabs, +.tabs-left > .nav-tabs { + border-bottom: 0; +} + +// Show/hide tabbable areas +.tab-content > .tab-pane, +.pill-content > .pill-pane { + display: none; +} +.tab-content > .active, +.pill-content > .active { + display: block; +} + + +// BOTTOM +// ------ + +.tabs-below > .nav-tabs { + border-top: 1px solid #ddd; +} +.tabs-below > .nav-tabs > li { + margin-top: -1px; + margin-bottom: 0; +} +.tabs-below > .nav-tabs > li > a { + .border-radius(0 0 4px 4px); + &:hover, + &:focus { + border-bottom-color: transparent; + border-top-color: #ddd; + } +} +.tabs-below > .nav-tabs > .active > a, +.tabs-below > .nav-tabs > .active > a:hover, +.tabs-below > .nav-tabs > .active > a:focus { + border-color: transparent #ddd #ddd #ddd; +} + +// LEFT & RIGHT +// ------------ + +// Common styles +.tabs-left > .nav-tabs > li, +.tabs-right > .nav-tabs > li { + float: none; +} +.tabs-left > .nav-tabs > li > a, +.tabs-right > .nav-tabs > li > a { + min-width: 74px; + margin-right: 0; + margin-bottom: 3px; +} + +// Tabs on the left +.tabs-left > .nav-tabs { + float: left; + margin-right: 19px; + border-right: 1px solid #ddd; +} +.tabs-left > .nav-tabs > li > a { + margin-right: -1px; + .border-radius(4px 0 0 4px); +} +.tabs-left > .nav-tabs > li > a:hover, +.tabs-left > .nav-tabs > li > a:focus { + border-color: @grayLighter #ddd @grayLighter @grayLighter; +} +.tabs-left > .nav-tabs .active > a, +.tabs-left > .nav-tabs .active > a:hover, +.tabs-left > .nav-tabs .active > a:focus { + border-color: #ddd transparent #ddd #ddd; + *border-right-color: @white; +} + +// Tabs on the right +.tabs-right > .nav-tabs { + float: right; + margin-left: 19px; + border-left: 1px solid #ddd; +} +.tabs-right > .nav-tabs > li > a { + margin-left: -1px; + .border-radius(0 4px 4px 0); +} +.tabs-right > .nav-tabs > li > a:hover, +.tabs-right > .nav-tabs > li > a:focus { + border-color: @grayLighter @grayLighter @grayLighter #ddd; +} +.tabs-right > .nav-tabs .active > a, +.tabs-right > .nav-tabs .active > a:hover, +.tabs-right > .nav-tabs .active > a:focus { + border-color: #ddd #ddd #ddd transparent; + *border-left-color: @white; +} + + + +// DISABLED STATES +// --------------- + +// Gray out text +.nav > .disabled > a { + color: @grayLight; +} +// Nuke hover/focus effects +.nav > .disabled > a:hover, +.nav > .disabled > a:focus { + text-decoration: none; + background-color: transparent; + cursor: default; +} diff --git a/public/css/bootstrap/pager.less b/public/css/bootstrap/pager.less new file mode 100644 index 0000000..1476188 --- /dev/null +++ b/public/css/bootstrap/pager.less @@ -0,0 +1,43 @@ +// +// Pager pagination +// -------------------------------------------------- + + +.pager { + margin: @baseLineHeight 0; + list-style: none; + text-align: center; + .clearfix(); +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + .border-radius(15px); +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #f5f5f5; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: @grayLight; + background-color: #fff; + cursor: default; +} \ No newline at end of file diff --git a/public/css/bootstrap/pagination.less b/public/css/bootstrap/pagination.less new file mode 100644 index 0000000..a789db2 --- /dev/null +++ b/public/css/bootstrap/pagination.less @@ -0,0 +1,123 @@ +// +// Pagination (multiple pages) +// -------------------------------------------------- + +// Space out pagination from surrounding content +.pagination { + margin: @baseLineHeight 0; +} + +.pagination ul { + // Allow for text-based alignment + display: inline-block; + .ie7-inline-block(); + // Reset default ul styles + margin-left: 0; + margin-bottom: 0; + // Visuals + .border-radius(@baseBorderRadius); + .box-shadow(0 1px 2px rgba(0,0,0,.05)); +} +.pagination ul > li { + display: inline; // Remove list-style and block-level defaults +} +.pagination ul > li > a, +.pagination ul > li > span { + float: left; // Collapse white-space + padding: 4px 12px; + line-height: @baseLineHeight; + text-decoration: none; + background-color: @paginationBackground; + border: 1px solid @paginationBorder; + border-left-width: 0; +} +.pagination ul > li > a:hover, +.pagination ul > li > a:focus, +.pagination ul > .active > a, +.pagination ul > .active > span { + background-color: @paginationActiveBackground; +} +.pagination ul > .active > a, +.pagination ul > .active > span { + color: @grayLight; + cursor: default; +} +.pagination ul > .disabled > span, +.pagination ul > .disabled > a, +.pagination ul > .disabled > a:hover, +.pagination ul > .disabled > a:focus { + color: @grayLight; + background-color: transparent; + cursor: default; +} +.pagination ul > li:first-child > a, +.pagination ul > li:first-child > span { + border-left-width: 1px; + .border-left-radius(@baseBorderRadius); +} +.pagination ul > li:last-child > a, +.pagination ul > li:last-child > span { + .border-right-radius(@baseBorderRadius); +} + + +// Alignment +// -------------------------------------------------- + +.pagination-centered { + text-align: center; +} +.pagination-right { + text-align: right; +} + + +// Sizing +// -------------------------------------------------- + +// Large +.pagination-large { + ul > li > a, + ul > li > span { + padding: @paddingLarge; + font-size: @fontSizeLarge; + } + ul > li:first-child > a, + ul > li:first-child > span { + .border-left-radius(@borderRadiusLarge); + } + ul > li:last-child > a, + ul > li:last-child > span { + .border-right-radius(@borderRadiusLarge); + } +} + +// Small and mini +.pagination-mini, +.pagination-small { + ul > li:first-child > a, + ul > li:first-child > span { + .border-left-radius(@borderRadiusSmall); + } + ul > li:last-child > a, + ul > li:last-child > span { + .border-right-radius(@borderRadiusSmall); + } +} + +// Small +.pagination-small { + ul > li > a, + ul > li > span { + padding: @paddingSmall; + font-size: @fontSizeSmall; + } +} +// Mini +.pagination-mini { + ul > li > a, + ul > li > span { + padding: @paddingMini; + font-size: @fontSizeMini; + } +} diff --git a/public/css/bootstrap/popovers.less b/public/css/bootstrap/popovers.less new file mode 100644 index 0000000..aae35c8 --- /dev/null +++ b/public/css/bootstrap/popovers.less @@ -0,0 +1,133 @@ +// +// Popovers +// -------------------------------------------------- + + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: @zindexPopover; + display: none; + max-width: 276px; + padding: 1px; + text-align: left; // Reset given new insertion method + background-color: @popoverBackground; + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0,0,0,.2); + .border-radius(6px); + .box-shadow(0 5px 10px rgba(0,0,0,.2)); + + // Overrides for proper insertion + white-space: normal; + + // Offset the popover to account for the popover arrow + &.top { margin-top: -10px; } + &.right { margin-left: 10px; } + &.bottom { margin-top: 10px; } + &.left { margin-left: -10px; } +} + +.popover-title { + margin: 0; // reset heading margin + padding: 8px 14px; + font-size: 14px; + font-weight: normal; + line-height: 18px; + background-color: @popoverTitleBackground; + border-bottom: 1px solid darken(@popoverTitleBackground, 5%); + .border-radius(5px 5px 0 0); + + &:empty { + display: none; + } +} + +.popover-content { + padding: 9px 14px; +} + +// Arrows +// +// .arrow is outer, .arrow:after is inner + +.popover .arrow, +.popover .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover .arrow { + border-width: @popoverArrowOuterWidth; +} +.popover .arrow:after { + border-width: @popoverArrowWidth; + content: ""; +} + +.popover { + &.top .arrow { + left: 50%; + margin-left: -@popoverArrowOuterWidth; + border-bottom-width: 0; + border-top-color: #999; // IE8 fallback + border-top-color: @popoverArrowOuterColor; + bottom: -@popoverArrowOuterWidth; + &:after { + bottom: 1px; + margin-left: -@popoverArrowWidth; + border-bottom-width: 0; + border-top-color: @popoverArrowColor; + } + } + &.right .arrow { + top: 50%; + left: -@popoverArrowOuterWidth; + margin-top: -@popoverArrowOuterWidth; + border-left-width: 0; + border-right-color: #999; // IE8 fallback + border-right-color: @popoverArrowOuterColor; + &:after { + left: 1px; + bottom: -@popoverArrowWidth; + border-left-width: 0; + border-right-color: @popoverArrowColor; + } + } + &.bottom .arrow { + left: 50%; + margin-left: -@popoverArrowOuterWidth; + border-top-width: 0; + border-bottom-color: #999; // IE8 fallback + border-bottom-color: @popoverArrowOuterColor; + top: -@popoverArrowOuterWidth; + &:after { + top: 1px; + margin-left: -@popoverArrowWidth; + border-top-width: 0; + border-bottom-color: @popoverArrowColor; + } + } + + &.left .arrow { + top: 50%; + right: -@popoverArrowOuterWidth; + margin-top: -@popoverArrowOuterWidth; + border-right-width: 0; + border-left-color: #999; // IE8 fallback + border-left-color: @popoverArrowOuterColor; + &:after { + right: 1px; + border-right-width: 0; + border-left-color: @popoverArrowColor; + bottom: -@popoverArrowWidth; + } + } + +} diff --git a/public/css/bootstrap/progress-bars.less b/public/css/bootstrap/progress-bars.less new file mode 100644 index 0000000..5e0c3dd --- /dev/null +++ b/public/css/bootstrap/progress-bars.less @@ -0,0 +1,122 @@ +// +// Progress bars +// -------------------------------------------------- + + +// ANIMATIONS +// ---------- + +// Webkit +@-webkit-keyframes progress-bar-stripes { + from { background-position: 40px 0; } + to { background-position: 0 0; } +} + +// Firefox +@-moz-keyframes progress-bar-stripes { + from { background-position: 40px 0; } + to { background-position: 0 0; } +} + +// IE9 +@-ms-keyframes progress-bar-stripes { + from { background-position: 40px 0; } + to { background-position: 0 0; } +} + +// Opera +@-o-keyframes progress-bar-stripes { + from { background-position: 0 0; } + to { background-position: 40px 0; } +} + +// Spec +@keyframes progress-bar-stripes { + from { background-position: 40px 0; } + to { background-position: 0 0; } +} + + + +// THE BARS +// -------- + +// Outer container +.progress { + overflow: hidden; + height: @baseLineHeight; + margin-bottom: @baseLineHeight; + #gradient > .vertical(#f5f5f5, #f9f9f9); + .box-shadow(inset 0 1px 2px rgba(0,0,0,.1)); + .border-radius(@baseBorderRadius); +} + +// Bar of progress +.progress .bar { + width: 0%; + height: 100%; + color: @white; + float: left; + font-size: 12px; + text-align: center; + text-shadow: 0 -1px 0 rgba(0,0,0,.25); + #gradient > .vertical(#149bdf, #0480be); + .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15)); + .box-sizing(border-box); + .transition(width .6s ease); +} +.progress .bar + .bar { + .box-shadow(~"inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15)"); +} + +// Striped bars +.progress-striped .bar { + #gradient > .striped(#149bdf); + .background-size(40px 40px); +} + +// Call animation for the active one +.progress.active .bar { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -moz-animation: progress-bar-stripes 2s linear infinite; + -ms-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} + + + +// COLORS +// ------ + +// Danger (red) +.progress-danger .bar, .progress .bar-danger { + #gradient > .vertical(#ee5f5b, #c43c35); +} +.progress-danger.progress-striped .bar, .progress-striped .bar-danger { + #gradient > .striped(#ee5f5b); +} + +// Success (green) +.progress-success .bar, .progress .bar-success { + #gradient > .vertical(#62c462, #57a957); +} +.progress-success.progress-striped .bar, .progress-striped .bar-success { + #gradient > .striped(#62c462); +} + +// Info (teal) +.progress-info .bar, .progress .bar-info { + #gradient > .vertical(#5bc0de, #339bb9); +} +.progress-info.progress-striped .bar, .progress-striped .bar-info { + #gradient > .striped(#5bc0de); +} + +// Warning (orange) +.progress-warning .bar, .progress .bar-warning { + #gradient > .vertical(lighten(@orange, 15%), @orange); +} +.progress-warning.progress-striped .bar, .progress-striped .bar-warning { + #gradient > .striped(lighten(@orange, 15%)); +} diff --git a/public/css/bootstrap/reset.less b/public/css/bootstrap/reset.less new file mode 100644 index 0000000..4806bd5 --- /dev/null +++ b/public/css/bootstrap/reset.less @@ -0,0 +1,216 @@ +// +// Reset CSS +// Adapted from http://github.com/necolas/normalize.css +// -------------------------------------------------- + + +// Display in IE6-9 and FF3 +// ------------------------- + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section { + display: block; +} + +// Display block in IE6-9 and FF3 +// ------------------------- + +audio, +canvas, +video { + display: inline-block; + *display: inline; + *zoom: 1; +} + +// Prevents modern browsers from displaying 'audio' without controls +// ------------------------- + +audio:not([controls]) { + display: none; +} + +// Base settings +// ------------------------- + +html { + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +// Focus states +a:focus { + .tab-focus(); +} +// Hover & Active +a:hover, +a:active { + outline: 0; +} + +// Prevents sub and sup affecting line-height in all browsers +// ------------------------- + +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} +sup { + top: -0.5em; +} +sub { + bottom: -0.25em; +} + +// Img border in a's and image quality +// ------------------------- + +img { + /* Responsive images (ensure images don't scale beyond their parents) */ + max-width: 100%; /* Part 1: Set a maxium relative to the parent */ + width: auto\9; /* IE7-8 need help adjusting responsive images */ + height: auto; /* Part 2: Scale the height according to the width, otherwise you get stretching */ + + vertical-align: middle; + border: 0; + -ms-interpolation-mode: bicubic; +} + +// Prevent max-width from affecting Google Maps +#map_canvas img, +.google-maps img { + max-width: none; +} + +// Forms +// ------------------------- + +// Font size in all browsers, margin changes, misc consistency +button, +input, +select, +textarea { + margin: 0; + font-size: 100%; + vertical-align: middle; +} +button, +input { + *overflow: visible; // Inner spacing ie IE6/7 + line-height: normal; // FF3/4 have !important on line-height in UA stylesheet +} +button::-moz-focus-inner, +input::-moz-focus-inner { // Inner padding and border oddities in FF3/4 + padding: 0; + border: 0; +} +button, +html input[type="button"], // Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; // Corrects inability to style clickable `input` types in iOS. + cursor: pointer; // Improves usability and consistency of cursor style between image-type `input` and others. +} +label, +select, +button, +input[type="button"], +input[type="reset"], +input[type="submit"], +input[type="radio"], +input[type="checkbox"] { + cursor: pointer; // Improves usability and consistency of cursor style between image-type `input` and others. +} +input[type="search"] { // Appearance in Safari/Chrome + .box-sizing(content-box); + -webkit-appearance: textfield; +} +input[type="search"]::-webkit-search-decoration, +input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; // Inner-padding issues in Chrome OSX, Safari 5 +} +textarea { + overflow: auto; // Remove vertical scrollbar in IE6-9 + vertical-align: top; // Readability and alignment cross-browser +} + + +// Printing +// ------------------------- +// Source: https://github.com/h5bp/html5-boilerplate/blob/master/css/main.css + +@media print { + + * { + text-shadow: none !important; + color: #000 !important; // Black prints faster: h5bp.com/s + background: transparent !important; + box-shadow: none !important; + } + + a, + a:visited { + text-decoration: underline; + } + + a[href]:after { + content: " (" attr(href) ")"; + } + + abbr[title]:after { + content: " (" attr(title) ")"; + } + + // Don't show links for images, or javascript/internal links + .ir a:after, + a[href^="javascript:"]:after, + a[href^="#"]:after { + content: ""; + } + + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + + thead { + display: table-header-group; // h5bp.com/t + } + + tr, + img { + page-break-inside: avoid; + } + + img { + max-width: 100% !important; + } + + @page { + margin: 0.5cm; + } + + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + + h2, + h3 { + page-break-after: avoid; + } +} diff --git a/public/css/bootstrap/responsive-1200px-min.less b/public/css/bootstrap/responsive-1200px-min.less new file mode 100644 index 0000000..4f35ba6 --- /dev/null +++ b/public/css/bootstrap/responsive-1200px-min.less @@ -0,0 +1,28 @@ +// +// Responsive: Large desktop and up +// -------------------------------------------------- + + +@media (min-width: 1200px) { + + // Fixed grid + #grid > .core(@gridColumnWidth1200, @gridGutterWidth1200); + + // Fluid grid + #grid > .fluid(@fluidGridColumnWidth1200, @fluidGridGutterWidth1200); + + // Input grid + #grid > .input(@gridColumnWidth1200, @gridGutterWidth1200); + + // Thumbnails + .thumbnails { + margin-left: -@gridGutterWidth1200; + } + .thumbnails > li { + margin-left: @gridGutterWidth1200; + } + .row-fluid .thumbnails { + margin-left: 0; + } + +} diff --git a/public/css/bootstrap/responsive-767px-max.less b/public/css/bootstrap/responsive-767px-max.less new file mode 100644 index 0000000..128f4ce --- /dev/null +++ b/public/css/bootstrap/responsive-767px-max.less @@ -0,0 +1,193 @@ +// +// Responsive: Landscape phone to desktop/tablet +// -------------------------------------------------- + + +@media (max-width: 767px) { + + // Padding to set content in a bit + body { + padding-left: 20px; + padding-right: 20px; + } + // Negative indent the now static "fixed" navbar + .navbar-fixed-top, + .navbar-fixed-bottom, + .navbar-static-top { + margin-left: -20px; + margin-right: -20px; + } + // Remove padding on container given explicit padding set on body + .container-fluid { + padding: 0; + } + + // TYPOGRAPHY + // ---------- + // Reset horizontal dl + .dl-horizontal { + dt { + float: none; + clear: none; + width: auto; + text-align: left; + } + dd { + margin-left: 0; + } + } + + // GRID & CONTAINERS + // ----------------- + // Remove width from containers + .container { + width: auto; + } + // Fluid rows + .row-fluid { + width: 100%; + } + // Undo negative margin on rows and thumbnails + .row, + .thumbnails { + margin-left: 0; + } + .thumbnails > li { + float: none; + margin-left: 0; // Reset the default margin for all li elements when no .span* classes are present + } + // Make all grid-sized elements block level again + [class*="span"], + .uneditable-input[class*="span"], // Makes uneditable inputs full-width when using grid sizing + .row-fluid [class*="span"] { + float: none; + display: block; + width: 100%; + margin-left: 0; + .box-sizing(border-box); + } + .span12, + .row-fluid .span12 { + width: 100%; + .box-sizing(border-box); + } + .row-fluid [class*="offset"]:first-child { + margin-left: 0; + } + + // FORM FIELDS + // ----------- + // Make span* classes full width + .input-large, + .input-xlarge, + .input-xxlarge, + input[class*="span"], + select[class*="span"], + textarea[class*="span"], + .uneditable-input { + .input-block-level(); + } + // But don't let it screw up prepend/append inputs + .input-prepend input, + .input-append input, + .input-prepend input[class*="span"], + .input-append input[class*="span"] { + display: inline-block; // redeclare so they don't wrap to new lines + width: auto; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 0; + } + + // Modals + .modal { + position: fixed; + top: 20px; + left: 20px; + right: 20px; + width: auto; + margin: 0; + &.fade { top: -100px; } + &.fade.in { top: 20px; } + } + +} + + + +// UP TO LANDSCAPE PHONE +// --------------------- + +@media (max-width: 480px) { + + // Smooth out the collapsing/expanding nav + .nav-collapse { + -webkit-transform: translate3d(0, 0, 0); // activate the GPU + } + + // Block level the page header small tag for readability + .page-header h1 small { + display: block; + line-height: @baseLineHeight; + } + + // Update checkboxes for iOS + input[type="checkbox"], + input[type="radio"] { + border: 1px solid #ccc; + } + + // Remove the horizontal form styles + .form-horizontal { + .control-label { + float: none; + width: auto; + padding-top: 0; + text-align: left; + } + // Move over all input controls and content + .controls { + margin-left: 0; + } + // Move the options list down to align with labels + .control-list { + padding-top: 0; // has to be padding because margin collaspes + } + // Move over buttons in .form-actions to align with .controls + .form-actions { + padding-left: 10px; + padding-right: 10px; + } + } + + // Medias + // Reset float and spacing to stack + .media .pull-left, + .media .pull-right { + float: none; + display: block; + margin-bottom: 10px; + } + // Remove side margins since we stack instead of indent + .media-object { + margin-right: 0; + margin-left: 0; + } + + // Modals + .modal { + top: 10px; + left: 10px; + right: 10px; + } + .modal-header .close { + padding: 10px; + margin: -10px; + } + + // Carousel + .carousel-caption { + position: static; + } + +} diff --git a/public/css/bootstrap/responsive-768px-979px.less b/public/css/bootstrap/responsive-768px-979px.less new file mode 100644 index 0000000..8e8c486 --- /dev/null +++ b/public/css/bootstrap/responsive-768px-979px.less @@ -0,0 +1,19 @@ +// +// Responsive: Tablet to desktop +// -------------------------------------------------- + + +@media (min-width: 768px) and (max-width: 979px) { + + // Fixed grid + #grid > .core(@gridColumnWidth768, @gridGutterWidth768); + + // Fluid grid + #grid > .fluid(@fluidGridColumnWidth768, @fluidGridGutterWidth768); + + // Input grid + #grid > .input(@gridColumnWidth768, @gridGutterWidth768); + + // No need to reset .thumbnails here since it's the same @gridGutterWidth + +} diff --git a/public/css/bootstrap/responsive-navbar.less b/public/css/bootstrap/responsive-navbar.less new file mode 100644 index 0000000..21cd3ba --- /dev/null +++ b/public/css/bootstrap/responsive-navbar.less @@ -0,0 +1,189 @@ +// +// Responsive: Navbar +// -------------------------------------------------- + + +// TABLETS AND BELOW +// ----------------- +@media (max-width: @navbarCollapseWidth) { + + // UNFIX THE TOPBAR + // ---------------- + // Remove any padding from the body + body { + padding-top: 0; + } + // Unfix the navbars + .navbar-fixed-top, + .navbar-fixed-bottom { + position: static; + } + .navbar-fixed-top { + margin-bottom: @baseLineHeight; + } + .navbar-fixed-bottom { + margin-top: @baseLineHeight; + } + .navbar-fixed-top .navbar-inner, + .navbar-fixed-bottom .navbar-inner { + padding: 5px; + } + .navbar .container { + width: auto; + padding: 0; + } + // Account for brand name + .navbar .brand { + padding-left: 10px; + padding-right: 10px; + margin: 0 0 0 -5px; + } + + // COLLAPSIBLE NAVBAR + // ------------------ + // Nav collapse clears brand + .nav-collapse { + clear: both; + } + // Block-level the nav + .nav-collapse .nav { + float: none; + margin: 0 0 (@baseLineHeight / 2); + } + .nav-collapse .nav > li { + float: none; + } + .nav-collapse .nav > li > a { + margin-bottom: 2px; + } + .nav-collapse .nav > .divider-vertical { + display: none; + } + .nav-collapse .nav .nav-header { + color: @navbarText; + text-shadow: none; + } + // Nav and dropdown links in navbar + .nav-collapse .nav > li > a, + .nav-collapse .dropdown-menu a { + padding: 9px 15px; + font-weight: bold; + color: @navbarLinkColor; + .border-radius(3px); + } + // Buttons + .nav-collapse .btn { + padding: 4px 10px 4px; + font-weight: normal; + .border-radius(@baseBorderRadius); + } + .nav-collapse .dropdown-menu li + li a { + margin-bottom: 2px; + } + .nav-collapse .nav > li > a:hover, + .nav-collapse .nav > li > a:focus, + .nav-collapse .dropdown-menu a:hover, + .nav-collapse .dropdown-menu a:focus { + background-color: @navbarBackground; + } + .navbar-inverse .nav-collapse .nav > li > a, + .navbar-inverse .nav-collapse .dropdown-menu a { + color: @navbarInverseLinkColor; + } + .navbar-inverse .nav-collapse .nav > li > a:hover, + .navbar-inverse .nav-collapse .nav > li > a:focus, + .navbar-inverse .nav-collapse .dropdown-menu a:hover, + .navbar-inverse .nav-collapse .dropdown-menu a:focus { + background-color: @navbarInverseBackground; + } + // Buttons in the navbar + .nav-collapse.in .btn-group { + margin-top: 5px; + padding: 0; + } + // Dropdowns in the navbar + .nav-collapse .dropdown-menu { + position: static; + top: auto; + left: auto; + float: none; + display: none; + max-width: none; + margin: 0 15px; + padding: 0; + background-color: transparent; + border: none; + .border-radius(0); + .box-shadow(none); + } + .nav-collapse .open > .dropdown-menu { + display: block; + } + + .nav-collapse .dropdown-menu:before, + .nav-collapse .dropdown-menu:after { + display: none; + } + .nav-collapse .dropdown-menu .divider { + display: none; + } + .nav-collapse .nav > li > .dropdown-menu { + &:before, + &:after { + display: none; + } + } + // Forms in navbar + .nav-collapse .navbar-form, + .nav-collapse .navbar-search { + float: none; + padding: (@baseLineHeight / 2) 15px; + margin: (@baseLineHeight / 2) 0; + border-top: 1px solid @navbarBackground; + border-bottom: 1px solid @navbarBackground; + .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1)"); + } + .navbar-inverse .nav-collapse .navbar-form, + .navbar-inverse .nav-collapse .navbar-search { + border-top-color: @navbarInverseBackground; + border-bottom-color: @navbarInverseBackground; + } + // Pull right (secondary) nav content + .navbar .nav-collapse .nav.pull-right { + float: none; + margin-left: 0; + } + // Hide everything in the navbar save .brand and toggle button */ + .nav-collapse, + .nav-collapse.collapse { + overflow: hidden; + height: 0; + } + // Navbar button + .navbar .btn-navbar { + display: block; + } + + // STATIC NAVBAR + // ------------- + .navbar-static .navbar-inner { + padding-left: 10px; + padding-right: 10px; + } + + +} + + +// DEFAULT DESKTOP +// --------------- + +@media (min-width: @navbarCollapseDesktopWidth) { + + // Required to make the collapsing navbar work on regular desktops + .nav-collapse.collapse { + height: auto !important; + overflow: visible !important; + } + +} diff --git a/public/css/bootstrap/responsive-utilities.less b/public/css/bootstrap/responsive-utilities.less new file mode 100644 index 0000000..bf43e8e --- /dev/null +++ b/public/css/bootstrap/responsive-utilities.less @@ -0,0 +1,59 @@ +// +// Responsive: Utility classes +// -------------------------------------------------- + + +// IE10 Metro responsive +// Required for Windows 8 Metro split-screen snapping with IE10 +// Source: http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/ +@-ms-viewport{ + width: device-width; +} + +// Hide from screenreaders and browsers +// Credit: HTML5 Boilerplate +.hidden { + display: none; + visibility: hidden; +} + +// Visibility utilities + +// For desktops +.visible-phone { display: none !important; } +.visible-tablet { display: none !important; } +.hidden-phone { } +.hidden-tablet { } +.hidden-desktop { display: none !important; } +.visible-desktop { display: inherit !important; } + +// Tablets & small desktops only +@media (min-width: 768px) and (max-width: 979px) { + // Hide everything else + .hidden-desktop { display: inherit !important; } + .visible-desktop { display: none !important ; } + // Show + .visible-tablet { display: inherit !important; } + // Hide + .hidden-tablet { display: none !important; } +} + +// Phones only +@media (max-width: 767px) { + // Hide everything else + .hidden-desktop { display: inherit !important; } + .visible-desktop { display: none !important; } + // Show + .visible-phone { display: inherit !important; } // Use inherit to restore previous behavior + // Hide + .hidden-phone { display: none !important; } +} + +// Print utilities +.visible-print { display: none !important; } +.hidden-print { } + +@media print { + .visible-print { display: inherit !important; } + .hidden-print { display: none !important; } +} diff --git a/public/css/bootstrap/responsive.less b/public/css/bootstrap/responsive.less new file mode 100644 index 0000000..b8366de --- /dev/null +++ b/public/css/bootstrap/responsive.less @@ -0,0 +1,48 @@ +/*! + * Bootstrap Responsive v2.3.1 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */ + + +// Responsive.less +// For phone and tablet devices +// ------------------------------------------------------------- + + +// REPEAT VARIABLES & MIXINS +// ------------------------- +// Required since we compile the responsive stuff separately + +@import "variables.less"; // Modify this for custom colors, font-sizes, etc +@import "mixins.less"; + + +// RESPONSIVE CLASSES +// ------------------ + +@import "responsive-utilities.less"; + + +// MEDIA QUERIES +// ------------------ + +// Large desktops +@import "responsive-1200px-min.less"; + +// Tablets to regular desktops +@import "responsive-768px-979px.less"; + +// Phones to portrait tablets and narrow desktops +@import "responsive-767px-max.less"; + + +// RESPONSIVE NAVBAR +// ------------------ + +// From 979px and below, show a button to toggle navbar contents +@import "responsive-navbar.less"; diff --git a/public/css/bootstrap/scaffolding.less b/public/css/bootstrap/scaffolding.less new file mode 100644 index 0000000..f17e8ca --- /dev/null +++ b/public/css/bootstrap/scaffolding.less @@ -0,0 +1,53 @@ +// +// Scaffolding +// -------------------------------------------------- + + +// Body reset +// ------------------------- + +body { + margin: 0; + font-family: @baseFontFamily; + font-size: @baseFontSize; + line-height: @baseLineHeight; + color: @textColor; + background-color: @bodyBackground; +} + + +// Links +// ------------------------- + +a { + color: @linkColor; + text-decoration: none; +} +a:hover, +a:focus { + color: @linkColorHover; + text-decoration: underline; +} + + +// Images +// ------------------------- + +// Rounded corners +.img-rounded { + .border-radius(6px); +} + +// Add polaroid-esque trim +.img-polaroid { + padding: 4px; + background-color: #fff; + border: 1px solid #ccc; + border: 1px solid rgba(0,0,0,.2); + .box-shadow(0 1px 3px rgba(0,0,0,.1)); +} + +// Perfect circle +.img-circle { + .border-radius(500px); // crank the border-radius so it works with most reasonably sized images +} diff --git a/public/css/bootstrap/sprites.less b/public/css/bootstrap/sprites.less new file mode 100644 index 0000000..1812bf7 --- /dev/null +++ b/public/css/bootstrap/sprites.less @@ -0,0 +1,197 @@ +// +// Sprites +// -------------------------------------------------- + + +// ICONS +// ----- + +// All icons receive the styles of the tag with a base class +// of .i and are then given a unique class to add width, height, +// and background-position. Your resulting HTML will look like +// . + +// For the white version of the icons, just add the .icon-white class: +// + +[class^="icon-"], +[class*=" icon-"] { + display: inline-block; + width: 14px; + height: 14px; + .ie7-restore-right-whitespace(); + line-height: 14px; + vertical-align: text-top; + background-image: url("@{iconSpritePath}"); + background-position: 14px 14px; + background-repeat: no-repeat; + margin-top: 1px; +} + +/* White icons with optional class, or on hover/focus/active states of certain elements */ +.icon-white, +.nav-pills > .active > a > [class^="icon-"], +.nav-pills > .active > a > [class*=" icon-"], +.nav-list > .active > a > [class^="icon-"], +.nav-list > .active > a > [class*=" icon-"], +.navbar-inverse .nav > .active > a > [class^="icon-"], +.navbar-inverse .nav > .active > a > [class*=" icon-"], +.dropdown-menu > li > a:hover > [class^="icon-"], +.dropdown-menu > li > a:focus > [class^="icon-"], +.dropdown-menu > li > a:hover > [class*=" icon-"], +.dropdown-menu > li > a:focus > [class*=" icon-"], +.dropdown-menu > .active > a > [class^="icon-"], +.dropdown-menu > .active > a > [class*=" icon-"], +.dropdown-submenu:hover > a > [class^="icon-"], +.dropdown-submenu:focus > a > [class^="icon-"], +.dropdown-submenu:hover > a > [class*=" icon-"], +.dropdown-submenu:focus > a > [class*=" icon-"] { + background-image: url("@{iconWhiteSpritePath}"); +} + +.icon-glass { background-position: 0 0; } +.icon-music { background-position: -24px 0; } +.icon-search { background-position: -48px 0; } +.icon-envelope { background-position: -72px 0; } +.icon-heart { background-position: -96px 0; } +.icon-star { background-position: -120px 0; } +.icon-star-empty { background-position: -144px 0; } +.icon-user { background-position: -168px 0; } +.icon-film { background-position: -192px 0; } +.icon-th-large { background-position: -216px 0; } +.icon-th { background-position: -240px 0; } +.icon-th-list { background-position: -264px 0; } +.icon-ok { background-position: -288px 0; } +.icon-remove { background-position: -312px 0; } +.icon-zoom-in { background-position: -336px 0; } +.icon-zoom-out { background-position: -360px 0; } +.icon-off { background-position: -384px 0; } +.icon-signal { background-position: -408px 0; } +.icon-cog { background-position: -432px 0; } +.icon-trash { background-position: -456px 0; } + +.icon-home { background-position: 0 -24px; } +.icon-file { background-position: -24px -24px; } +.icon-time { background-position: -48px -24px; } +.icon-road { background-position: -72px -24px; } +.icon-download-alt { background-position: -96px -24px; } +.icon-download { background-position: -120px -24px; } +.icon-upload { background-position: -144px -24px; } +.icon-inbox { background-position: -168px -24px; } +.icon-play-circle { background-position: -192px -24px; } +.icon-repeat { background-position: -216px -24px; } +.icon-refresh { background-position: -240px -24px; } +.icon-list-alt { background-position: -264px -24px; } +.icon-lock { background-position: -287px -24px; } // 1px off +.icon-flag { background-position: -312px -24px; } +.icon-headphones { background-position: -336px -24px; } +.icon-volume-off { background-position: -360px -24px; } +.icon-volume-down { background-position: -384px -24px; } +.icon-volume-up { background-position: -408px -24px; } +.icon-qrcode { background-position: -432px -24px; } +.icon-barcode { background-position: -456px -24px; } + +.icon-tag { background-position: 0 -48px; } +.icon-tags { background-position: -25px -48px; } // 1px off +.icon-book { background-position: -48px -48px; } +.icon-bookmark { background-position: -72px -48px; } +.icon-print { background-position: -96px -48px; } +.icon-camera { background-position: -120px -48px; } +.icon-font { background-position: -144px -48px; } +.icon-bold { background-position: -167px -48px; } // 1px off +.icon-italic { background-position: -192px -48px; } +.icon-text-height { background-position: -216px -48px; } +.icon-text-width { background-position: -240px -48px; } +.icon-align-left { background-position: -264px -48px; } +.icon-align-center { background-position: -288px -48px; } +.icon-align-right { background-position: -312px -48px; } +.icon-align-justify { background-position: -336px -48px; } +.icon-list { background-position: -360px -48px; } +.icon-indent-left { background-position: -384px -48px; } +.icon-indent-right { background-position: -408px -48px; } +.icon-facetime-video { background-position: -432px -48px; } +.icon-picture { background-position: -456px -48px; } + +.icon-pencil { background-position: 0 -72px; } +.icon-map-marker { background-position: -24px -72px; } +.icon-adjust { background-position: -48px -72px; } +.icon-tint { background-position: -72px -72px; } +.icon-edit { background-position: -96px -72px; } +.icon-share { background-position: -120px -72px; } +.icon-check { background-position: -144px -72px; } +.icon-move { background-position: -168px -72px; } +.icon-step-backward { background-position: -192px -72px; } +.icon-fast-backward { background-position: -216px -72px; } +.icon-backward { background-position: -240px -72px; } +.icon-play { background-position: -264px -72px; } +.icon-pause { background-position: -288px -72px; } +.icon-stop { background-position: -312px -72px; } +.icon-forward { background-position: -336px -72px; } +.icon-fast-forward { background-position: -360px -72px; } +.icon-step-forward { background-position: -384px -72px; } +.icon-eject { background-position: -408px -72px; } +.icon-chevron-left { background-position: -432px -72px; } +.icon-chevron-right { background-position: -456px -72px; } + +.icon-plus-sign { background-position: 0 -96px; } +.icon-minus-sign { background-position: -24px -96px; } +.icon-remove-sign { background-position: -48px -96px; } +.icon-ok-sign { background-position: -72px -96px; } +.icon-question-sign { background-position: -96px -96px; } +.icon-info-sign { background-position: -120px -96px; } +.icon-screenshot { background-position: -144px -96px; } +.icon-remove-circle { background-position: -168px -96px; } +.icon-ok-circle { background-position: -192px -96px; } +.icon-ban-circle { background-position: -216px -96px; } +.icon-arrow-left { background-position: -240px -96px; } +.icon-arrow-right { background-position: -264px -96px; } +.icon-arrow-up { background-position: -289px -96px; } // 1px off +.icon-arrow-down { background-position: -312px -96px; } +.icon-share-alt { background-position: -336px -96px; } +.icon-resize-full { background-position: -360px -96px; } +.icon-resize-small { background-position: -384px -96px; } +.icon-plus { background-position: -408px -96px; } +.icon-minus { background-position: -433px -96px; } +.icon-asterisk { background-position: -456px -96px; } + +.icon-exclamation-sign { background-position: 0 -120px; } +.icon-gift { background-position: -24px -120px; } +.icon-leaf { background-position: -48px -120px; } +.icon-fire { background-position: -72px -120px; } +.icon-eye-open { background-position: -96px -120px; } +.icon-eye-close { background-position: -120px -120px; } +.icon-warning-sign { background-position: -144px -120px; } +.icon-plane { background-position: -168px -120px; } +.icon-calendar { background-position: -192px -120px; } +.icon-random { background-position: -216px -120px; width: 16px; } +.icon-comment { background-position: -240px -120px; } +.icon-magnet { background-position: -264px -120px; } +.icon-chevron-up { background-position: -288px -120px; } +.icon-chevron-down { background-position: -313px -119px; } // 1px, 1px off +.icon-retweet { background-position: -336px -120px; } +.icon-shopping-cart { background-position: -360px -120px; } +.icon-folder-close { background-position: -384px -120px; width: 16px; } +.icon-folder-open { background-position: -408px -120px; width: 16px; } +.icon-resize-vertical { background-position: -432px -119px; } // 1px, 1px off +.icon-resize-horizontal { background-position: -456px -118px; } // 1px, 2px off + +.icon-hdd { background-position: 0 -144px; } +.icon-bullhorn { background-position: -24px -144px; } +.icon-bell { background-position: -48px -144px; } +.icon-certificate { background-position: -72px -144px; } +.icon-thumbs-up { background-position: -96px -144px; } +.icon-thumbs-down { background-position: -120px -144px; } +.icon-hand-right { background-position: -144px -144px; } +.icon-hand-left { background-position: -168px -144px; } +.icon-hand-up { background-position: -192px -144px; } +.icon-hand-down { background-position: -216px -144px; } +.icon-circle-arrow-right { background-position: -240px -144px; } +.icon-circle-arrow-left { background-position: -264px -144px; } +.icon-circle-arrow-up { background-position: -288px -144px; } +.icon-circle-arrow-down { background-position: -312px -144px; } +.icon-globe { background-position: -336px -144px; } +.icon-wrench { background-position: -360px -144px; } +.icon-tasks { background-position: -384px -144px; } +.icon-filter { background-position: -408px -144px; } +.icon-briefcase { background-position: -432px -144px; } +.icon-fullscreen { background-position: -456px -144px; } diff --git a/public/css/bootstrap/tables.less b/public/css/bootstrap/tables.less new file mode 100644 index 0000000..0e35271 --- /dev/null +++ b/public/css/bootstrap/tables.less @@ -0,0 +1,244 @@ +// +// Tables +// -------------------------------------------------- + + +// BASE TABLES +// ----------------- + +table { + max-width: 100%; + background-color: @tableBackground; + border-collapse: collapse; + border-spacing: 0; +} + +// BASELINE STYLES +// --------------- + +.table { + width: 100%; + margin-bottom: @baseLineHeight; + // Cells + th, + td { + padding: 8px; + line-height: @baseLineHeight; + text-align: left; + vertical-align: top; + border-top: 1px solid @tableBorder; + } + th { + font-weight: bold; + } + // Bottom align for column headings + thead th { + vertical-align: bottom; + } + // Remove top border from thead by default + caption + thead tr:first-child th, + caption + thead tr:first-child td, + colgroup + thead tr:first-child th, + colgroup + thead tr:first-child td, + thead:first-child tr:first-child th, + thead:first-child tr:first-child td { + border-top: 0; + } + // Account for multiple tbody instances + tbody + tbody { + border-top: 2px solid @tableBorder; + } + + // Nesting + .table { + background-color: @bodyBackground; + } +} + + + +// CONDENSED TABLE W/ HALF PADDING +// ------------------------------- + +.table-condensed { + th, + td { + padding: 4px 5px; + } +} + + +// BORDERED VERSION +// ---------------- + +.table-bordered { + border: 1px solid @tableBorder; + border-collapse: separate; // Done so we can round those corners! + *border-collapse: collapse; // IE7 can't round corners anyway + border-left: 0; + .border-radius(@baseBorderRadius); + th, + td { + border-left: 1px solid @tableBorder; + } + // Prevent a double border + caption + thead tr:first-child th, + caption + tbody tr:first-child th, + caption + tbody tr:first-child td, + colgroup + thead tr:first-child th, + colgroup + tbody tr:first-child th, + colgroup + tbody tr:first-child td, + thead:first-child tr:first-child th, + tbody:first-child tr:first-child th, + tbody:first-child tr:first-child td { + border-top: 0; + } + // For first th/td in the first row in the first thead or tbody + thead:first-child tr:first-child > th:first-child, + tbody:first-child tr:first-child > td:first-child, + tbody:first-child tr:first-child > th:first-child { + .border-top-left-radius(@baseBorderRadius); + } + // For last th/td in the first row in the first thead or tbody + thead:first-child tr:first-child > th:last-child, + tbody:first-child tr:first-child > td:last-child, + tbody:first-child tr:first-child > th:last-child { + .border-top-right-radius(@baseBorderRadius); + } + // For first th/td (can be either) in the last row in the last thead, tbody, and tfoot + thead:last-child tr:last-child > th:first-child, + tbody:last-child tr:last-child > td:first-child, + tbody:last-child tr:last-child > th:first-child, + tfoot:last-child tr:last-child > td:first-child, + tfoot:last-child tr:last-child > th:first-child { + .border-bottom-left-radius(@baseBorderRadius); + } + // For last th/td (can be either) in the last row in the last thead, tbody, and tfoot + thead:last-child tr:last-child > th:last-child, + tbody:last-child tr:last-child > td:last-child, + tbody:last-child tr:last-child > th:last-child, + tfoot:last-child tr:last-child > td:last-child, + tfoot:last-child tr:last-child > th:last-child { + .border-bottom-right-radius(@baseBorderRadius); + } + + // Clear border-radius for first and last td in the last row in the last tbody for table with tfoot + tfoot + tbody:last-child tr:last-child td:first-child { + .border-bottom-left-radius(0); + } + tfoot + tbody:last-child tr:last-child td:last-child { + .border-bottom-right-radius(0); + } + + // Special fixes to round the left border on the first td/th + caption + thead tr:first-child th:first-child, + caption + tbody tr:first-child td:first-child, + colgroup + thead tr:first-child th:first-child, + colgroup + tbody tr:first-child td:first-child { + .border-top-left-radius(@baseBorderRadius); + } + caption + thead tr:first-child th:last-child, + caption + tbody tr:first-child td:last-child, + colgroup + thead tr:first-child th:last-child, + colgroup + tbody tr:first-child td:last-child { + .border-top-right-radius(@baseBorderRadius); + } + +} + + + + +// ZEBRA-STRIPING +// -------------- + +// Default zebra-stripe styles (alternating gray and transparent backgrounds) +.table-striped { + tbody { + > tr:nth-child(odd) > td, + > tr:nth-child(odd) > th { + background-color: @tableBackgroundAccent; + } + } +} + + +// HOVER EFFECT +// ------------ +// Placed here since it has to come after the potential zebra striping +.table-hover { + tbody { + tr:hover > td, + tr:hover > th { + background-color: @tableBackgroundHover; + } + } +} + + +// TABLE CELL SIZING +// ----------------- + +// Reset default grid behavior +table td[class*="span"], +table th[class*="span"], +.row-fluid table td[class*="span"], +.row-fluid table th[class*="span"] { + display: table-cell; + float: none; // undo default grid column styles + margin-left: 0; // undo default grid column styles +} + +// Change the column widths to account for td/th padding +.table td, +.table th { + &.span1 { .tableColumns(1); } + &.span2 { .tableColumns(2); } + &.span3 { .tableColumns(3); } + &.span4 { .tableColumns(4); } + &.span5 { .tableColumns(5); } + &.span6 { .tableColumns(6); } + &.span7 { .tableColumns(7); } + &.span8 { .tableColumns(8); } + &.span9 { .tableColumns(9); } + &.span10 { .tableColumns(10); } + &.span11 { .tableColumns(11); } + &.span12 { .tableColumns(12); } +} + + + +// TABLE BACKGROUNDS +// ----------------- +// Exact selectors below required to override .table-striped + +.table tbody tr { + &.success > td { + background-color: @successBackground; + } + &.error > td { + background-color: @errorBackground; + } + &.warning > td { + background-color: @warningBackground; + } + &.info > td { + background-color: @infoBackground; + } +} + +// Hover states for .table-hover +.table-hover tbody tr { + &.success:hover > td { + background-color: darken(@successBackground, 5%); + } + &.error:hover > td { + background-color: darken(@errorBackground, 5%); + } + &.warning:hover > td { + background-color: darken(@warningBackground, 5%); + } + &.info:hover > td { + background-color: darken(@infoBackground, 5%); + } +} diff --git a/public/css/bootstrap/thumbnails.less b/public/css/bootstrap/thumbnails.less new file mode 100644 index 0000000..4fd07d2 --- /dev/null +++ b/public/css/bootstrap/thumbnails.less @@ -0,0 +1,53 @@ +// +// Thumbnails +// -------------------------------------------------- + + +// Note: `.thumbnails` and `.thumbnails > li` are overriden in responsive files + +// Make wrapper ul behave like the grid +.thumbnails { + margin-left: -@gridGutterWidth; + list-style: none; + .clearfix(); +} +// Fluid rows have no left margin +.row-fluid .thumbnails { + margin-left: 0; +} + +// Float li to make thumbnails appear in a row +.thumbnails > li { + float: left; // Explicity set the float since we don't require .span* classes + margin-bottom: @baseLineHeight; + margin-left: @gridGutterWidth; +} + +// The actual thumbnail (can be `a` or `div`) +.thumbnail { + display: block; + padding: 4px; + line-height: @baseLineHeight; + border: 1px solid #ddd; + .border-radius(@baseBorderRadius); + .box-shadow(0 1px 3px rgba(0,0,0,.055)); + .transition(all .2s ease-in-out); +} +// Add a hover/focus state for linked versions only +a.thumbnail:hover, +a.thumbnail:focus { + border-color: @linkColor; + .box-shadow(0 1px 4px rgba(0,105,214,.25)); +} + +// Images and captions +.thumbnail > img { + display: block; + max-width: 100%; + margin-left: auto; + margin-right: auto; +} +.thumbnail .caption { + padding: 9px; + color: @gray; +} diff --git a/public/css/bootstrap/tooltip.less b/public/css/bootstrap/tooltip.less new file mode 100644 index 0000000..83d5f2b --- /dev/null +++ b/public/css/bootstrap/tooltip.less @@ -0,0 +1,70 @@ +// +// Tooltips +// -------------------------------------------------- + + +// Base class +.tooltip { + position: absolute; + z-index: @zindexTooltip; + display: block; + visibility: visible; + font-size: 11px; + line-height: 1.4; + .opacity(0); + &.in { .opacity(80); } + &.top { margin-top: -3px; padding: 5px 0; } + &.right { margin-left: 3px; padding: 0 5px; } + &.bottom { margin-top: 3px; padding: 5px 0; } + &.left { margin-left: -3px; padding: 0 5px; } +} + +// Wrapper for the tooltip content +.tooltip-inner { + max-width: 200px; + padding: 8px; + color: @tooltipColor; + text-align: center; + text-decoration: none; + background-color: @tooltipBackground; + .border-radius(@baseBorderRadius); +} + +// Arrows +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip { + &.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -@tooltipArrowWidth; + border-width: @tooltipArrowWidth @tooltipArrowWidth 0; + border-top-color: @tooltipArrowColor; + } + &.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -@tooltipArrowWidth; + border-width: @tooltipArrowWidth @tooltipArrowWidth @tooltipArrowWidth 0; + border-right-color: @tooltipArrowColor; + } + &.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -@tooltipArrowWidth; + border-width: @tooltipArrowWidth 0 @tooltipArrowWidth @tooltipArrowWidth; + border-left-color: @tooltipArrowColor; + } + &.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -@tooltipArrowWidth; + border-width: 0 @tooltipArrowWidth @tooltipArrowWidth; + border-bottom-color: @tooltipArrowColor; + } +} diff --git a/public/css/bootstrap/type.less b/public/css/bootstrap/type.less new file mode 100644 index 0000000..337138a --- /dev/null +++ b/public/css/bootstrap/type.less @@ -0,0 +1,247 @@ +// +// Typography +// -------------------------------------------------- + + +// Body text +// ------------------------- + +p { + margin: 0 0 @baseLineHeight / 2; +} +.lead { + margin-bottom: @baseLineHeight; + font-size: @baseFontSize * 1.5; + font-weight: 200; + line-height: @baseLineHeight * 1.5; +} + + +// Emphasis & misc +// ------------------------- + +// Ex: 14px base font * 85% = about 12px +small { font-size: 85%; } + +strong { font-weight: bold; } +em { font-style: italic; } +cite { font-style: normal; } + +// Utility classes +.muted { color: @grayLight; } +a.muted:hover, +a.muted:focus { color: darken(@grayLight, 10%); } + +.text-warning { color: @warningText; } +a.text-warning:hover, +a.text-warning:focus { color: darken(@warningText, 10%); } + +.text-error { color: @errorText; } +a.text-error:hover, +a.text-error:focus { color: darken(@errorText, 10%); } + +.text-info { color: @infoText; } +a.text-info:hover, +a.text-info:focus { color: darken(@infoText, 10%); } + +.text-success { color: @successText; } +a.text-success:hover, +a.text-success:focus { color: darken(@successText, 10%); } + +.text-left { text-align: left; } +.text-right { text-align: right; } +.text-center { text-align: center; } + + +// Headings +// ------------------------- + +h1, h2, h3, h4, h5, h6 { + margin: (@baseLineHeight / 2) 0; + font-family: @headingsFontFamily; + font-weight: @headingsFontWeight; + line-height: @baseLineHeight; + color: @headingsColor; + text-rendering: optimizelegibility; // Fix the character spacing for headings + small { + font-weight: normal; + line-height: 1; + color: @grayLight; + } +} + +h1, +h2, +h3 { line-height: @baseLineHeight * 2; } + +h1 { font-size: @baseFontSize * 2.75; } // ~38px +h2 { font-size: @baseFontSize * 2.25; } // ~32px +h3 { font-size: @baseFontSize * 1.75; } // ~24px +h4 { font-size: @baseFontSize * 1.25; } // ~18px +h5 { font-size: @baseFontSize; } +h6 { font-size: @baseFontSize * 0.85; } // ~12px + +h1 small { font-size: @baseFontSize * 1.75; } // ~24px +h2 small { font-size: @baseFontSize * 1.25; } // ~18px +h3 small { font-size: @baseFontSize; } +h4 small { font-size: @baseFontSize; } + + +// Page header +// ------------------------- + +.page-header { + padding-bottom: (@baseLineHeight / 2) - 1; + margin: @baseLineHeight 0 (@baseLineHeight * 1.5); + border-bottom: 1px solid @grayLighter; +} + + + +// Lists +// -------------------------------------------------- + +// Unordered and Ordered lists +ul, ol { + padding: 0; + margin: 0 0 @baseLineHeight / 2 25px; +} +ul ul, +ul ol, +ol ol, +ol ul { + margin-bottom: 0; +} +li { + line-height: @baseLineHeight; +} + +// Remove default list styles +ul.unstyled, +ol.unstyled { + margin-left: 0; + list-style: none; +} + +// Single-line list items +ul.inline, +ol.inline { + margin-left: 0; + list-style: none; + > li { + display: inline-block; + .ie7-inline-block(); + padding-left: 5px; + padding-right: 5px; + } +} + +// Description Lists +dl { + margin-bottom: @baseLineHeight; +} +dt, +dd { + line-height: @baseLineHeight; +} +dt { + font-weight: bold; +} +dd { + margin-left: @baseLineHeight / 2; +} +// Horizontal layout (like forms) +.dl-horizontal { + .clearfix(); // Ensure dl clears floats if empty dd elements present + dt { + float: left; + width: @horizontalComponentOffset - 20; + clear: left; + text-align: right; + .text-overflow(); + } + dd { + margin-left: @horizontalComponentOffset; + } +} + +// MISC +// ---- + +// Horizontal rules +hr { + margin: @baseLineHeight 0; + border: 0; + border-top: 1px solid @hrBorder; + border-bottom: 1px solid @white; +} + +// Abbreviations and acronyms +abbr[title], +// Added data-* attribute to help out our tooltip plugin, per https://github.com/twitter/bootstrap/issues/5257 +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted @grayLight; +} +abbr.initialism { + font-size: 90%; + text-transform: uppercase; +} + +// Blockquotes +blockquote { + padding: 0 0 0 15px; + margin: 0 0 @baseLineHeight; + border-left: 5px solid @grayLighter; + p { + margin-bottom: 0; + font-size: @baseFontSize * 1.25; + font-weight: 300; + line-height: 1.25; + } + small { + display: block; + line-height: @baseLineHeight; + color: @grayLight; + &:before { + content: '\2014 \00A0'; + } + } + + // Float right with text-align: right + &.pull-right { + float: right; + padding-right: 15px; + padding-left: 0; + border-right: 5px solid @grayLighter; + border-left: 0; + p, + small { + text-align: right; + } + small { + &:before { + content: ''; + } + &:after { + content: '\00A0 \2014'; + } + } + } +} + +// Quotes +q:before, +q:after, +blockquote:before, +blockquote:after { + content: ""; +} + +// Addresses +address { + display: block; + margin-bottom: @baseLineHeight; + font-style: normal; + line-height: @baseLineHeight; +} diff --git a/public/css/bootstrap/utilities.less b/public/css/bootstrap/utilities.less new file mode 100644 index 0000000..314b4ff --- /dev/null +++ b/public/css/bootstrap/utilities.less @@ -0,0 +1,30 @@ +// +// Utility classes +// -------------------------------------------------- + + +// Quick floats +.pull-right { + float: right; +} +.pull-left { + float: left; +} + +// Toggling content +.hide { + display: none; +} +.show { + display: block; +} + +// Visibility +.invisible { + visibility: hidden; +} + +// For Affix plugin +.affix { + position: fixed; +} diff --git a/public/css/bootstrap/variables.less b/public/css/bootstrap/variables.less new file mode 100644 index 0000000..31c131b --- /dev/null +++ b/public/css/bootstrap/variables.less @@ -0,0 +1,301 @@ +// +// Variables +// -------------------------------------------------- + + +// Global values +// -------------------------------------------------- + + +// Grays +// ------------------------- +@black: #000; +@grayDarker: #222; +@grayDark: #333; +@gray: #555; +@grayLight: #999; +@grayLighter: #eee; +@white: #fff; + + +// Accent colors +// ------------------------- +@blue: #049cdb; +@blueDark: #0064cd; +@green: #46a546; +@red: #9d261d; +@yellow: #ffc40d; +@orange: #f89406; +@pink: #c3325f; +@purple: #7a43b6; + + +// Scaffolding +// ------------------------- +@bodyBackground: @white; +@textColor: @grayDark; + + +// Links +// ------------------------- +@linkColor: #08c; +@linkColorHover: darken(@linkColor, 15%); + + +// Typography +// ------------------------- +@sansFontFamily: "Helvetica Neue", Helvetica, Arial, sans-serif; +@serifFontFamily: Georgia, "Times New Roman", Times, serif; +@monoFontFamily: Monaco, Menlo, Consolas, "Courier New", monospace; + +@baseFontSize: 14px; +@baseFontFamily: @sansFontFamily; +@baseLineHeight: 20px; +@altFontFamily: @serifFontFamily; + +@headingsFontFamily: inherit; // empty to use BS default, @baseFontFamily +@headingsFontWeight: bold; // instead of browser default, bold +@headingsColor: inherit; // empty to use BS default, @textColor + + +// Component sizing +// ------------------------- +// Based on 14px font-size and 20px line-height + +@fontSizeLarge: @baseFontSize * 1.25; // ~18px +@fontSizeSmall: @baseFontSize * 0.85; // ~12px +@fontSizeMini: @baseFontSize * 0.75; // ~11px + +@paddingLarge: 11px 19px; // 44px +@paddingSmall: 2px 10px; // 26px +@paddingMini: 0 6px; // 22px + +@baseBorderRadius: 4px; +@borderRadiusLarge: 6px; +@borderRadiusSmall: 3px; + + +// Tables +// ------------------------- +@tableBackground: transparent; // overall background-color +@tableBackgroundAccent: #f9f9f9; // for striping +@tableBackgroundHover: #f5f5f5; // for hover +@tableBorder: #ddd; // table and cell border + +// Buttons +// ------------------------- +@btnBackground: @white; +@btnBackgroundHighlight: darken(@white, 10%); +@btnBorder: #ccc; + +@btnPrimaryBackground: @linkColor; +@btnPrimaryBackgroundHighlight: spin(@btnPrimaryBackground, 20%); + +@btnInfoBackground: #5bc0de; +@btnInfoBackgroundHighlight: #2f96b4; + +@btnSuccessBackground: #62c462; +@btnSuccessBackgroundHighlight: #51a351; + +@btnWarningBackground: lighten(@orange, 15%); +@btnWarningBackgroundHighlight: @orange; + +@btnDangerBackground: #ee5f5b; +@btnDangerBackgroundHighlight: #bd362f; + +@btnInverseBackground: #444; +@btnInverseBackgroundHighlight: @grayDarker; + + +// Forms +// ------------------------- +@inputBackground: @white; +@inputBorder: #ccc; +@inputBorderRadius: @baseBorderRadius; +@inputDisabledBackground: @grayLighter; +@formActionsBackground: #f5f5f5; +@inputHeight: @baseLineHeight + 10px; // base line-height + 8px vertical padding + 2px top/bottom border + + +// Dropdowns +// ------------------------- +@dropdownBackground: @white; +@dropdownBorder: rgba(0,0,0,.2); +@dropdownDividerTop: #e5e5e5; +@dropdownDividerBottom: @white; + +@dropdownLinkColor: @grayDark; +@dropdownLinkColorHover: @white; +@dropdownLinkColorActive: @white; + +@dropdownLinkBackgroundActive: @linkColor; +@dropdownLinkBackgroundHover: @dropdownLinkBackgroundActive; + + + +// COMPONENT VARIABLES +// -------------------------------------------------- + + +// Z-index master list +// ------------------------- +// Used for a bird's eye view of components dependent on the z-axis +// Try to avoid customizing these :) +@zindexDropdown: 1000; +@zindexPopover: 1010; +@zindexTooltip: 1030; +@zindexFixedNavbar: 1030; +@zindexModalBackdrop: 1040; +@zindexModal: 1050; + + +// Sprite icons path +// ------------------------- +@iconSpritePath: "../img/glyphicons-halflings.png"; +@iconWhiteSpritePath: "../img/glyphicons-halflings-white.png"; + + +// Input placeholder text color +// ------------------------- +@placeholderText: @grayLight; + + +// Hr border color +// ------------------------- +@hrBorder: @grayLighter; + + +// Horizontal forms & lists +// ------------------------- +@horizontalComponentOffset: 180px; + + +// Wells +// ------------------------- +@wellBackground: #f5f5f5; + + +// Navbar +// ------------------------- +@navbarCollapseWidth: 979px; +@navbarCollapseDesktopWidth: @navbarCollapseWidth + 1; + +@navbarHeight: 40px; +@navbarBackgroundHighlight: #ffffff; +@navbarBackground: darken(@navbarBackgroundHighlight, 5%); +@navbarBorder: darken(@navbarBackground, 12%); + +@navbarText: #777; +@navbarLinkColor: #777; +@navbarLinkColorHover: @grayDark; +@navbarLinkColorActive: @gray; +@navbarLinkBackgroundHover: transparent; +@navbarLinkBackgroundActive: darken(@navbarBackground, 5%); + +@navbarBrandColor: @navbarLinkColor; + +// Inverted navbar +@navbarInverseBackground: #111111; +@navbarInverseBackgroundHighlight: #222222; +@navbarInverseBorder: #252525; + +@navbarInverseText: @grayLight; +@navbarInverseLinkColor: @grayLight; +@navbarInverseLinkColorHover: @white; +@navbarInverseLinkColorActive: @navbarInverseLinkColorHover; +@navbarInverseLinkBackgroundHover: transparent; +@navbarInverseLinkBackgroundActive: @navbarInverseBackground; + +@navbarInverseSearchBackground: lighten(@navbarInverseBackground, 25%); +@navbarInverseSearchBackgroundFocus: @white; +@navbarInverseSearchBorder: @navbarInverseBackground; +@navbarInverseSearchPlaceholderColor: #ccc; + +@navbarInverseBrandColor: @navbarInverseLinkColor; + + +// Pagination +// ------------------------- +@paginationBackground: #fff; +@paginationBorder: #ddd; +@paginationActiveBackground: #f5f5f5; + + +// Hero unit +// ------------------------- +@heroUnitBackground: @grayLighter; +@heroUnitHeadingColor: inherit; +@heroUnitLeadColor: inherit; + + +// Form states and alerts +// ------------------------- +@warningText: #c09853; +@warningBackground: #fcf8e3; +@warningBorder: darken(spin(@warningBackground, -10), 3%); + +@errorText: #b94a48; +@errorBackground: #f2dede; +@errorBorder: darken(spin(@errorBackground, -10), 3%); + +@successText: #468847; +@successBackground: #dff0d8; +@successBorder: darken(spin(@successBackground, -10), 5%); + +@infoText: #3a87ad; +@infoBackground: #d9edf7; +@infoBorder: darken(spin(@infoBackground, -10), 7%); + + +// Tooltips and popovers +// ------------------------- +@tooltipColor: #fff; +@tooltipBackground: #000; +@tooltipArrowWidth: 5px; +@tooltipArrowColor: @tooltipBackground; + +@popoverBackground: #fff; +@popoverArrowWidth: 10px; +@popoverArrowColor: #fff; +@popoverTitleBackground: darken(@popoverBackground, 3%); + +// Special enhancement for popovers +@popoverArrowOuterWidth: @popoverArrowWidth + 1; +@popoverArrowOuterColor: rgba(0,0,0,.25); + + + +// GRID +// -------------------------------------------------- + + +// Default 940px grid +// ------------------------- +@gridColumns: 12; +@gridColumnWidth: 60px; +@gridGutterWidth: 20px; +@gridRowWidth: (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1)); + +// 1200px min +@gridColumnWidth1200: 70px; +@gridGutterWidth1200: 30px; +@gridRowWidth1200: (@gridColumns * @gridColumnWidth1200) + (@gridGutterWidth1200 * (@gridColumns - 1)); + +// 768px-979px +@gridColumnWidth768: 42px; +@gridGutterWidth768: 20px; +@gridRowWidth768: (@gridColumns * @gridColumnWidth768) + (@gridGutterWidth768 * (@gridColumns - 1)); + + +// Fluid grid +// ------------------------- +@fluidGridColumnWidth: percentage(@gridColumnWidth/@gridRowWidth); +@fluidGridGutterWidth: percentage(@gridGutterWidth/@gridRowWidth); + +// 1200px min +@fluidGridColumnWidth1200: percentage(@gridColumnWidth1200/@gridRowWidth1200); +@fluidGridGutterWidth1200: percentage(@gridGutterWidth1200/@gridRowWidth1200); + +// 768px-979px +@fluidGridColumnWidth768: percentage(@gridColumnWidth768/@gridRowWidth768); +@fluidGridGutterWidth768: percentage(@gridGutterWidth768/@gridRowWidth768); diff --git a/public/css/bootstrap/wells.less b/public/css/bootstrap/wells.less new file mode 100644 index 0000000..84a744b --- /dev/null +++ b/public/css/bootstrap/wells.less @@ -0,0 +1,29 @@ +// +// Wells +// -------------------------------------------------- + + +// Base class +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: @wellBackground; + border: 1px solid darken(@wellBackground, 7%); + .border-radius(@baseBorderRadius); + .box-shadow(inset 0 1px 1px rgba(0,0,0,.05)); + blockquote { + border-color: #ddd; + border-color: rgba(0,0,0,.15); + } +} + +// Sizes +.well-large { + padding: 24px; + .border-radius(@borderRadiusLarge); +} +.well-small { + padding: 9px; + .border-radius(@borderRadiusSmall); +} diff --git a/public/css/old/bio-header.less b/public/css/old/bio-header.less new file mode 100644 index 0000000..0a96e30 --- /dev/null +++ b/public/css/old/bio-header.less @@ -0,0 +1,97 @@ + + +.bio-header { + background-color: @pageHeaderBackground; + height: @pageHeaderHeight; +} + +.bio-header .brand { + float: left; + display: block; + height: @pageHeaderBrandHeight; + width: @pageHeaderBrandWidth; + line-height: 600px; + overflow: hidden; + background: url("/img/logo.svg") 0 0 no-repeat; + background-size: contain; +} + +.bio-header .nav { + position: relative; + left: 0; + display: block; + float: right; + margin: 0; +} + +.bio-header .nav > li { + float: left; + border-left: 1px solid @pageHeaderLinkBorder; + padding: @pageHeaderNavMargin 0; +} + +.bio-header .nav > li > a { + float: none; + padding: (((@pageHeaderHeight - 20) / 2) - @pageHeaderNavMargin) 10px; + color: @pageHeaderLinkColor; + text-shadow: 0 -1px 0 rgba(0,0,0,.25); + font-size: 12px; +} + +.bio-header .nav > li > a > i { + background-image: url("@{iconWhiteSpritePath}"); +} + +.bio-header .nav > li > a > .text { + margin: 0 2px 0 5px; +} + +.bio-header .nav > li.photo { + padding: 0; +} + +.bio-header .nav > li > a:focus, +.bio-header .nav > li > a:hover { + background-color: @pageHeaderBackgroundHover; + color: @pageHeaderLinkColorHover; + text-decoration: none; +} + +.bio-header .nav > .active > a, +.bio-header .nav > .active > a:hover, +.bio-header .nav > .active > a:focus { + color: @pageHeaderLinkColorActive; + text-decoration: none; + background-color: @pageHeaderLinkBackgroundActive; +} + + + +.error-panel { + position: fixed; + z-index: 1200; + width: 100%; + padding: 5px 10px; + color: white; + background: #ba6d6d; +} + +.progress-panel { + float: left; + margin: 6px 15px; +} + +.progress-panel h3 { + color: #fff; + margin: 0; + padding: 0; + font-size: 10px; + font-weight: normal; + line-height: 20px; +} + +.progress-panel .progress { + width: 200px; + height: 10px; + margin: 0; +} \ No newline at end of file diff --git a/public/css/old/bio-nav.less b/public/css/old/bio-nav.less new file mode 100644 index 0000000..fab1278 --- /dev/null +++ b/public/css/old/bio-nav.less @@ -0,0 +1,42 @@ +.bio-nav { + .clearfix(); +} + +.bio-nav .nav { + position: relative; + left: 0; + display: block; + float: left; + margin: 0; +} + +.bio-nav .nav > li { + float: left; + border-left: 1px solid @bioNavLinkBorder; + padding: 10px; +} + +.bio-nav .nav > li > a { + float: none; + color: @bioNavLinkColor; + font-size: 11px; + font-weight: bold; +} + +.bio-nav .nav > li > a > i { + background-image: url("@{iconWhiteSpritePath}"); +} + +.bio-nav .nav > li > a > .text { + margin: 0 2px 0 5px; +} + +.bio-nav .nav > li:focus, +.bio-nav .nav > li:hover, +.bio-nav .nav > li.active { + background-color: @bioNavBackgroundHover; + + > a { + background-color: transparent; + } +} \ No newline at end of file diff --git a/public/css/old/biomed.less b/public/css/old/biomed.less new file mode 100644 index 0000000..6c51259 --- /dev/null +++ b/public/css/old/biomed.less @@ -0,0 +1,43 @@ + +// Core variables and mixins +@import "variables.less"; // Modify this for custom colors, font-sizes, etc +@import "mixins.less"; + +// CSS Reset +@import "reset.less"; + +// Grid system and page structure +@import "scaffolding.less"; +@import "grid.less"; +@import "layouts.less"; + +// Base CSS +@import "type.less"; +@import "forms.less"; +@import "tables.less"; + +// Components: common +@import "sprites.less"; + +// Components: Buttons & Alerts +@import "buttons.less"; +// @import "button-groups.less"; +// @import "alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less + + +// Components: Nav +@import "navs.less"; +@import "bio-nav.less"; + +//@import "navbar.less"; +@import "breadcrumbs.less"; +@import "pagination.less"; +//@import "pager.less"; +@import "progress-bars.less"; +@import "dropdowns.less"; +@import "datepicker.less"; +@import "timepicker.less"; + + +@import "bio-header.less"; +@import "widgets.less"; \ No newline at end of file diff --git a/public/css/old/breadcrumbs.less b/public/css/old/breadcrumbs.less new file mode 100644 index 0000000..b052392 --- /dev/null +++ b/public/css/old/breadcrumbs.less @@ -0,0 +1,43 @@ +.breadcrumb { + margin: 0; + padding: 0 7px; + list-style: none; + background-color: #fff; + border-bottom: 1px solid #e3ebed; + + > li { + display: inline-block; + .ie7-inline-block(); + + padding: 8px 0; + font-size: 11px; + + > a { + color: #666666; + + &:hover { + color: #333333; + + i { + opacity: 1; + } + } + + i { + margin: 0 5px; + opacity: .6; + } + } + + > .divider { + background-image: url("../../img/breadcrumb.png"); + background-position: center center; + background-repeat: no-repeat; + padding: 0 15px; + } + } + > .active { + font-weight: bold; + color: #444444; + } +} diff --git a/public/css/old/buttons.less b/public/css/old/buttons.less new file mode 100644 index 0000000..f3f209e --- /dev/null +++ b/public/css/old/buttons.less @@ -0,0 +1,221 @@ +// +// Buttons +// -------------------------------------------------- + + +// Base styles +// -------------------------------------------------- + +// Core +.btn { + display: inline-block; + .ie7-inline-block(); + padding: 6px 12px; + margin-bottom: 0; // For input.btn + font-size: @baseFontSize; + line-height: @baseLineHeight; + color: #333; + text-align: center; + vertical-align: middle; + cursor: pointer; + .buttonBackground(@btnBackground, @grayDark, 0 1px 1px rgba(255,255,255,.75)); + + border: none; + + .ie7-restore-left-whitespace(); // Give IE7 some love + + // Hover/focus state + &:hover, + &:focus { + color: @grayDark; + text-decoration: none; +// background-position: 0 -15px; + + // transition is only when going to hover/focus, otherwise the background + // behind the gradient (there for IE<=9 fallback) gets mismatched +// .transition(background-position .1s linear); + } + + // Focus state for keyboard and accessibility + &:focus { + .tab-focus(); + } + + // Active state + &.active, + &:active { + outline: 0; + .box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)"); + } + + // Disabled state + &.disabled, + &[disabled] { + cursor: default; + .opacity(65); + .box-shadow(none); + } + +} + + + +// Button Sizes +// -------------------------------------------------- + +// Large +.btn-large { + padding: @paddingLarge; + font-size: @fontSizeLarge; +} +.btn-large [class^="icon-"], +.btn-large [class*=" icon-"] { + margin-top: 4px; +} + +// Small +.btn-small { + padding: @paddingSmall; + font-size: @fontSizeSmall; +} +.btn-small [class^="icon-"], +.btn-small [class*=" icon-"] { + margin-top: 0; +} +.btn-mini [class^="icon-"], +.btn-mini [class*=" icon-"] { + margin-top: -1px; +} + +// Mini +.btn-mini { + padding: @paddingMini; + font-size: @fontSizeMini; +} + + +// Block button +// ------------------------- + +.btn-block { + display: block; + width: 100%; + padding-left: 0; + padding-right: 0; + .box-sizing(border-box); +} + +// Vertically space out multiple block buttons +.btn-block + .btn-block { + margin-top: 5px; +} + +// Specificity overrides +input[type="submit"], +input[type="reset"], +input[type="button"] { + &.btn-block { + width: 100%; + } +} + + + +// Alternate buttons +// -------------------------------------------------- + +// Provide *some* extra contrast for those who can get it +.btn-primary.active, +.btn-warning.active, +.btn-danger.active, +.btn-success.active, +.btn-info.active, +.btn-inverse.active { + color: rgba(255,255,255,.75); +} + +// Set the backgrounds +// ------------------------- +.btn-primary { + .buttonBackground(@btnPrimaryBackground); +} +// Warning appears are orange +.btn-warning { + .buttonBackground(@btnWarningBackground); +} +// Danger and error appear as red +.btn-danger { + .buttonBackground(@btnDangerBackground); +} +// Success appears as green +.btn-success { + .buttonBackground(@btnSuccessBackground); +} +// Info appears as a neutral blue +.btn-info { + .buttonBackground(@btnInfoBackground); +} +// Inverse appears as dark gray +.btn-inverse { + .buttonBackground(@btnInverseBackground); +} + + +// Cross-browser Jank +// -------------------------------------------------- + +button.btn, +input[type="submit"].btn { + + // Firefox 3.6 only I believe + &::-moz-focus-inner { + padding: 0; + border: 0; + } + + // IE7 has some default padding on button controls + *padding-top: 3px; + *padding-bottom: 3px; + + &.btn-large { + *padding-top: 7px; + *padding-bottom: 7px; + } + &.btn-small { + *padding-top: 3px; + *padding-bottom: 3px; + } + &.btn-mini { + *padding-top: 1px; + *padding-bottom: 1px; + } +} + + +// Link buttons +// -------------------------------------------------- + +// Make a button look and behave like a link +.btn-link, +.btn-link:active, +.btn-link[disabled] { + background-color: transparent; + .box-shadow(none); +} +.btn-link { + border-color: transparent; + cursor: pointer; + color: @linkColor; + .border-radius(0); +} +.btn-link:hover, +.btn-link:focus { + color: @linkColorHover; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +.btn-link[disabled]:focus { + color: @grayDark; + text-decoration: none; +} \ No newline at end of file diff --git a/public/css/old/datepicker.less b/public/css/old/datepicker.less new file mode 100644 index 0000000..6df8494 --- /dev/null +++ b/public/css/old/datepicker.less @@ -0,0 +1,180 @@ +/*! + * Datepicker for Bootstrap + * + * Copyright 2012 Stefan Petre + * Improvements by Andrew Rowls + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +.datepicker { + padding: 4px; + .border-radius(4px); + &-inline { + width: 220px; + } + direction: ltr; + &&-rtl { + direction: rtl; + table tr td span { + float: right; + } + } + &-dropdown { + top: 0; + left: 0; + &:before { + content: ''; + display: inline-block; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-bottom-color: rgba(0,0,0,.2); + position: absolute; + top: -7px; + left: 6px; + } + &:after { + content: ''; + display: inline-block; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid @white; + position: absolute; + top: -6px; + left: 7px; + } + } + >div { + display: none; + } + &.days div.datepicker-days { + display: block; + } + &.months div.datepicker-months { + display: block; + } + &.years div.datepicker-years { + display: block; + } + table{ + margin: 0; + } + td, + th{ + text-align: center; + width: 20px; + height: 20px; + .border-radius(4px); + + border: none; + } + // Inline display inside a table presents some problems with + // border and background colors. + .table-striped & table tr { + td, th { + background-color:transparent; + } + } + table tr td { + &.day:hover { + background: @grayLighter; + cursor: pointer; + } + &.old, + &.new { + color: @grayLight; + } + &.disabled, + &.disabled:hover { + background: none; + color: @grayLight; + cursor: default; + } + &.today, + &.today:hover, + &.today.disabled, + &.today.disabled:hover { + @todayBackground: lighten(@orange, 30%); + .buttonBackground(@todayBackground, spin(@todayBackground, 20)); + color: #000 !important; + } + &.active, + &.active:hover, + &.active.disabled, + &.active.disabled:hover { + .buttonBackground(@btnPrimaryBackground, spin(@btnPrimaryBackground, 20)); + color: #fff; + text-shadow: 0 -1px 0 rgba(0,0,0,.25); + } + span { + display: block; + width: 23%; + height: 54px; + line-height: 54px; + float: left; + margin: 1%; + cursor: pointer; + .border-radius(4px); + &:hover { + background: @grayLighter; + } + &.disabled, + &.disabled:hover { + background:none; + color: @grayLight; + cursor: default; + } + &.active, + &.active:hover, + &.active.disabled, + &.active.disabled:hover { + .buttonBackground(@btnPrimaryBackground, spin(@btnPrimaryBackground, 20)); + color: #fff; + text-shadow: 0 -1px 0 rgba(0,0,0,.25); + } + &.old { + color: @grayLight; + } + } + } + + th.switch { + width: 145px; + } + + thead tr:first-child th, + tfoot tr:first-child th { + cursor: pointer; + &:hover{ + background: @grayLighter; + } + } + /*.dow { + border-top: 1px solid #ddd !important; + }*/ + + // Basic styling for calendar-week cells + .cw { + font-size: 10px; + width: 12px; + padding: 0 2px 0 5px; + vertical-align: middle; + } + thead tr:first-child th.cw { + cursor: default; + background-color: transparent; + } +} +.input-append, +.input-prepend { + &.date { + .add-on i { + display: block; + cursor: pointer; + width: 16px; + height: 16px; + } + } +} \ No newline at end of file diff --git a/public/css/old/dropdowns.less b/public/css/old/dropdowns.less new file mode 100644 index 0000000..bbfe3fd --- /dev/null +++ b/public/css/old/dropdowns.less @@ -0,0 +1,237 @@ +// +// Dropdown menus +// -------------------------------------------------- + + +// Use the .menu class on any
  • element within the topbar or ul.tabs and you'll get some superfancy dropdowns +.dropup, +.dropdown { + position: relative; +} +.dropdown-toggle { + // The caret makes the toggle a bit too tall in IE7 + *margin-bottom: -3px; +} +.dropdown-toggle:active, +.open .dropdown-toggle { + outline: 0; +} + +// Dropdown arrow/caret +// -------------------- +.caret { + display: inline-block; + width: 0; + height: 0; + vertical-align: top; + border-top: 4px solid @black; + border-right: 4px solid transparent; + border-left: 4px solid transparent; + content: ""; +} + +// Place the caret +.dropdown .caret { + margin-top: 8px; + margin-left: 2px; +} + +// The dropdown menu (ul) +// ---------------------- +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: @zindexDropdown; + display: none; // none by default, but block on "open" of the menu + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; // override default ul + list-style: none; + background-color: @dropdownBackground; + border: 1px solid #ccc; // Fallback for IE7-8 + border: 1px solid @dropdownBorder; + *border-right-width: 2px; + *border-bottom-width: 2px; + .border-radius(6px); + .box-shadow(0 5px 10px rgba(0,0,0,.2)); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; + + // Aligns the dropdown menu to right + &.pull-right { + right: 0; + left: auto; + } + + // Dividers (basically an hr) within the dropdown + .divider { + .nav-divider(@dropdownDividerTop, @dropdownDividerBottom); + } + + // Links within the dropdown menu + > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: @baseLineHeight; + color: @dropdownLinkColor; + white-space: nowrap; + } +} + +// Hover/Focus state +// ----------- +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus, +.dropdown-submenu:hover > a, +.dropdown-submenu:focus > a { + text-decoration: none; + color: @dropdownLinkColorHover; + #gradient > .vertical(@dropdownLinkBackgroundHover, darken(@dropdownLinkBackgroundHover, 5%)); +} + +// Active state +// ------------ +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: @dropdownLinkColorActive; + text-decoration: none; + outline: 0; + #gradient > .vertical(@dropdownLinkBackgroundActive, darken(@dropdownLinkBackgroundActive, 5%)); +} + +// Disabled state +// -------------- +// Gray out text and ensure the hover/focus state remains gray +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: @grayLight; +} +// Nuke hover/focus effects +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + background-color: transparent; + background-image: none; // Remove CSS gradient + .reset-filter(); + cursor: default; +} + +// Open state for the dropdown +// --------------------------- +.open { + // IE7's z-index only goes to the nearest positioned ancestor, which would + // make the menu appear below buttons that appeared later on the page + *z-index: @zindexDropdown; + + & > .dropdown-menu { + display: block; + } +} + +// Right aligned dropdowns +// --------------------------- +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} + +// Allow for dropdowns to go bottom up (aka, dropup-menu) +// ------------------------------------------------------ +// Just add .dropup after the standard .dropdown class and you're set, bro. +// TODO: abstract this so that the navbar fixed styles are not placed here? +.dropup, +.navbar-fixed-bottom .dropdown { + // Reverse the caret + .caret { + border-top: 0; + border-bottom: 4px solid @black; + content: ""; + } + // Different positioning for bottom up menu + .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 1px; + } +} + +// Sub menus +// --------------------------- +.dropdown-submenu { + position: relative; +} +// Default dropdowns +.dropdown-submenu > .dropdown-menu { + top: 0; + left: 100%; + margin-top: -6px; + margin-left: -1px; + .border-radius(0 6px 6px 6px); +} +.dropdown-submenu:hover > .dropdown-menu { + display: block; +} + +// Dropups +.dropup .dropdown-submenu > .dropdown-menu { + top: auto; + bottom: 0; + margin-top: 0; + margin-bottom: -2px; + .border-radius(5px 5px 5px 0); +} + +// Caret to indicate there is a submenu +.dropdown-submenu > a:after { + display: block; + content: " "; + float: right; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; + border-width: 5px 0 5px 5px; + border-left-color: darken(@dropdownBackground, 20%); + margin-top: 5px; + margin-right: -10px; +} +.dropdown-submenu:hover > a:after { + border-left-color: @dropdownLinkColorHover; +} + +// Left aligned submenus +.dropdown-submenu.pull-left { + // Undo the float + // Yes, this is awkward since .pull-left adds a float, but it sticks to our conventions elsewhere. + float: none; + + // Positioning the submenu + > .dropdown-menu { + left: -100%; + margin-left: 10px; + .border-radius(6px 0 6px 6px); + } +} + +// Tweak nav headers +// ----------------- +// Increase padding from 15px to 20px on sides +.dropdown .dropdown-menu .nav-header { + padding-left: 20px; + padding-right: 20px; +} + +// Typeahead +// --------- +.typeahead { + z-index: 1051; + margin-top: 2px; // give it some space to breathe + .border-radius(@baseBorderRadius); +} diff --git a/public/css/old/forms.less b/public/css/old/forms.less new file mode 100644 index 0000000..49f72a9 --- /dev/null +++ b/public/css/old/forms.less @@ -0,0 +1,639 @@ +// +// Forms +// -------------------------------------------------- + + +// GENERAL STYLES +// -------------- + +// Make all forms have space below them +form { + margin: 0 0 @baseLineHeight; +} + +fieldset { + padding: 0; + margin: 0; + border: 0; +} + +// Groups of fields with labels on top (legends) +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: @baseLineHeight; + font-size: @baseFontSize * 1.5; + line-height: @baseLineHeight * 2; + color: @grayDark; + border: 0; + border-bottom: 1px solid #e5e5e5; + + // Small + small { + font-size: @baseLineHeight * .75; + color: @grayLight; + } +} + +// Set font for forms +label, +input, +button, +select, +textarea { + #font > .shorthand(@baseFontSize,normal,@baseLineHeight); // Set size, weight, line-height here +} +input, +button, +select, +textarea { + font-family: @baseFontFamily; // And only set font-family here for those that need it (note the missing label element) +} + +// Identify controls by their labels +label { + display: block; + margin-bottom: 5px; +} + +// Form controls +// ------------------------- + +// Shared size and type resets +select, +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + display: inline-block; + height: @baseLineHeight; + padding: 4px 6px; + margin-bottom: @baseLineHeight / 2; + font-size: @baseFontSize; + line-height: @baseLineHeight; + color: @gray; + vertical-align: middle; +} + +// Reset appearance properties for textual inputs and textarea +// Declare width for legacy (can't be on input[type=*] selectors or it's too specific) +input, +textarea, +.uneditable-input { + width: 206px; // plus 12px padding and 2px border +} +// Reset height since textareas have rows +textarea { + height: auto; +} +// Everything else +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + background-color: @inputBackground; + border: 1px solid @inputBorder; + .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); + .transition(~"border linear .2s, box-shadow linear .2s"); + + // Focus state + &:focus { + border-color: rgba(82,168,236,.8); + outline: 0; + outline: thin dotted \9; /* IE6-9 */ + .box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6)"); + } +} + +// Position radios and checkboxes better +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + *margin-top: 0; /* IE7 */ + margin-top: 1px \9; /* IE8-9 */ + line-height: normal; +} + +// Reset width of input images, buttons, radios, checkboxes +input[type="file"], +input[type="image"], +input[type="submit"], +input[type="reset"], +input[type="button"], +input[type="radio"], +input[type="checkbox"] { + width: auto; // Override of generic input selector +} + +// Set the height of select and file controls to match text inputs +select, +input[type="file"] { + height: @inputHeight; /* In IE7, the height of the select element cannot be changed by height, only font-size */ + *margin-top: 4px; /* For IE7, add top margin to align select with labels */ + line-height: @inputHeight; +} + +// Make select elements obey height by applying a border +select { + width: 220px; // default input width + 10px of padding that doesn't get applied + border: 1px solid @inputBorder; + background-color: @inputBackground; // Chrome on Linux and Mobile Safari need background-color +} + +// Make multiple select elements height not fixed +select[multiple], +select[size] { + height: auto; +} + +// Focus for select, file, radio, and checkbox +select:focus, +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + .tab-focus(); +} + + +// Uneditable inputs +// ------------------------- + +// Make uneditable inputs look inactive +.uneditable-input, +.uneditable-textarea { + color: @grayLight; + background-color: darken(@inputBackground, 1%); + border-color: @inputBorder; + .box-shadow(inset 0 1px 2px rgba(0,0,0,.025)); + cursor: not-allowed; +} + +// For text that needs to appear as an input but should not be an input +.uneditable-input { + overflow: hidden; // prevent text from wrapping, but still cut it off like an input does + white-space: nowrap; +} + +// Make uneditable textareas behave like a textarea +.uneditable-textarea { + width: auto; + height: auto; +} + + +// Placeholder +// ------------------------- + +// Placeholder text gets special styles because when browsers invalidate entire lines if it doesn't understand a selector +input, +textarea { + .placeholder(); +} + + +// CHECKBOXES & RADIOS +// ------------------- + +// Indent the labels to position radios/checkboxes as hanging +.radio, +.checkbox { + min-height: @baseLineHeight; // clear the floating input if there is no label text + padding-left: 20px; +} +.radio input[type="radio"], +.checkbox input[type="checkbox"] { + float: left; + margin-left: -20px; +} + +// Move the options list down to align with labels +.controls > .radio:first-child, +.controls > .checkbox:first-child { + padding-top: 5px; // has to be padding because margin collaspes +} + +// Radios and checkboxes on same line +// TODO v3: Convert .inline to .control-inline +.radio.inline, +.checkbox.inline { + display: inline-block; + padding-top: 5px; + margin-bottom: 0; + vertical-align: middle; +} +.radio.inline + .radio.inline, +.checkbox.inline + .checkbox.inline { + margin-left: 10px; // space out consecutive inline controls +} + + + +// INPUT SIZES +// ----------- + +// General classes for quick sizes +.input-mini { width: 60px; } +.input-small { width: 90px; } +.input-medium { width: 150px; } +.input-large { width: 210px; } +.input-xlarge { width: 270px; } +.input-xxlarge { width: 530px; } + +// Grid style input sizes +input[class*="span"], +select[class*="span"], +textarea[class*="span"], +.uneditable-input[class*="span"], +// Redeclare since the fluid row class is more specific +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"] { + float: none; + margin-left: 0; +} +// Ensure input-prepend/append never wraps +.input-append input[class*="span"], +.input-append .uneditable-input[class*="span"], +.input-prepend input[class*="span"], +.input-prepend .uneditable-input[class*="span"], +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"], +.row-fluid .input-prepend [class*="span"], +.row-fluid .input-append [class*="span"] { + display: inline-block; +} + + + +// GRID SIZING FOR INPUTS +// ---------------------- + +// Grid sizes +#grid > .input(@gridColumnWidth, @gridGutterWidth); + +// Control row for multiple inputs per line +.controls-row { + .clearfix(); // Clear the float from controls +} + +// Float to collapse white-space for proper grid alignment +.controls-row [class*="span"], +// Redeclare the fluid grid collapse since we undo the float for inputs +.row-fluid .controls-row [class*="span"] { + float: left; +} +// Explicity set top padding on all checkboxes/radios, not just first-child +.controls-row .checkbox[class*="span"], +.controls-row .radio[class*="span"] { + padding-top: 5px; +} + + + + +// DISABLED STATE +// -------------- + +// Disabled and read-only inputs +input[disabled], +select[disabled], +textarea[disabled], +input[readonly], +select[readonly], +textarea[readonly] { + cursor: not-allowed; + background-color: @inputDisabledBackground; +} +// Explicitly reset the colors here +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"][readonly], +input[type="checkbox"][readonly] { + background-color: transparent; +} + + + + +// FORM FIELD FEEDBACK STATES +// -------------------------- + +// Warning +.control-group.warning { + .formFieldState(@warningText, @warningText, @warningBackground); +} +// Error +.control-group.error { + .formFieldState(@errorText, @errorText, @errorBackground); +} +// Success +.control-group.success { + .formFieldState(@successText, @successText, @successBackground); +} +// Success +.control-group.info { + .formFieldState(@infoText, @infoText, @infoBackground); +} + +// HTML5 invalid states +// Shares styles with the .control-group.error above +input:focus:invalid, +textarea:focus:invalid, +select:focus:invalid { + color: #b94a48; + border-color: #ee5f5b; + &:focus { + border-color: darken(#ee5f5b, 10%); + @shadow: 0 0 6px lighten(#ee5f5b, 20%); + .box-shadow(@shadow); + } +} + + + +// FORM ACTIONS +// ------------ + +.form-actions { + padding: (@baseLineHeight - 1) 20px @baseLineHeight; + margin-top: @baseLineHeight; + margin-bottom: @baseLineHeight; + background-color: @formActionsBackground; + border-top: 1px solid #e5e5e5; + .clearfix(); // Adding clearfix to allow for .pull-right button containers +} + + + +// HELP TEXT +// --------- + +.help-block, +.help-inline { + color: lighten(@textColor, 15%); // lighten the text some for contrast +} + +.help-block { + display: block; // account for any element using help-block + margin-bottom: @baseLineHeight / 2; +} + +.help-inline { + display: inline-block; + .ie7-inline-block(); + vertical-align: middle; + padding-left: 5px; +} + + + +// INPUT GROUPS +// ------------ + +// Allow us to put symbols and text within the input field for a cleaner look +.input-append, +.input-prepend { + display: inline-block; + margin-bottom: @baseLineHeight / 2; + vertical-align: middle; + font-size: 0; // white space collapse hack + white-space: nowrap; // Prevent span and input from separating + + // Reset the white space collapse hack + input, + select, + .uneditable-input, + .dropdown-menu, + .popover { + font-size: @baseFontSize; + } + + input, + select, + .uneditable-input { + position: relative; // placed here by default so that on :focus we can place the input above the .add-on for full border and box-shadow goodness + margin-bottom: 0; // prevent bottom margin from screwing up alignment in stacked forms + *margin-left: 0; + vertical-align: top; + // Make input on top when focused so blue border and shadow always show + &:focus { + z-index: 2; + } + } + .add-on { + display: inline-block; + width: auto; + height: @baseLineHeight; + min-width: 16px; + padding: 4px 5px; + font-size: @baseFontSize; + font-weight: normal; + line-height: @baseLineHeight; + text-align: center; + text-shadow: 0 1px 0 @white; + background-color: @grayLighter; + border: 1px solid #ccc; + } + .add-on, + .btn, + .btn-group > .dropdown-toggle { + vertical-align: top; + } + .active { + background-color: lighten(@green, 30); + border-color: @green; + } +} + +.input-prepend { + .add-on, + .btn { + margin-right: -1px; + } +} + +.input-append { + .add-on, + .btn, + .btn-group { + margin-left: -1px; + } +} + + +.input-prepend.input-append { + .add-on:first-child, + .btn:first-child { + margin-right: -1px; + } + .add-on:last-child, + .btn:last-child { + margin-left: -1px; + } + .btn-group:first-child { + margin-left: 0; + } +} + + + + +// SEARCH FORM +// ----------- + +input.search-query { + padding-right: 14px; + padding-right: 4px \9; + padding-left: 14px; + padding-left: 4px \9; + margin-bottom: 0; // Remove the default margin on all inputs +} + + + +// HORIZONTAL & VERTICAL FORMS +// --------------------------- + +// Common properties +// ----------------- + +.form-search, +.form-inline, +.form-horizontal { + input, + textarea, + select, + .help-inline, + .uneditable-input, + .input-prepend, + .input-append { + display: inline-block; + .ie7-inline-block(); + margin-bottom: 0; + vertical-align: middle; + } + // Re-hide hidden elements due to specifity + .hide { + display: none; + } +} +.form-search label, +.form-inline label, +.form-search .btn-group, +.form-inline .btn-group { + display: inline-block; +} +// Remove margin for input-prepend/-append +.form-search .input-append, +.form-inline .input-append, +.form-search .input-prepend, +.form-inline .input-prepend { + margin-bottom: 0; +} +// Inline checkbox/radio labels (remove padding on left) +.form-search .radio, +.form-search .checkbox, +.form-inline .radio, +.form-inline .checkbox { + padding-left: 0; + margin-bottom: 0; + vertical-align: middle; +} +// Remove float and margin, set to inline-block +.form-search .radio input[type="radio"], +.form-search .checkbox input[type="checkbox"], +.form-inline .radio input[type="radio"], +.form-inline .checkbox input[type="checkbox"] { + float: left; + margin-right: 3px; + margin-left: 0; +} + + +// Margin to space out fieldsets +.control-group { + margin-bottom: @baseLineHeight / 2; +} + +// Legend collapses margin, so next element is responsible for spacing +legend + .control-group { + margin-top: @baseLineHeight; + -webkit-margin-top-collapse: separate; +} + +// Horizontal-specific styles +// -------------------------- + +.form-horizontal { + // Increase spacing between groups + .control-group { + margin-bottom: @baseLineHeight; + .clearfix(); + } + // Float the labels left + .control-label { + float: left; + width: @horizontalComponentOffset - 20; + padding-top: 5px; + text-align: right; + } + // Move over all input controls and content + .controls { + // Super jank IE7 fix to ensure the inputs in .input-append and input-prepend + // don't inherit the margin of the parent, in this case .controls + *display: inline-block; + *padding-left: 20px; + margin-left: @horizontalComponentOffset; + *margin-left: 0; + &:first-child { + *padding-left: @horizontalComponentOffset; + } + } + // Remove bottom margin on block level help text since that's accounted for on .control-group + .help-block { + margin-bottom: 0; + } + // And apply it only to .help-block instances that follow a form control + input, + select, + textarea, + .uneditable-input, + .input-prepend, + .input-append { + + .help-block { + margin-top: @baseLineHeight / 2; + } + } + // Move over buttons in .form-actions to align with .controls + .form-actions { + padding-left: @horizontalComponentOffset; + } +} diff --git a/public/css/old/grid.less b/public/css/old/grid.less new file mode 100644 index 0000000..2d93e67 --- /dev/null +++ b/public/css/old/grid.less @@ -0,0 +1,21 @@ +// +// Grid system +// -------------------------------------------------- + + +// Fixed (940px) +#grid > .core(@gridColumnWidth, @gridGutterWidth); + +// Fluid (940px) +#grid > .fluid(@fluidGridColumnWidth, @fluidGridGutterWidth); + +// Reset utility classes due to specificity +[class*="span"].hide, +.row-fluid [class*="span"].hide { + display: none; +} + +[class*="span"].pull-right, +.row-fluid [class*="span"].pull-right { + float: right; +} diff --git a/public/css/old/layouts.less b/public/css/old/layouts.less new file mode 100644 index 0000000..ff5a253 --- /dev/null +++ b/public/css/old/layouts.less @@ -0,0 +1,16 @@ +// +// Layouts +// -------------------------------------------------- + + +// Container (centered, fixed-width layouts) +.container { + .container-fixed(); +} + +// Fluid layouts (left aligned, with sidebar, min- & max-width content) +.container-fluid { + padding-right: @gridGutterWidth; + padding-left: @gridGutterWidth; + .clearfix(); +} \ No newline at end of file diff --git a/public/css/old/mixins.less b/public/css/old/mixins.less new file mode 100644 index 0000000..8dd4f52 --- /dev/null +++ b/public/css/old/mixins.less @@ -0,0 +1,704 @@ +// +// Mixins +// -------------------------------------------------- + + +// UTILITY MIXINS +// -------------------------------------------------- + +// Clearfix +// -------- +// For clearing floats like a boss h5bp.com/q +.clearfix { + *zoom: 1; + &:before, + &:after { + display: table; + content: ""; + // Fixes Opera/contenteditable bug: + // http://nicolasgallagher.com/micro-clearfix-hack/#comment-36952 + line-height: 0; + } + &:after { + clear: both; + } +} + +// Webkit-style focus +// ------------------ +.tab-focus() { + // Default + outline: thin dotted #333; + // Webkit + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +// Center-align a block level element +// ---------------------------------- +.center-block() { + display: block; + margin-left: auto; + margin-right: auto; +} + +// IE7 inline-block +// ---------------- +.ie7-inline-block() { + *display: inline; /* IE7 inline-block hack */ + *zoom: 1; +} + +// IE7 likes to collapse whitespace on either side of the inline-block elements. +// Ems because we're attempting to match the width of a space character. Left +// version is for form buttons, which typically come after other elements, and +// right version is for icons, which come before. Applying both is ok, but it will +// mean that space between those elements will be .6em (~2 space characters) in IE7, +// instead of the 1 space in other browsers. +.ie7-restore-left-whitespace() { + *margin-left: .3em; + + &:first-child { + *margin-left: 0; + } +} + +.ie7-restore-right-whitespace() { + *margin-right: .3em; +} + +// Sizing shortcuts +// ------------------------- +.size(@height, @width) { + width: @width; + height: @height; +} +.square(@size) { + .size(@size, @size); +} + +// Placeholder text +// ------------------------- +.placeholder(@color: @placeholderText) { + &:-moz-placeholder { + color: @color; + } + &:-ms-input-placeholder { + color: @color; + } + &::-webkit-input-placeholder { + color: @color; + } +} + +// Text overflow +// ------------------------- +// Requires inline-block or block for proper styling +.text-overflow() { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +// CSS image replacement +// ------------------------- +// Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757 +.hide-text { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + + +// FONTS +// -------------------------------------------------- + +#font { + #family { + .serif() { + font-family: @serifFontFamily; + } + .sans-serif() { + font-family: @sansFontFamily; + } + .monospace() { + font-family: @monoFontFamily; + } + } + .shorthand(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) { + font-size: @size; + font-weight: @weight; + line-height: @lineHeight; + } + .serif(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) { + #font > #family > .serif; + #font > .shorthand(@size, @weight, @lineHeight); + } + .sans-serif(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) { + #font > #family > .sans-serif; + #font > .shorthand(@size, @weight, @lineHeight); + } + .monospace(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) { + #font > #family > .monospace; + #font > .shorthand(@size, @weight, @lineHeight); + } +} + + +// FORMS +// -------------------------------------------------- + +// Block level inputs +.input-block-level { + display: block; + width: 100%; + min-height: @inputHeight; // Make inputs at least the height of their button counterpart (base line-height + padding + border) + .box-sizing(border-box); // Makes inputs behave like true block-level elements +} + + + +// Mixin for form field states +.formFieldState(@textColor: #555, @borderColor: #ccc, @backgroundColor: #f5f5f5) { + // Set the text color + .control-label, + .help-block, + .help-inline { + color: @textColor; + } + // Style inputs accordingly + .checkbox, + .radio, + input, + select, + textarea { + color: @textColor; + } + input, + select, + textarea { + border-color: @borderColor; + .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work + &:focus { + border-color: darken(@borderColor, 10%); + @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@borderColor, 20%); + .box-shadow(@shadow); + } + } + // Give a small background color for input-prepend/-append + .input-prepend .add-on, + .input-append .add-on { + color: @textColor; + background-color: @backgroundColor; + border-color: @textColor; + } +} + + + +// CSS3 PROPERTIES +// -------------------------------------------------- + +// Border Radius +.border-radius(@radius) { + -webkit-border-radius: @radius; + -moz-border-radius: @radius; + border-radius: @radius; +} + +// Single Corner Border Radius +.border-top-left-radius(@radius) { + -webkit-border-top-left-radius: @radius; + -moz-border-radius-topleft: @radius; + border-top-left-radius: @radius; +} +.border-top-right-radius(@radius) { + -webkit-border-top-right-radius: @radius; + -moz-border-radius-topright: @radius; + border-top-right-radius: @radius; +} +.border-bottom-right-radius(@radius) { + -webkit-border-bottom-right-radius: @radius; + -moz-border-radius-bottomright: @radius; + border-bottom-right-radius: @radius; +} +.border-bottom-left-radius(@radius) { + -webkit-border-bottom-left-radius: @radius; + -moz-border-radius-bottomleft: @radius; + border-bottom-left-radius: @radius; +} + +// Single Side Border Radius +.border-top-radius(@radius) { + .border-top-right-radius(@radius); + .border-top-left-radius(@radius); +} +.border-right-radius(@radius) { + .border-top-right-radius(@radius); + .border-bottom-right-radius(@radius); +} +.border-bottom-radius(@radius) { + .border-bottom-right-radius(@radius); + .border-bottom-left-radius(@radius); +} +.border-left-radius(@radius) { + .border-top-left-radius(@radius); + .border-bottom-left-radius(@radius); +} + +// Drop shadows +.box-shadow(@shadow) { + -webkit-box-shadow: @shadow; + -moz-box-shadow: @shadow; + box-shadow: @shadow; +} + +// Transitions +.transition(@transition) { + -webkit-transition: @transition; + -moz-transition: @transition; + -o-transition: @transition; + transition: @transition; +} +.transition-delay(@transition-delay) { + -webkit-transition-delay: @transition-delay; + -moz-transition-delay: @transition-delay; + -o-transition-delay: @transition-delay; + transition-delay: @transition-delay; +} +.transition-duration(@transition-duration) { + -webkit-transition-duration: @transition-duration; + -moz-transition-duration: @transition-duration; + -o-transition-duration: @transition-duration; + transition-duration: @transition-duration; +} + +// Transformations +.rotate(@degrees) { + -webkit-transform: rotate(@degrees); + -moz-transform: rotate(@degrees); + -ms-transform: rotate(@degrees); + -o-transform: rotate(@degrees); + transform: rotate(@degrees); +} +.scale(@ratio) { + -webkit-transform: scale(@ratio); + -moz-transform: scale(@ratio); + -ms-transform: scale(@ratio); + -o-transform: scale(@ratio); + transform: scale(@ratio); +} +.translate(@x, @y) { + -webkit-transform: translate(@x, @y); + -moz-transform: translate(@x, @y); + -ms-transform: translate(@x, @y); + -o-transform: translate(@x, @y); + transform: translate(@x, @y); +} +.skew(@x, @y) { + -webkit-transform: skew(@x, @y); + -moz-transform: skew(@x, @y); + -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twitter/bootstrap/issues/4885 + -o-transform: skew(@x, @y); + transform: skew(@x, @y); + -webkit-backface-visibility: hidden; // See https://github.com/twitter/bootstrap/issues/5319 +} +.translate3d(@x, @y, @z) { + -webkit-transform: translate3d(@x, @y, @z); + -moz-transform: translate3d(@x, @y, @z); + -o-transform: translate3d(@x, @y, @z); + transform: translate3d(@x, @y, @z); +} + +// Backface visibility +// Prevent browsers from flickering when using CSS 3D transforms. +// Default value is `visible`, but can be changed to `hidden +// See git pull https://github.com/dannykeane/bootstrap.git backface-visibility for examples +.backface-visibility(@visibility){ + -webkit-backface-visibility: @visibility; + -moz-backface-visibility: @visibility; + backface-visibility: @visibility; +} + +// Background clipping +// Heads up: FF 3.6 and under need "padding" instead of "padding-box" +.background-clip(@clip) { + -webkit-background-clip: @clip; + -moz-background-clip: @clip; + background-clip: @clip; +} + +// Background sizing +.background-size(@size) { + -webkit-background-size: @size; + -moz-background-size: @size; + -o-background-size: @size; + background-size: @size; +} + + +// Box sizing +.box-sizing(@boxmodel) { + -webkit-box-sizing: @boxmodel; + -moz-box-sizing: @boxmodel; + box-sizing: @boxmodel; +} + +// User select +// For selecting text on the page +.user-select(@select) { + -webkit-user-select: @select; + -moz-user-select: @select; + -ms-user-select: @select; + -o-user-select: @select; + user-select: @select; +} + +// Resize anything +.resizable(@direction) { + resize: @direction; // Options: horizontal, vertical, both + overflow: auto; // Safari fix +} + +// CSS3 Content Columns +.content-columns(@columnCount, @columnGap: @gridGutterWidth) { + -webkit-column-count: @columnCount; + -moz-column-count: @columnCount; + column-count: @columnCount; + -webkit-column-gap: @columnGap; + -moz-column-gap: @columnGap; + column-gap: @columnGap; +} + +// Optional hyphenation +.hyphens(@mode: auto) { + word-wrap: break-word; + -webkit-hyphens: @mode; + -moz-hyphens: @mode; + -ms-hyphens: @mode; + -o-hyphens: @mode; + hyphens: @mode; +} + +// Opacity +.opacity(@opacity) { + opacity: @opacity / 100; + filter: ~"alpha(opacity=@{opacity})"; +} + + + +// BACKGROUNDS +// -------------------------------------------------- + +// Add an alphatransparency value to any background or border color (via Elyse Holladay) +#translucent { + .background(@color: @white, @alpha: 1) { + background-color: hsla(hue(@color), saturation(@color), lightness(@color), @alpha); + } + .border(@color: @white, @alpha: 1) { + border-color: hsla(hue(@color), saturation(@color), lightness(@color), @alpha); + .background-clip(padding-box); + } +} + +// Gradient Bar Colors for buttons and alerts +.gradientBar(@primaryColor, @secondaryColor, @textColor: #fff, @textShadow: 0 -1px 0 rgba(0,0,0,.25)) { + color: @textColor; + text-shadow: @textShadow; + #gradient > .vertical(@primaryColor, @secondaryColor); + border-color: @secondaryColor @secondaryColor darken(@secondaryColor, 15%); + border-color: rgba(0,0,0,.1) rgba(0,0,0,.1) fadein(rgba(0,0,0,.1), 15%); +} + +// Gradients +#gradient { + .horizontal(@startColor: #555, @endColor: #333) { + background-color: @endColor; + background-image: -moz-linear-gradient(left, @startColor, @endColor); // FF 3.6+ + background-image: -webkit-gradient(linear, 0 0, 100% 0, from(@startColor), to(@endColor)); // Safari 4+, Chrome 2+ + background-image: -webkit-linear-gradient(left, @startColor, @endColor); // Safari 5.1+, Chrome 10+ + background-image: -o-linear-gradient(left, @startColor, @endColor); // Opera 11.10 + background-image: linear-gradient(to right, @startColor, @endColor); // Standard, IE10 + background-repeat: repeat-x; + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)",argb(@startColor),argb(@endColor))); // IE9 and down + } + .vertical(@startColor: #555, @endColor: #333) { + background-color: mix(@startColor, @endColor, 60%); + background-image: -moz-linear-gradient(top, @startColor, @endColor); // FF 3.6+ + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), to(@endColor)); // Safari 4+, Chrome 2+ + background-image: -webkit-linear-gradient(top, @startColor, @endColor); // Safari 5.1+, Chrome 10+ + background-image: -o-linear-gradient(top, @startColor, @endColor); // Opera 11.10 + background-image: linear-gradient(to bottom, @startColor, @endColor); // Standard, IE10 + background-repeat: repeat-x; + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@startColor),argb(@endColor))); // IE9 and down + } + .directional(@startColor: #555, @endColor: #333, @deg: 45deg) { + background-color: @endColor; + background-repeat: repeat-x; + background-image: -moz-linear-gradient(@deg, @startColor, @endColor); // FF 3.6+ + background-image: -webkit-linear-gradient(@deg, @startColor, @endColor); // Safari 5.1+, Chrome 10+ + background-image: -o-linear-gradient(@deg, @startColor, @endColor); // Opera 11.10 + background-image: linear-gradient(@deg, @startColor, @endColor); // Standard, IE10 + } + .horizontal-three-colors(@startColor: #00b3ee, @midColor: #7a43b6, @colorStop: 50%, @endColor: #c3325f) { + background-color: mix(@midColor, @endColor, 80%); + background-image: -webkit-gradient(left, linear, 0 0, 0 100%, from(@startColor), color-stop(@colorStop, @midColor), to(@endColor)); + background-image: -webkit-linear-gradient(left, @startColor, @midColor @colorStop, @endColor); + background-image: -moz-linear-gradient(left, @startColor, @midColor @colorStop, @endColor); + background-image: -o-linear-gradient(left, @startColor, @midColor @colorStop, @endColor); + background-image: linear-gradient(to right, @startColor, @midColor @colorStop, @endColor); + background-repeat: no-repeat; + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@startColor),argb(@endColor))); // IE9 and down, gets no color-stop at all for proper fallback + } + + .vertical-three-colors(@startColor: #00b3ee, @midColor: #7a43b6, @colorStop: 50%, @endColor: #c3325f) { + background-color: mix(@midColor, @endColor, 80%); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), color-stop(@colorStop, @midColor), to(@endColor)); + background-image: -webkit-linear-gradient(@startColor, @midColor @colorStop, @endColor); + background-image: -moz-linear-gradient(top, @startColor, @midColor @colorStop, @endColor); + background-image: -o-linear-gradient(@startColor, @midColor @colorStop, @endColor); + background-image: linear-gradient(@startColor, @midColor @colorStop, @endColor); + background-repeat: no-repeat; + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@startColor),argb(@endColor))); // IE9 and down, gets no color-stop at all for proper fallback + } + .radial(@innerColor: #555, @outerColor: #333) { + background-color: @outerColor; + background-image: -webkit-gradient(radial, center center, 0, center center, 460, from(@innerColor), to(@outerColor)); + background-image: -webkit-radial-gradient(circle, @innerColor, @outerColor); + background-image: -moz-radial-gradient(circle, @innerColor, @outerColor); + background-image: -o-radial-gradient(circle, @innerColor, @outerColor); + background-repeat: no-repeat; + } + .striped(@color: #555, @angle: 45deg) { + background-color: @color; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(.25, rgba(255,255,255,.15)), color-stop(.25, transparent), color-stop(.5, transparent), color-stop(.5, rgba(255,255,255,.15)), color-stop(.75, rgba(255,255,255,.15)), color-stop(.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); + } +} +// Reset filters for IE +.reset-filter() { + filter: e(%("progid:DXImageTransform.Microsoft.gradient(enabled = false)")); +} + + + +// COMPONENT MIXINS +// -------------------------------------------------- + +// Horizontal dividers +// ------------------------- +// Dividers (basically an hr) within dropdowns and nav lists +.nav-divider(@top: #e5e5e5, @bottom: @white) { + // IE7 needs a set width since we gave a height. Restricting just + // to IE7 to keep the 1px left/right space in other browsers. + // It is unclear where IE is getting the extra space that we need + // to negative-margin away, but so it goes. + *width: 100%; + height: 1px; + margin: ((@baseLineHeight / 2) - 1) 1px; // 8px 1px + *margin: -5px 0 5px; + overflow: hidden; + background-color: @top; + border-bottom: 1px solid @bottom; +} + +// Button backgrounds +// ------------------ + +.buttonBackground(@backgroundColor, @textColor: #fff, @textShadow: 0 -1px 0 rgba(0,0,0,.25)) { + + color: @textColor; + text-shadow: @textShadow; + background-color: @backgroundColor; + .reset-filter(); + + // in these cases the gradient won't cover the background, so we override + &:hover, &:focus, &:active, &.active, &.disabled, &[disabled] { + color: @textColor; + background-color: @backgroundColor; + } + + // IE 7 + 8 can't handle box-shadow to show active, so we darken a bit ourselves + &:active, + &.active { + background-color: darken(@backgroundColor, 10%) e("\9"); + } +} + + +// Navbar vertical align +// ------------------------- +// Vertically center elements in the navbar. +// Example: an element has a height of 30px, so write out `.navbarVerticalAlign(30px);` to calculate the appropriate top margin. +.navbarVerticalAlign(@elementHeight) { + margin-top: (@navbarHeight - @elementHeight) / 2; +} + + + +// Grid System +// ----------- + +// Centered container element +.container-fixed() { + margin-right: auto; + margin-left: auto; + .clearfix(); +} + +// Table columns +.tableColumns(@columnSpan: 1) { + float: none; // undo default grid column styles + width: ((@gridColumnWidth) * @columnSpan) + (@gridGutterWidth * (@columnSpan - 1)) - 16; // 16 is total padding on left and right of table cells + margin-left: 0; // undo default grid column styles +} + +// Make a Grid +// Use .makeRow and .makeColumn to assign semantic layouts grid system behavior +.makeRow() { + margin-left: @gridGutterWidth * -1; + .clearfix(); +} +.makeColumn(@columns: 1, @offset: 0) { + float: left; + margin-left: (@gridColumnWidth * @offset) + (@gridGutterWidth * (@offset - 1)) + (@gridGutterWidth * 2); + width: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns - 1)); +} + +// The Grid +#grid { + + .core (@gridColumnWidth, @gridGutterWidth) { + + .spanX (@index) when (@index > 0) { + .span@{index} { .span(@index); } + .spanX(@index - 1); + } + .spanX (0) {} + + .offsetX (@index) when (@index > 0) { + .offset@{index} { .offset(@index); } + .offsetX(@index - 1); + } + .offsetX (0) {} + + .offset (@columns) { + margin-left: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns + 1)); + } + + .span (@columns) { + width: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns - 1)); + } + + .row { + margin-left: @gridGutterWidth * -1; + .clearfix(); + } + + [class*="span"] { + float: left; + min-height: 1px; // prevent collapsing columns + margin-left: @gridGutterWidth; + } + + // Set the container width, and override it for fixed navbars in media queries + .container, + .navbar-static-top .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { .span(@gridColumns); } + + // generate .spanX and .offsetX + .spanX (@gridColumns); + .offsetX (@gridColumns); + + } + + .fluid (@fluidGridColumnWidth, @fluidGridGutterWidth) { + + .spanX (@index) when (@index > 0) { + .span@{index} { .span(@index); } + .spanX(@index - 1); + } + .spanX (0) {} + + .offsetX (@index) when (@index > 0) { + .offset@{index} { .offset(@index); } + .offset@{index}:first-child { .offsetFirstChild(@index); } + .offsetX(@index - 1); + } + .offsetX (0) {} + + .offset (@columns) { + margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) + (@fluidGridGutterWidth*2); + *margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) - (.5 / @gridRowWidth * 100 * 1%) + (@fluidGridGutterWidth*2) - (.5 / @gridRowWidth * 100 * 1%); + } + + .offsetFirstChild (@columns) { + margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) + (@fluidGridGutterWidth); + *margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) - (.5 / @gridRowWidth * 100 * 1%) + @fluidGridGutterWidth - (.5 / @gridRowWidth * 100 * 1%); + } + + .span (@columns) { + width: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)); + *width: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) - (.5 / @gridRowWidth * 100 * 1%); + } + + .row-fluid { + width: 100%; + .clearfix(); + [class*="span"] { + .input-block-level(); + float: left; + margin-left: @fluidGridGutterWidth; + *margin-left: @fluidGridGutterWidth - (.5 / @gridRowWidth * 100 * 1%); + } + [class*="span"]:first-child { + margin-left: 0; + } + + // Space grid-sized controls properly if multiple per line + .controls-row [class*="span"] + [class*="span"] { + margin-left: @fluidGridGutterWidth; + } + + // generate .spanX and .offsetX + .spanX (@gridColumns); + .offsetX (@gridColumns); + } + + } + + .input(@gridColumnWidth, @gridGutterWidth) { + + .spanX (@index) when (@index > 0) { + input.span@{index}, textarea.span@{index}, .uneditable-input.span@{index} { .span(@index); } + .spanX(@index - 1); + } + .spanX (0) {} + + .span(@columns) { + width: ((@gridColumnWidth) * @columns) + (@gridGutterWidth * (@columns - 1)) - 14; + } + + input, + textarea, + .uneditable-input { + margin-left: 0; // override margin-left from core grid system + } + + // Space grid-sized controls properly if multiple per line + .controls-row [class*="span"] + [class*="span"] { + margin-left: @gridGutterWidth; + } + + // generate .spanX + .spanX (@gridColumns); + + } +} diff --git a/public/css/old/navs.less b/public/css/old/navs.less new file mode 100644 index 0000000..71ada20 --- /dev/null +++ b/public/css/old/navs.less @@ -0,0 +1,409 @@ +// +// Navs +// -------------------------------------------------- + + +// BASE CLASS +// ---------- + +.nav { + margin-left: 0; + margin-bottom: @baseLineHeight; + list-style: none; +} + +// Make links block level +.nav > li > a { + display: block; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: @grayLighter; +} + +// Prevent IE8 from misplacing imgs +// See https://github.com/h5bp/html5-boilerplate/issues/984#issuecomment-3985989 +.nav > li > a > img { + max-width: none; +} + +// Redeclare pull classes because of specifity +.nav > .pull-right { + float: right; +} + +// Nav headers (for dropdowns and lists) +.nav-header { + display: block; + padding: 3px 15px; + font-size: 11px; + font-weight: bold; + line-height: @baseLineHeight; + color: @grayLight; + text-shadow: 0 1px 0 rgba(255,255,255,.5); + text-transform: uppercase; +} +// Space them out when they follow another list item (link) +.nav li + .nav-header { + margin-top: 9px; +} + + + +// NAV LIST +// -------- + +.nav-list { + padding-left: 15px; + padding-right: 15px; + margin-bottom: 0; +} +.nav-list > li > a, +.nav-list .nav-header { + margin-left: -15px; + margin-right: -15px; + text-shadow: 0 1px 0 rgba(255,255,255,.5); +} +.nav-list > li > a { + padding: 3px 15px; +} +.nav-list > .active > a, +.nav-list > .active > a:hover, +.nav-list > .active > a:focus { + color: @white; + text-shadow: 0 -1px 0 rgba(0,0,0,.2); + background-color: @linkColor; +} +.nav-list [class^="icon-"], +.nav-list [class*=" icon-"] { + margin-right: 2px; +} +// Dividers (basically an hr) within the dropdown +.nav-list .divider { + .nav-divider(); +} + + + +// TABS AND PILLS +// ------------- + +// Common styles +.nav-tabs, +.nav-pills { + .clearfix(); +} +.nav-tabs > li, +.nav-pills > li { + float: left; +} +.nav-tabs > li > a, +.nav-pills > li > a { + padding-right: 12px; + padding-left: 12px; + margin-right: 2px; + line-height: 14px; // keeps the overall height an even number +} + +// TABS +// ---- + +// Give the tabs something to sit on +.nav-tabs { + border-bottom: 1px solid #ddd; +} +// Make the list-items overlay the bottom border +.nav-tabs > li { + margin-bottom: -1px; +} +// Actual tabs (as links) +.nav-tabs > li > a { + padding-top: 8px; + padding-bottom: 8px; + line-height: @baseLineHeight; + border: 1px solid transparent; + .border-radius(4px 4px 0 0); + &:hover, + &:focus { + border-color: @grayLighter @grayLighter #ddd; + } +} +// Active state, and it's :hover/:focus to override normal :hover/:focus +.nav-tabs > .active > a, +.nav-tabs > .active > a:hover, +.nav-tabs > .active > a:focus { + color: @gray; + background-color: @bodyBackground; + border: 1px solid #ddd; + border-bottom-color: transparent; + cursor: default; +} + + +// PILLS +// ----- + +// Links rendered as pills +.nav-pills > li > a { + padding-top: 8px; + padding-bottom: 8px; + margin-top: 2px; + margin-bottom: 2px; + .border-radius(5px); +} + +// Active state +.nav-pills > .active > a, +.nav-pills > .active > a:hover, +.nav-pills > .active > a:focus { + color: @white; + background-color: @linkColor; +} + + + +// STACKED NAV +// ----------- + +// Stacked tabs and pills +.nav-stacked > li { + float: none; +} +.nav-stacked > li > a { + margin-right: 0; // no need for the gap between nav items +} + +// Tabs +.nav-tabs.nav-stacked { + border-bottom: 0; +} +.nav-tabs.nav-stacked > li > a { + border: 1px solid #ddd; + .border-radius(0); +} +.nav-tabs.nav-stacked > li:first-child > a { + .border-top-radius(4px); +} +.nav-tabs.nav-stacked > li:last-child > a { + .border-bottom-radius(4px); +} +.nav-tabs.nav-stacked > li > a:hover, +.nav-tabs.nav-stacked > li > a:focus { + border-color: #ddd; + z-index: 2; +} + +// Pills +.nav-pills.nav-stacked > li > a { + margin-bottom: 3px; +} +.nav-pills.nav-stacked > li:last-child > a { + margin-bottom: 1px; // decrease margin to match sizing of stacked tabs +} + + + +// DROPDOWNS +// --------- + +.nav-tabs .dropdown-menu { + .border-radius(0 0 6px 6px); // remove the top rounded corners here since there is a hard edge above the menu +} +.nav-pills .dropdown-menu { + .border-radius(6px); // make rounded corners match the pills +} + +// Default dropdown links +// ------------------------- +// Make carets use linkColor to start +.nav .dropdown-toggle .caret { + border-top-color: @linkColor; + border-bottom-color: @linkColor; + margin-top: 6px; +} +.nav .dropdown-toggle:hover .caret, +.nav .dropdown-toggle:focus .caret { + border-top-color: @linkColorHover; + border-bottom-color: @linkColorHover; +} +/* move down carets for tabs */ +.nav-tabs .dropdown-toggle .caret { + margin-top: 8px; +} + +// Active dropdown links +// ------------------------- +.nav .active .dropdown-toggle .caret { + border-top-color: #fff; + border-bottom-color: #fff; +} +.nav-tabs .active .dropdown-toggle .caret { + border-top-color: @gray; + border-bottom-color: @gray; +} + +// Active:hover/:focus dropdown links +// ------------------------- +.nav > .dropdown.active > a:hover, +.nav > .dropdown.active > a:focus { + cursor: pointer; +} + +// Open dropdowns +// ------------------------- +.nav-tabs .open .dropdown-toggle, +.nav-pills .open .dropdown-toggle, +.nav > li.dropdown.open.active > a:hover, +.nav > li.dropdown.open.active > a:focus { + color: @white; + background-color: @grayLight; + border-color: @grayLight; +} +.nav li.dropdown.open .caret, +.nav li.dropdown.open.active .caret, +.nav li.dropdown.open a:hover .caret, +.nav li.dropdown.open a:focus .caret { + border-top-color: @white; + border-bottom-color: @white; + .opacity(100); +} + +// Dropdowns in stacked tabs +.tabs-stacked .open > a:hover, +.tabs-stacked .open > a:focus { + border-color: @grayLight; +} + + + +// TABBABLE +// -------- + + +// COMMON STYLES +// ------------- + +// Clear any floats +.tabbable { + .clearfix(); +} +.tab-content { + overflow: auto; // prevent content from running below tabs +} + +// Remove border on bottom, left, right +.tabs-below > .nav-tabs, +.tabs-right > .nav-tabs, +.tabs-left > .nav-tabs { + border-bottom: 0; +} + +// Show/hide tabbable areas +.tab-content > .tab-pane, +.pill-content > .pill-pane { + display: none; +} +.tab-content > .active, +.pill-content > .active { + display: block; +} + + +// BOTTOM +// ------ + +.tabs-below > .nav-tabs { + border-top: 1px solid #ddd; +} +.tabs-below > .nav-tabs > li { + margin-top: -1px; + margin-bottom: 0; +} +.tabs-below > .nav-tabs > li > a { + .border-radius(0 0 4px 4px); + &:hover, + &:focus { + border-bottom-color: transparent; + border-top-color: #ddd; + } +} +.tabs-below > .nav-tabs > .active > a, +.tabs-below > .nav-tabs > .active > a:hover, +.tabs-below > .nav-tabs > .active > a:focus { + border-color: transparent #ddd #ddd #ddd; +} + +// LEFT & RIGHT +// ------------ + +// Common styles +.tabs-left > .nav-tabs > li, +.tabs-right > .nav-tabs > li { + float: none; +} +.tabs-left > .nav-tabs > li > a, +.tabs-right > .nav-tabs > li > a { + min-width: 74px; + margin-right: 0; + margin-bottom: 3px; +} + +// Tabs on the left +.tabs-left > .nav-tabs { + float: left; + margin-right: 19px; + border-right: 1px solid #ddd; +} +.tabs-left > .nav-tabs > li > a { + margin-right: -1px; + .border-radius(4px 0 0 4px); +} +.tabs-left > .nav-tabs > li > a:hover, +.tabs-left > .nav-tabs > li > a:focus { + border-color: @grayLighter #ddd @grayLighter @grayLighter; +} +.tabs-left > .nav-tabs .active > a, +.tabs-left > .nav-tabs .active > a:hover, +.tabs-left > .nav-tabs .active > a:focus { + border-color: #ddd transparent #ddd #ddd; + *border-right-color: @white; +} + +// Tabs on the right +.tabs-right > .nav-tabs { + float: right; + margin-left: 19px; + border-left: 1px solid #ddd; +} +.tabs-right > .nav-tabs > li > a { + margin-left: -1px; + .border-radius(0 4px 4px 0); +} +.tabs-right > .nav-tabs > li > a:hover, +.tabs-right > .nav-tabs > li > a:focus { + border-color: @grayLighter @grayLighter @grayLighter #ddd; +} +.tabs-right > .nav-tabs .active > a, +.tabs-right > .nav-tabs .active > a:hover, +.tabs-right > .nav-tabs .active > a:focus { + border-color: #ddd #ddd #ddd transparent; + *border-left-color: @white; +} + + + +// DISABLED STATES +// --------------- + +// Gray out text +.nav > .disabled > a { + color: @grayLight; +} +// Nuke hover/focus effects +.nav > .disabled > a:hover, +.nav > .disabled > a:focus { + text-decoration: none; + background-color: transparent; + cursor: default; +} diff --git a/public/css/old/pagination.less b/public/css/old/pagination.less new file mode 100644 index 0000000..0827d07 --- /dev/null +++ b/public/css/old/pagination.less @@ -0,0 +1,104 @@ +// +// Pagination (multiple pages) +// -------------------------------------------------- + +// Space out pagination from surrounding content +.pagination { + margin: @baseLineHeight 0; +} + +.pagination ul { + // Allow for text-based alignment + display: inline-block; + .ie7-inline-block(); + // Reset default ul styles + margin-left: 0; + margin-bottom: 0; + // Visuals + .box-shadow(0 1px 2px rgba(0,0,0,.05)); +} +.pagination ul > li { + display: inline; // Remove list-style and block-level defaults +} +.pagination ul > li > a, +.pagination ul > li > span { + float: left; // Collapse white-space + padding: 4px 12px; + line-height: @baseLineHeight; + text-decoration: none; + background-color: @paginationBackground; + border: 1px solid @paginationBorder; + border-left-width: 0; +} +.pagination ul > li > a:hover, +.pagination ul > li > a:focus, +.pagination ul > .active > a, +.pagination ul > .active > span { + background-color: @paginationActiveBackground; +} +.pagination ul > .active > a, +.pagination ul > .active > span { + color: @grayLight; + cursor: default; +} +.pagination ul > .disabled > span, +.pagination ul > .disabled > a, +.pagination ul > .disabled > a:hover, +.pagination ul > .disabled > a:focus { + color: @grayLight; + background-color: transparent; + cursor: default; +} +.pagination ul > li:first-child > a, +.pagination ul > li:first-child > span { + border-left-width: 1px; +} +.pagination ul > li:last-child > a, +.pagination ul > li:last-child > span { +} + + +// Alignment +// -------------------------------------------------- + +.pagination-centered { + text-align: center; +} +.pagination-right { + text-align: right; +} + + +// Sizing +// -------------------------------------------------- + +// Large +.pagination-large { + ul > li > a, + ul > li > span { + padding: @paddingLarge; + font-size: @fontSizeLarge; + } +} + +// Small and mini +.pagination-mini, +.pagination-small { +} + +// Small +.pagination-small { + ul > li > a, + ul > li > span { + padding: @paddingSmall; + font-size: @fontSizeSmall; + } +} +// Mini +.pagination-mini { + ul > li > a, + ul > li > span { + padding: @paddingMini; + font-size: @fontSizeMini; + } +} diff --git a/public/css/old/progress-bars.less b/public/css/old/progress-bars.less new file mode 100644 index 0000000..22e285c --- /dev/null +++ b/public/css/old/progress-bars.less @@ -0,0 +1,129 @@ +// +// Progress bars +// -------------------------------------------------- + + +// ANIMATIONS +// ---------- + +// Webkit +@-webkit-keyframes progress-bar-stripes { + from { background-position: 40px 0; } + to { background-position: 0 0; } +} + +// Firefox +@-moz-keyframes progress-bar-stripes { + from { background-position: 40px 0; } + to { background-position: 0 0; } +} + +// IE9 +@-ms-keyframes progress-bar-stripes { + from { background-position: 40px 0; } + to { background-position: 0 0; } +} + +// Opera +@-o-keyframes progress-bar-stripes { + from { background-position: 0 0; } + to { background-position: 40px 0; } +} + +// Spec +@keyframes progress-bar-stripes { + from { background-position: 40px 0; } + to { background-position: 0 0; } +} + + + +// THE BARS +// -------- + +// Outer container +.progress { + overflow: hidden; + height: @baseLineHeight; + margin-bottom: @baseLineHeight; + #gradient > .vertical(#f5f5f5, #f9f9f9); + .box-shadow(inset 0 1px 2px rgba(0,0,0,.1)); +} + +// Bar of progress +.progress .bar { + width: 0%; + height: 100%; + color: @white; + float: left; + font-size: 12px; + text-align: center; + text-shadow: 0 -1px 0 rgba(0,0,0,.25); + #gradient > .vertical(#149bdf, #0480be); + .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15)); + .box-sizing(border-box); + .transition(width .6s ease); +} +.progress .bar + .bar { + .box-shadow(~"inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15)"); +} + +// Striped bars +.progress-striped .bar { + #gradient > .striped(#149bdf); + .background-size(40px 40px); +} + +// Call animation for the active one +.progress.active .bar { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -moz-animation: progress-bar-stripes 2s linear infinite; + -ms-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} + + + +// COLORS +// ------ + +// Danger (red) +.progress-danger .bar, .progress .bar-danger { + #gradient > .vertical(#ee5f5b, #c43c35); +} +.progress-danger.progress-striped .bar, .progress-striped .bar-danger { + #gradient > .striped(#ee5f5b); +} + +// Success (green) +.progress-success .bar, .progress .bar-success { + #gradient > .vertical(#62c462, #57a957); +} +.progress-success.progress-striped .bar, .progress-striped .bar-success { + #gradient > .striped(#62c462); +} + +// Info (teal) +.progress-info .bar, .progress .bar-info { + #gradient > .vertical(#5bc0de, #339bb9); +} +.progress-info.progress-striped .bar, .progress-striped .bar-info { + #gradient > .striped(#5bc0de); +} + +// Warning (orange) +.progress-warning .bar, .progress .bar-warning { + #gradient > .vertical(lighten(@orange, 15%), @orange); +} +.progress-warning.progress-striped .bar, .progress-striped .bar-warning { + #gradient > .striped(lighten(@orange, 15%)); +} + +// Inverted Gray +.progress-inverted .bar, .progress .bar-inverted { + #gradient > .vertical(lighten(@gray, 15%), @gray); +} +.progress-inverted.progress-striped .bar, .progress-striped .bar-inverted { + #gradient > .striped(lighten(@gray, 15%)); +} diff --git a/public/css/old/reset.less b/public/css/old/reset.less new file mode 100644 index 0000000..5063c7d --- /dev/null +++ b/public/css/old/reset.less @@ -0,0 +1,216 @@ +// +// Reset CSS +// Adapted from http://github.com/necolas/normalize.css +// -------------------------------------------------- + + +// Display in IE6-9 and FF3 +// ------------------------- + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section { + display: block; +} + +// Display block in IE6-9 and FF3 +// ------------------------- + +audio, +canvas, +video { + display: inline-block; + *display: inline; + *zoom: 1; +} + +// Prevents modern browsers from displaying 'audio' without controls +// ------------------------- + +audio:not([controls]) { + display: none; +} + +// Base settings +// ------------------------- + +html { + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +// Focus states +a:focus { + .tab-focus(); +} +// Hover & Active +a:hover, +a:active { + outline: 0; +} + +// Prevents sub and sup affecting line-height in all browsers +// ------------------------- + +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} +sup { + top: -0.5em; +} +sub { + bottom: -0.25em; +} + +// Img border in a's and image quality +// ------------------------- + +img { + /* Responsive images (ensure images don't scale beyond their parents) */ + max-width: 100%; /* Part 1: Set a maxium relative to the parent */ + width: auto\9; /* IE7-8 need help adjusting responsive images */ + height: auto; /* Part 2: Scale the height according to the width, otherwise you get stretching */ + + vertical-align: middle; + border: 0; + -ms-interpolation-mode: bicubic; +} + +// Prevent max-width from affecting Google Maps +#map_canvas img, +.google-maps img { + max-width: none; +} + +// Forms +// ------------------------- + +// Font size in all browsers, margin changes, misc consistency +button, +input, +select, +textarea { + margin: 0; + font-size: 100%; + vertical-align: middle; +} +button, +input { + *overflow: visible; // Inner spacing ie IE6/7 + line-height: normal; // FF3/4 have !important on line-height in UA stylesheet +} +button::-moz-focus-inner, +input::-moz-focus-inner { // Inner padding and border oddities in FF3/4 + padding: 0; + border: 0; +} +button, +html input[type="button"], // Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; // Corrects inability to style clickable `input` types in iOS. + cursor: pointer; // Improves usability and consistency of cursor style between image-type `input` and others. +} +label, +select, +button, +input[type="button"], +input[type="reset"], +input[type="submit"], +input[type="radio"], +input[type="checkbox"] { + cursor: pointer; // Improves usability and consistency of cursor style between image-type `input` and others. +} +input[type="search"] { // Appearance in Safari/Chrome + .box-sizing(content-box); + -webkit-appearance: textfield; +} +input[type="search"]::-webkit-search-decoration, +input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; // Inner-padding issues in Chrome OSX, Safari 5 +} +textarea { + overflow: auto; // Remove vertical scrollbar in IE6-9 + vertical-align: top; // Readability and alignment cross-browser +} + + +// Printing +// ------------------------- +// Source: https://github.com/h5bp/html5-boilerplate/blob/master/css/main.css + +@media print { + + * { + text-shadow: none !important; + color: #000 !important; // Black prints faster: h5bp.com/s + background: transparent !important; + box-shadow: none !important; + } + + a, + a:visited { + text-decoration: underline; + } + + a[href]:after { + content: " (" attr(href) ")"; + } + + abbr[title]:after { + content: " (" attr(title) ")"; + } + + // Don't show links for images, or javascript/internal links + .ir a:after, + a[href^="javascript:"]:after, + a[href^="#"]:after { + content: ""; + } + + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + + thead { + display: table-header-group; // h5bp.com/t + } + + tr, + img { + page-break-inside: avoid; + } + + img { + max-width: 100% !important; + } + + @page { + margin: 0.5cm; + } + + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + + h2, + h3 { + page-break-after: avoid; + } +} diff --git a/public/css/old/scaffolding.less b/public/css/old/scaffolding.less new file mode 100644 index 0000000..4d4f375 --- /dev/null +++ b/public/css/old/scaffolding.less @@ -0,0 +1,156 @@ +// +// Scaffolding +// -------------------------------------------------- + + +// Body reset +// ------------------------- + +body { + margin: 0; + font-family: @baseFontFamily; + font-size: @baseFontSize; + line-height: @baseLineHeight; + color: @textColor; + background-color: @bodyBackground; +} + +// Fonts +@font-face { + font-family: 'FontAwesome'; + src: url('/fonts/fontawesome-webfont.eot'); + src: url('/fonts/fontawesome-webfont.eot?#iefix') format('eot'), + url('/fonts/fontawesome-webfont.woff') format('woff'), + url('/fonts/fontawesome-webfont.ttf') format('truetype'), + url('/fonts/fontawesome-webfont.svg#FontAwesome') format('svg'); + font-weight: normal; + font-style: normal; +} + + +// Links +// ------------------------- + +a { + color: @linkColor; + text-decoration: none; +} +a:hover, +a:focus { + color: @linkColorHover; + text-decoration: none; +} + + +// Images +// ------------------------- + +// Rounded corners +.img-rounded { + .border-radius(6px); +} + +// Add polaroid-esque trim +.img-polaroid { + padding: 4px; + background-color: #fff; + border: 1px solid #ccc; + border: 1px solid rgba(0,0,0,.2); + .box-shadow(0 1px 3px rgba(0,0,0,.1)); +} + +// Perfect circle +.img-circle { + .border-radius(500px); // crank the border-radius so it works with most reasonably sized images +} + + + +.content { + background: url("/img/bodyBg.png") repeat #fff; + min-height: 500px; +} + + +.content-header h1 { + margin: 20px 0 0 20px; + color: #555555; + font-size: 28px; + font-weight: normal; +} + + +.bio-input-label { + float: left; + padding-right: 12px; + text-align: right; + width: 170px; +} + +.bio-input { + margin-left: 185px; + margin-bottom: 20px; + + p { + font-weight: bold; + margin: 0; + + } +} + + + +.axis path, +.axis line { + fill: none; + stroke: #000; + shape-rendering: crispEdges; +} + +.x.axis line { + stroke: lightgrey; + } + +.x.axis .minor { + stroke-opacity: .5; +} + +.highlighter line { + fill: none; + stroke: red; + stroke-opacity: .5; + shape-rendering: crispEdges; +} + +.highlighter rect { + fill: red; + fill-opacity: .1; +} + +.fade { + fill-opacity: .1; +} + +.axis text { + font-size: 10px; +} + + +.content-header { + margin-bottom: 20px; +} + +.toolbelt .input-append { + margin-bottom: 0; +} + + +.loader { + width: 32px; height: 32px; display: inline-block; + background: transparent url(/img/loader.gif) no-repeat; + -webkit-animation: 'animation' 1s steps(10, end) infinite; + -moz-animation: 'animation' 1s steps(10, end) infinite; +} + +@-webkit-keyframes animation { from { background-position: 0 0; } to { background-position: 100% 0; } } +@-moz-keyframes animation { from { background-position: 0 0; } to { background-position: 100% 0; } } \ No newline at end of file diff --git a/public/css/old/sprites.less b/public/css/old/sprites.less new file mode 100644 index 0000000..19194d7 --- /dev/null +++ b/public/css/old/sprites.less @@ -0,0 +1,197 @@ +// +// Sprites +// -------------------------------------------------- + + +// ICONS +// ----- + +// All icons receive the styles of the tag with a base class +// of .i and are then given a unique class to add width, height, +// and background-position. Your resulting HTML will look like +// . + +// For the white version of the icons, just add the .icon-white class: +// + +[class^="icon-"], +[class*=" icon-"] { + display: inline-block; + width: 14px; + height: 14px; + .ie7-restore-right-whitespace(); + line-height: 14px; + vertical-align: text-top; + background-image: url("@{iconSpritePath}"); + background-position: 14px 14px; + background-repeat: no-repeat; + margin-top: 1px; +} + +/* White icons with optional class, or on hover/focus/active states of certain elements */ +.icon-white, +.nav-pills > .active > a > [class^="icon-"], +.nav-pills > .active > a > [class*=" icon-"], +.nav-list > .active > a > [class^="icon-"], +.nav-list > .active > a > [class*=" icon-"], +.navbar-inverse .nav > .active > a > [class^="icon-"], +.navbar-inverse .nav > .active > a > [class*=" icon-"], +.dropdown-menu > li > a:hover > [class^="icon-"], +.dropdown-menu > li > a:focus > [class^="icon-"], +.dropdown-menu > li > a:hover > [class*=" icon-"], +.dropdown-menu > li > a:focus > [class*=" icon-"], +.dropdown-menu > .active > a > [class^="icon-"], +.dropdown-menu > .active > a > [class*=" icon-"], +.dropdown-submenu:hover > a > [class^="icon-"], +.dropdown-submenu:focus > a > [class^="icon-"], +.dropdown-submenu:hover > a > [class*=" icon-"], +.dropdown-submenu:focus > a > [class*=" icon-"] { + background-image: url("@{iconWhiteSpritePath}"); +} + +.icon-glass { background-position: 0 0; } +.icon-music { background-position: -24px 0; } +.icon-search { background-position: -48px 0; } +.icon-envelope { background-position: -72px 0; } +.icon-heart { background-position: -96px 0; } +.icon-star { background-position: -120px 0; } +.icon-star-empty { background-position: -144px 0; } +.icon-user { background-position: -168px 0; } +.icon-film { background-position: -192px 0; } +.icon-th-large { background-position: -216px 0; } +.icon-th { background-position: -240px 0; } +.icon-th-list { background-position: -264px 0; } +.icon-ok { background-position: -288px 0; } +.icon-remove { background-position: -312px 0; } +.icon-zoom-in { background-position: -336px 0; } +.icon-zoom-out { background-position: -360px 0; } +.icon-off { background-position: -384px 0; } +.icon-signal { background-position: -408px 0; } +.icon-cog { background-position: -432px 0; } +.icon-trash { background-position: -456px 0; } + +.icon-home { background-position: 0 -24px; } +.icon-file { background-position: -24px -24px; } +.icon-time { background-position: -48px -24px; } +.icon-road { background-position: -72px -24px; } +.icon-download-alt { background-position: -96px -24px; } +.icon-download { background-position: -120px -24px; } +.icon-upload { background-position: -144px -24px; } +.icon-inbox { background-position: -168px -24px; } +.icon-play-circle { background-position: -192px -24px; } +.icon-repeat { background-position: -216px -24px; } +.icon-refresh { background-position: -240px -24px; } +.icon-list-alt { background-position: -264px -24px; } +.icon-lock { background-position: -287px -24px; } // 1px off +.icon-flag { background-position: -312px -24px; } +.icon-headphones { background-position: -336px -24px; } +.icon-volume-off { background-position: -360px -24px; } +.icon-volume-down { background-position: -384px -24px; } +.icon-volume-up { background-position: -408px -24px; } +.icon-qrcode { background-position: -432px -24px; } +.icon-barcode { background-position: -456px -24px; } + +.icon-tag { background-position: 0 -48px; } +.icon-tags { background-position: -25px -48px; } // 1px off +.icon-book { background-position: -48px -48px; } +.icon-bookmark { background-position: -72px -48px; } +.icon-print { background-position: -96px -48px; } +.icon-camera { background-position: -120px -48px; } +.icon-font { background-position: -144px -48px; } +.icon-bold { background-position: -167px -48px; } // 1px off +.icon-italic { background-position: -192px -48px; } +.icon-text-height { background-position: -216px -48px; } +.icon-text-width { background-position: -240px -48px; } +.icon-align-left { background-position: -264px -48px; } +.icon-align-center { background-position: -288px -48px; } +.icon-align-right { background-position: -312px -48px; } +.icon-align-justify { background-position: -336px -48px; } +.icon-list { background-position: -360px -48px; } +.icon-indent-left { background-position: -384px -48px; } +.icon-indent-right { background-position: -408px -48px; } +.icon-facetime-video { background-position: -432px -48px; } +.icon-picture { background-position: -456px -48px; } + +.icon-pencil { background-position: 0 -72px; } +.icon-map-marker { background-position: -24px -72px; } +.icon-adjust { background-position: -48px -72px; } +.icon-tint { background-position: -72px -72px; } +.icon-edit { background-position: -96px -72px; } +.icon-share { background-position: -120px -72px; } +.icon-check { background-position: -144px -72px; } +.icon-move { background-position: -168px -72px; } +.icon-step-backward { background-position: -192px -72px; } +.icon-fast-backward { background-position: -216px -72px; } +.icon-backward { background-position: -240px -72px; } +.icon-play { background-position: -264px -72px; } +.icon-pause { background-position: -288px -72px; } +.icon-stop { background-position: -312px -72px; } +.icon-forward { background-position: -336px -72px; } +.icon-fast-forward { background-position: -360px -72px; } +.icon-step-forward { background-position: -384px -72px; } +.icon-eject { background-position: -408px -72px; } +.icon-chevron-left { background-position: -432px -72px; } +.icon-chevron-right { background-position: -456px -72px; } + +.icon-plus-sign { background-position: 0 -96px; } +.icon-minus-sign { background-position: -24px -96px; } +.icon-remove-sign { background-position: -48px -96px; } +.icon-ok-sign { background-position: -72px -96px; } +.icon-question-sign { background-position: -96px -96px; } +.icon-info-sign { background-position: -120px -96px; } +.icon-screenshot { background-position: -144px -96px; } +.icon-remove-circle { background-position: -168px -96px; } +.icon-ok-circle { background-position: -192px -96px; } +.icon-ban-circle { background-position: -216px -96px; } +.icon-arrow-left { background-position: -240px -96px; } +.icon-arrow-right { background-position: -264px -96px; } +.icon-arrow-up { background-position: -289px -96px; } // 1px off +.icon-arrow-down { background-position: -312px -96px; } +.icon-share-alt { background-position: -336px -96px; } +.icon-resize-full { background-position: -360px -96px; } +.icon-resize-small { background-position: -384px -96px; } +.icon-plus { background-position: -408px -96px; } +.icon-minus { background-position: -433px -96px; } +.icon-asterisk { background-position: -456px -96px; } + +.icon-exclamation-sign { background-position: 0 -120px; } +.icon-gift { background-position: -24px -120px; } +.icon-leaf { background-position: -48px -120px; } +.icon-fire { background-position: -72px -120px; } +.icon-eye-open { background-position: -96px -120px; } +.icon-eye-close { background-position: -120px -120px; } +.icon-warning-sign { background-position: -144px -120px; } +.icon-plane { background-position: -168px -120px; } +.icon-calendar { background-position: -192px -120px; } +.icon-random { background-position: -216px -120px; width: 16px; } +.icon-comment { background-position: -240px -120px; } +.icon-magnet { background-position: -264px -120px; } +.icon-chevron-up { background-position: -288px -120px; } +.icon-chevron-down { background-position: -313px -119px; } // 1px, 1px off +.icon-retweet { background-position: -336px -120px; } +.icon-shopping-cart { background-position: -360px -120px; } +.icon-folder-close { background-position: -384px -120px; width: 16px; } +.icon-folder-open { background-position: -408px -120px; width: 16px; } +.icon-resize-vertical { background-position: -432px -119px; } // 1px, 1px off +.icon-resize-horizontal { background-position: -456px -118px; } // 1px, 2px off + +.icon-hdd { background-position: 0 -144px; } +.icon-bullhorn { background-position: -24px -144px; } +.icon-bell { background-position: -48px -144px; } +.icon-certificate { background-position: -72px -144px; } +.icon-thumbs-up { background-position: -96px -144px; } +.icon-thumbs-down { background-position: -120px -144px; } +.icon-hand-right { background-position: -144px -144px; } +.icon-hand-left { background-position: -168px -144px; } +.icon-hand-up { background-position: -192px -144px; } +.icon-hand-down { background-position: -216px -144px; } +.icon-circle-arrow-right { background-position: -240px -144px; } +.icon-circle-arrow-left { background-position: -264px -144px; } +.icon-circle-arrow-up { background-position: -288px -144px; } +.icon-circle-arrow-down { background-position: -312px -144px; } +.icon-globe { background-position: -336px -144px; } +.icon-wrench { background-position: -360px -144px; } +.icon-tasks { background-position: -384px -144px; } +.icon-filter { background-position: -408px -144px; } +.icon-briefcase { background-position: -432px -144px; } +.icon-fullscreen { background-position: -456px -144px; } diff --git a/public/css/old/tables.less b/public/css/old/tables.less new file mode 100644 index 0000000..8a27cdc --- /dev/null +++ b/public/css/old/tables.less @@ -0,0 +1,257 @@ +// +// Tables +// -------------------------------------------------- + + +// BASE TABLES +// ----------------- + +table { + max-width: 100%; + background-color: @tableBackground; + border-collapse: collapse; + border-spacing: 0; +} + +// BASELINE STYLES +// --------------- + +.table { + width: 100%; + margin-bottom: @baseLineHeight; + // Cells + th, + td { + padding: 8px; + line-height: @baseLineHeight; + text-align: left; + vertical-align: top; + border-top: 1px solid @tableBorder; + } + th { + font-size: 10px; + text-align: left; + color: #666666; + background-color: #efefef; + padding: 2px 10px; + } + // Bottom align for column headings + thead th { + vertical-align: bottom; + } + // Remove top border from thead by default + caption + thead tr:first-child th, + caption + thead tr:first-child td, + colgroup + thead tr:first-child th, + colgroup + thead tr:first-child td, + thead:first-child tr:first-child th, + thead:first-child tr:first-child td { + border-top: 0; + } + // Account for multiple tbody instances + tbody + tbody { + border-top: 2px solid @tableBorder; + } + + // Nesting + .table { + background-color: @bodyBackground; + } +} + +td.table-loading { + text-align: center; + padding: 30px; +} + +td.table-message { + text-align: center; + font-style: italic; +} + +// CONDENSED TABLE W/ HALF PADDING +// ------------------------------- + +.table-condensed { + th, + td { + padding: 4px 5px; + } +} + +// BORDERED VERSION +// ---------------- + +.table-bordered { + border: 1px solid @tableBorder; + border-collapse: separate; // Done so we can round those corners! + *border-collapse: collapse; // IE7 can't round corners anyway + border-left: 0; + .border-radius(@baseBorderRadius); + th, + td { + border-left: 1px solid @tableBorder; + } + // Prevent a double border + caption + thead tr:first-child th, + caption + tbody tr:first-child th, + caption + tbody tr:first-child td, + colgroup + thead tr:first-child th, + colgroup + tbody tr:first-child th, + colgroup + tbody tr:first-child td, + thead:first-child tr:first-child th, + tbody:first-child tr:first-child th, + tbody:first-child tr:first-child td { + border-top: 0; + } + // For first th/td in the first row in the first thead or tbody + thead:first-child tr:first-child > th:first-child, + tbody:first-child tr:first-child > td:first-child, + tbody:first-child tr:first-child > th:first-child { + .border-top-left-radius(@baseBorderRadius); + } + // For last th/td in the first row in the first thead or tbody + thead:first-child tr:first-child > th:last-child, + tbody:first-child tr:first-child > td:last-child, + tbody:first-child tr:first-child > th:last-child { + .border-top-right-radius(@baseBorderRadius); + } + // For first th/td (can be either) in the last row in the last thead, tbody, and tfoot + thead:last-child tr:last-child > th:first-child, + tbody:last-child tr:last-child > td:first-child, + tbody:last-child tr:last-child > th:first-child, + tfoot:last-child tr:last-child > td:first-child, + tfoot:last-child tr:last-child > th:first-child { + .border-bottom-left-radius(@baseBorderRadius); + } + // For last th/td (can be either) in the last row in the last thead, tbody, and tfoot + thead:last-child tr:last-child > th:last-child, + tbody:last-child tr:last-child > td:last-child, + tbody:last-child tr:last-child > th:last-child, + tfoot:last-child tr:last-child > td:last-child, + tfoot:last-child tr:last-child > th:last-child { + .border-bottom-right-radius(@baseBorderRadius); + } + + // Clear border-radius for first and last td in the last row in the last tbody for table with tfoot + tfoot + tbody:last-child tr:last-child td:first-child { + .border-bottom-left-radius(0); + } + tfoot + tbody:last-child tr:last-child td:last-child { + .border-bottom-right-radius(0); + } + + // Special fixes to round the left border on the first td/th + caption + thead tr:first-child th:first-child, + caption + tbody tr:first-child td:first-child, + colgroup + thead tr:first-child th:first-child, + colgroup + tbody tr:first-child td:first-child { + .border-top-left-radius(@baseBorderRadius); + } + caption + thead tr:first-child th:last-child, + caption + tbody tr:first-child td:last-child, + colgroup + thead tr:first-child th:last-child, + colgroup + tbody tr:first-child td:last-child { + .border-top-right-radius(@baseBorderRadius); + } + +} + + + + +// ZEBRA-STRIPING +// -------------- + +// Default zebra-stripe styles (alternating gray and transparent backgrounds) +.table-striped { + border: 1px solid @tableBorder; + + tbody { + > tr:nth-child(odd) > td, + > tr:nth-child(odd) > th { + background-color: @tableBackgroundAccent; + } + } +} + + +// HOVER EFFECT +// ------------ +// Placed here since it has to come after the potential zebra striping +.table-hover { + tbody { + tr:hover > td, + tr:hover > th { + background-color: @tableBackgroundHover; + } + } +} + + +// TABLE CELL SIZING +// ----------------- + +// Reset default grid behavior +table td[class*="span"], +table th[class*="span"], +.row-fluid table td[class*="span"], +.row-fluid table th[class*="span"] { + display: table-cell; + float: none; // undo default grid column styles + margin-left: 0; // undo default grid column styles +} + +// Change the column widths to account for td/th padding +.table td, +.table th { + &.span1 { .tableColumns(1); } + &.span2 { .tableColumns(2); } + &.span3 { .tableColumns(3); } + &.span4 { .tableColumns(4); } + &.span5 { .tableColumns(5); } + &.span6 { .tableColumns(6); } + &.span7 { .tableColumns(7); } + &.span8 { .tableColumns(8); } + &.span9 { .tableColumns(9); } + &.span10 { .tableColumns(10); } + &.span11 { .tableColumns(11); } + &.span12 { .tableColumns(12); } +} + + + +// TABLE BACKGROUNDS +// ----------------- +// Exact selectors below required to override .table-striped + +.table tbody tr { + &.success > td { + background-color: @successBackground; + } + &.error > td { + background-color: @errorBackground; + } + &.warning > td { + background-color: @warningBackground; + } + &.info > td { + background-color: @infoBackground; + } +} + +// Hover states for .table-hover +.table-hover tbody tr { + &.success:hover > td { + background-color: darken(@successBackground, 5%); + } + &.error:hover > td { + background-color: darken(@errorBackground, 5%); + } + &.warning:hover > td { + background-color: darken(@warningBackground, 5%); + } + &.info:hover > td { + background-color: darken(@infoBackground, 5%); + } +} diff --git a/public/css/old/timepicker.less b/public/css/old/timepicker.less new file mode 100644 index 0000000..8070432 --- /dev/null +++ b/public/css/old/timepicker.less @@ -0,0 +1,144 @@ +/*! + * Timepicker Component for Twitter Bootstrap + * + * Copyright 2013 Joris de Wit + * + * Contributors https://github.com/jdewit/bootstrap-timepicker/graphs/contributors + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +.bootstrap-timepicker { + position: relative; + + &.pull-right { + .bootstrap-timepicker-widget { + &.dropdown-menu { + left: auto; + right: 0; + + &:before { + left: auto; + right: 12px; + } + &:after { + left: auto; + right: 13px; + } + } + } + } + + .add-on { + cursor: pointer; + i { + display: inline-block; + width: 16px; + height: 16px; + } + } +} +.bootstrap-timepicker-widget { + &.dropdown-menu { + padding: 2px 3px 2px 2px; + &.open { + display: inline-block; + } + &:before { + border-bottom: 7px solid rgba(0, 0, 0, 0.2); + border-left: 7px solid transparent; + border-right: 7px solid transparent; + content: ""; + display: inline-block; + left: 9px; + position: absolute; + top: -7px; + } + &:after { + border-bottom: 6px solid #FFFFFF; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + content: ""; + display: inline-block; + left: 10px; + position: absolute; + top: -6px; + } + } + + a.btn, input { + border-radius: 4px; + } + + table { + width: 100%; + margin: 0; + + td { + text-align: center; + height: 30px; + margin: 0; + padding: 2px; + + &:not(.separator) { + min-width: 30px; + } + + span { + width: 100%; + } + a { + border: 1px transparent solid; + width: 100%; + display: inline-block; + margin: 0; + padding: 8px 0; + outline: 0; + color: #333; + + &:hover { + text-decoration: none; + background-color: #eee; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + border-color: #ddd; + } + + i { + margin-top: 2px; + } + } + input { + width: 25px; + margin: 0; + text-align: center; + } + } + } +} + +.bootstrap-timepicker-widget .modal-content { + padding: 4px; +} + +@media (min-width: 767px) { + .bootstrap-timepicker-widget.modal { + width: 200px; + margin-left: -100px; + } +} + +@media (max-width: 767px) { + .bootstrap-timepicker { + width: 100%; + + .dropdown-menu { + width: 100%; + } + } +} + + + + diff --git a/public/css/old/type.less b/public/css/old/type.less new file mode 100644 index 0000000..cfe8ec6 --- /dev/null +++ b/public/css/old/type.less @@ -0,0 +1,247 @@ +// +// Typography +// -------------------------------------------------- + + +// Body text +// ------------------------- + +p { + margin: 0 0 @baseLineHeight / 2; +} +.lead { + margin-bottom: @baseLineHeight; + font-size: @baseFontSize * 1.5; + font-weight: 200; + line-height: @baseLineHeight * 1.5; +} + + +// Emphasis & misc +// ------------------------- + +// Ex: 14px base font * 85% = about 12px +small { font-size: 85%; } + +strong { font-weight: bold; } +em { font-style: italic; } +cite { font-style: normal; } + +// Utility classes +.muted { color: @grayLight; } +a.muted:hover, +a.muted:focus { color: darken(@grayLight, 10%); } + +.text-warning { color: @warningText; } +a.text-warning:hover, +a.text-warning:focus { color: darken(@warningText, 10%); } + +.text-error { color: @errorText; } +a.text-error:hover, +a.text-error:focus { color: darken(@errorText, 10%); } + +.text-info { color: @infoText; } +a.text-info:hover, +a.text-info:focus { color: darken(@infoText, 10%); } + +.text-success { color: @successText; } +a.text-success:hover, +a.text-success:focus { color: darken(@successText, 10%); } + +.text-left { text-align: left; } +.text-right { text-align: right; } +.text-center { text-align: center; } + + +// Headings +// ------------------------- + +h1, h2, h3, h4, h5, h6 { + margin: (@baseLineHeight / 2) 0; + font-family: @headingsFontFamily; + font-weight: @headingsFontWeight; + line-height: @baseLineHeight; + color: @headingsColor; + text-rendering: optimizelegibility; // Fix the character spacing for headings + small { + font-weight: normal; + line-height: 1; + color: @grayLight; + } +} + +h1, +h2, +h3 { line-height: @baseLineHeight * 2; } + +h1 { font-size: @baseFontSize * 2.75; } // ~38px +h2 { font-size: @baseFontSize * 2.25; } // ~32px +h3 { font-size: @baseFontSize * 1.75; } // ~24px +h4 { font-size: @baseFontSize * 1.25; } // ~18px +h5 { font-size: @baseFontSize; } +h6 { font-size: @baseFontSize * 0.85; } // ~12px + +h1 small { font-size: @baseFontSize * 1.75; } // ~24px +h2 small { font-size: @baseFontSize * 1.25; } // ~18px +h3 small { font-size: @baseFontSize; } +h4 small { font-size: @baseFontSize; } + + +// Page header +// ------------------------- + +.page-header { + padding-bottom: (@baseLineHeight / 2) - 1; + margin: @baseLineHeight 0 (@baseLineHeight * 1.5); + border-bottom: 1px solid @grayLighter; +} + + + +// Lists +// -------------------------------------------------- + +// Unordered and Ordered lists +ul, ol { + padding: 0; + margin: 0 0 @baseLineHeight / 2 25px; +} +ul ul, +ul ol, +ol ol, +ol ul { + margin-bottom: 0; +} +li { + line-height: @baseLineHeight; +} + +// Remove default list styles +ul.unstyled, +ol.unstyled { + margin-left: 0; + list-style: none; +} + +// Single-line list items +ul.inline, +ol.inline { + margin-left: 0; + list-style: none; + > li { + display: inline-block; + .ie7-inline-block(); + padding-left: 5px; + padding-right: 5px; + } +} + +// Description Lists +dl { + margin-bottom: @baseLineHeight; +} +dt, +dd { + line-height: @baseLineHeight; +} +dt { + font-weight: bold; +} +dd { + margin-left: @baseLineHeight / 2; +} +// Horizontal layout (like forms) +.dl-horizontal { + .clearfix(); // Ensure dl clears floats if empty dd elements present + dt { + float: left; + width: @horizontalComponentOffset - 20; + clear: left; + text-align: right; + .text-overflow(); + } + dd { + margin-left: @horizontalComponentOffset; + } +} + +// MISC +// ---- + +// Horizontal rules +hr { + margin: @baseLineHeight 0; + border: 0; + border-top: 1px solid @hrBorder; + border-bottom: 1px solid @white; +} + +// Abbreviations and acronyms +abbr[title], +// Added data-* attribute to help out our tooltip plugin, per https://github.com/twitter/bootstrap/issues/5257 +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted @grayLight; +} +abbr.initialism { + font-size: 90%; + text-transform: uppercase; +} + +// Blockquotes +blockquote { + padding: 0 0 0 15px; + margin: 0 0 @baseLineHeight; + border-left: 5px solid @grayLighter; + p { + margin-bottom: 0; + font-size: @baseFontSize * 1.25; + font-weight: 300; + line-height: 1.25; + } + small { + display: block; + line-height: @baseLineHeight; + color: @grayLight; + &:before { + content: '\2014 \00A0'; + } + } + + // Float right with text-align: right + &.pull-right { + float: right; + padding-right: 15px; + padding-left: 0; + border-right: 5px solid @grayLighter; + border-left: 0; + p, + small { + text-align: right; + } + small { + &:before { + content: ''; + } + &:after { + content: '\00A0 \2014'; + } + } + } +} + +// Quotes +q:before, +q:after, +blockquote:before, +blockquote:after { + content: ""; +} + +// Addresses +address { + display: block; + margin-bottom: @baseLineHeight; + font-style: normal; + line-height: @baseLineHeight; +} diff --git a/public/css/old/variables.less b/public/css/old/variables.less new file mode 100644 index 0000000..9818b69 --- /dev/null +++ b/public/css/old/variables.less @@ -0,0 +1,311 @@ + +@pageHeaderHeight: 50px; + +@pageHeaderBrandHeight: @pageHeaderHeight; +@pageHeaderBrandWidth: (@pageHeaderBrandHeight * 6.32258064516129); + +@pageHeaderNavMargin: 6px; +@pageHeaderBackground: #2e363f; +@pageHeaderBackgroundHover: #232a32; +@pageHeaderLinkBackgroundActive: #111; +@pageHeaderLinkBorder: #363e48; +@pageHeaderLinkColor: #999; +@pageHeaderLinkColorHover: #fff; +@pageHeaderLinkColorActive: #fff; + + +@bioNavLinkColor: #fff; +@bioNavLinkColorHover: #fff; +@bioNavLinkBorder: #41bedd; +@bioNavBackgroundHover: #41bedd; + + + + +// +// Variables +// -------------------------------------------------- + + +// Global values +// -------------------------------------------------- + + +// Grays +// ------------------------- +@black: #000; +@grayDarker: #222; +@grayDark: #333; +@gray: #555; +@grayLight: #999; +@grayLighter: #eee; +@white: #fff; + + +// Accent colors +// ------------------------- +@blue: #049cdb; +@blueDark: #0064cd; +@green: #46a546; +@red: #9d261d; +@yellow: #ffc40d; +@orange: #f89406; +@pink: #c3325f; +@purple: #7a43b6; + + +// Scaffolding +// ------------------------- +@bodyBackground: #49cced; +@textColor: @grayDark; + + +// Links +// ------------------------- +@linkColor: #666; +@linkColorHover: #F77825; + + +// Typography +// ------------------------- +@sansFontFamily: Arial, sans-serif; +@serifFontFamily: Georgia, "Times New Roman", Times, serif; +@monoFontFamily: Monaco, Menlo, Consolas, "Courier New", monospace; + +@baseFontSize: 13px; +@baseFontFamily: @sansFontFamily; +@baseLineHeight: 20px; +@altFontFamily: @serifFontFamily; + +@headingsFontFamily: inherit; // empty to use BS default, @baseFontFamily +@headingsFontWeight: bold; // instead of browser default, bold +@headingsColor: inherit; // empty to use BS default, @textColor + + +// Component sizing +// ------------------------- +// Based on 14px font-size and 20px line-height + +@fontSizeLarge: @baseFontSize * 1.25; // ~18px +@fontSizeSmall: @baseFontSize * 0.85; // ~12px +@fontSizeMini: @baseFontSize * 0.75; // ~11px + +@paddingLarge: 11px 19px; // 44px +@paddingSmall: 2px 10px; // 26px +@paddingMini: 0 6px; // 22px + +@baseBorderRadius: 4px; +@borderRadiusLarge: 6px; +@borderRadiusSmall: 3px; + + +// Tables +// ------------------------- +@tableBackground: #fff; // overall background-color +@tableBackgroundAccent: #f9f9f9; // for striping +@tableBackgroundHover: #f5f5f5; // for hover +@tableBorder: #ddd; // table and cell border + +// Buttons +// ------------------------- +@btnBackground: #dadada; +@btnPrimaryBackground: #006dcc; +@btnInfoBackground: #49afcd; +@btnSuccessBackground: #5bb75b; +@btnWarningBackground: #faa732; +@btnDangerBackground: #da4f49; +@btnInverseBackground: #363636; + + +// Forms +// ------------------------- +@inputBackground: @white; +@inputBorder: #ccc; +@inputBorderRadius: @baseBorderRadius; +@inputDisabledBackground: @grayLighter; +@formActionsBackground: #f5f5f5; +@inputHeight: @baseLineHeight + 10px; // base line-height + 8px vertical padding + 2px top/bottom border + + +// Dropdowns +// ------------------------- +@dropdownBackground: @white; +@dropdownBorder: rgba(0,0,0,.2); +@dropdownDividerTop: #e5e5e5; +@dropdownDividerBottom: @white; + +@dropdownLinkColor: @grayDark; +@dropdownLinkColorHover: @white; +@dropdownLinkColorActive: @white; + +@dropdownLinkBackgroundActive: @linkColor; +@dropdownLinkBackgroundHover: @dropdownLinkBackgroundActive; + + + +// COMPONENT VARIABLES +// -------------------------------------------------- + + +// Z-index master list +// ------------------------- +// Used for a bird's eye view of components dependent on the z-axis +// Try to avoid customizing these :) +@zindexDropdown: 1000; +@zindexPopover: 1010; +@zindexTooltip: 1030; +@zindexFixedNavbar: 1030; +@zindexModalBackdrop: 1040; +@zindexModal: 1050; + + +// Sprite icons path +// ------------------------- +@iconSpritePath: "/img/glyphicons-halflings.png"; +@iconWhiteSpritePath: "/img/glyphicons-halflings-white.png"; + + +// Input placeholder text color +// ------------------------- +@placeholderText: @grayLight; + + +// Hr border color +// ------------------------- +@hrBorder: @grayLighter; + + +// Horizontal forms & lists +// ------------------------- +@horizontalComponentOffset: 180px; + + +// Wells +// ------------------------- +@wellBackground: #f5f5f5; + + +// Navbar +// ------------------------- +@navbarCollapseWidth: 979px; +@navbarCollapseDesktopWidth: @navbarCollapseWidth + 1; + +@navbarHeight: 40px; +@navbarBackgroundHighlight: #ffffff; +@navbarBackground: darken(@navbarBackgroundHighlight, 5%); +@navbarBorder: darken(@navbarBackground, 12%); + +@navbarText: #777; +@navbarLinkColor: #777; +@navbarLinkColorHover: @grayDark; +@navbarLinkColorActive: @gray; +@navbarLinkBackgroundHover: transparent; +@navbarLinkBackgroundActive: darken(@navbarBackground, 5%); + +@navbarBrandColor: @navbarLinkColor; + +// Inverted navbar +@navbarInverseBackground: #111111; +@navbarInverseBackgroundHighlight: #222222; +@navbarInverseBorder: #252525; + +@navbarInverseText: @grayLight; +@navbarInverseLinkColor: @grayLight; +@navbarInverseLinkColorHover: @white; +@navbarInverseLinkColorActive: @navbarInverseLinkColorHover; +@navbarInverseLinkBackgroundHover: transparent; +@navbarInverseLinkBackgroundActive: @navbarInverseBackground; + +@navbarInverseSearchBackground: lighten(@navbarInverseBackground, 25%); +@navbarInverseSearchBackgroundFocus: @white; +@navbarInverseSearchBorder: @navbarInverseBackground; +@navbarInverseSearchPlaceholderColor: #ccc; + +@navbarInverseBrandColor: @navbarInverseLinkColor; + + +// Pagination +// ------------------------- +@paginationBackground: #fff; +@paginationBorder: #ddd; +@paginationActiveBackground: #f5f5f5; + + +// Hero unit +// ------------------------- +@heroUnitBackground: @grayLighter; +@heroUnitHeadingColor: inherit; +@heroUnitLeadColor: inherit; + + +// Form states and alerts +// ------------------------- +@warningText: #c09853; +@warningBackground: #fcf8e3; +@warningBorder: darken(spin(@warningBackground, -10), 3%); + +@errorText: #b94a48; +@errorBackground: #f2dede; +@errorBorder: darken(spin(@errorBackground, -10), 3%); + +@successText: #468847; +@successBackground: #dff0d8; +@successBorder: darken(spin(@successBackground, -10), 5%); + +@infoText: #3a87ad; +@infoBackground: #d9edf7; +@infoBorder: darken(spin(@infoBackground, -10), 7%); + + +// Tooltips and popovers +// ------------------------- +@tooltipColor: #fff; +@tooltipBackground: #000; +@tooltipArrowWidth: 5px; +@tooltipArrowColor: @tooltipBackground; + +@popoverBackground: #fff; +@popoverArrowWidth: 10px; +@popoverArrowColor: #fff; +@popoverTitleBackground: darken(@popoverBackground, 3%); + +// Special enhancement for popovers +@popoverArrowOuterWidth: @popoverArrowWidth + 1; +@popoverArrowOuterColor: rgba(0,0,0,.25); + + + +// GRID +// -------------------------------------------------- + + +// Default 940px grid +// ------------------------- +@gridColumns: 12; +@gridColumnWidth: 60px; +@gridGutterWidth: 20px; +@gridRowWidth: (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1)); + +// 1200px min +@gridColumnWidth1200: 70px; +@gridGutterWidth1200: 30px; +@gridRowWidth1200: (@gridColumns * @gridColumnWidth1200) + (@gridGutterWidth1200 * (@gridColumns - 1)); + +// 768px-979px +@gridColumnWidth768: 42px; +@gridGutterWidth768: 20px; +@gridRowWidth768: (@gridColumns * @gridColumnWidth768) + (@gridGutterWidth768 * (@gridColumns - 1)); + + +// Fluid grid +// ------------------------- +@fluidGridColumnWidth: percentage(@gridColumnWidth/@gridRowWidth); +@fluidGridGutterWidth: percentage(@gridGutterWidth/@gridRowWidth); + +// 1200px min +@fluidGridColumnWidth1200: percentage(@gridColumnWidth1200/@gridRowWidth1200); +@fluidGridGutterWidth1200: percentage(@gridGutterWidth1200/@gridRowWidth1200); + +// 768px-979px +@fluidGridColumnWidth768: percentage(@gridColumnWidth768/@gridRowWidth768); +@fluidGridGutterWidth768: percentage(@gridGutterWidth768/@gridRowWidth768); diff --git a/public/css/old/widgets.less b/public/css/old/widgets.less new file mode 100644 index 0000000..ee6a81c --- /dev/null +++ b/public/css/old/widgets.less @@ -0,0 +1,247 @@ +.widget-box, .tabbable { + background: none repeat scroll 0 0 #fff; + border-top: 1px solid #CDCDCD; + border-left: 1px solid #CDCDCD; + border-right: 1px solid #CDCDCD; + border-bottom: 1px solid #CDCDCD; + margin-top: 16px; + margin-bottom: 16px; +} + +.tabbable .nav-tabs { + background-color: #efefef; + border-bottom: 1px solid #CDCDCD; + height: 36px; +} + +.widget-title { + background-color: #efefef; + border-bottom: 1px solid #CDCDCD; + height: 36px; +} + +.widget-title .icon { + border-right: 1px solid #cdcdcd; + padding: 8px 10px 7px 11px; + float: left; + opacity: .7; +} + +.widget-title h5 { + color: #666666; + text-shadow: 0 1px 0 #ffffff; + float: left; + font-size: 12px; + font-weight: bold; + padding: 12px; + line-height: 12px; + margin: 0; +} + +.widget-title .controls { + float: right; + padding: 3px; +} + +.widget-title .controls .input-append { + margin-bottom: 0; +} + +.widget-content { + padding: 12px 15px; + border-bottom: 1px solid #cdcdcd; +} + +.tabbable .nav-tabs { + border-bottom: 0 none; +} + +.tabbable .nav-tabs li:first-child a { + border-left: medium none !important; +} + +.tabbable .nav-tabs li a { + border-radius: 0 0 0 0; + border-top: medium none; + color: #999999; + margin: 0; + outline: medium none; + font-weight: bold; + text-shadow: 0 1px 0 #FFFFFF; +} + +.tabbable .nav-tabs li a:hover { + background-color: transparent !important; + border-color: #D6D6D6; + border-width: 0 1px; + color: #666666; +} + +.tabbable .nav-tabs li.active a { + background-color: #fff !important; + color: #444444; +} + +.tabbable .controls { + margin-bottom: 0; +} + +.nopadding { + padding: 0 !important; +} +.nopadding .table { + margin-bottom: 0; +} +.nopadding .table-bordered { + border: 0; +} + +.nopadding td:first-child, +.nopadding th:first-child { + border-left: none; +} + + + + + + + + + + + + + +.editor-panel { + margin-bottom: 10px; + + address { + margin: 0; + } +} + +.editor-label { + display: block; + float: left; + width: 170px; + text-align: right; + padding-right: 12px; +} + +.editor-enable { + display: block; + font-weight: normal; +} + +.editor-details { + margin-left: 185px; + margin-right: 20px; + overflow: hidden; +} + +.editor-details form { + background: none repeat scroll 0 0 #F9F9F9; + border: 1px solid #CDCDCD; + padding: 20px 0 0 0; +} + +.editor-details .form-actions { + margin-bottom: 0; +} + +.editor-summary { + font-weight: bold; + margin-left: 185px; +} + +.tab-content { + padding: 0 20px; +} + +.client-workorders-table { + td { + vertical-align: middle; + } + + .workorder-reason { + display: block; + font-weight: bold; + } + + .workorder-techs-label { + font-weight: bold; + } +} + +.toolbelt { + line-height: 30px; + padding: 5px 0; + .clearfix(); +} + +.toolbelt .primary { + float: left; +} + +.toolbelt .secondary { + float: right; +} + + +.frequency { + margin: 20px 0 0 0; + width: auto; + + .name { + text-align: right; + background: white; + } + + th { + background: white; + } + + td { + text-align: center; + } + + .icon::before { + font-family: FontAwesome; + font-weight: normal; + font-style: normal; + display: inline-block; + text-decoration: inherit; + } + + .false { + width: 20px; + height: 20px; + background-color: #F9F9F9; + color: #CCC; + cursor: pointer; + + .icon::before { + content: "\f00d"; + } + } + + .true { + width: 20px; + height: 20px; + background-color: #DFF0D8; + color: #468847; + cursor: pointer; + + .icon::before { + content: "\f00c"; + } + } + + .controls { + text-align: right; + background: white; + } +} + + diff --git a/public/fonts/Vegur-Bold.otf b/public/fonts/Vegur-Bold.otf new file mode 100644 index 0000000000000000000000000000000000000000..ae2e540acfbb8f7c676a5e00862200f58c15540c GIT binary patch literal 15956 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 Kp4?*6`2PVG-IMD8 literal 0 HcmV?d00001 diff --git a/public/fonts/Vegur-Light.otf b/public/fonts/Vegur-Light.otf new file mode 100644 index 0000000000000000000000000000000000000000..ce5b6f80820bb5ab5841fb4749c8da5977a4dfad GIT binary patch literal 15300 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 RR{{v)}(4YVS literal 0 HcmV?d00001 diff --git a/public/fonts/Vegur-Regular.otf b/public/fonts/Vegur-Regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..28978bca8faba972745b5c31660e58016576c6e5 GIT binary patch literal 15248 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 Tjpjj_=EEDn3ce@GMNR%6X?xFz literal 0 HcmV?d00001 diff --git a/public/img/bodyBg.png b/public/img/bodyBg.png new file mode 100644 index 0000000000000000000000000000000000000000..50cbde3078d77a97f26a8fbddf263f627609273f GIT binary patch literal 941 zcmaJ=J#W)M7&e3fiYj$NfEZ4WgoK3cvs1^76B8w|ODxqe!dWO6@cD1v^=& zy0Ibg5BLErY%m~32L1pW10#YTfDq?2P8kT6&iA3`dEV!J@4Xk@&g0#kyE_cS?3!)8 zN9SwVxPF!X@4ve7fKGRaF(iFFCW##(rs3id0;X?IP!HMe;^`+;W0(T)217Emj#LNx zyq#hA)DJ0}VQTd>w4E6uV1y=Kps_#Re_(;YoO&nriGySEcLhSYW{lLS`C!VQOe~K?_F+$UNj6Scaf1@?vRUQIv;3 zf+7^4B#06xDr!kmAq4A>rO_gHtoHQ7bu7Bm*eM~QDhNrE@JWfs(L@j{l}e@|NgPFR z@gg90$^~&TSI|-HL|#Zd9Dq#G9^pCBSnBj51wY)#2JyN~v|vJNhl0q%ETtT1S^tOn z{stYB9=i1VPvLm52$9f3F`h>by|{5P3l*xZ2-yTj1B_4eDt4!s;CPBd(CW+J*b7{o z#4EIAsb&xpJ8+PxYb@pSp69AiQS^$@(95N!EQ*HM+?VwOMS{lufg#G}X3o{IGxt$I za;|&HHMVlIM({%#Sx1rg47rC9_Q5J))!Xi+xm9n?b+>zIZRHBojF4UHU)P);(ca0H zo2I3kP1~b@_IgB{`rGW@8GR5%Q*R8?FQZ>SzZR<1YT-8g_@w^)w|oBf?DbK2>*bZb Y6Q=a}=J!4BT|0X~rqR*gG*8a|0Qj>YO8@`> literal 0 HcmV?d00001 diff --git a/public/img/breadcrumb.png b/public/img/breadcrumb.png new file mode 100644 index 0000000000000000000000000000000000000000..d3462402e4f803860a6b11fdc9bff76f7e3a955d GIT binary patch literal 1118 zcmaJ=KTH!*9KH$?sDe}-@K56Nh!eDT*V2|=sBrXJsPV8VO{f!G?;h=i-o4}Ql@?-* z)CDF?(8a+xGLbl$=pczCXksurIpN}Hkn&Z=!9ShR6B-Q`pWdn~+IE8*p*u=-hqrrn)$`FM)%e-H+^}&#R}efQ}EK z>+XZ4-b(lGld(b9?!a6XtZ$?S-RHivyNj+|@Aorj)eg;-D{Xmh3DwQjPi<@W?;ZNz hEnNcrp0!_s7gE&I@$+A|pDT}@|C@|YiQD6I)jy!GSD*j@ literal 0 HcmV?d00001 diff --git a/public/img/glyphicons-halflings-white.png b/public/img/glyphicons-halflings-white.png new file mode 100644 index 0000000000000000000000000000000000000000..3bf6484a29d8da269f9bc874b25493a45fae3bae GIT binary patch literal 8777 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 B7G?kd literal 0 HcmV?d00001 diff --git a/public/img/glyphicons-halflings.png b/public/img/glyphicons-halflings.png new file mode 100644 index 0000000000000000000000000000000000000000..a9969993201f9cee63cf9f49217646347297b643 GIT binary patch literal 12799 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 literal 0 HcmV?d00001 diff --git a/public/img/loader.gif b/public/img/loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..94ec0747c50b65d01387fed7a0e74cd1bd1b617c GIT binary patch literal 1442 zcmV;T1zq|_Nk%w1VPF9u0J8u9|NsBq-rmm6&c(&WwY9aSrKOaVl!SzYb8~ZJV`BgS z00000000000000000000A^8LV2LS&7EC2ui0AK+i000F4@W@GlxHtpCyQkn`Wh9WC zXd0|+JCc~0&TSpMuprNS+_w+VwG1$;I3sx&#L!^1gf}MLkl18iOZ*XH1Ajg^O@mif55wagbk@2xCif zK9HK8Hh3MCTVHy6b{89@1gRpcI*ov`el&%)w2zd#N1RHgPm^83noht&nU={`W|W~{ zu5Zy&u!eW8*Sd(ctG6n--@ZQ0Xyi8LOq#@G$IO^gq47tc%+jvLo2aSStkc`ZbrWX? z9HC#_2DR&iu7)~&3xmB=L(j)NO3#W_J2Fk5s2TzP3j*wyu!y&U4PBJ{!>d?IB=*8Y z95V3|MtSgV)`NDBO}>pjLgr{iN@$;#Q&!?aC{STbGDvU4)H2gb%`6X>QPlZ~ry6{u zJo3}yPsd2IO2aWlIdJXJs1M;zmGrdJRV-O?ZY`G4T~Cfb{{|)b?JS+L1``o{+qPWV zgXJK}^-8s_#JgfD+PKJ9%3Qo)@rboXSfsYnjGekY+Bn@~#lIxCcKA7pWty0gX|BPU zOkcZ#M+N>`2!Qe9$dfBy&b+zv=g^}|pH98H_3PNPYv0bjyZ1&PT^JZozI;bO<}k+$ODe*>G;c&ZG?vem>xt4;FM4j z>0y$4AlYP_Ook=pk8++#CzUgjSxS}aX*mOz>3La>o*ald1)*=uY2}+DxLIe1YcjfI zq>mDaW|)eqcjb+UCYAu8<@uQcsN@NnPNzODTB(YcmZ@fcY&II_koV1+rk$$NN>-W+8bx@)VYyqaJCtdqhjsiV4Dnk=TcYP#UC*Ou99sKR1N?2M^u z+at2MDr;@J*g`7psv%OVZie!9`M;>uWv+zXxxS%oge_ ze$dwIYOR#!+hW6sW_$390B?+^xHSq4EWtM>EFi+@;ydrf`#MbVyA~g5tG)EjJn^zE zXN{Z;9wO!Rb*=aR+E**FH(q@c-nkHg$1!+-i@UA3*>)U$Bj)p2UWe+9 z&y9iHsKN007FBcpH`cz}y+rAJdo4OMr@NPW>ua-~gX>$uE}!g5Nmd1x2SgB03Q-6@G@k;$7z7j|BZ=f$;tZkqIxMO&hcwin6f392IPP$c wne*WmK{vi2qK|Y((^b literal 0 HcmV?d00001 diff --git a/public/img/logo.svg b/public/img/logo.svg new file mode 100644 index 0000000..abfc22e --- /dev/null +++ b/public/img/logo.svg @@ -0,0 +1,298 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/js/app.js b/public/js/app.js new file mode 100644 index 0000000..d00049e --- /dev/null +++ b/public/js/app.js @@ -0,0 +1,74 @@ +var biomed = {}; + +angular.module('biomed', ['biomed.filters', 'biomed.services', 'biomed.directives', 'ngResource', 'ui.bootstrap.dialog']) + .run(function($rootScope) { + $rootScope.TECH_GROUPS = { + all: 'All', + biomed: 'Biomed', + ice: 'Ice', + sales: 'Sales', + other: 'Others' + }; + }) + .config(function($routeProvider, $locationProvider, $httpProvider) { + + var JSON_START = /^\s*(\[|\{[^\{])/, + JSON_END = /[\}\]]\s*$/, + PROTECTION_PREFIX = /^\)\]\}',?\n/, + DATE_MATCHER = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/; + + $httpProvider.defaults.transformResponse = [function(data) { + if (angular.isString(data)) { + data = data.replace(PROTECTION_PREFIX, ''); + if (JSON_START.test(data) && JSON_END.test(data)) { + data = JSON.parse(data, function(key, val) { + if (DATE_MATCHER.test(val)) + return new Date(val); + return val; + }) + } + + return data; + } + }]; + + $locationProvider.html5Mode(true); + $routeProvider + .when('/schedule', { + templateUrl: '/partials/schedule/index.html', + controller: biomed.ScheduleIndexCtrl + }) + .when('/schedule/pms', { + templateUrl: '/partials/schedule/pms.html', + controller: biomed.SchedulePmsCtrl + }) + .when('/clients', { + templateUrl: '/partials/clients/index.html', + controller: biomed.ClientIndexCtrl, + reloadOnSearch: false + }) + .when('/clients/add', { + templateUrl: '/partials/clients/add.html', + controller: biomed.ClientAddCtrl + }) + .when('/clients/:id', { + templateUrl: '/partials/clients/edit.html', + controller: biomed.ClientEditCtrl + }) + .when('/workorders', { + templateUrl: '/partials/workorders/index.html', + controller: biomed.WorkorderIndexCtrl, + reloadOnSearch: false + }) + .when('/workorders/add', { + templateUrl: '/partials/workorders/add.html', + controller: biomed.WorkorderAddCtrl + }) + .when('/workorders/:id', { + templateUrl: '/partials/workorders/edit.html', + controller: biomed.WorkorderEditCtrl + }) + .otherwise({ + redirectTo: '/schedule' + }); + }); diff --git a/public/js/controllers.js b/public/js/controllers.js new file mode 100644 index 0000000..c67ec11 --- /dev/null +++ b/public/js/controllers.js @@ -0,0 +1,533 @@ +biomed.AccountCtrl = function($scope, Account) { + $scope.account = Account.get(); +}; + +biomed.ScheduleIndexCtrl = function($scope, $location, Users, Schedule, LocationBinder) { + +// LocationBinder($scope, ['date'], { date: new Date() }); + updateUsers(); + + if (!$scope.date) { + $scope.date = new Date(); + } + + $scope.group = 'all'; + + $scope.$watch('date', updateDate); + $scope.$watch('group', updateUsers); + + $scope.onEntryClick = function(entry) { + $location.path('/workorders/' + entry.workorder._id); + }; + + function updateDate() { + Schedule.index({ + date: $scope.date.toJSON() + }, function(result) { + $scope.schedule = result; + }); + } + + function updateUsers() { + Users.index({ group: $scope.group }, function(result) { + $scope.users = result; + }); + } + +}; + +biomed.SchedulePmsCtrl = function($scope, Clients) { + $scope.loading = true; + + $scope.month = moment().month(); + + var allData = Clients.frequencies(function() { + filter(); + $scope.loading = false; + }); + + function filter() { + $scope.pms = []; + + angular.forEach(allData, function(client) { + angular.forEach(client.frequencies, function(value, key) { + if (value[$scope.month]) { + $scope.pms.push({ + reason: key, + client: client + }); + } + }); + }); + } + + $scope.$watch('month', filter); +}; + +biomed.ClientIndexCtrl = function($scope, $filter, $routeParams, Clients, LocationBinder) { + $scope.loading = true; + + var allData = Clients.index(function() { + $scope.loading = false; + $scope.filter(); + }); + + var filteredData = []; + var index = 0; + var initialPageSize = 100; + var pageSize = 5; + + $scope.canLoad = true; + + $scope.$watch('query', function() { + $scope.filter(); + }); + + LocationBinder($scope, ['query']); + + $scope.filter = function() { + filteredData = $filter('filter')(allData, $scope.query); + index = initialPageSize; + $scope.canLoad = true; + $scope.clients = filteredData.slice(0, initialPageSize); + }; + + $scope.addItems = function() { + $scope.clients = $scope.clients.concat(filteredData.slice(index, index + pageSize)); + index += pageSize; + $scope.canLoad = index < filteredData.length; + } +}; + +biomed.ClientAddCtrl = function($scope, Clients, $location) { + + $scope.save = function() { + $scope.model.contacts = [$scope.primaryContact, $scope.secondaryContact]; + + Clients.create($scope.model, function(result) { + $location.path("/clients/" + result._id); + }) + }; +}; + +biomed.ClientEditCtrl = function($scope, $routeParams, Clients) { + $scope.route = $routeParams; + $scope.loading = true; + + $scope.master = Clients.get($routeParams, function() { + $scope.loading = false; + }); + + $scope.workorders = Clients.workorders($routeParams, function() { + updatePms(); + }); + + $scope.identification = createController(); + $scope.address = createController(); + $scope.primaryContact = createContactController(0); + $scope.secondaryContact = createContactController(1); + $scope.other = createOtherController(); + + function updatePms() { + var currentMonth = new Date().getMonth(); + + $scope.pms = []; + + angular.forEach($scope.master.frequencies, function(value, key) { + if (value[currentMonth]) { + $scope.pms.push(key); + } + }); + } + + function createOtherController() { + var controller = { + edit: function() { + if (!$scope.editing) { + angular.copy($scope.master, controller.model); + controller.visible = true; + $scope.editing = true; + } + }, + destroy: function() { + Clients.destroy({id: $scope.master._id}); + window.history.back(); + }, + reset: function() { + angular.copy($scope.master, controller.model); + controller.visible = false; + $scope.editing = false; + }, + model: {}, + form: {} + }; + + return controller; + } + + function createController() { + var controller = { + edit: function() { + if (!$scope.editing) { + angular.copy($scope.master, controller.model); + controller.visible = true; + $scope.editing = true; + } + }, + save: function() { + Clients.update({id: $scope.master._id}, controller.model); + angular.copy(controller.model, $scope.master); + controller.visible = false; + $scope.editing = false; + }, + reset: function() { + angular.copy($scope.master, controller.model); + controller.visible = false; + $scope.editing = false; + }, + model: {}, + form: {} + }; + + return controller; + } + + function createContactController(index) { + var controller = { + edit: function() { + if (!$scope.editing) { + angular.copy($scope.master, controller.model); + + if (!controller.model.contacts[index]) { + controller.model.contacts[index] = {}; + } + + controller.visible = true; + $scope.editing = true; + } + }, + save: function() { + Clients.update({id: $scope.master._id}, controller.model); + angular.copy(controller.model, $scope.master); + controller.visible = false; + $scope.editing = false; + }, + reset: function() { + angular.copy($scope.master, controller.model); + controller.visible = false; + $scope.editing = false; + }, + model: {}, + form: {} + }; + + return controller; + } + + $scope.toggleFrequency = function(frequency, month) { + $scope.master.frequencies[frequency][month] =! $scope.master.frequencies[frequency][month]; + Clients.update({id: $scope.master._id}, $scope.master, function() { + updatePms(); + }); + } +}; + +biomed.WorkorderIndexCtrl = function($scope, $filter, $routeParams, Workorders, LocationBinder) { + $scope.loading = true; + + var data = {}; + + var defaultEnd = moment().toDate(); + var defaultStart = moment(defaultEnd).subtract('days', 7).toDate(); + + LocationBinder($scope, ['query', 'start', 'end'], { + start: defaultStart, + end: defaultEnd + }); + + fetchData(); + + + var filteredData = []; + var index = 0; + var initialPageSize = 100; + var pageSize = 5; + + $scope.canLoad = true; + + $scope.$watch('query', filter); + + $scope.$watch('start', fetchData); + + $scope.$watch('end', fetchData); + + + $scope.addItems = function() { + $scope.workorders = $scope.workorders.concat(filteredData.slice(index, index + pageSize)); + index += pageSize; + $scope.canLoad = index < filteredData.length; + } + + function filter() { + filteredData = $filter('filter')(data, $scope.query); + index = initialPageSize; + $scope.canLoad = true; + $scope.workorders = filteredData.slice(0, initialPageSize); + }; + + function fetchData() { + $scope.loading = true; + + data = Workorders.index({ + start: $scope.start.toJSON(), + end: $scope.end.toJSON() + }, function() { + $scope.loading = false; + filter(); + }); + } +} + +biomed.WorkorderAddCtrl = function($scope, $location, Workorders, Schedule, Clients, Users) { + + $scope.group = 'all'; + $scope.model = {}; + $scope.picker = { + start: '09:00:00', + end: '17:00:00' + }; + $scope.picker.date = new Date(); + + var search = $location.search(); + + if (search.clientId) { + $scope.model.client = search.clientId; + } + + if (search.reason) { + $scope.model.reason = search.reason; + } + + if (search.remarks) { + $scope.model.remarks = search.remarks; + } + + updateAllUsers(); + updateUsers(); + + $scope.$watch('group', updateUsers); + + Clients.index(function(result) { + $scope.clients = result; + }); + + $scope.$watch('picker.date', function() { + Schedule.index({ + date: $scope.picker.date.toJSON() + }, function(result) { + $scope.schedule = result; + }); + }); + + $scope.save = function() { + var picker = $scope.picker; + var model = $scope.model; + + var date = moment(picker.date).format('YYYY-MM-DD'); + model.status = 'scheduled'; + model.scheduling = {}; + model.scheduling.start = moment(date + 'T' + picker.start).toDate(); + model.scheduling.end = moment(date + 'T' + picker.end).toDate(); + + Workorders.create(model, function(result) { + $location.path("/workorders/" + result._id); + }); + }; + + function updateUsers() { + Users.index({ group: $scope.group }, function(result) { + $scope.users = result; + }); + } + + + function updateAllUsers() { + var criteria = {}; + + Users.index(criteria, function(result) { + $scope.allUsers = result; + + $scope.usersMap = {}; + $scope.allUsers.forEach(function(user) { + $scope.usersMap[user._id] = user; + }); + }); + } +} + +biomed.WorkorderEditCtrl = function($scope, $routeParams, Workorders, Schedule, Users) { + $scope.group = 'all'; + $scope.route = $routeParams; + $scope.loading = true; + + updateAllUsers(); + updateUsers(); + + $scope.$watch('group', updateUsers); + + $scope.master = Workorders.get($routeParams, function() { + $scope.loading = false; + }); + + $scope.status = createController(); + $scope.remarks = createController(); + $scope.scheduling = createSchedulingController(); + + $scope.destroy = function() { + Workorders.destroy({id: $scope.master._id}); + window.history.back(); + } + + function createController() { + var controller = { + edit: function() { + if (!$scope.editing) { + angular.copy($scope.master, controller.model); + controller.visible = true; + $scope.editing = true; + } + }, + save: function() { + Workorders.update({id: $scope.master._id}, controller.model); + angular.copy(controller.model, $scope.master); + controller.visible = false; + $scope.editing = false; + }, + reset: function() { + angular.copy($scope.master, controller.model); + controller.visible = false; + $scope.editing = false; + }, + model: {}, + form: {} + }; + + return controller; + } + + function createSchedulingController() { + var controller = { + edit: function() { + if (!$scope.editing) { + angular.copy($scope.master, controller.model); + + controller.date = moment(controller.model.scheduling.start).startOf('day').toDate(); + controller.start = moment(controller.model.scheduling.start).format('HH:mm:ss'); + controller.end = moment(controller.model.scheduling.end).format('HH:mm:ss'); + + controller.techs = controller.model.techs.map(function(t) { return t._id; }); + + controller.visible = true; + $scope.editing = true; + } + }, + save: function() { + var date = moment(controller.date).format('YYYY-MM-DD'); + controller.model.scheduling.start = moment(date + 'T' + controller.start).toDate(); + controller.model.scheduling.end = moment(date + 'T' + controller.end).toDate(); + + controller.model.techs = controller.techs.map(function(t) { return $scope.usersMap[t]; }); + + Workorders.update({id: $scope.master._id}, controller.model); + angular.copy(controller.model, $scope.master); + controller.visible = false; + $scope.editing = false; + }, + reset: function() { + angular.copy($scope.master, controller.model); + controller.visible = false; + $scope.editing = false; + }, + model: {}, + form: {} + }; + + + $scope.$watch('scheduling.date', function() { + + Schedule.index({ + date: $scope.scheduling.date.toJSON() + }, function(result) { + $scope.scheduling.schedule = result; + }); + }); + + return controller; + } + + function updateUsers() { + Users.index({ group: $scope.group }, function(result) { + $scope.users = result; + }); + } + + function updateAllUsers() { + var criteria = {}; + + Users.index(criteria, function(result) { + $scope.allUsers = result; + + $scope.usersMap = {}; + $scope.allUsers.forEach(function(user) { + $scope.usersMap[user._id] = user; + }); + }); + } +}; + + +biomed.PageCtrl = function($scope, $dialog) { + $scope.opts = { + backdrop: true, + keyboard: true, + backdropClick: true, + dialogFade: true, + backdropFade: true, + templateUrl: '/partials/messages.html', + controller: 'biomed.MessagesCtrl' + }; + + $scope.openDialog = function(){ + var d = $dialog.dialog($scope.opts); + d.open(); + }; +}; + +biomed.MessagesCtrl = function($scope, dialog, Users, Messages) { + $scope.model = {}; + + $scope.model.messages = [ + { message: 'Telephoned', checked: false }, + { message: 'Came to see you', checked: false }, + { message: 'Wants to see you', checked: false }, + { message: 'Returned your call', checked: false }, + { message: 'Please call', checked: false }, + { message: 'Will call again', checked: false }, + { message: 'Rush', checked: false }, + { message: 'Special Attention', checked: false } + ]; + + Users.index({ perms: "messages.receive" }, function(result) { + $scope.users = result; + }); + + $scope.send = function() { + Messages.send($scope.model, function(result) { + dialog.close(); + }); + }; + + $scope.cancel = function() { + dialog.close(); + }; +}; diff --git a/public/js/directives.js b/public/js/directives.js new file mode 100644 index 0000000..0909b39 --- /dev/null +++ b/public/js/directives.js @@ -0,0 +1,423 @@ +angular.module('biomed.directives', []) +.directive("bioNavbar", function($location) { + return { + restrict: 'A', + link: function($scope, element, attrs, controller) { + $scope.$watch(function() { + return $location.path(); + }, function(newValue, oldValue) { + element.find('li[data-match-route]').each(function(k, li) { + var $li = angular.element(li); + var pattern = $li.attr('data-match-route'); + var regex = new RegExp('^' + pattern + '$', ['i']); + + if (regex.test(newValue)) { + $li.addClass('active'); + } else { + $li.removeClass('active'); + } + }); + }); + } + } +}) +.directive('infiniteScroll', function ($window) { + return { + link:function (scope, element, attrs) { + var offset = parseInt(attrs.threshold) || 0; + var e = angular.element($window); + + e.bind("scroll", function() { + if (scope.canLoad && e.height() + e.scrollTop() >= $(document).height() - offset) { + scope.$apply(attrs.infiniteScroll); + } + }); + } + } +}) +.directive('tabbable', function() { + return { + restrict: 'C', + compile: function(element) { + var navTabs = angular.element(''), + tabContent = angular.element('
    '); + + tabContent.append(element.contents()); + element.append(navTabs).append(tabContent); + }, + controller: ['$scope', '$element', function($scope, $element) { + var navTabs = $element.contents().eq(0), + ngModel = $element.controller('ngModel') || {}, + tabs = [], + selectedTab; + + ngModel.$render = function() { + var $viewValue = this.$viewValue; + + if (selectedTab ? (selectedTab.value != $viewValue) : $viewValue) { + if(selectedTab) { + selectedTab.paneElement.removeClass('active'); + selectedTab.tabElement.removeClass('active'); + selectedTab = null; + } + if($viewValue) { + for(var i = 0, ii = tabs.length; i < ii; i++) { + if ($viewValue == tabs[i].value) { + selectedTab = tabs[i]; + break; + } + } + if (selectedTab) { + selectedTab.paneElement.addClass('active'); + selectedTab.tabElement.addClass('active'); + } + } + + } + }; + + this.addPane = function(element, attr) { + var li = angular.element('
  • '), + a = li.find('a'), + tab = { + paneElement: element, + paneAttrs: attr, + tabElement: li + }; + + tabs.push(tab); + + attr.$observe('value', update)(); + attr.$observe('title', function(){ update(); a.text(tab.title); })(); + + function update() { + tab.title = attr.title; + tab.value = attr.value || attr.title; + if (!ngModel.$setViewValue && (!ngModel.$viewValue || tab == selectedTab)) { + // we are not part of angular + ngModel.$viewValue = tab.value; + } + ngModel.$render(); + } + + navTabs.append(li); + li.bind('click', function(event) { + event.preventDefault(); + event.stopPropagation(); + if (ngModel.$setViewValue) { + $scope.$apply(function() { + ngModel.$setViewValue(tab.value); + ngModel.$render(); + }); + } else { + // we are not part of angular + ngModel.$viewValue = tab.value; + ngModel.$render(); + } + }); + + return function() { + tab.tabElement.remove(); + for(var i = 0, ii = tabs.length; i < ii; i++ ) { + if (tab == tabs[i]) { + tabs.splice(i, 1); + } + } + }; + } + }] + }; +}) +.directive('tabPane', function() { + return { + require: '^tabbable', + restrict: 'C', + link: function(scope, element, attrs, tabsCtrl) { + element.bind('$remove', tabsCtrl.addPane(element, attrs)); + } + }; +}) +.directive('datepicker', function($timeout) { + var isTouch = 'ontouchstart' in window && !window.navigator.userAgent.match(/PhantomJS/i); + + return { + restrict: 'A', + require: '?ngModel', + link: function postLink(scope, element, attrs, controller) { + + var format = 'MM-DD-YYYY'; + + // Handle date validity according to dateFormat + if(controller) { + controller.$formatters.unshift(function(value) { + return moment(value).format(format); + }); + + controller.$parsers.unshift(function(viewValue) { + if (angular.isDate(viewValue)) { + return viewValue; + } else { + var date = moment(viewValue); + if (date.isValid()) { + return date; + } else { + return undefined; + } + } + }); + } + + // Use native interface for touch devices + if(isTouch && element.prop('type') === 'text') { + + element.prop('type', 'date'); + element.on('change', function(ev) { + scope.$apply(function () { + controller.$setViewValue(moment(element.val()).toDate()); + }); + }); + + } else { + + // If we have a controller (i.e. ngModelController) then wire it up + if(controller) { + element.on('changeDate', function(ev) { + scope.$apply(function () { + controller.$setViewValue(moment(element.val()).toDate()); + }); + }); + } + + // Popover GarbageCollection + var $popover = element.closest('.popover'); + if($popover) { + $popover.on('hide', function(e) { + var datepicker = element.data('datepicker'); + if(datepicker) { + datepicker.picker.remove(); + element.data('datepicker', null); + } + }); + } + + // Create datepicker + element.attr('data-toggle', 'datepicker'); + element.datepicker({ + autoclose: true, + format: 'mm-dd-yyyy', + forceParse: attrs.forceParse || false + }); + + } + + // Support add-on + var component = element.siblings('[data-toggle="datepicker"]'); + if(component.length) { + component.on('click', function() { element.trigger('focus'); }); + } + + } + }; +}) +.directive('techpicker', function() { + + return { + restrict: 'E', + scope: { + users: '=', + schedule: '=', + date: '=', + onEntryClick: '&' + }, + templateUrl: '/partials/techPicker.html', + replace: true, + link: function($scope, element, attrs) { + var timePickerParser = d3.time.format('%I:%M%p'); + + var rangeStart = timePickerParser.parse('7:00am'); + var rangeEnd = timePickerParser.parse('10:00pm'); + + var x = d3.time.scale() + .range([0, 100]) + .domain([rangeStart, rangeEnd]); + + var color = d3.scale.category20(); + + var totalHours = moment.duration(moment(rangeEnd) - moment(rangeStart)).hours(); + var hourWidth = 100 / totalHours; + + $scope.hourMarkers = []; + for (var i = 0; i < totalHours; i++) { + var date = moment(rangeStart).add('hours', i).toDate(); + + $scope.hourMarkers.push({ + date: date, + style: { + left: x(date) + "%", + width: hourWidth + "%" + } + }); + } + + $scope.$watch('users', function(newVal, oldVal) { + generateDate(); + }); + + $scope.$watch('schedule', function(newVal, oldVal) { + generateDate(); + }); + + function generateDate() { + var data = {}; + + var labels = []; + + angular.forEach($scope.users, function(user) { + var key = user.name.first + ' ' + user.name.last; + labels.push(key); + data[key] = []; + }); + + labels.sort(); + color.domain(labels); + + angular.forEach($scope.schedule, function(workorder) { + angular.forEach(workorder.techs, function(tech) { + var key = tech.name.first + ' ' + tech.name.last; + + if (!data[key]) + return; + + var start = moment(workorder.scheduling.start).year(1900).month(0).date(1).toDate(); + var end = moment(workorder.scheduling.end).year(1900).month(0).date(1).toDate(); + + data[key].push({ + style: { + backgroundColor: color(key), + left: x(start) + "%", + width: (x(end) - x(start)) + "%" + }, + workorder: workorder + }); + }) + }); + $scope.data = data; + } + } + }; +}) +.directive('uiSelect2', function ($timeout) { + var options = {}; + + return { + require: '?ngModel', + compile: function (tElm, tAttrs) { + var watch, + repeatOption, + repeatAttr, + isSelect = tElm.is('select'), + isMultiple = (tAttrs.multiple !== undefined); + + // Enable watching of the options dataset if in use + if (tElm.is('select')) { + repeatOption = tElm.find('option[ng-repeat], option[data-ng-repeat]'); + + if (repeatOption.length) { + repeatAttr = repeatOption.attr('ng-repeat') || repeatOption.attr('data-ng-repeat'); + watch = jQuery.trim(repeatAttr.split('|')[0]).split(' ').pop(); + } + } + + return function (scope, elm, attrs, controller) { + // instance-specific options + var opts = angular.extend({}, options, scope.$eval(attrs.uiSelect2)); + + if (isSelect) { + // Use element."); + } + }); + } + + opts = $.extend({}, { + populateResults: function(container, results, query) { + var populate, data, result, children, id=this.opts.id, self=this; + + populate=function(results, container, depth) { + + var i, l, result, selectable, compound, node, label, innerContainer, formatted; + for (i = 0, l = results.length; i < l; i = i + 1) { + + result=results[i]; + selectable=id(result) !== undefined; + compound=result.children && result.children.length > 0; + + node=$("
  • "); + node.addClass("select2-results-dept-"+depth); + node.addClass("select2-result"); + node.addClass(selectable ? "select2-result-selectable" : "select2-result-unselectable"); + if (compound) { node.addClass("select2-result-with-children"); } + node.addClass(self.opts.formatResultCssClass(result)); + + label=$("
    "); + label.addClass("select2-result-label"); + + formatted=opts.formatResult(result, label, query); + if (formatted!==undefined) { + label.html(self.opts.escapeMarkup(formatted)); + } + + node.append(label); + + if (compound) { + + innerContainer=$("
      "); + innerContainer.addClass("select2-result-sub"); + populate(result.children, innerContainer, depth+1); + node.append(innerContainer); + } + + node.data("select2-data", result); + container.append(node); + } + }; + + populate(results, container, 0); + } + }, $.fn.select2.defaults, opts); + + if (typeof(opts.id) !== "function") { + idKey = opts.id; + opts.id = function (e) { return e[idKey]; }; + } + + if (select) { + opts.query = this.bind(function (query) { + var data = { results: [], more: false }, + term = query.term, + children, firstChild, process; + + process=function(element, collection) { + var group; + if (element.is("option")) { + if (query.matcher(term, element.text(), element)) { + collection.push({id:element.attr("value"), text:element.text(), element: element.get(), css: element.attr("class")}); + } + } else if (element.is("optgroup")) { + group={text:element.attr("label"), children:[], element: element.get(), css: element.attr("class")}; + element.children().each2(function(i, elm) { process(elm, group.children); }); + if (group.children.length>0) { + collection.push(group); + } + } + }; + + children=element.children(); + + // ignore the placeholder option if there is one + if (this.getPlaceholder() !== undefined && children.length > 0) { + firstChild = children[0]; + if ($(firstChild).text() === "") { + children=children.not(firstChild); + } + } + + children.each2(function(i, elm) { process(elm, data.results); }); + + query.callback(data); + }); + // this is needed because inside val() we construct choices from options and there id is hardcoded + opts.id=function(e) { return e.id; }; + opts.formatResultCssClass = function(data) { return data.css; } + } else { + if (!("query" in opts)) { + if ("ajax" in opts) { + ajaxUrl = opts.element.data("ajax-url"); + if (ajaxUrl && ajaxUrl.length > 0) { + opts.ajax.url = ajaxUrl; + } + opts.query = ajax(opts.ajax); + } else if ("data" in opts) { + opts.query = local(opts.data); + } else if ("tags" in opts) { + opts.query = tags(opts.tags); + opts.createSearchChoice = function (term) { return {id: term, text: term}; }; + opts.initSelection = function (element, callback) { + var data = []; + $(splitVal(element.val(), opts.separator)).each(function () { + var id = this, text = this, tags=opts.tags; + if ($.isFunction(tags)) tags=tags(); + $(tags).each(function() { if (equal(this.id, id)) { text = this.text; return false; } }); + data.push({id: id, text: text}); + }); + + callback(data); + }; + } + } + } + if (typeof(opts.query) !== "function") { + throw "query function not defined for Select2 " + opts.element.attr("id"); + } + + return opts; + }, + + /** + * Monitor the original element for changes and update select2 accordingly + */ + // abstract + monitorSource: function () { + this.opts.element.bind("change.select2", this.bind(function (e) { + if (this.opts.element.data("select2-change-triggered") !== true) { + this.initSelection(); + } + })); + }, + + /** + * Triggers the change event on the source element + */ + // abstract + triggerChange: function (details) { + + details = details || {}; + details= $.extend({}, details, { type: "change", val: this.val() }); + // prevents recursive triggering + this.opts.element.data("select2-change-triggered", true); + this.opts.element.trigger(details); + this.opts.element.data("select2-change-triggered", false); + + // some validation frameworks ignore the change event and listen instead to keyup, click for selects + // so here we trigger the click event manually + this.opts.element.click(); + + // ValidationEngine ignorea the change event and listens instead to blur + // so here we trigger the blur event manually if so desired + if (this.opts.blurOnChange) + this.opts.element.blur(); + }, + + + // abstract + enable: function() { + if (this.enabled) return; + + this.enabled=true; + this.container.removeClass("select2-container-disabled"); + }, + + // abstract + disable: function() { + if (!this.enabled) return; + + this.close(); + + this.enabled=false; + this.container.addClass("select2-container-disabled"); + }, + + // abstract + opened: function () { + return this.container.hasClass("select2-dropdown-open"); + }, + + // abstract + positionDropdown: function() { + var offset = this.container.offset(), + height = this.container.outerHeight(), + width = this.container.outerWidth(), + dropHeight = this.dropdown.outerHeight(), + viewportBottom = $(window).scrollTop() + document.documentElement.clientHeight, + dropTop = offset.top + height, + dropLeft = offset.left, + enoughRoomBelow = dropTop + dropHeight <= viewportBottom, + enoughRoomAbove = (offset.top - dropHeight) >= this.body().scrollTop(), + aboveNow = this.dropdown.hasClass("select2-drop-above"), + bodyOffset, + above, + css; + + // console.log("below/ droptop:", dropTop, "dropHeight", dropHeight, "sum", (dropTop+dropHeight)+" viewport bottom", viewportBottom, "enough?", enoughRoomBelow); + // console.log("above/ offset.top", offset.top, "dropHeight", dropHeight, "top", (offset.top-dropHeight), "scrollTop", this.body().scrollTop(), "enough?", enoughRoomAbove); + + // fix positioning when body has an offset and is not position: static + + if (this.body().css('position') !== 'static') { + bodyOffset = this.body().offset(); + dropTop -= bodyOffset.top; + dropLeft -= bodyOffset.left; + } + + // always prefer the current above/below alignment, unless there is not enough room + + if (aboveNow) { + above = true; + if (!enoughRoomAbove && enoughRoomBelow) above = false; + } else { + above = false; + if (!enoughRoomBelow && enoughRoomAbove) above = true; + } + + if (above) { + dropTop = offset.top - dropHeight; + this.container.addClass("select2-drop-above"); + this.dropdown.addClass("select2-drop-above"); + } + else { + this.container.removeClass("select2-drop-above"); + this.dropdown.removeClass("select2-drop-above"); + } + + css = $.extend({ + top: dropTop, + left: dropLeft, + width: width + }, evaluate(this.opts.dropdownCss)); + + this.dropdown.css(css); + }, + + // abstract + shouldOpen: function() { + var event; + + if (this.opened()) return false; + + event = $.Event("open"); + this.opts.element.trigger(event); + return !event.isDefaultPrevented(); + }, + + // abstract + clearDropdownAlignmentPreference: function() { + // clear the classes used to figure out the preference of where the dropdown should be opened + this.container.removeClass("select2-drop-above"); + this.dropdown.removeClass("select2-drop-above"); + }, + + /** + * Opens the dropdown + * + * @return {Boolean} whether or not dropdown was opened. This method will return false if, for example, + * the dropdown is already open, or if the 'open' event listener on the element called preventDefault(). + */ + // abstract + open: function () { + + if (!this.shouldOpen()) return false; + + window.setTimeout(this.bind(this.opening), 1); + + return true; + }, + + /** + * Performs the opening of the dropdown + */ + // abstract + opening: function() { + var cid = this.containerId, selector = this.containerSelector, + scroll = "scroll." + cid, resize = "resize." + cid; + + this.container.parents().each(function() { + $(this).bind(scroll, function() { + var s2 = $(selector); + if (s2.length == 0) { + $(this).unbind(scroll); + } + s2.select2("close"); + }); + }); + + $(window).bind(resize, function() { + var s2 = $(selector); + if (s2.length == 0) { + $(window).unbind(resize); + } + s2.select2("close"); + }); + + this.clearDropdownAlignmentPreference(); + + if (this.search.val() === " ") { this.search.val(""); } + + this.container.addClass("select2-dropdown-open").addClass("select2-container-active"); + + this.updateResults(true); + + if(this.dropdown[0] !== this.body().children().last()[0]) { + this.dropdown.detach().appendTo(this.body()); + } + + this.dropdown.show(); + + this.positionDropdown(); + this.dropdown.addClass("select2-drop-active"); + + this.ensureHighlightVisible(); + + this.focusSearch(); + }, + + // abstract + close: function () { + if (!this.opened()) return; + + var self = this; + + this.container.parents().each(function() { + $(this).unbind("scroll." + self.containerId); + }); + $(window).unbind("resize." + this.containerId); + + this.clearDropdownAlignmentPreference(); + + this.dropdown.hide(); + this.container.removeClass("select2-dropdown-open").removeClass("select2-container-active"); + this.results.empty(); + this.clearSearch(); + + this.opts.element.trigger($.Event("close")); + }, + + // abstract + clearSearch: function () { + + }, + + // abstract + ensureHighlightVisible: function () { + var results = this.results, children, index, child, hb, rb, y, more; + + index = this.highlight(); + + if (index < 0) return; + + if (index == 0) { + + // if the first element is highlighted scroll all the way to the top, + // that way any unselectable headers above it will also be scrolled + // into view + + results.scrollTop(0); + return; + } + + children = results.find(".select2-result-selectable"); + + child = $(children[index]); + + hb = child.offset().top + child.outerHeight(); + + // if this is the last child lets also make sure select2-more-results is visible + if (index === children.length - 1) { + more = results.find("li.select2-more-results"); + if (more.length > 0) { + hb = more.offset().top + more.outerHeight(); + } + } + + rb = results.offset().top + results.outerHeight(); + if (hb > rb) { + results.scrollTop(results.scrollTop() + (hb - rb)); + } + y = child.offset().top - results.offset().top; + + // make sure the top of the element is visible + if (y < 0) { + results.scrollTop(results.scrollTop() + y); // y is negative + } + }, + + // abstract + moveHighlight: function (delta) { + var choices = this.results.find(".select2-result-selectable"), + index = this.highlight(); + + while (index > -1 && index < choices.length) { + index += delta; + var choice = $(choices[index]); + if (choice.hasClass("select2-result-selectable") && !choice.hasClass("select2-disabled")) { + this.highlight(index); + break; + } + } + }, + + // abstract + highlight: function (index) { + var choices = this.results.find(".select2-result-selectable").not(".select2-disabled"); + + if (arguments.length === 0) { + return indexOf(choices.filter(".select2-highlighted")[0], choices.get()); + } + + if (index >= choices.length) index = choices.length - 1; + if (index < 0) index = 0; + + choices.removeClass("select2-highlighted"); + + $(choices[index]).addClass("select2-highlighted"); + this.ensureHighlightVisible(); + + }, + + // abstract + countSelectableResults: function() { + return this.results.find(".select2-result-selectable").not(".select2-disabled").length; + }, + + // abstract + highlightUnderEvent: function (event) { + var el = $(event.target).closest(".select2-result-selectable"); + if (el.length > 0 && !el.is(".select2-highlighted")) { + var choices = this.results.find('.select2-result-selectable'); + this.highlight(choices.index(el)); + } else if (el.length == 0) { + // if we are over an unselectable item remove al highlights + this.results.find(".select2-highlighted").removeClass("select2-highlighted"); + } + }, + + // abstract + loadMoreIfNeeded: function () { + var results = this.results, + more = results.find("li.select2-more-results"), + below, // pixels the element is below the scroll fold, below==0 is when the element is starting to be visible + offset = -1, // index of first element without data + page = this.resultsPage + 1, + self=this, + term=this.search.val(), + context=this.context; + + if (more.length === 0) return; + below = more.offset().top - results.offset().top - results.height(); + + if (below <= 0) { + more.addClass("select2-active"); + this.opts.query({ + term: term, + page: page, + context: context, + matcher: this.opts.matcher, + callback: this.bind(function (data) { + + // ignore a response if the select2 has been closed before it was received + if (!self.opened()) return; + + + self.opts.populateResults.call(this, results, data.results, {term: term, page: page, context:context}); + + if (data.more===true) { + more.detach().appendTo(results).text(self.opts.formatLoadMore(page+1)); + window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10); + } else { + more.remove(); + } + self.positionDropdown(); + self.resultsPage = page; + })}); + } + }, + + /** + * Default tokenizer function which does nothing + */ + tokenize: function() { + + }, + + /** + * @param initial whether or not this is the call to this method right after the dropdown has been opened + */ + // abstract + updateResults: function (initial) { + var search = this.search, results = this.results, opts = this.opts, data, self=this, input; + + // if the search is currently hidden we do not alter the results + if (initial !== true && (this.showSearchInput === false || !this.opened())) { + return; + } + + search.addClass("select2-active"); + + function postRender() { + results.scrollTop(0); + search.removeClass("select2-active"); + self.positionDropdown(); + } + + function render(html) { + results.html(self.opts.escapeMarkup(html)); + postRender(); + } + + if (opts.maximumSelectionSize >=1) { + data = this.data(); + if ($.isArray(data) && data.length >= opts.maximumSelectionSize && checkFormatter(opts.formatSelectionTooBig, "formatSelectionTooBig")) { + render("
    • " + opts.formatSelectionTooBig(opts.maximumSelectionSize) + "
    • "); + return; + } + } + + if (search.val().length < opts.minimumInputLength && checkFormatter(opts.formatInputTooShort, "formatInputTooShort")) { + render("
    • " + opts.formatInputTooShort(search.val(), opts.minimumInputLength) + "
    • "); + return; + } + else { + render("
    • " + opts.formatSearching() + "
    • "); + } + + // give the tokenizer a chance to pre-process the input + input = this.tokenize(); + if (input != undefined && input != null) { + search.val(input); + } + + this.resultsPage = 1; + opts.query({ + term: search.val(), + page: this.resultsPage, + context: null, + matcher: opts.matcher, + callback: this.bind(function (data) { + var def; // default choice + + // ignore a response if the select2 has been closed before it was received + if (!this.opened()) return; + + // save context, if any + this.context = (data.context===undefined) ? null : data.context; + + // create a default choice and prepend it to the list + if (this.opts.createSearchChoice && search.val() !== "") { + def = this.opts.createSearchChoice.call(null, search.val(), data.results); + if (def !== undefined && def !== null && self.id(def) !== undefined && self.id(def) !== null) { + if ($(data.results).filter( + function () { + return equal(self.id(this), self.id(def)); + }).length === 0) { + data.results.unshift(def); + } + } + } + + if (data.results.length === 0 && checkFormatter(opts.formatNoMatches, "formatNoMatches")) { + render("
    • " + opts.formatNoMatches(search.val()) + "
    • "); + return; + } + + results.empty(); + self.opts.populateResults.call(this, results, data.results, {term: search.val(), page: this.resultsPage, context:null}); + + if (data.more === true && checkFormatter(opts.formatLoadMore, "formatLoadMore")) { + results.append("
    • " + self.opts.escapeMarkup(opts.formatLoadMore(this.resultsPage)) + "
    • "); + window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10); + } + + this.postprocessResults(data, initial); + + postRender(); + })}); + }, + + // abstract + cancel: function () { + this.close(); + }, + + // abstract + blur: function () { + this.close(); + this.container.removeClass("select2-container-active"); + this.dropdown.removeClass("select2-drop-active"); + // synonymous to .is(':focus'), which is available in jquery >= 1.6 + if (this.search[0] === document.activeElement) { this.search.blur(); } + this.clearSearch(); + this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"); + }, + + // abstract + focusSearch: function () { + // need to do it here as well as in timeout so it works in IE + this.search.show(); + this.search.focus(); + + /* we do this in a timeout so that current event processing can complete before this code is executed. + this makes sure the search field is focussed even if the current event would blur it */ + window.setTimeout(this.bind(function () { + // reset the value so IE places the cursor at the end of the input box + this.search.show(); + this.search.focus(); + this.search.val(this.search.val()); + }), 10); + }, + + // abstract + selectHighlighted: function () { + var index=this.highlight(), + highlighted=this.results.find(".select2-highlighted").not(".select2-disabled"), + data = highlighted.closest('.select2-result-selectable').data("select2-data"); + if (data) { + highlighted.addClass("select2-disabled"); + this.highlight(index); + this.onSelect(data); + } + }, + + // abstract + getPlaceholder: function () { + return this.opts.element.attr("placeholder") || + this.opts.element.attr("data-placeholder") || // jquery 1.4 compat + this.opts.element.data("placeholder") || + this.opts.placeholder; + }, + + /** + * Get the desired width for the container element. This is + * derived first from option `width` passed to select2, then + * the inline 'style' on the original element, and finally + * falls back to the jQuery calculated element width. + */ + // abstract + initContainerWidth: function () { + function resolveContainerWidth() { + var style, attrs, matches, i, l; + + if (this.opts.width === "off") { + return null; + } else if (this.opts.width === "element"){ + return this.opts.element.outerWidth() === 0 ? 'auto' : this.opts.element.outerWidth() + 'px'; + } else if (this.opts.width === "copy" || this.opts.width === "resolve") { + // check if there is inline style on the element that contains width + style = this.opts.element.attr('style'); + if (style !== undefined) { + attrs = style.split(';'); + for (i = 0, l = attrs.length; i < l; i = i + 1) { + matches = attrs[i].replace(/\s/g, '') + .match(/width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/); + if (matches !== null && matches.length >= 1) + return matches[1]; + } + } + + if (this.opts.width === "resolve") { + // next check if css('width') can resolve a width that is percent based, this is sometimes possible + // when attached to input type=hidden or elements hidden via css + style = this.opts.element.css('width'); + if (style.indexOf("%") > 0) return style; + + // finally, fallback on the calculated width of the element + return (this.opts.element.outerWidth() === 0 ? 'auto' : this.opts.element.outerWidth() + 'px'); + } + + return null; + } else if ($.isFunction(this.opts.width)) { + return this.opts.width(); + } else { + return this.opts.width; + } + }; + + var width = resolveContainerWidth.call(this); + if (width !== null) { + this.container.attr("style", "width: "+width); + } + } + }); + + SingleSelect2 = clazz(AbstractSelect2, { + + // single + + createContainer: function () { + var container = $("
      ", { + "class": "select2-container" + }).html([ + " ", + " ", + "
      " , + "
      ", + "
      " , + " " , + "
        " , + "
      " , + "
      "].join("")); + return container; + }, + + // single + opening: function () { + this.search.show(); + this.parent.opening.apply(this, arguments); + this.dropdown.removeClass("select2-offscreen"); + }, + + // single + close: function () { + if (!this.opened()) return; + this.parent.close.apply(this, arguments); + this.dropdown.removeAttr("style").addClass("select2-offscreen").insertAfter(this.selection).show(); + }, + + // single + focus: function () { + this.close(); + this.selection.focus(); + }, + + // single + isFocused: function () { + return this.selection[0] === document.activeElement; + }, + + // single + cancel: function () { + this.parent.cancel.apply(this, arguments); + this.selection.focus(); + }, + + // single + initContainer: function () { + + var selection, + container = this.container, + dropdown = this.dropdown, + clickingInside = false; + + this.selection = selection = container.find(".select2-choice"); + + this.search.bind("keydown", this.bind(function (e) { + if (!this.enabled) return; + + if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) { + // prevent the page from scrolling + killEvent(e); + return; + } + + if (this.opened()) { + switch (e.which) { + case KEY.UP: + case KEY.DOWN: + this.moveHighlight((e.which === KEY.UP) ? -1 : 1); + killEvent(e); + return; + case KEY.TAB: + case KEY.ENTER: + this.selectHighlighted(); + killEvent(e); + return; + case KEY.ESC: + this.cancel(e); + killEvent(e); + return; + } + } else { + + if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) { + return; + } + + if (this.opts.openOnEnter === false && e.which === KEY.ENTER) { + return; + } + + this.open(); + + if (e.which === KEY.ENTER) { + // do not propagate the event otherwise we open, and propagate enter which closes + return; + } + } + })); + + this.search.bind("focus", this.bind(function() { + this.selection.attr("tabIndex", "-1"); + })); + this.search.bind("blur", this.bind(function() { + if (!this.opened()) this.container.removeClass("select2-container-active"); + window.setTimeout(this.bind(function() { this.selection.attr("tabIndex", this.opts.element.attr("tabIndex")); }), 10); + })); + + selection.bind("mousedown", this.bind(function (e) { + clickingInside = true; + + if (this.opened()) { + this.close(); + this.selection.focus(); + } else if (this.enabled) { + this.open(); + } + + clickingInside = false; + })); + + dropdown.bind("mousedown", this.bind(function() { this.search.focus(); })); + + selection.bind("focus", this.bind(function() { + this.container.addClass("select2-container-active"); + // hide the search so the tab key does not focus on it + this.search.attr("tabIndex", "-1"); + })); + + selection.bind("blur", this.bind(function() { + if (!this.opened()) { + this.container.removeClass("select2-container-active"); + } + window.setTimeout(this.bind(function() { this.search.attr("tabIndex", this.opts.element.attr("tabIndex")); }), 10); + })); + + selection.bind("keydown", this.bind(function(e) { + if (!this.enabled) return; + + if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) { + // prevent the page from scrolling + killEvent(e); + return; + } + + if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) + || e.which === KEY.ESC) { + return; + } + + if (this.opts.openOnEnter === false && e.which === KEY.ENTER) { + return; + } + + if (e.which == KEY.DELETE) { + if (this.opts.allowClear) { + this.clear(); + } + return; + } + + this.open(); + + if (e.which === KEY.ENTER) { + // do not propagate the event otherwise we open, and propagate enter which closes + killEvent(e); + return; + } + + // do not set the search input value for non-alpha-numeric keys + // otherwise pressing down results in a '(' being set in the search field + if (e.which < 48 ) { // '0' == 48 + killEvent(e); + return; + } + + var keyWritten = String.fromCharCode(e.which).toLowerCase(); + + if (e.shiftKey) { + keyWritten = keyWritten.toUpperCase(); + } + + // focus the field before calling val so the cursor ends up after the value instead of before + this.search.focus(); + this.search.val(keyWritten); + + // prevent event propagation so it doesnt replay on the now focussed search field and result in double key entry + killEvent(e); + })); + + selection.delegate("abbr", "mousedown", this.bind(function (e) { + if (!this.enabled) return; + this.clear(); + killEvent(e); + this.close(); + this.triggerChange(); + this.selection.focus(); + })); + + this.setPlaceholder(); + + this.search.bind("focus", this.bind(function() { + this.container.addClass("select2-container-active"); + })); + }, + + // single + clear: function() { + this.opts.element.val(""); + this.selection.find("span").empty(); + this.selection.removeData("select2-data"); + this.setPlaceholder(); + }, + + /** + * Sets selection based on source element's value + */ + // single + initSelection: function () { + var selected; + if (this.opts.element.val() === "") { + this.close(); + this.setPlaceholder(); + } else { + var self = this; + this.opts.initSelection.call(null, this.opts.element, function(selected){ + if (selected !== undefined && selected !== null) { + self.updateSelection(selected); + self.close(); + self.setPlaceholder(); + } + }); + } + }, + + // single + prepareOpts: function () { + var opts = this.parent.prepareOpts.apply(this, arguments); + + if (opts.element.get(0).tagName.toLowerCase() === "select") { + // install the selection initializer + opts.initSelection = function (element, callback) { + var selected = element.find(":selected"); + // a single select box always has a value, no need to null check 'selected' + if ($.isFunction(callback)) + callback({id: selected.attr("value"), text: selected.text()}); + }; + } + + return opts; + }, + + // single + setPlaceholder: function () { + var placeholder = this.getPlaceholder(); + + if (this.opts.element.val() === "" && placeholder !== undefined) { + + // check for a first blank option if attached to a select + if (this.select && this.select.find("option:first").text() !== "") return; + + this.selection.find("span").html(this.opts.escapeMarkup(placeholder)); + + this.selection.addClass("select2-default"); + + this.selection.find("abbr").hide(); + } + }, + + // single + postprocessResults: function (data, initial) { + var selected = 0, self = this, showSearchInput = true; + + // find the selected element in the result list + + this.results.find(".select2-result-selectable").each2(function (i, elm) { + if (equal(self.id(elm.data("select2-data")), self.opts.element.val())) { + selected = i; + return false; + } + }); + + // and highlight it + + this.highlight(selected); + + // hide the search box if this is the first we got the results and there are a few of them + + if (initial === true) { + showSearchInput = this.showSearchInput = countResults(data.results) >= this.opts.minimumResultsForSearch; + this.dropdown.find(".select2-search")[showSearchInput ? "removeClass" : "addClass"]("select2-search-hidden"); + + //add "select2-with-searchbox" to the container if search box is shown + $(this.dropdown, this.container)[showSearchInput ? "addClass" : "removeClass"]("select2-with-searchbox"); + } + + }, + + // single + onSelect: function (data) { + var old = this.opts.element.val(); + + this.opts.element.val(this.id(data)); + this.updateSelection(data); + this.close(); + this.selection.focus(); + + if (!equal(old, this.id(data))) { this.triggerChange(); } + }, + + // single + updateSelection: function (data) { + + var container=this.selection.find("span"), formatted; + + this.selection.data("select2-data", data); + + container.empty(); + formatted=this.opts.formatSelection(data, container); + if (formatted !== undefined) { + container.append(this.opts.escapeMarkup(formatted)); + } + + this.selection.removeClass("select2-default"); + + if (this.opts.allowClear && this.getPlaceholder() !== undefined) { + this.selection.find("abbr").show(); + } + }, + + // single + val: function () { + var val, data = null, self = this; + + if (arguments.length === 0) { + return this.opts.element.val(); + } + + val = arguments[0]; + + if (this.select) { + this.select + .val(val) + .find(":selected").each2(function (i, elm) { + data = {id: elm.attr("value"), text: elm.text()}; + return false; + }); + this.updateSelection(data); + this.setPlaceholder(); + } else { + if (this.opts.initSelection === undefined) { + throw new Error("cannot call val() if initSelection() is not defined"); + } + // val is an id. !val is true for [undefined,null,''] + if (!val) { + this.clear(); + return; + } + this.opts.element.val(val); + this.opts.initSelection(this.opts.element, function(data){ + self.opts.element.val(!data ? "" : self.id(data)); + self.updateSelection(data); + self.setPlaceholder(); + }); + } + }, + + // single + clearSearch: function () { + this.search.val(""); + }, + + // single + data: function(value) { + var data; + + if (arguments.length === 0) { + data = this.selection.data("select2-data"); + if (data == undefined) data = null; + return data; + } else { + if (!value || value === "") { + this.clear(); + } else { + this.opts.element.val(!value ? "" : this.id(value)); + this.updateSelection(value); + } + } + } + }); + + MultiSelect2 = clazz(AbstractSelect2, { + + // multi + createContainer: function () { + var container = $("
      ", { + "class": "select2-container select2-container-multi" + }).html([ + "
        ", + //"
      • California
      • " , + "
      • " , + " " , + "
      • " , + "
      " , + ""].join("")); + return container; + }, + + // multi + prepareOpts: function () { + var opts = this.parent.prepareOpts.apply(this, arguments); + + // TODO validate placeholder is a string if specified + + if (opts.element.get(0).tagName.toLowerCase() === "select") { + // install sthe selection initializer + opts.initSelection = function (element,callback) { + + var data = []; + element.find(":selected").each2(function (i, elm) { + data.push({id: elm.attr("value"), text: elm.text()}); + }); + + if ($.isFunction(callback)) + callback(data); + }; + } + + return opts; + }, + + // multi + initContainer: function () { + + var selector = ".select2-choices", selection; + + this.searchContainer = this.container.find(".select2-search-field"); + this.selection = selection = this.container.find(selector); + + this.search.bind("keydown", this.bind(function (e) { + if (!this.enabled) return; + + if (e.which === KEY.BACKSPACE && this.search.val() === "") { + this.close(); + + var choices, + selected = selection.find(".select2-search-choice-focus"); + if (selected.length > 0) { + this.unselect(selected.first()); + this.search.width(10); + killEvent(e); + return; + } + + choices = selection.find(".select2-search-choice"); + if (choices.length > 0) { + choices.last().addClass("select2-search-choice-focus"); + } + } else { + selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"); + } + + if (this.opened()) { + switch (e.which) { + case KEY.UP: + case KEY.DOWN: + this.moveHighlight((e.which === KEY.UP) ? -1 : 1); + killEvent(e); + return; + case KEY.ENTER: + case KEY.TAB: + this.selectHighlighted(); + killEvent(e); + return; + case KEY.ESC: + this.cancel(e); + killEvent(e); + return; + } + } + + if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) + || e.which === KEY.BACKSPACE || e.which === KEY.ESC) { + return; + } + + if (this.opts.openOnEnter === false && e.which === KEY.ENTER) { + return; + } + + this.open(); + + if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) { + // prevent the page from scrolling + killEvent(e); + } + })); + + this.search.bind("keyup", this.bind(this.resizeSearch)); + + this.search.bind("blur", this.bind(function(e) { + this.container.removeClass("select2-container-active"); + this.search.removeClass("select2-focused"); + this.clearSearch(); + e.stopImmediatePropagation(); + })); + + this.container.delegate(selector, "mousedown", this.bind(function (e) { + if (!this.enabled) return; + if ($(e.target).closest(".select2-search-choice").length > 0) { + // clicked inside a select2 search choice, do not open + return; + } + this.clearPlaceholder(); + this.open(); + this.focusSearch(); + e.preventDefault(); + })); + + this.container.delegate(selector, "focus", this.bind(function () { + if (!this.enabled) return; + this.container.addClass("select2-container-active"); + this.dropdown.addClass("select2-drop-active"); + this.clearPlaceholder(); + })); + + // set the placeholder if necessary + this.clearSearch(); + }, + + // multi + enable: function() { + if (this.enabled) return; + + this.parent.enable.apply(this, arguments); + + this.search.removeAttr("disabled"); + }, + + // multi + disable: function() { + if (!this.enabled) return; + + this.parent.disable.apply(this, arguments); + + this.search.attr("disabled", true); + }, + + // multi + initSelection: function () { + var data; + if (this.opts.element.val() === "") { + this.updateSelection([]); + this.close(); + // set the placeholder if necessary + this.clearSearch(); + } + if (this.select || this.opts.element.val() !== "") { + var self = this; + this.opts.initSelection.call(null, this.opts.element, function(data){ + if (data !== undefined && data !== null) { + self.updateSelection(data); + self.close(); + // set the placeholder if necessary + self.clearSearch(); + } + }); + } + }, + + // multi + clearSearch: function () { + var placeholder = this.getPlaceholder(); + + if (placeholder !== undefined && this.getVal().length === 0 && this.search.hasClass("select2-focused") === false) { + this.search.val(placeholder).addClass("select2-default"); + // stretch the search box to full width of the container so as much of the placeholder is visible as possible + this.resizeSearch(); + } else { + // we set this to " " instead of "" and later clear it on focus() because there is a firefox bug + // that does not properly render the caret when the field starts out blank + this.search.val(" ").width(10); + } + }, + + // multi + clearPlaceholder: function () { + if (this.search.hasClass("select2-default")) { + this.search.val("").removeClass("select2-default"); + } else { + // work around for the space character we set to avoid firefox caret bug + if (this.search.val() === " ") this.search.val(""); + } + }, + + // multi + opening: function () { + this.parent.opening.apply(this, arguments); + + this.clearPlaceholder(); + this.resizeSearch(); + this.focusSearch(); + }, + + // multi + close: function () { + if (!this.opened()) return; + this.parent.close.apply(this, arguments); + }, + + // multi + focus: function () { + this.close(); + this.search.focus(); + }, + + // multi + isFocused: function () { + return this.search.hasClass("select2-focused"); + }, + + // multi + updateSelection: function (data) { + var ids = [], filtered = [], self = this; + + // filter out duplicates + $(data).each(function () { + if (indexOf(self.id(this), ids) < 0) { + ids.push(self.id(this)); + filtered.push(this); + } + }); + data = filtered; + + this.selection.find(".select2-search-choice").remove(); + $(data).each(function () { + self.addSelectedChoice(this); + }); + self.postprocessResults(); + }, + + tokenize: function() { + var input = this.search.val(); + input = this.opts.tokenizer(input, this.data(), this.bind(this.onSelect), this.opts); + if (input != null && input != undefined) { + this.search.val(input); + if (input.length > 0) { + this.open(); + } + } + + }, + + // multi + onSelect: function (data) { + this.addSelectedChoice(data); + if (this.select) { this.postprocessResults(); } + + if (this.opts.closeOnSelect) { + this.close(); + this.search.width(10); + } else { + if (this.countSelectableResults()>0) { + this.search.width(10); + this.resizeSearch(); + this.positionDropdown(); + } else { + // if nothing left to select close + this.close(); + } + } + + // since its not possible to select an element that has already been + // added we do not need to check if this is a new element before firing change + this.triggerChange({ added: data }); + + this.focusSearch(); + }, + + // multi + cancel: function () { + this.close(); + this.focusSearch(); + }, + + // multi + addSelectedChoice: function (data) { + var choice=$( + "
    • " + + "
      " + + " " + + "
    • "), + id = this.id(data), + val = this.getVal(), + formatted; + + formatted=this.opts.formatSelection(data, choice); + choice.find("div").replaceWith("
      "+this.opts.escapeMarkup(formatted)+"
      "); + choice.find(".select2-search-choice-close") + .bind("mousedown", killEvent) + .bind("click dblclick", this.bind(function (e) { + if (!this.enabled) return; + + $(e.target).closest(".select2-search-choice").fadeOut('fast', this.bind(function(){ + this.unselect($(e.target)); + this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"); + this.close(); + this.focusSearch(); + })).dequeue(); + killEvent(e); + })).bind("focus", this.bind(function () { + if (!this.enabled) return; + this.container.addClass("select2-container-active"); + this.dropdown.addClass("select2-drop-active"); + })); + + choice.data("select2-data", data); + choice.insertBefore(this.searchContainer); + + val.push(id); + this.setVal(val); + }, + + // multi + unselect: function (selected) { + var val = this.getVal(), + data, + index; + + selected = selected.closest(".select2-search-choice"); + + if (selected.length === 0) { + throw "Invalid argument: " + selected + ". Must be .select2-search-choice"; + } + + data = selected.data("select2-data"); + + index = indexOf(this.id(data), val); + + if (index >= 0) { + val.splice(index, 1); + this.setVal(val); + if (this.select) this.postprocessResults(); + } + selected.remove(); + this.triggerChange({ removed: data }); + }, + + // multi + postprocessResults: function () { + var val = this.getVal(), + choices = this.results.find(".select2-result-selectable"), + compound = this.results.find(".select2-result-with-children"), + self = this; + + choices.each2(function (i, choice) { + var id = self.id(choice.data("select2-data")); + if (indexOf(id, val) >= 0) { + choice.addClass("select2-disabled").removeClass("select2-result-selectable"); + } else { + choice.removeClass("select2-disabled").addClass("select2-result-selectable"); + } + }); + + compound.each2(function(i, e) { + if (e.find(".select2-result-selectable").length==0) { + e.addClass("select2-disabled"); + } else { + e.removeClass("select2-disabled"); + } + }); + + choices.each2(function (i, choice) { + if (!choice.hasClass("select2-disabled") && choice.hasClass("select2-result-selectable")) { + self.highlight(0); + return false; + } + }); + + }, + + // multi + resizeSearch: function () { + + var minimumWidth, left, maxWidth, containerLeft, searchWidth, + sideBorderPadding = getSideBorderPadding(this.search); + + minimumWidth = measureTextWidth(this.search) + 10; + + left = this.search.offset().left; + + maxWidth = this.selection.width(); + containerLeft = this.selection.offset().left; + + searchWidth = maxWidth - (left - containerLeft) - sideBorderPadding; + if (searchWidth < minimumWidth) { + searchWidth = maxWidth - sideBorderPadding; + } + + if (searchWidth < 40) { + searchWidth = maxWidth - sideBorderPadding; + } + this.search.width(searchWidth); + }, + + // multi + getVal: function () { + var val; + if (this.select) { + val = this.select.val(); + return val === null ? [] : val; + } else { + val = this.opts.element.val(); + return splitVal(val, this.opts.separator); + } + }, + + // multi + setVal: function (val) { + var unique; + if (this.select) { + this.select.val(val); + } else { + unique = []; + // filter out duplicates + $(val).each(function () { + if (indexOf(this, unique) < 0) unique.push(this); + }); + this.opts.element.val(unique.length === 0 ? "" : unique.join(this.opts.separator)); + } + }, + + // multi + val: function () { + var val, data = [], self=this; + + if (arguments.length === 0) { + return this.getVal(); + } + + val = arguments[0]; + + if (!val) { + this.opts.element.val(""); + this.updateSelection([]); + this.clearSearch(); + return; + } + + // val is a list of ids + this.setVal(val); + + if (this.select) { + this.select.find(":selected").each(function () { + data.push({id: $(this).attr("value"), text: $(this).text()}); + }); + this.updateSelection(data); + } else { + if (this.opts.initSelection === undefined) { + throw new Error("val() cannot be called if initSelection() is not defined") + } + + this.opts.initSelection(this.opts.element, function(data){ + var ids=$(data).map(self.id); + self.setVal(ids); + self.updateSelection(data); + self.clearSearch(); + }); + } + this.clearSearch(); + }, + + // multi + onSortStart: function() { + if (this.select) { + throw new Error("Sorting of elements is not supported when attached to instead."); + } + + // collapse search field into 0 width so its container can be collapsed as well + this.search.width(0); + // hide the container + this.searchContainer.hide(); + }, + + // multi + onSortEnd:function() { + + var val=[], self=this; + + // show search and move it to the end of the list + this.searchContainer.show(); + // make sure the search container is the last item in the list + this.searchContainer.appendTo(this.searchContainer.parent()); + // since we collapsed the width in dragStarted, we resize it here + this.resizeSearch(); + + // update selection + + this.selection.find(".select2-search-choice").each(function() { + val.push(self.opts.id($(this).data("select2-data"))); + }); + this.setVal(val); + this.triggerChange(); + }, + + // multi + data: function(values) { + var self=this, ids; + if (arguments.length === 0) { + return this.selection + .find(".select2-search-choice") + .map(function() { return $(this).data("select2-data"); }) + .get(); + } else { + if (!values) { values = []; } + ids = $.map(values, function(e) { return self.opts.id(e)}); + this.setVal(ids); + this.updateSelection(values); + this.clearSearch(); + } + } + }); + + $.fn.select2 = function () { + + var args = Array.prototype.slice.call(arguments, 0), + opts, + select2, + value, multiple, allowedMethods = ["val", "destroy", "opened", "open", "close", "focus", "isFocused", "container", "onSortStart", "onSortEnd", "enable", "disable", "positionDropdown", "data"]; + + this.each(function () { + if (args.length === 0 || typeof(args[0]) === "object") { + opts = args.length === 0 ? {} : $.extend({}, args[0]); + opts.element = $(this); + + if (opts.element.get(0).tagName.toLowerCase() === "select") { + multiple = opts.element.attr("multiple"); + } else { + multiple = opts.multiple || false; + if ("tags" in opts) {opts.multiple = multiple = true;} + } + + select2 = multiple ? new MultiSelect2() : new SingleSelect2(); + select2.init(opts); + } else if (typeof(args[0]) === "string") { + + if (indexOf(args[0], allowedMethods) < 0) { + throw "Unknown method: " + args[0]; + } + + value = undefined; + select2 = $(this).data("select2"); + if (select2 === undefined) return; + if (args[0] === "container") { + value=select2.container; + } else { + value = select2[args[0]].apply(select2, args.slice(1)); + } + if (value !== undefined) {return false;} + } else { + throw "Invalid arguments to select2 plugin: " + args; + } + }); + return (value === undefined) ? this : value; + }; + + // plugin defaults, accessible to users + $.fn.select2.defaults = { + width: "copy", + closeOnSelect: true, + openOnEnter: true, + containerCss: {}, + dropdownCss: {}, + containerCssClass: "", + dropdownCssClass: "", + formatResult: function(result, container, query) { + var markup=[]; + markMatch(result.text, query.term, markup); + return markup.join(""); + }, + formatSelection: function (data, container) { + return data ? data.text : undefined; + }, + formatResultCssClass: function(data) {return undefined;}, + formatNoMatches: function () { return "No matches found"; }, + formatInputTooShort: function (input, min) { return "Please enter " + (min - input.length) + " more characters"; }, + formatSelectionTooBig: function (limit) { return "You can only select " + limit + " item" + (limit == 1 ? "" : "s"); }, + formatLoadMore: function (pageNumber) { return "Loading more results..."; }, + formatSearching: function () { return "Searching..."; }, + minimumResultsForSearch: 0, + minimumInputLength: 0, + maximumSelectionSize: 0, + id: function (e) { return e.id; }, + matcher: function(term, text) { + return text.toUpperCase().indexOf(term.toUpperCase()) >= 0; + }, + separator: ",", + tokenSeparators: [], + tokenizer: defaultTokenizer, + escapeMarkup: function (markup) { + if (markup && typeof(markup) === "string") { + return markup.replace(/&/g, "&"); + } + return markup; + }, + blurOnChange: false + }; + + // exports + window.Select2 = { + query: { + ajax: ajax, + local: local, + tags: tags + }, util: { + debounce: debounce, + markMatch: markMatch + }, "class": { + "abstract": AbstractSelect2, + "single": SingleSelect2, + "multi": MultiSelect2 + } + }; + +}(jQuery)); diff --git a/public/js/services.js b/public/js/services.js new file mode 100644 index 0000000..47456d9 --- /dev/null +++ b/public/js/services.js @@ -0,0 +1,114 @@ +angular.module('biomed.services', []) +.factory("Clients", function($resource) { + return $resource('/api/clients/:id/:cmd', + { id: "@id", cmd: "@cmd" }, + { + index: { method: 'GET', params: {}, isArray: true }, + frequencies:{ method: 'GET', params: { cmd: 'frequencies' }, isArray: true }, + get: { method: 'GET', params: { id: 0} }, + create: { method: 'POST', params: {} }, + update: { method: 'POST', params: { id: 0} }, + destroy: { method: 'DELETE', params: { id: 0 } }, + workorders: { method: 'GET', params: { id: 0, cmd: 'workorders' }, isArray: true } + }); +}) +.factory("Workorders", function($resource) { + return $resource('/api/workorders/:id', + { id: "@id" }, + { + index: { method: 'GET', params: {}, isArray: true }, + get: { method: 'GET', params: { id: 0} }, + create: { method: 'POST', params: {} }, + update: { method: 'POST', params: { id: 0} }, + destroy: { method: 'DELETE', params: { id: 0 } }, + }); +}) +.factory("Users", function($resource) { + return $resource('/api/users', { }, + { + index: { method: 'GET', isArray: true }, + }); +}) +.factory("Schedule", function($resource) { + return $resource('/api/schedule', { }, + { + index: { method: 'GET', isArray: true }, + }); +}) +.factory("Account", function($resource) { + return $resource('/api/account', + { id: "@id", cmd: "@cmd" }, + { + get: { method: 'GET' }, + }); +}) +.factory("Messages", function($resource) { + return $resource('/api/messages/:cmd', + { cmd: "@cmd" }, + { + send: { method: 'POST', params: { cmd: 'send' } }, + }); +}) +.factory("LocationBinder", function($location, $timeout) { + var DATE_MATCHER = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/; + + return function($scope, params, defaults) { + + function init() { + var search = $location.search(); + + params.forEach(function(binding) { + var val = search[binding]; + + if (val) { + if (DATE_MATCHER.test(val)) { + val = new Date(val); + } + + $scope[binding] = val; + } else if (defaults && defaults[binding]) { + $scope[binding] = defaults[binding]; + } + }); + } + init(); + + var updateTimeout; + function updateLocation() { + console.log("Update Timer Fired"); + if (updateTimeout) $timeout.cancel(updateTimeout); + updateTimeout = $timeout(function() { + params.forEach(function(binding) { + + var val = $scope[binding] || null; + if (angular.isDate(val)) { + val = val.toJSON(); + } + + $location.search(binding, val); + }); + }, 1000); + } + + params.forEach(function(binding) { + $scope.$watch(binding, function() { + updateLocation(); + }); + }); + + $scope.$on('$routeUpdate', function() { + console.log('Route updated'); + var search = $location.search(); + + params.forEach(function(binding) { + var val = search[binding]; + + if (DATE_MATCHER.test(val)) { + val = new Date(val); + } + + $scope[binding] = val; + }); + }); + } +}); \ No newline at end of file diff --git a/public/partials/clients/add.html b/public/partials/clients/add.html new file mode 100644 index 0000000..487eb44 --- /dev/null +++ b/public/partials/clients/add.html @@ -0,0 +1,175 @@ + +
      +

      New Client

      +
      + +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +
      +
      + +
      + +
      + +
      +
      +
      diff --git a/public/partials/clients/edit.html b/public/partials/clients/edit.html new file mode 100644 index 0000000..6c31385 --- /dev/null +++ b/public/partials/clients/edit.html @@ -0,0 +1,288 @@ + + +
      +

      {{master.name}}

      +

      {{master.identifier}}

      +
      +
      +
      +
      +
      + +
      +
      + {{master.name}}
      + {{master.identifier}}
      + Edit +
      +
      +
      + +
      + + Required +
      +
      +
      + +
      + + Required +
      +
      +
      + + +
      +
      +
      +
      +
      + +
      +
      + {{master.address.street1}}
      + {{master.address.street2}}
      + {{master.address.city}}, {{master.address.state}}. {{master.address.zip}}
      + Edit +
      +
      +
      + +
      + + Required +
      +
      +
      + +
      + +
      +
      +
      + +
      + + Required +
      +
      +
      + +
      + + Required +
      +
      +
      + +
      + + Required +
      +
      +
      + + +
      +
      +
      +
      +
      + +
      +
      + {{master.contacts[0].name}}
      + {{master.contacts[0].phone}}
      + {{master.contacts[0].email}}
      + Edit +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + + +
      +
      +
      +
      +
      + +
      +
      + {{master.contacts[1].name}}
      + {{master.contacts[1].phone}}
      + {{master.contacts[1].email}}
      + Edit +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + + +
      +
      +
      +
      +
      + +
      + +
      +
      +
      +
      +
      +
      +
      + + + + + + + + + + + + + + + + + + + + + + +
      WorkorderDateStatus
      There is no information to display.
      + Preventive Maintenance ({{pm}})
      +
      This MonthPM Due
      + {{workorder.reason}}
      + Techs: {{ workorder.techs | techs }}
      + {{workorder.remarks}} +
      {{workorder.scheduling.start | date}}{{workorder.status}}
      +
      +
      +
      +
      + + + + + + + + + + + + + + + + + + + + + + + + +
      JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC
      {{frequency}}
      +
      +
      \ No newline at end of file diff --git a/public/partials/clients/index.html b/public/partials/clients/index.html new file mode 100644 index 0000000..d0f92e0 --- /dev/null +++ b/public/partials/clients/index.html @@ -0,0 +1,41 @@ + +
      +

      Clients

      +
      + +
      +
      +
      + Create new Client +
      + Search: +
      + + +
      +
      +
      + + + + + + + + + + + + + + + + + + + +
      Client NameContactPhone
      There is no information to display.
      {{client.identifier | uppercase}}{{client.name}}{{client.contacts[0].name}}{{client.contacts[0].phone}}
      +
      +
      \ No newline at end of file diff --git a/public/partials/messages.html b/public/partials/messages.html new file mode 100644 index 0000000..270ab5b --- /dev/null +++ b/public/partials/messages.html @@ -0,0 +1,75 @@ + + + \ No newline at end of file diff --git a/public/partials/schedule/index.html b/public/partials/schedule/index.html new file mode 100644 index 0000000..f973639 --- /dev/null +++ b/public/partials/schedule/index.html @@ -0,0 +1,34 @@ + +
      +

      Schedule

      +
      + +
      +
      +
      + Create new Workorder + View PMs +
      + Group: +
      + + +
      + Date: +
      + + +
      +
      +
      + +
      +
      \ No newline at end of file diff --git a/public/partials/schedule/pms.html b/public/partials/schedule/pms.html new file mode 100644 index 0000000..fc42e24 --- /dev/null +++ b/public/partials/schedule/pms.html @@ -0,0 +1,54 @@ + +
      +

      Preventive Maintenance

      +
      + +
      +
      +
      + Create new Workorder +
      + Month: +
      + + +
      +
      +
      + + + + + + + + + + + + + + + + + + +
      Client NameReasonContactPhone
      There is no information to display.
      {{pm.client.name}} ({{pm.client.identifier | uppercase}})
      +
      {{pm.reason}}{{pm.client.contacts[0].name}}{{pm.lient.contacts[0].phone}}
      +
      +
      \ No newline at end of file diff --git a/public/partials/techPicker.html b/public/partials/techPicker.html new file mode 100644 index 0000000..9b00ee9 --- /dev/null +++ b/public/partials/techPicker.html @@ -0,0 +1,20 @@ +
      +
      +
      + {{ hour.date | date:'h a'}} +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      {{name}}
      +
      +
      {{entry.workorder.client.identifier}}
      +
      +
      +
      diff --git a/public/partials/workorders/add.html b/public/partials/workorders/add.html new file mode 100644 index 0000000..04656fc --- /dev/null +++ b/public/partials/workorders/add.html @@ -0,0 +1,171 @@ + + +
      +

      New Workorder

      +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +
      +
      +
      + +
      +
      +
      + +
      + + + to + +
      +
      +
      + +
      + +
      +
      +
      +
      + Group: + +
      +
      + +
      +
      +
      +
      + +
      + +
      +
      +
      diff --git a/public/partials/workorders/edit.html b/public/partials/workorders/edit.html new file mode 100644 index 0000000..01a4a2c --- /dev/null +++ b/public/partials/workorders/edit.html @@ -0,0 +1,207 @@ + + +
      +

      #{{master.biomedId}} - {{master.reason}}

      +

      {{master.client.name}} ({{master.client.identifier}})

      +
      +
      +
      +
      +
      + +
      +
      + {{master.status}}
      + Edit +
      +
      +
      + +
      + +
      +
      +
      + + +
      +
      +
      +
      +
      + +
      +
      + {{master.remarks}}
      + Edit +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + + +
      +
      +
      +
      +
      + +
      +
      + {{master.scheduling.start | date}}
      + From {{master.scheduling.start | time}} to {{master.scheduling.end | time}}
      + Techs: {{ master.techs | techs }}
      + Edit +
      +
      +
      + +
      + + + to + +
      +
      +
      + +
      + +
      +
      +
      +
      + Group: + +
      +
      + +
      + + +
      +
      +
      +
      +
      + +
      + +
      +
      +
      +
      + No history is currently available. +
      +
      +
      \ No newline at end of file diff --git a/public/partials/workorders/index.html b/public/partials/workorders/index.html new file mode 100644 index 0000000..90c5066 --- /dev/null +++ b/public/partials/workorders/index.html @@ -0,0 +1,52 @@ + +

      Workorders

      + +
      +
      +
      + Create new workorder +
      +
      + + +
      + Start: +
      + + +
      + End: +
      + + +
      +
      +
      + + + + + + + + + + + + + + + + + + + +
      WorkorderClientDateStatus
      There is no information to display.
      + #{{workorder.biomedId}} - {{workorder.reason}}
      + Techs: {{ workorder.techs | techs }}
      + {{workorder.remarks}} +
      {{workorder.client.name}} ({{workorder.client.identifier}}){{workorder.scheduling.start | date}}{{workorder.status}}
      +
      +
      diff --git a/server.js b/server.js new file mode 100644 index 0000000..e6251c1 --- /dev/null +++ b/server.js @@ -0,0 +1,44 @@ +var express = require('express') + fs = require('fs'), + passport = require('passport'); + +var env = process.env.NODE_ENV || 'development', + config = require('./config/config')[env], + mongoose = require('mongoose'); + + +// bootstrap db connection +mongoose.set('debug', config.debug); +mongoose.connect(config.database); + +// bootstrap model +var modelPath = __dirname + '/app/model' +fs.readdirSync(modelPath).forEach(function (file) { + require(modelPath + '/' + file) +}) + +require('./config/passport')(passport, config); + +var app = express(), + http = require('http'), + server = http.createServer(app), + io = require('socket.io').listen(server); + +// Configure piler +var piler = require('./config/piler')(app, server, io, config); + +// Express settings +require('./config/express')(app, config, passport, piler); + +var auth = require('./config/auth')(app, passport); + +var calendar = require('./config/calendar')(config); + +// Bootstrap routes +require('./config/routes')(app, auth, piler, calendar, config); + +GLOBAL.health = 'OK' + +var port = process.env.PORT || 8000 +server.listen(port) +console.log('Express app started on port ' + port) \ No newline at end of file diff --git a/templates/calendarDescription.tmpl b/templates/calendarDescription.tmpl new file mode 100644 index 0000000..3a4a547 --- /dev/null +++ b/templates/calendarDescription.tmpl @@ -0,0 +1,22 @@ +Workorder ID: + %(biomedId) + +Customer: + %(client.name) (%(client.identifier)) + +Phone: + %(client.contacts[0].phone) + +Address: + %(client.address.street1) + %(client.address.street2) + %(client.address.city), %(client.address.state). %(client.address.zip) + +Reason: + %(workorder.reason) + +Status: + %(workorder.status) + +Remarks: + %(workorder.remarks) diff --git a/test.js b/test.js new file mode 100644 index 0000000..b8bc13d --- /dev/null +++ b/test.js @@ -0,0 +1,21 @@ +var env = process.env.NODE_ENV || 'development', + config = require('./config/config')[env], + fs = require('fs'), + calendar = require('./config/calendar')(config), + moment = require('moment'); + +var event = { + summary: 'Summary', + location: 'Location', + start: moment().hour(5).minute(30).toDate(), + end: moment().hour(6).minute(0).toDate(), + attendees: [ 'akirayasha@gmail.com' ] +}; + +console.log(event); + +calendar.scheduleEvent(event, function(err, res) { + console.log("Result"); + console.log(err); + console.log(res); +}); diff --git a/users.json b/users.json new file mode 100644 index 0000000..b6dca93 --- /dev/null +++ b/users.json @@ -0,0 +1,332 @@ +[ + { + "id": 65, + "email":"al.spell@atlanticbiomedical.com", + "deleted":false, + "groups":[ + "all", "biomed" + ], + "perms":[ + ], + "name":{ + "first":"Al", + "last":"Spell" + } + }, + { + "id": 7, + "email":"allan.cooke@atlanticbiomedical.com", + "deleted":false, + "groups":[ + "all", "biomed", "ice" + ], + "perms":[ + "messages.receive" + ], + "name":{ + "first":"Allan", + "last":"Cooke" + } + }, + { + "id": 50, + "email":"andrew.gibson@atlanticbiomedical.com", + "deleted":false, + "groups":[ + "all", "biomed" + ], + "perms":[ + ], + "name":{ + "first":"Andrew", + "last":"Gibson" + } + }, + { + "id": 11, + "email":"carl.endres@atlanticbiomedical.com", + "deleted":false, + "groups":[ + "all", "biomed", "ice", "other" + ], + "perms":[ + "system.login", + "messages.receive" + ], + "name":{ + "first":"Carl", + "last":"Endres" + } + }, + { + "email":"charles.timms@atlanticbiomedical.com", + "deleted":false, + "groups":[ + "all", "ice" + ], + "perms":[ + ], + "name":{ + "first":"Charles", + "last":"Timms" + } + }, + { + "id": [ 10, 27 ], + "email":"chris.endres@atlanticbiomedical.com", + "deleted":false, + "groups":[ + "all", "sales", "ice", "other" + ], + "perms":[ + "system.login", + "messages.receive" + ], + "name":{ + "first":"Chris", + "last":"Endres" + } + }, + { + "id": 47, + "email":"frank.insley@atlanticbiomedical.com", + "deleted":false, + "groups":[ + "all", "biomed" + ], + "perms":[ + ], + "name":{ + "first":"Frank", + "last":"Insley" + } + }, + { + "id": 64, + "email":"jake.jones@atlanticbiomedical.com", + "deleted":false, + "groups":[ + "all", "biomed", "ice" + ], + "perms":[ + ], + "name":{ + "first":"Jake", + "last":"Jones" + } + }, + { + "id": 63, + "email":"jay.mcclure@atlanticbiomedical.com", + "deleted":false, + "groups":[ + "all", "other" + ], + "perms":[ + ], + "name":{ + "first":"Jay", + "last":"Mcclure" + } + }, + { + "id": 31, + "email":"jon.hass@atlanticbiomedical.com", + "deleted":false, + "groups":[ + "all", "biomed" + ], + "perms":[ + ], + "name":{ + "first":"Jon", + "last":"Hass" + } + }, + { + "id": 59, + "email":"kelli.kirk@atlanticbiomedical.com", + "deleted":false, + "groups":[ + ], + "perms":[ + "system.login", + "messages.receive" + ], + "name":{ + "first":"Kevin", + "last":"Murphy" + } + }, + { + "email":"kelly.scalia@atlanticbiomedical.com", + "deleted":false, + "groups":[ + ], + "perms":[ + "system.login", + "messages.receive" + ], + "name":{ + "first":"Kelly", + "last":"Scalia" + } + }, + { + "id": 71, + "email":"kevin.murphy@atlanticbiomedical.com", + "deleted":false, + "groups":[ + "all", "sales", "other" + ], + "perms":[ + "messages.receive" + ], + "name":{ + "first":"Kevin", + "last":"Murphy" + } + }, + { + "id": 61, + "email":"matthew.kolenda@atlanticbiomedical.com", + "deleted":false, + "groups":[ + "all", "biomed" + ], + "perms":[ + ], + "name":{ + "first":"Matt", + "last":"Kolenda" + } + }, + { + "id": 34, + "email":"michael.bingel@atlanticbiomedical.com", + "deleted":false, + "groups":[ + "all", "biomed" + ], + "perms":[ + ], + "name":{ + "first":"Michael", + "last":"Bingle" + } + }, + { + "id": 67, + "email":"mike.szabo@atlanticbiomedical.com", + "deleted":false, + "groups":[ + "all", "biomed" + ], + "perms":[ + ], + "name":{ + "first":"Mike", + "last":"Szabo" + } + }, + { + "id": 60, + "email":"norma.thomas@atlanticbiomedical.com", + "deleted":false, + "groups":[ + ], + "perms":[ + "system.login" + ], + "name":{ + "first":"Norma", + "last":"Thomas" + } + }, + { + "email":"ron.malin@atlanticbiomedical.com", + "deleted":false, + "groups":[ + "all", "ice" + ], + "perms":[ + ], + "name":{ + "first":"Ron", + "last":"Malin" + } + }, + { + "id": 32, + "email":"ryan.foley@atlanticbiomedical.com", + "deleted":false, + "groups":[ + "all", "sales", "ice", "other" + ], + "perms":[ + "system.login", + "messages.receive" + ], + "name":{ + "first":"Ryan", + "last":"Foley" + } + }, + { + "email":"scott.sowards@atlanticbiomedical.com", + "deleted":false, + "groups":[ + "all", "ice" + ], + "perms":[ + ], + + "name":{ + "first":"Scott", + "last":"Sowards" + } + }, + { + "id": 36, + "email":"senate.fouty@atlanticbiomedical.com", + "deleted":false, + "groups":[ + "all", "ice" + ], + "perms":[ + ], + "name":{ + "first":"Senate", + "last":"Fouty" + } + }, + { + "id": 69, + "email":"michelle.conley@atlanticbiomedical.com", + "deleted":false, + "groups":[ + ], + "perms":[ + "system.login", + "messages.receive" + ], + "name":{ + "first":"Shelly", + "last":"Conley" + } + }, + { + "email":"tim.bingel@atlanticbiomedical.com", + "deleted":false, + "groups":[ + "all", "ice" + ], + "perms":[ + "system.login" + ], + "name":{ + "first":"Tim", + "last":"Bingel" + } + } +] \ No newline at end of file