commit d392a540e7426e83affb81910728ff087f6a6af8 Author: root Date: Mon May 6 03:38:29 2013 -0400 Initial Commit 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 0000000..ae2e540 Binary files /dev/null and b/public/fonts/Vegur-Bold.otf differ diff --git a/public/fonts/Vegur-Light.otf b/public/fonts/Vegur-Light.otf new file mode 100644 index 0000000..ce5b6f8 Binary files /dev/null and b/public/fonts/Vegur-Light.otf differ diff --git a/public/fonts/Vegur-Regular.otf b/public/fonts/Vegur-Regular.otf new file mode 100644 index 0000000..28978bc Binary files /dev/null and b/public/fonts/Vegur-Regular.otf differ diff --git a/public/img/bodyBg.png b/public/img/bodyBg.png new file mode 100644 index 0000000..50cbde3 Binary files /dev/null and b/public/img/bodyBg.png differ diff --git a/public/img/breadcrumb.png b/public/img/breadcrumb.png new file mode 100644 index 0000000..d346240 Binary files /dev/null and b/public/img/breadcrumb.png differ diff --git a/public/img/glyphicons-halflings-white.png b/public/img/glyphicons-halflings-white.png new file mode 100644 index 0000000..3bf6484 Binary files /dev/null and b/public/img/glyphicons-halflings-white.png differ diff --git a/public/img/glyphicons-halflings.png b/public/img/glyphicons-halflings.png new file mode 100644 index 0000000..a996999 Binary files /dev/null and b/public/img/glyphicons-halflings.png differ diff --git a/public/img/loader.gif b/public/img/loader.gif new file mode 100644 index 0000000..94ec074 Binary files /dev/null and b/public/img/loader.gif differ 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