├── .gitignore ├── Readme.md ├── app-cluster.js ├── app.js ├── app ├── conf │ ├── development.js │ ├── index.js │ ├── io.js │ ├── post-routing.js │ ├── pre-routing.js │ ├── production.js │ └── test.js ├── controllers │ ├── chat.js │ ├── error.js │ └── index.js ├── libs │ └── util.js ├── models │ ├── Counter.js │ ├── IP.js │ ├── Message.js │ └── Room.js └── views │ ├── chat.html │ ├── error │ └── 500.html │ ├── home.html │ └── layouts │ └── default.html ├── autoload ├── apps.js ├── autoload.js ├── controllers.js ├── index.js ├── inflection.js ├── libs.js ├── models │ ├── mongo.js │ └── redis.js ├── routes.js ├── session.js ├── setup.js ├── sockets.js └── util.js ├── config.js ├── config.json ├── loadApp.js ├── loadConfig.js ├── package.json ├── public ├── css │ ├── bootstrap.css │ └── styles.css ├── favicon.ico ├── images │ ├── bubble.png │ └── stripe.png ├── js │ ├── app.js │ └── socket.chat.js └── lib │ ├── jquery-1.7.1.min.js │ └── phpjs.js ├── scripts ├── cleanMongodb.js ├── cleanRedis.js └── createSecretRoom.js └── urls.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | nochan.json 4 | .project 5 | .idea 6 | *.iml 7 | npm-debug.log -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Express-chat 2 | 3 | Express chat is a node.js multi-server chat application. 4 | Messages are sent with Socket-IO and saved in MongoDB. 5 | 6 | 7 | ## Requirements 8 | * Node.js >= v0.10.0 9 | * MongoDB and Redis installed and running. 10 | 11 | ## Installation 12 | * git clone git://github.com/Tug/express-chat.git 13 | * cd express-chat 14 | * npm install . 15 | 16 | ## Configuring 17 | The config.json file will overwrite properties definied in config.js. Edit it to set your configuration properties such as database host, port, username, password, etc. 18 | 19 | 20 | ## Running 21 | * node app 22 | or 23 | * node app myconfig.js 24 | 25 | -------------------------------------------------------------------------------- /app-cluster.js: -------------------------------------------------------------------------------- 1 | var cluster = require('cluster'); 2 | var net = require('net'); 3 | 4 | var userconfig = require('./loadConfig'); 5 | var config = require('./config')(userconfig); 6 | 7 | if(cluster.isMaster) { 8 | var workers = []; 9 | var num = require('os').cpus().length; 10 | // Master will spawn `num` workers 11 | for (var i = 0; i < num; i++) { 12 | !function spawn(i) { 13 | workers[i] = cluster.fork(); 14 | // Restart worker on exit 15 | workers[i].on('exit', function() { 16 | console.error('Worker '+i+' died'); 17 | spawn(i); 18 | }); 19 | }(i); 20 | } 21 | var seed = ~~(Math.random() * 1e9); 22 | net.createServer(function(c) { 23 | // Get int31 hash of ip 24 | var worker, 25 | ipHash = hash(c.remoteAddress, seed); 26 | 27 | //TODO: do we need this, error handling should be done in the worker no? 28 | // https://github.com/indutny/sticky-session/pull/12 29 | // handle errors like ECONNRESET on the socket 30 | c.on('error', function (err) { 31 | console.error('sticky: socket error', err); 32 | }); 33 | 34 | // Pass connection to worker 35 | worker = workers[ipHash % workers.length]; 36 | worker.send('sticky:connection', c); 37 | 38 | }).listen(config.port, config.hostname, function(err) { 39 | if(err) { 40 | console.log(err.stack || err); 41 | return; 42 | } 43 | console.log("Server's master process started on port "+config.port); 44 | }); 45 | 46 | } else { 47 | 48 | require('./loadApp')(userconfig, function(err, app, model, config, closeApp) { 49 | if(err) { 50 | console.error(err.stack || err); 51 | return; 52 | } 53 | 54 | var server = app.server; 55 | 56 | // Worker process 57 | process.on('message', function(msg, socket) { 58 | if(msg !== 'sticky:connection') return; 59 | server.emit('connection', socket); 60 | }); 61 | 62 | // do not bind to port 63 | server.listen(); 64 | 65 | console.log("Worker ready"); 66 | 67 | }, false); 68 | 69 | } 70 | 71 | function hash(ip, seed) { 72 | var nums = (ip || '').split(/\./g); 73 | var hash = nums.reduce(function(r, num) { 74 | r += parseInt(num, 10); 75 | r %= 2147483648; 76 | r += (r << 10) 77 | r %= 2147483648; 78 | r ^= r >> 6; 79 | return r; 80 | }, seed); 81 | 82 | hash += hash << 3; 83 | hash %= 2147483648; 84 | hash ^= hash >> 11; 85 | hash += hash << 15; 86 | hash %= 2147483648; 87 | 88 | return hash >>> 0; 89 | } 90 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | var debug = require('debug')('nirror'); 5 | var userconfig = require('./loadConfig'); 6 | 7 | require('./loadApp')(userconfig, function(err, app, model, config) { 8 | if(err) { 9 | console.log(err.stack || err); 10 | return; 11 | } 12 | console.log('Server started on port %s in %s mode', 13 | app.server.address().port, 14 | app.express.settings.env); 15 | }); 16 | -------------------------------------------------------------------------------- /app/conf/development.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(app, model, callback) { 3 | 4 | app.middleware(app.controllers.error.serverError); 5 | 6 | callback(); 7 | 8 | }; 9 | -------------------------------------------------------------------------------- /app/conf/index.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | preRouting: require("./pre-routing"), 4 | postRouting: require("./post-routing") 5 | }; 6 | -------------------------------------------------------------------------------- /app/conf/io.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(app, model, callback) { 3 | 4 | var io = app.io; 5 | 6 | var key = app.config.session.key; 7 | 8 | io.use(function(socket, next) { 9 | var req = socket.request; 10 | app.cookieParser(req, {}, function (parseErr) { 11 | if(parseErr) { 12 | next("Could not parse cookie from headers. "+(parseErr && parseErr.message), false); 13 | return; 14 | } 15 | req.cookies = req.secureCookies || req.signedCookies || req.cookies; 16 | req.sessionID = req.cookies[key]; 17 | app.sessionStore.load(req.sessionID, function(storeErr, sess) { 18 | if(storeErr || !sess) { 19 | next("Session does not exist. "+(storeErr && storeErr.message), false); 20 | return; 21 | } 22 | req.session = sess; 23 | next(null, true); 24 | }); 25 | }); 26 | }); 27 | 28 | callback(); 29 | 30 | }; -------------------------------------------------------------------------------- /app/conf/post-routing.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(app, model, callback) { 3 | 4 | require("./"+app.config.settings.env)(app, model, callback); 5 | 6 | }; 7 | -------------------------------------------------------------------------------- /app/conf/pre-routing.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(app, model, callback) { 3 | 4 | // req middlewares 5 | app.middleware("limit", '100kb'); 6 | app.middleware("json"); 7 | app.middleware("urlencoded"); 8 | app.cookieParser = app.middleware("cookieParser"); 9 | app.session = app.middleware("session"); 10 | // app.sessionStore is loaded from `autoload` 11 | 12 | // res middlewares 13 | app.middleware("static"); 14 | app.middleware("favicon"); 15 | 16 | // give access to the various objects in the views 17 | app.express.use(function(req, res, next) { 18 | res.locals.routes = app.routes; 19 | res.locals.config = app.config; 20 | res.locals.libs = app.libs; 21 | next(); 22 | }); 23 | 24 | // other configuration modules 25 | app.libs.Step(function() { 26 | var group = this.group(); 27 | 28 | require("./io")(app, model, group()); 29 | 30 | }, callback); 31 | 32 | 33 | }; 34 | -------------------------------------------------------------------------------- /app/conf/production.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(app, model, callback) { 3 | 4 | app.middleware(app.controllers.error.serverError); 5 | 6 | callback(); 7 | 8 | }; 9 | -------------------------------------------------------------------------------- /app/conf/test.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(app, model, callback) { 3 | 4 | app.middleware("errorHandler", { dumpExceptions: true, showStack: true }); 5 | callback(); 6 | 7 | }; 8 | -------------------------------------------------------------------------------- /app/controllers/chat.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(app, model) { 3 | 4 | var Room = model.mongoose.model('Room') 5 | , Message = model.mongoose.model('Message') 6 | , Counter = model.mongoose.model('Counter') 7 | , IP = model.mongoose.model('IP') 8 | , Step = app.libs.Step; 9 | 10 | var MAX_MESSAGE_LEN = app.config.limits.maxMessageLength; 11 | var MAX_USERNAME_LEN = app.config.limits.maxUsernameLength; 12 | 13 | var chatIOUrl = app.routes.io("chat.socket"); 14 | 15 | var actions = {}; 16 | 17 | actions.index = function(req, res, next) { 18 | var roomid = req.params.roomid; 19 | Room.findOne({_id: roomid}, function(err, room) { 20 | if(err || !room) { 21 | res.redirect(app.routes.url("index.index")); 22 | return; 23 | } 24 | var roomName = room.title || "Room "+room.id; 25 | res.render('chat.html', { title: roomName }); 26 | }); 27 | }; 28 | 29 | function handleError(err) { 30 | if(err) { 31 | console.error(err.stack || err); 32 | } 33 | } 34 | 35 | /* 36 | * Always authorize 37 | */ 38 | actions.authorizeSocket = function(socket, next) { 39 | var req = socket.request; 40 | next(); 41 | }; 42 | 43 | actions.joinRoom = function(socket, roomid, lastMessageNum, callback) { 44 | callback = (typeof callback === "function") ? callback : handleError; 45 | if(typeof roomid !== "string" || roomid.length > 64) { 46 | return callback('roomid invalid'); 47 | } 48 | if(typeof lastMessageNum !== "number" || lastMessageNum < 0) { 49 | return callback('lastMessageNum invalid'); 50 | } 51 | 52 | var req = socket.request; 53 | socket.roomid = roomid; 54 | 55 | Step( 56 | function reloadSession() { 57 | req.session.reload(this); 58 | }, 59 | function userExists() { 60 | var next = this; 61 | // TODO: verify when this case happens 62 | // if the session of the user contains an object for this room 63 | // we reuse this object (the page was probably refreshed) 64 | // and try to force disconnect the previous socket 65 | if(req.session.rooms[roomid]) { 66 | var userinfo = req.session.rooms[roomid]; 67 | var username = userinfo.username; 68 | var sid = userinfo.sid; 69 | if(sid && sid != socket.id && app.io.sockets[sid]) { // disconnect previous socket 70 | app.io.sockets[sid].disconnect(); 71 | } 72 | next(null, username); 73 | } else { 74 | next(null, false); 75 | } 76 | }, 77 | function generateUsername(err, username) { 78 | var next = this; 79 | if(username) { 80 | next(null, username); 81 | return; 82 | } 83 | Counter.getNextValue(roomid, function(errc, value) { 84 | if(errc || value == null) { 85 | callback('server could not generate a username : '+errc.message); 86 | return; 87 | } 88 | if(value == 1) username = "OP"; 89 | else username = "Anonymous"+value; 90 | next(null, username); 91 | }); 92 | }, 93 | function sendUsername(err, username) { 94 | var next = this; 95 | req.session.rooms[roomid] = { 96 | username : username 97 | , sid : socket.id 98 | }; 99 | callback(null, username); 100 | socket.broadcast.to(roomid).json.emit('user joined', username); 101 | next(null, username); 102 | }, 103 | function addUser(err, username) { 104 | var next = this; 105 | Room.findByIdAndUpdate(roomid, {"$addToSet": {users: username}}, function(err) { 106 | next(); // next even if error 107 | }); 108 | }, 109 | function sendMessagesAndUsers() { 110 | var messageCallback = this.parallel(); 111 | var userCallback = this.parallel(); 112 | Message.allFrom(roomid, lastMessageNum+1, function(err, messages) { 113 | if(!err && messages) { 114 | messages.forEach(function(msg) { 115 | socket.emit("new message", msg.publicFields()); 116 | }); 117 | } 118 | messageCallback(); 119 | }); 120 | Room.findById(roomid, "users", function(err, room) { 121 | if(!err && room) { 122 | socket.emit('users', room.users); 123 | } 124 | userCallback(); 125 | }); 126 | }, 127 | function joinRoom() { 128 | var next = this; 129 | socket.join(roomid); 130 | req.session.save(next); 131 | }, 132 | function ready() { 133 | socket.emit('ready'); 134 | } 135 | ); 136 | }; 137 | 138 | actions.changeName = function(socket, data, callback) { 139 | callback = (typeof callback === "function") ? callback : handleError; 140 | if(typeof data !== "string" || data.length > MAX_USERNAME_LEN) { 141 | return callback('username invalid'); 142 | } 143 | var req = socket.request; 144 | var roomid = socket.roomid; 145 | var newname = data; 146 | Step( 147 | function reloadSession() { 148 | req.session.reload(this); 149 | }, 150 | // TODO: make $addToSet an $pull into one atomic operation 151 | // Forbidden by MongoDB for now : https://jira.mongodb.org/browse/SERVER-1050 152 | function addNewUsername(err) { 153 | var next = this; 154 | if(err) return next(err); 155 | if(!req.session.rooms || !req.session.rooms[roomid]) { 156 | callback('user info not found'); 157 | return; 158 | } 159 | var oldname = req.session.rooms[roomid].username; 160 | Room.update({ 161 | _id: roomid, 162 | users: { $ne: newname } 163 | }, { 164 | "$addToSet": { users: newname } 165 | }, function(err, updated) { 166 | if(err) return next(err); 167 | if(updated < 1) { 168 | callback('username exist'); 169 | } else { 170 | next(null, oldname); 171 | } 172 | }); 173 | }, 174 | function removeOldUsername(err, oldname) { 175 | var next = this; 176 | if(err) return next(err); 177 | Room.update({ _id: roomid }, { 178 | "$pull": { users: oldname } 179 | }, function(err) { 180 | next(null, oldname); 181 | }); 182 | }, 183 | function updateUserInfo(err, oldname) { 184 | var next = this; 185 | if(err) return next(err); 186 | req.session.rooms[roomid].username = newname; 187 | req.session.save(function(err) { 188 | next(err, oldname); 189 | }); 190 | }, 191 | function notifyUsernameChange(err, oldname) { 192 | var next = this; 193 | if(err) return next(err); 194 | var renameObj = {oldname: oldname, newname: newname}; 195 | socket.broadcast.to(roomid).json.emit('user renamed', renameObj); 196 | callback(null, renameObj); 197 | }, 198 | function handleError(err) { 199 | if(err) console.error(err); 200 | } 201 | ); 202 | }; 203 | 204 | actions.receiveMessage = function(socket, data, callback) { 205 | callback = (typeof callback === "function") ? callback : handleError; 206 | if(typeof data !== "string" || data.length > MAX_MESSAGE_LEN) { 207 | return callback("Invalid parameters"); 208 | } 209 | var req = socket.request; 210 | var roomid = socket.roomid; 211 | if(!req.session.rooms || !req.session.rooms[roomid]) { 212 | return callback("Not connected to this room"); 213 | } 214 | Step( 215 | function canChat() { 216 | var nextstep = this; 217 | IP.loadFromSocket(socket, function(err, ip) { 218 | if(!ip.canChat()) { 219 | socket.emit('new message', { username: "system", body: "No flood !"}); 220 | } else { 221 | ip.chat(nextstep); 222 | } 223 | }); 224 | }, 225 | function chat() { 226 | var userinfo = req.session.rooms[roomid]; 227 | var username = userinfo.username; 228 | var message = new Message({ 229 | roomid: roomid, 230 | username: username, 231 | body: data, 232 | userip: socket.handshake.address.address 233 | }); 234 | message.save(function(err) { 235 | if(err) console.log(err); 236 | app.io.of(chatIOUrl).in(roomid).emit('new message', message.publicFields()); 237 | callback(err); 238 | }); 239 | } 240 | ); 241 | }; 242 | 243 | actions.disconnect = function(socket, callback) { 244 | callback = (typeof callback === "function") ? callback : handleError; 245 | var req = socket.request; 246 | var roomid = socket.roomid; 247 | if(req.session.rooms && req.session.rooms[roomid]) { 248 | var username = req.session.rooms[roomid].username; 249 | Room.findByIdAndUpdate(roomid, {"$pull": {users: username}}, function(err, doc) { 250 | socket.broadcast.to(roomid).json.emit("user left", username); 251 | callback(err); 252 | }); 253 | } else { 254 | callback(); 255 | } 256 | }; 257 | 258 | actions.socket = function(socket) { 259 | var req = socket.request; 260 | 261 | socket.roomid = null; 262 | req.session.rooms = req.session.rooms || {}; 263 | 264 | socket.on('join room', actions.joinRoom.bind(null, socket)); 265 | socket.on('message', actions.receiveMessage.bind(null, socket)); 266 | socket.on('change name', actions.changeName.bind(null, socket)); 267 | socket.on('disconnect', actions.disconnect.bind(null, socket)); 268 | }; 269 | 270 | return actions; 271 | 272 | }; 273 | 274 | 275 | -------------------------------------------------------------------------------- /app/controllers/error.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(app, model) { 3 | 4 | var actions = {}; 5 | var env = app.config.settings.env; 6 | var debug = (app.config.settings.debug) 7 | ? console.log 8 | : require('debug')('controllers:error'); 9 | 10 | actions.notFound = function(req, res) { 11 | res.send(400, "Invalid request URI"); 12 | }; 13 | 14 | actions.notAuthenticated = function(req, res) { 15 | res.send(400, "Bad Authentication data"); 16 | }; 17 | 18 | actions.serverError = function(err, req, res, next) { 19 | if (err.status) res.statusCode = err.status; 20 | if (res.statusCode < 400) res.statusCode = 500; 21 | debug(((err && debug) ? err.stack : err.message) || err); 22 | var accept = req.headers.accept || ''; 23 | // html 24 | if (~accept.indexOf('html')) { 25 | res.render("error/500", { error: err }); 26 | // json 27 | } else if (~accept.indexOf('json')) { 28 | var json = JSON.stringify({ error: err.message }); 29 | res.setHeader('Content-Type', 'application/json'); 30 | res.end(json); 31 | // plain text 32 | } else { 33 | res.writeHead(res.statusCode, { 'Content-Type': 'text/plain' }); 34 | res.end(err.message); 35 | } 36 | }; 37 | 38 | return actions; 39 | 40 | }; 41 | 42 | -------------------------------------------------------------------------------- /app/controllers/index.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(app, model) { 3 | 4 | var Room = model.mongoose.model('Room'); 5 | 6 | var actions = {}; 7 | 8 | actions.index = function(req, res, next) { 9 | var query = Room.find() 10 | .where('ispublic').equals(true) 11 | .sort('-messageCount') 12 | .limit(100); 13 | query.exec(function(err, docs) { 14 | var rooms = docs.map(function(doc) { return doc.publicFields(); }); 15 | res.render('home', { rooms: rooms }); 16 | }); 17 | }; 18 | 19 | actions.createRoom = function(req, res, next) { 20 | var ispublic = !!req.body.ispublic; 21 | var title = req.body.title || null; 22 | if(title !== null && (typeof title !== 'string' || title.length > 100)) { 23 | next(new Error("wrong input name")); 24 | return; 25 | } 26 | var room = new Room({ispublic: ispublic, title: title}); 27 | room.save(function(err) { 28 | if(err) { 29 | console.log(err); 30 | next(err); 31 | } else { 32 | res.redirect(app.routes.url("chat.index", {"roomid": room.id })); 33 | } 34 | }); 35 | }; 36 | 37 | return actions; 38 | 39 | }; 40 | 41 | -------------------------------------------------------------------------------- /app/libs/util.js: -------------------------------------------------------------------------------- 1 | 2 | var crypto = require('crypto'); 3 | var Step = require('step'); 4 | 5 | (function(exports) { 6 | 7 | exports.randomString = function(strlen) { 8 | strlen = strlen || 10; 9 | var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz"; 10 | var randstr = ''; 11 | var randBuffer = crypto.randomBytes(strlen); 12 | for (var i=0; i MIN_MSG_INTERVAL; 96 | }; 97 | 98 | IP.methods.canUpload = function(bytes) { 99 | if(this.hasServedTime()) this.reset(); 100 | return (this.uploaded + bytes <= MAX_UP) && this.totalUp < MAX_TOTAL_UP; 101 | }; 102 | 103 | IP.methods.canDownload = function(bytes) { 104 | if(this.hasServedTime()) this.reset(); 105 | return (this.downloaded + bytes <= MAX_DOWN); // && this.totalDown < MAX_TOTAL_DOWN; 106 | }; 107 | 108 | IP.methods.hasServedTime = function() { 109 | return Date.now() - this.lastsaved.getTime() >= RELOAD_TIME; 110 | }; 111 | 112 | IP.methods.reset = function(next) { 113 | this.totalUp = 0; 114 | this.totalDown = 0; 115 | this.simulUp = 0; 116 | this.simulDown = 0; 117 | this.uploaded = 0; 118 | this.downloaded = 0; 119 | this.save(next); 120 | }; 121 | 122 | var IPModel = model.mongoose.model('IP', IP); 123 | return IPModel; 124 | } 125 | 126 | -------------------------------------------------------------------------------- /app/models/Message.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(app, model) { 3 | 4 | var mongoose = app.libs.mongoose; 5 | 6 | var Message = new mongoose.Schema({ 7 | roomid : { type: String, index: true } 8 | , num : { type: Number, index: true } 9 | , username : String 10 | , userip : String 11 | , body : String 12 | , date : { type: Date, default: Date.now } 13 | }, 14 | {safe: undefined}); 15 | 16 | Message.pre('save', function(next) { 17 | var RoomModel = model.mongoose.model('Room'); 18 | var self = this; 19 | RoomModel.findByIdAndUpdate(self.roomid, 20 | {$inc: {messageCount: 1}}, 21 | {select: 'messageCount'}, 22 | function(err, doc) { 23 | if(err || !doc) next(err || new Error('doc is null')); 24 | else { 25 | self.num = doc.messageCount; 26 | next(); 27 | } 28 | }); 29 | }); 30 | 31 | Message.statics.allFrom = function(roomid, messageNum, callback) { 32 | MessageModel 33 | .where('roomid', roomid) 34 | .where('num').gte(messageNum) 35 | .sort('num') 36 | .exec(callback); 37 | }; 38 | 39 | Message.methods.publicFields = function() { 40 | return { 41 | num : this.num 42 | , username : this.username 43 | , body : this.body 44 | , date : this.date 45 | }; 46 | }; 47 | 48 | var MessageModel = model.mongoose.model('Message', Message); 49 | return MessageModel; 50 | } 51 | 52 | -------------------------------------------------------------------------------- /app/models/Room.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(app, model) { 3 | 4 | var mongoose = app.libs.mongoose; 5 | var randomString = app.libs.util.randomString; 6 | var life = 2 * 24 * 60 * 60 * 1000; // 2 days 7 | 8 | var Room = new mongoose.Schema({ 9 | _id : { type: String, index: {unique: true}, default: randomString } 10 | , title : String 11 | , creationDate : { type: Date, default: Date.now } 12 | , deathDate : Date 13 | , messageCount : {type: Number, default: 0 } 14 | , ispublic : {type: Boolean, default: false, index: true} 15 | , users : [{ type: String }] 16 | }, 17 | {safe: undefined}); 18 | 19 | Room.statics.exist = function(roomid, callback) { 20 | RoomModel.count({_id: roomid}, callback); 21 | }; 22 | 23 | Room.methods.publicFields = function() { 24 | return { 25 | id : this._id, 26 | creationDate : this.creationDate, 27 | title : this.title, 28 | messageCount : this.messageCount 29 | }; 30 | }; 31 | 32 | Room.pre('save', function(next) { 33 | this.deathDate = new Date(Date.now()+life); 34 | next(); 35 | }); 36 | 37 | Room.post('remove', function() { 38 | var MessageModel = model.mongoose.model('Message'); 39 | var CounterModel = model.mongoose.model('Counter'); 40 | MessageModel.allFrom(this._id, 0, function(err, messages) { 41 | if(messages != null) { 42 | messages.forEach(function(msg) { 43 | msg.remove(); 44 | }); 45 | } 46 | }); 47 | CounterModel.reset(this._id, function() {}); 48 | }); 49 | 50 | var RoomModel = model.mongoose.model('Room', Room); 51 | return RoomModel; 52 | } 53 | 54 | -------------------------------------------------------------------------------- /app/views/chat.html: -------------------------------------------------------------------------------- 1 | <% layout('layouts/default') -%> 2 | 3 | 4 | 5 | 6 | 7 |
8 |
9 |
10 | Express-chat 11 | 16 |
17 | 18 | 19 |
20 |
21 |
22 |
23 | 24 |
25 |
26 | 29 |
30 |
31 |
32 |
    33 |
    34 |
    35 |
    36 | 37 |
    38 |
    39 |
    40 |
    41 |
    42 |
      43 |
      44 |
      45 | 46 |
      47 |
      48 |
      49 |
      50 | 51 |
      52 |

      © GPL licensed

      53 |
      54 | 55 |
      56 | 57 | -------------------------------------------------------------------------------- /app/views/error/500.html: -------------------------------------------------------------------------------- 1 |
      2 |
      3 |

      4 | <% if(typeof error !== 'undefined') { %> 5 | <%= error.toString() %> 6 | <% } %> 7 |

      8 |
      9 |
      -------------------------------------------------------------------------------- /app/views/home.html: -------------------------------------------------------------------------------- 1 | <% layout('layouts/default') -%> 2 | 3 |
      4 |
      5 | 8 |
      9 |
      10 |

      Create chat room

      11 |
      12 |
      13 |
      14 |
      15 | Enter Room information 16 |
      17 | 18 |
      19 | 20 |
      21 |
      22 |
      23 | 24 |
      25 | 26 |
      27 |
      28 |
      29 | 30 |
      31 |
      32 |
      33 |
      34 |
      35 |
      36 |
      37 |

      Public rooms

      38 |
      39 |
      40 |
      41 | 48 |
      49 |
      50 |
      51 | 52 |
      53 |

      © GPL licensed

      54 |
      55 | 56 |
      57 | -------------------------------------------------------------------------------- /app/views/layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Express-chat 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | <%-body -%> 23 | 24 | 25 | -------------------------------------------------------------------------------- /autoload/apps.js: -------------------------------------------------------------------------------- 1 | 2 | var path = require('path'); 3 | var http = require('http'); 4 | var express = require('express'); 5 | 6 | module.exports = function(app, model, appsConfig) { 7 | 8 | var apps = []; 9 | 10 | for(var appName in appsConfig) { 11 | var appConfig = appsConfig[appName]; 12 | var clientApp = {}; 13 | clientApp.name = appName; 14 | clientApp.config = appConfig; 15 | clientApp.express = express(); 16 | clientApp.server = http.createServer(clientApp.express); 17 | configureApp(clientApp, app.config); 18 | if(!app.controllers.apps || !app.controllers.apps[appName]) { 19 | throw new Error('Controller not found for app '+appName); 20 | } 21 | clientApp.express.use(app.controllers.apps[appName]); 22 | apps.push(clientApp); 23 | } 24 | 25 | // configure app that can only display a view 26 | function configureApp(app, config) { 27 | app.express.configure(function() { 28 | if(config.engines) { 29 | for(var key in config.engines) { 30 | app.express.engine(key, config.engines[key]); 31 | } 32 | } 33 | if(config.paths.views) { 34 | app.express.set('views', config.paths.views); 35 | } 36 | if(config.settings) { 37 | for(var key in config.settings) { 38 | app.express.set(key, config.settings[key]); 39 | } 40 | } 41 | app.config.assets = config.assets; 42 | app.middleware = function(middleware, options) { 43 | switch(typeof middleware) { 44 | case "string": 45 | switch(middleware) { 46 | case "static": 47 | for(var route in (config.paths.statics || {})) { 48 | var staticPath = config.paths.statics[route]; 49 | // if path is not absolute, make it so 50 | if(path.resolve(staticPath) !== staticPath) { 51 | staticPath = path.join(config.application_root, staticPath); 52 | } 53 | app.express.use(route, express.static(staticPath)); 54 | } 55 | break; 56 | default: 57 | app.express.use(express[middleware](options || config[middleware])); 58 | } 59 | break; 60 | default: 61 | app.express.use(middleware); 62 | } 63 | }; 64 | app.middleware('static'); 65 | require(path.join(config.paths.conf, "assets"))(app, model, function() {}); 66 | }); 67 | } 68 | 69 | return apps; 70 | }; 71 | -------------------------------------------------------------------------------- /autoload/autoload.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(app, model, directories) { 3 | var fs = require("fs") 4 | , path = require("path") 5 | , util = require("./util") 6 | , directories = (typeof directories === "string") ? [ directories ] : (directories || []); 7 | 8 | function isPathAbsolute(testPath) { 9 | return path.resolve(testPath)===testPath; 10 | } 11 | 12 | var files = util.flattenArray(directories.map(function(dir) { 13 | if(!isPathAbsolute(dir)) { 14 | dir = path.join(app.config.application_root, dir); 15 | } 16 | if(!fs.existsSync(dir)) return []; 17 | return fs.readdirSync(dir).map(function(filename){ 18 | return path.join(dir, filename); 19 | }); 20 | })); 21 | 22 | var objects = {}; 23 | 24 | files.forEach(function(f) { 25 | var name = path.basename(f).replace(/.js$/,''); 26 | objects[name] = require(f)(app, model); 27 | }); 28 | 29 | return objects; 30 | }; 31 | -------------------------------------------------------------------------------- /autoload/controllers.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | exports.draw = function(expressApp, routes, controllers) { 4 | for(var routename in routes) { 5 | require("./routes").draw(expressApp, routes[routename], controllers); 6 | } 7 | }; 8 | 9 | 10 | -------------------------------------------------------------------------------- /autoload/index.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = require('./setup').createApplication; 3 | 4 | -------------------------------------------------------------------------------- /autoload/inflection.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007 Ryan Schuft (ryan.schuft@gmail.com) 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | /* 24 | This code is based in part on the work done in Ruby to support 25 | infection as part of Ruby on Rails in the ActiveSupport's Inflector 26 | and Inflections classes. It was initally ported to Javascript by 27 | Ryan Schuft (ryan.schuft@gmail.com). 28 | 29 | The code is available at http://code.google.com/p/inflection-js/ 30 | 31 | The basic usage is: 32 | 1. Include this script on your web page. 33 | 2. Call functions on any String object in Javascript 34 | 35 | Currently implemented functions: 36 | 37 | String.pluralize(plural) == String 38 | renders a singular English language noun into its plural form 39 | normal results can be overridden by passing in an alternative 40 | 41 | String.singularize(singular) == String 42 | renders a plural English language noun into its singular form 43 | normal results can be overridden by passing in an alterative 44 | 45 | String.camelize(lowFirstLetter) == String 46 | renders a lower case underscored word into camel case 47 | the first letter of the result will be upper case unless you pass true 48 | also translates "/" into "::" (underscore does the opposite) 49 | 50 | String.underscore() == String 51 | renders a camel cased word into words seperated by underscores 52 | also translates "::" back into "/" (camelize does the opposite) 53 | 54 | String.humanize(lowFirstLetter) == String 55 | renders a lower case and underscored word into human readable form 56 | defaults to making the first letter capitalized unless you pass true 57 | 58 | String.capitalize() == String 59 | renders all characters to lower case and then makes the first upper 60 | 61 | String.dasherize() == String 62 | renders all underbars and spaces as dashes 63 | 64 | String.titleize() == String 65 | renders words into title casing (as for book titles) 66 | 67 | String.demodulize() == String 68 | renders class names that are prepended by modules into just the class 69 | 70 | String.tableize() == String 71 | renders camel cased singular words into their underscored plural form 72 | 73 | String.classify() == String 74 | renders an underscored plural word into its camel cased singular form 75 | 76 | String.foreign_key(dropIdUbar) == String 77 | renders a class name (camel cased singular noun) into a foreign key 78 | defaults to seperating the class from the id with an underbar unless 79 | you pass true 80 | 81 | String.ordinalize() == String 82 | renders all numbers found in the string into their sequence like "22nd" 83 | */ 84 | 85 | /* 86 | This function adds plurilization support to every String object 87 | Signature: 88 | String.pluralize(plural) == String 89 | Arguments: 90 | plural - String (optional) - overrides normal output with said String 91 | Returns: 92 | String - singular English language nouns are returned in plural form 93 | Examples: 94 | "person".pluralize() == "people" 95 | "octopus".pluralize() == "octopi" 96 | "Hat".pluralize() == "Hats" 97 | "person".pluralize("guys") == "guys" 98 | */ 99 | if(!String.prototype.pluralize) 100 | 101 | String.prototype.pluralize=function(plural) 102 | { 103 | var str=this; 104 | if(plural)str=plural; 105 | else 106 | { 107 | var uncountable=false; 108 | for(var x=0;!uncountable&&x 2) ? line[2] : "get"; 13 | var premiddlewares = (line.length > 3) ? line[3] : null; 14 | var postmiddlewares = (line.length > 4) ? line[4] : null; 15 | 16 | var actions = []; 17 | actions.push(premiddlewares); 18 | actions.push(handler); 19 | actions.push(postmiddlewares); 20 | 21 | routes[handler] = { 22 | url : url, 23 | method : method, 24 | actions : actions, 25 | }; 26 | }); 27 | return routes; 28 | }; 29 | 30 | exports.draw = function(app, route, controllers) { 31 | var actions = chainActions(route.actions, controllers); 32 | app[route.method].apply(app, [route.url, actions]); 33 | }; 34 | 35 | 36 | var chainActions = exports.chainActions = function(actions, controllers) { 37 | if(actions == null) { 38 | return []; 39 | } else if(actions.constructor === Array) { 40 | var args = []; 41 | actions.forEach(function(action) { 42 | args = args.concat(chainActions(action, controllers)); 43 | }); 44 | if(args.length == 1) return args[0]; 45 | else return args; 46 | } else if(typeof actions === "function") { 47 | return actions; 48 | } else { 49 | return getActionHandler(actions, controllers); 50 | } 51 | } 52 | 53 | var separator = "."; 54 | function getActionHandler(handler, controllers) { 55 | 56 | if(typeof handler === 'function') return handler; 57 | if(typeof handler !== 'string') return null; 58 | 59 | var parts = handler.split(separator), 60 | action = parts.pop(), 61 | controllerId = parts.join(separator); 62 | 63 | if(controllerId == null || action == null) { 64 | console.log("handler syntax error : ", handler); 65 | return null; 66 | } 67 | 68 | var controller = controllers[controllerId]; 69 | 70 | if(controller == null) { 71 | console.log("No controller found for controller : "+controllerId); 72 | return null; 73 | } 74 | 75 | var actionHandler = controller[action]; 76 | 77 | if(actionHandler == null) { 78 | console.log("No function matching "+action+" found in controller "+controllerId); 79 | return null; 80 | } 81 | 82 | return actionHandler; 83 | } 84 | 85 | 86 | -------------------------------------------------------------------------------- /autoload/session.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('express'); 3 | 4 | exports.autoload = function(model, sessionConfig) { 5 | 6 | var SessionStore = null; 7 | var options = null; 8 | 9 | switch(sessionConfig.engine) { 10 | case "mongo": 11 | SessionStore = require('connect-mongo')(express); 12 | options = { db: model.mongo }; 13 | break; 14 | case "redis": 15 | SessionStore = require('connect-redis')(express); 16 | options = { client: model.redis.createClient() }; 17 | break; 18 | default: 19 | SessionStore = express.session.MemoryStore; 20 | } 21 | 22 | var store = new SessionStore(options); 23 | 24 | return store; 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /autoload/setup.js: -------------------------------------------------------------------------------- 1 | 2 | var path = require('path') 3 | , express = require('express') 4 | , http = require('http') 5 | , debug = require('debug')('autoload') 6 | , Step = require('step') 7 | , autoload = require('./autoload'); 8 | 9 | module.exports.createApplication = function(app, model, config, callback) { 10 | 11 | Step( 12 | 13 | function loadLibraries() { 14 | 15 | debug('Loading system libraries...'); 16 | app.libs = app.libs || {}; 17 | app.libs.express = express; 18 | app.libs.Step = Step; 19 | app.libs.step = Step; 20 | app.libs.connect = require('connect'); 21 | app.libs.mongodb = require('mongodb'); 22 | app.libs.mongoose = require('mongoose'); 23 | app.libs.redis = require('redis'); 24 | app.libs['socket.io'] = require('socket.io'); 25 | 26 | debug('Loading project libraries...'); 27 | require('./libs.js').autoload(app.libs, config.paths.libs); 28 | 29 | this(); 30 | }, 31 | 32 | function createModel(err) { 33 | var next = this; 34 | if(err) return next(err); 35 | 36 | debug('Creating models...'); 37 | 38 | var group = this.group(); 39 | for(var dbName in config.database) { 40 | require('./models/'+dbName).autoload(app, model, config.database[dbName], group()); 41 | } 42 | }, 43 | 44 | function createApplication(err) { 45 | var next = this; 46 | if(err) return next(err); 47 | 48 | debug('Creating application...'); 49 | 50 | app.config = config; 51 | app.express = express(); 52 | app.server = http.createServer(app.express); 53 | 54 | app.sessionStore = require('./session.js').autoload(model, config.session); 55 | config.session.store = app.sessionStore; 56 | // convention so we don't need to set the secret key twice in the config 57 | config.cookieParser = config.session.secret; 58 | 59 | next(); 60 | }, 61 | 62 | function loadModels(err) { 63 | var next = this; 64 | if(err) return next(err); 65 | 66 | debug('Loading models...'); 67 | app.models = autoload(app, model, config.paths.models); 68 | 69 | next(); 70 | }, 71 | 72 | function readRoutes(err) { 73 | var next = this; 74 | if(err) return next(err); 75 | 76 | debug('Reading routes...'); 77 | var routeConfig = require(config.paths.routes)(app); 78 | config.urls = routeConfig.urls; 79 | config.ios = routeConfig.ios; 80 | 81 | app.routes = { 82 | urls : require('./routes.js').readUrls(config.urls) 83 | , ios : require('./routes.js').readUrls(config.ios) 84 | }; 85 | 86 | app.routes.url = require('./util.js').urlFromController(app.routes.urls); 87 | app.routes.io = require('./util.js').urlFromController(app.routes.ios); 88 | 89 | next(); 90 | }, 91 | 92 | function loadJobs(err) { 93 | var next = this; 94 | if(err) return next(err); 95 | 96 | debug('Loading jobs...'); 97 | app.jobs = autoload(app, model, config.paths.jobs); 98 | 99 | next(); 100 | }, 101 | 102 | function configureApplication(err) { 103 | var next = this; 104 | if(err) return next(err); 105 | 106 | if(config.socketio) { 107 | debug('Loading socket-io...'); 108 | app.io = require('./sockets.js').autoload(app, model, config); 109 | } 110 | 111 | // we don't use the path here (default path is "/" in express) 112 | // because the convention with express-autoload is to centralize routing definitions in a file 113 | // TODO: add a mean to configure namespace middlewares (thus removing special case for `static`) 114 | app.middleware = function(middleware, options, useIt) { 115 | if(typeof options === 'boolean') { 116 | useIt = options; 117 | options = undefined; 118 | } 119 | useIt = (useIt != undefined) ? useIt : true; 120 | var middlewareFunc; 121 | switch(typeof middleware) { 122 | case "string": 123 | switch(middleware) { 124 | case "static": 125 | for(var route in (config.paths.statics || {})) { 126 | var staticPath = config.paths.statics[route]; 127 | // if path is not absolute, make it so 128 | if(path.resolve(staticPath) !== staticPath) { 129 | staticPath = path.join(config.application_root, staticPath); 130 | } 131 | app.express.use(route, express.static(staticPath)); 132 | } 133 | return; 134 | default: 135 | middlewareFunc = express[middleware](options || config[middleware]); 136 | } 137 | break; 138 | default: 139 | middlewareFunc = middleware; 140 | } 141 | if(middlewareFunc && useIt) app.express.use(middlewareFunc); 142 | return middlewareFunc; 143 | }; 144 | 145 | debug('Configuring application...'); 146 | 147 | // general configuration 148 | app.express.configure(function() { 149 | if(config.engines) { 150 | for(var key in config.engines) { 151 | app.express.engine(key, config.engines[key]); 152 | } 153 | } 154 | if(config.paths.views) { 155 | app.express.set('views', config.paths.views); 156 | } 157 | if(config.settings) { 158 | for(var key in config.settings) { 159 | app.express.set(key, config.settings[key]); 160 | } 161 | } 162 | (config.middlewares || []).forEach(function(middleware) { 163 | app.middleware(middleware); 164 | }); 165 | }); 166 | 167 | // custom configuration 168 | if(config.paths.conf) { 169 | require(config.paths.conf).preRouting(app, model, next); 170 | } else { 171 | next(); 172 | } 173 | 174 | }, 175 | 176 | function loadControllers(err) { 177 | var next = this; 178 | if(err) return next(err); 179 | 180 | app.middleware(app.express.router); 181 | 182 | debug('Loading controllers...'); 183 | app.controllers = autoload(app, model, config.paths.controllers); 184 | 185 | debug('Mapping controllers...'); 186 | require('./controllers.js').draw(app.express, app.routes.urls, app.controllers); 187 | 188 | debug('Mapping sockets...'); 189 | require('./sockets.js').draw(app.io, app.routes.ios, app.controllers); 190 | 191 | next(); 192 | }, 193 | 194 | function loadApps(err) { 195 | var next = this; 196 | if(err) return next(err); 197 | 198 | debug('Loading client apps...'); 199 | app.apps = require('./apps.js')(app, model, config.apps); 200 | 201 | next(); 202 | }, 203 | 204 | function postRoutingConfiguration(err) { 205 | var next = this; 206 | if(err) return next(err); 207 | 208 | // custom configuration 209 | if(config.paths.conf) { 210 | require(config.paths.conf).postRouting(app, model, next); 211 | } else { 212 | next(); 213 | } 214 | }, 215 | 216 | function end(err) { 217 | callback(err); 218 | } 219 | 220 | ); 221 | 222 | }; 223 | 224 | -------------------------------------------------------------------------------- /autoload/sockets.js: -------------------------------------------------------------------------------- 1 | 2 | var chainActions = require("./routes").chainActions; 3 | 4 | exports.autoload = function(app, model, config) { 5 | var io = require('socket.io')(app.server, config.socketio.options); 6 | switch(config.socketio.adapter) { 7 | case "redis": 8 | io.adapter(require('socket.io-redis')(config.database.redis)); 9 | break; 10 | } 11 | return io; 12 | }; 13 | 14 | 15 | exports.draw = function(io, routes, controllers) { 16 | // DOES NOT support post controllers 17 | for(var routename in routes) { 18 | (function(route) { 19 | var appio = (route.url === '/') ? io : io.of(route.url); 20 | var actions = chainActions(route.actions, controllers); 21 | var main = actions.pop(); 22 | actions.forEach(function(action) { 23 | appio.use(action); 24 | }); 25 | appio.on(route.method, main); 26 | })(routes[routename]); 27 | } 28 | }; 29 | 30 | -------------------------------------------------------------------------------- /autoload/util.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports.urlFromController = function(routes) { 3 | return function(handler, keys) { 4 | var url = routes[handler].url; 5 | // hack copied from Connect router 6 | // TODO: support complex routes 7 | return url.replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, 8 | function(_, slash, format, key, capture, optional) { 9 | return slash + keys[key]; 10 | }); 11 | }; 12 | }; 13 | 14 | function mergeRecursive(obj1, obj2) { 15 | for(var p in obj2) { 16 | try { 17 | // Property in destination object set; update its value. 18 | if(obj2[p].constructor==Object) { 19 | obj1[p] = mergeRecursive(obj1[p], obj2[p]); 20 | } else { 21 | obj1[p] = obj2[p]; 22 | } 23 | } catch(e) { 24 | // Property in destination object not set; create it and set its value. 25 | obj1[p] = obj2[p]; 26 | } 27 | } 28 | return obj1; 29 | } 30 | module.exports.mergeRecursive = mergeRecursive; 31 | 32 | module.exports.camelize = function(string,lowFirstLetter) 33 | { 34 | var str=string.toLowerCase(); 35 | var str_path=str.split('/'); 36 | for(var i=0;i", 6 | "keywords": [ 7 | "express-chat", 8 | "chat", 9 | "cloud", 10 | "mongodb", 11 | "redis", 12 | "socket-io" 13 | ], 14 | "dependencies": { 15 | "connect": "2.6.0", 16 | "connect-mongo": "^0.4.1", 17 | "connect-redis": "1.4.4", 18 | "debug": "0.7.0", 19 | "ejs-locals": "~1.0.2", 20 | "express": "3.0.0", 21 | "express-session": "^1.9.1", 22 | "mongodb": "~1.3.19", 23 | "mongoose": "^3.8.18", 24 | "redis": "0.8.1", 25 | "socket.io": "^1.2.0", 26 | "socket.io-client": "^1.2.0", 27 | "socket.io-handshake": "0.0.10", 28 | "socket.io-redis": "^0.1.3", 29 | "step": "0.0.5" 30 | }, 31 | "main": "./app.js", 32 | "engines": { 33 | "node": ">= 0.10.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /public/css/bootstrap.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Xtra 1.4.0 3 | * 4 | * Copyright 2011 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | * Date: 20-11-2011 16:49:24,60 10 | */ 11 | /* Reset.less 12 | * Props to Eric Meyer (meyerweb.com) for his CSS reset file. We're using an adapted version here that cuts out some of the reset HTML elements we will never need here (i.e., dfn, samp, etc). 13 | * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */ 14 | html, body { 15 | margin: 0; 16 | padding: 0; 17 | } 18 | h1, 19 | h2, 20 | h3, 21 | h4, 22 | h5, 23 | h6, 24 | p, 25 | blockquote, 26 | pre, 27 | a, 28 | abbr, 29 | acronym, 30 | address, 31 | cite, 32 | code, 33 | del, 34 | dfn, 35 | em, 36 | img, 37 | q, 38 | s, 39 | samp, 40 | small, 41 | strike, 42 | strong, 43 | sub, 44 | sup, 45 | tt, 46 | var, 47 | dd, 48 | dl, 49 | dt, 50 | li, 51 | ol, 52 | ul, 53 | fieldset, 54 | form, 55 | label, 56 | legend, 57 | button, 58 | table, 59 | caption, 60 | tbody, 61 | tfoot, 62 | thead, 63 | tr, 64 | th, 65 | td { 66 | margin: 0; 67 | padding: 0; 68 | border: 0; 69 | font-weight: normal; 70 | font-style: normal; 71 | font-size: 100%; 72 | line-height: 1; 73 | font-family: inherit; 74 | } 75 | table { 76 | border-collapse: collapse; 77 | border-spacing: 0; 78 | } 79 | ol, ul { 80 | list-style: none; 81 | } 82 | q:before, 83 | q:after, 84 | blockquote:before, 85 | blockquote:after { 86 | content: ""; 87 | } 88 | html { 89 | overflow-y: scroll; 90 | font-size: 100%; 91 | -webkit-text-size-adjust: 100%; 92 | -ms-text-size-adjust: 100%; 93 | } 94 | a:focus { 95 | outline: thin dotted; 96 | } 97 | a:hover, a:active { 98 | outline: 0; 99 | } 100 | article, 101 | aside, 102 | details, 103 | figcaption, 104 | figure, 105 | footer, 106 | header, 107 | hgroup, 108 | nav, 109 | section { 110 | display: block; 111 | } 112 | audio, canvas, video { 113 | display: inline-block; 114 | *display: inline; 115 | *zoom: 1; 116 | } 117 | audio:not([controls]) { 118 | display: none; 119 | } 120 | sub, sup { 121 | font-size: 75%; 122 | line-height: 0; 123 | position: relative; 124 | vertical-align: baseline; 125 | } 126 | sup { 127 | top: -0.5em; 128 | } 129 | sub { 130 | bottom: -0.25em; 131 | } 132 | img { 133 | border: 0; 134 | -ms-interpolation-mode: bicubic; 135 | } 136 | button, 137 | input, 138 | select, 139 | textarea { 140 | font-size: 100%; 141 | margin: 0; 142 | vertical-align: baseline; 143 | *vertical-align: middle; 144 | } 145 | button, input { 146 | line-height: normal; 147 | *overflow: visible; 148 | } 149 | button::-moz-focus-inner, input::-moz-focus-inner { 150 | border: 0; 151 | padding: 0; 152 | } 153 | button, 154 | input[type="button"], 155 | input[type="reset"], 156 | input[type="submit"] { 157 | cursor: pointer; 158 | -webkit-appearance: button; 159 | } 160 | input[type="search"] { 161 | -webkit-appearance: textfield; 162 | -webkit-box-sizing: content-box; 163 | -moz-box-sizing: content-box; 164 | box-sizing: content-box; 165 | } 166 | input[type="search"]::-webkit-search-decoration { 167 | -webkit-appearance: none; 168 | } 169 | textarea { 170 | overflow: auto; 171 | vertical-align: top; 172 | } 173 | /* Variables.less 174 | * Variables to customize the look and feel of Bootstrap 175 | * ----------------------------------------------------- */ 176 | /* VariablesXtra.less 177 | * Variables to customize the look and feel of Bootstrap 178 | * ----------------------------------------------------- */ 179 | /* Mixins.less 180 | * Snippets of reusable CSS to develop faster and keep code readable 181 | * ----------------------------------------------------------------- */ 182 | /* MixinsXtra.less 183 | * Snippets of reusable CSS to develop faster and keep code readable 184 | * ----------------------------------------------------------------- */ 185 | /* 186 | * Scaffolding 187 | * Basic and global styles for generating a grid system, structural layout, and page templates 188 | * ------------------------------------------------------------------------------------------- */ 189 | body { 190 | background-color: #ffffff; 191 | margin: 0; 192 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 193 | font-size: 13px; 194 | font-weight: normal; 195 | line-height: 18px; 196 | color: #404040; 197 | } 198 | .container { 199 | width: 940px; 200 | margin-left: auto; 201 | margin-right: auto; 202 | zoom: 1; 203 | } 204 | .container:before, .container:after { 205 | display: table; 206 | content: ""; 207 | zoom: 1; 208 | } 209 | .container:after { 210 | clear: both; 211 | } 212 | .container-fluid { 213 | position: relative; 214 | min-width: 940px; 215 | padding-left: 20px; 216 | padding-right: 20px; 217 | zoom: 1; 218 | } 219 | .container-fluid:before, .container-fluid:after { 220 | display: table; 221 | content: ""; 222 | zoom: 1; 223 | } 224 | .container-fluid:after { 225 | clear: both; 226 | } 227 | .container-fluid > .sidebar { 228 | position: absolute; 229 | top: 0; 230 | left: 20px; 231 | width: 220px; 232 | } 233 | .container-fluid > .content { 234 | margin-left: 240px; 235 | } 236 | a { 237 | color: #0069d6; 238 | text-decoration: none; 239 | line-height: inherit; 240 | font-weight: inherit; 241 | } 242 | a:hover { 243 | color: #00438a; 244 | text-decoration: underline; 245 | } 246 | .pull-right { 247 | float: right; 248 | } 249 | .pull-left { 250 | float: left; 251 | } 252 | .hide { 253 | display: none; 254 | } 255 | .show { 256 | display: block; 257 | } 258 | .row { 259 | zoom: 1; 260 | margin-left: -20px; 261 | } 262 | .row:before, .row:after { 263 | display: table; 264 | content: ""; 265 | zoom: 1; 266 | } 267 | .row:after { 268 | clear: both; 269 | } 270 | .row > [class*="span"] { 271 | display: inline; 272 | float: left; 273 | margin-left: 20px; 274 | } 275 | .span1 { 276 | width: 40px; 277 | } 278 | .span2 { 279 | width: 100px; 280 | } 281 | .span3 { 282 | width: 160px; 283 | } 284 | .span4 { 285 | width: 220px; 286 | } 287 | .span5 { 288 | width: 280px; 289 | } 290 | .span6 { 291 | width: 340px; 292 | } 293 | .span7 { 294 | width: 400px; 295 | } 296 | .span8 { 297 | width: 460px; 298 | } 299 | .span9 { 300 | width: 520px; 301 | } 302 | .span10 { 303 | width: 580px; 304 | } 305 | .span11 { 306 | width: 640px; 307 | } 308 | .span12 { 309 | width: 700px; 310 | } 311 | .span13 { 312 | width: 760px; 313 | } 314 | .span14 { 315 | width: 820px; 316 | } 317 | .span15 { 318 | width: 880px; 319 | } 320 | .span16 { 321 | width: 940px; 322 | } 323 | .span17 { 324 | width: 1000px; 325 | } 326 | .span18 { 327 | width: 1060px; 328 | } 329 | .span19 { 330 | width: 1120px; 331 | } 332 | .span20 { 333 | width: 1180px; 334 | } 335 | .span21 { 336 | width: 1240px; 337 | } 338 | .span22 { 339 | width: 1300px; 340 | } 341 | .span23 { 342 | width: 1360px; 343 | } 344 | .span24 { 345 | width: 1420px; 346 | } 347 | .row > .offset1 { 348 | margin-left: 80px; 349 | } 350 | .row > .offset2 { 351 | margin-left: 140px; 352 | } 353 | .row > .offset3 { 354 | margin-left: 200px; 355 | } 356 | .row > .offset4 { 357 | margin-left: 260px; 358 | } 359 | .row > .offset5 { 360 | margin-left: 320px; 361 | } 362 | .row > .offset6 { 363 | margin-left: 380px; 364 | } 365 | .row > .offset7 { 366 | margin-left: 440px; 367 | } 368 | .row > .offset8 { 369 | margin-left: 500px; 370 | } 371 | .row > .offset9 { 372 | margin-left: 560px; 373 | } 374 | .row > .offset10 { 375 | margin-left: 620px; 376 | } 377 | .row > .offset11 { 378 | margin-left: 680px; 379 | } 380 | .row > .offset12 { 381 | margin-left: 740px; 382 | } 383 | .span-one-third { 384 | width: 300px; 385 | } 386 | .span-two-thirds { 387 | width: 620px; 388 | } 389 | .row > .offset-one-third { 390 | margin-left: 340px; 391 | } 392 | .row > .offset-two-thirds { 393 | margin-left: 660px; 394 | } 395 | /* Typography.less 396 | * Headings, body text, lists, code, and more for a versatile and durable typography system 397 | * ---------------------------------------------------------------------------------------- */ 398 | p { 399 | font-size: 13px; 400 | font-weight: normal; 401 | line-height: 18px; 402 | margin-bottom: 9px; 403 | } 404 | p small { 405 | font-size: 11px; 406 | color: #bfbfbf; 407 | } 408 | h1, 409 | h2, 410 | h3, 411 | h4, 412 | h5, 413 | h6 { 414 | font-weight: bold; 415 | color: #404040; 416 | } 417 | h1 small, 418 | h2 small, 419 | h3 small, 420 | h4 small, 421 | h5 small, 422 | h6 small { 423 | color: #bfbfbf; 424 | } 425 | h1 { 426 | margin-bottom: 18px; 427 | font-size: 30px; 428 | line-height: 36px; 429 | } 430 | h1 small { 431 | font-size: 18px; 432 | } 433 | h2 { 434 | font-size: 24px; 435 | line-height: 36px; 436 | } 437 | h2 small { 438 | font-size: 14px; 439 | } 440 | h3, 441 | h4, 442 | h5, 443 | h6 { 444 | line-height: 36px; 445 | } 446 | h3 { 447 | font-size: 18px; 448 | } 449 | h3 small { 450 | font-size: 14px; 451 | } 452 | h4 { 453 | font-size: 16px; 454 | } 455 | h4 small { 456 | font-size: 12px; 457 | } 458 | h5 { 459 | font-size: 14px; 460 | } 461 | h6 { 462 | font-size: 13px; 463 | color: #bfbfbf; 464 | text-transform: uppercase; 465 | } 466 | ul, ol { 467 | margin: 0 0 18px 25px; 468 | } 469 | ul ul, 470 | ul ol, 471 | ol ol, 472 | ol ul { 473 | margin-bottom: 0; 474 | } 475 | ul { 476 | list-style: disc; 477 | } 478 | ol { 479 | list-style: decimal; 480 | } 481 | li { 482 | line-height: 18px; 483 | color: #808080; 484 | } 485 | ul.unstyled { 486 | list-style: none; 487 | margin-left: 0; 488 | } 489 | dl { 490 | margin-bottom: 18px; 491 | } 492 | dl dt, dl dd { 493 | line-height: 18px; 494 | } 495 | dl dt { 496 | font-weight: bold; 497 | } 498 | dl dd { 499 | margin-left: 9px; 500 | } 501 | hr { 502 | margin: 20px 0 19px; 503 | border: 0; 504 | border-bottom: 1px solid #eee; 505 | } 506 | strong { 507 | font-style: inherit; 508 | font-weight: bold; 509 | } 510 | em { 511 | font-style: italic; 512 | font-weight: inherit; 513 | line-height: inherit; 514 | } 515 | .muted { 516 | color: #bfbfbf; 517 | } 518 | blockquote { 519 | margin-bottom: 18px; 520 | border-left: 5px solid #eee; 521 | padding-left: 15px; 522 | } 523 | blockquote p { 524 | font-size: 14px; 525 | font-weight: 300; 526 | line-height: 18px; 527 | margin-bottom: 0; 528 | } 529 | blockquote small { 530 | display: block; 531 | font-size: 12px; 532 | font-weight: 300; 533 | line-height: 18px; 534 | color: #bfbfbf; 535 | } 536 | blockquote small:before { 537 | content: '\2014 \00A0'; 538 | } 539 | address { 540 | display: block; 541 | line-height: 18px; 542 | margin-bottom: 18px; 543 | } 544 | code, pre { 545 | padding: 0 3px 2px; 546 | font-family: Monaco, Andale Mono, Courier New, monospace; 547 | font-size: 12px; 548 | -webkit-border-radius: 3px; 549 | -moz-border-radius: 3px; 550 | border-radius: 3px; 551 | } 552 | code { 553 | background-color: #fee9cc; 554 | color: rgba(0, 0, 0, 0.75); 555 | padding: 1px 3px; 556 | } 557 | pre { 558 | background-color: #f5f5f5; 559 | display: block; 560 | padding: 8.5px; 561 | margin: 0 0 18px; 562 | line-height: 18px; 563 | font-size: 12px; 564 | border: 1px solid #ccc; 565 | border: 1px solid rgba(0, 0, 0, 0.15); 566 | -webkit-border-radius: 3px; 567 | -moz-border-radius: 3px; 568 | border-radius: 3px; 569 | white-space: pre; 570 | white-space: pre-wrap; 571 | word-wrap: break-word; 572 | } 573 | /* TypographyXtra.less 574 | * Headings, body text, lists, code, and more for a versatile and durable typography system 575 | * ---------------------------------------------------------------------------------------- */ 576 | @font-face { 577 | font-family: "IconicStrokeRegular"; 578 | font-style: normal; 579 | font-weight: normal; 580 | src: url('fonts/iconic_stroke.eot'); 581 | src: local('@fontFamily'), url('fonts/iconic_stroke.eot?#iefix') format('embedded-opentype'), url('fonts/iconic_stroke.woff') format('woff'), url('fonts/iconic_stroke.ttf') format('truetype'), url('fonts/iconic_stroke.svg#IconicStrokeRegular') format('svg'), url("fonts/iconic_stroke.otf") format('opentype'); 582 | } 583 | /* Forms.less 584 | * Base styles for various input types, form layouts, and states 585 | * ------------------------------------------------------------- */ 586 | form { 587 | margin-bottom: 18px; 588 | } 589 | fieldset { 590 | margin-bottom: 18px; 591 | padding-top: 18px; 592 | } 593 | fieldset legend { 594 | display: block; 595 | padding-left: 150px; 596 | font-size: 19.5px; 597 | line-height: 1; 598 | color: #404040; 599 | *padding: 0 0 5px 145px; 600 | /* IE6-7 */ 601 | 602 | *line-height: 1.5; 603 | /* IE6-7 */ 604 | 605 | } 606 | form .clearfix { 607 | margin-bottom: 18px; 608 | zoom: 1; 609 | } 610 | form .clearfix:before, form .clearfix:after { 611 | display: table; 612 | content: ""; 613 | zoom: 1; 614 | } 615 | form .clearfix:after { 616 | clear: both; 617 | } 618 | label, 619 | input, 620 | select, 621 | textarea { 622 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 623 | font-size: 13px; 624 | font-weight: normal; 625 | line-height: normal; 626 | } 627 | label { 628 | padding-top: 6px; 629 | font-size: 13px; 630 | line-height: 18px; 631 | float: left; 632 | width: 130px; 633 | text-align: right; 634 | color: #404040; 635 | } 636 | form .input { 637 | margin-left: 150px; 638 | } 639 | input[type=checkbox], input[type=radio] { 640 | cursor: pointer; 641 | } 642 | input, 643 | textarea, 644 | select, 645 | .uneditable-input { 646 | display: inline-block; 647 | width: 210px; 648 | height: 18px; 649 | padding: 4px; 650 | font-size: 13px; 651 | line-height: 18px; 652 | color: #808080; 653 | border: 1px solid #ccc; 654 | -webkit-border-radius: 3px; 655 | -moz-border-radius: 3px; 656 | border-radius: 3px; 657 | } 658 | select { 659 | padding: initial; 660 | } 661 | input[type=checkbox], input[type=radio] { 662 | width: auto; 663 | height: auto; 664 | padding: 0; 665 | margin: 3px 0; 666 | *margin-top: 0; 667 | /* IE6-7 */ 668 | 669 | line-height: normal; 670 | border: none; 671 | } 672 | input[type=file] { 673 | background-color: #ffffff; 674 | padding: initial; 675 | border: initial; 676 | line-height: initial; 677 | -webkit-box-shadow: none; 678 | -moz-box-shadow: none; 679 | box-shadow: none; 680 | } 681 | input[type=button], input[type=reset], input[type=submit] { 682 | width: auto; 683 | height: auto; 684 | } 685 | select, input[type=file] { 686 | height: 27px; 687 | *height: auto; 688 | line-height: 27px; 689 | *margin-top: 4px; 690 | /* For IE7, add top margin to align select with labels */ 691 | 692 | } 693 | select[multiple] { 694 | height: inherit; 695 | background-color: #ffffff; 696 | } 697 | textarea { 698 | height: auto; 699 | } 700 | .uneditable-input { 701 | background-color: #ffffff; 702 | display: block; 703 | border-color: #eee; 704 | -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); 705 | -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); 706 | box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); 707 | cursor: not-allowed; 708 | } 709 | :-moz-placeholder { 710 | color: #bfbfbf; 711 | } 712 | ::-webkit-input-placeholder { 713 | color: #bfbfbf; 714 | } 715 | input, textarea { 716 | -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; 717 | -moz-transition: border linear 0.2s, box-shadow linear 0.2s; 718 | -ms-transition: border linear 0.2s, box-shadow linear 0.2s; 719 | -o-transition: border linear 0.2s, box-shadow linear 0.2s; 720 | transition: border linear 0.2s, box-shadow linear 0.2s; 721 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); 722 | -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); 723 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); 724 | } 725 | input:focus, textarea:focus { 726 | outline: 0; 727 | border-color: rgba(82, 168, 236, 0.8); 728 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); 729 | -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); 730 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); 731 | } 732 | input[type=file]:focus, input[type=checkbox]:focus, select:focus { 733 | -webkit-box-shadow: none; 734 | -moz-box-shadow: none; 735 | box-shadow: none; 736 | outline: 1px dotted #666; 737 | } 738 | form .clearfix.error > label, form .clearfix.error .help-block, form .clearfix.error .help-inline { 739 | color: #b94a48; 740 | } 741 | form .clearfix.error input, form .clearfix.error textarea { 742 | color: #b94a48; 743 | border-color: #ee5f5b; 744 | } 745 | form .clearfix.error input:focus, form .clearfix.error textarea:focus { 746 | border-color: #e9322d; 747 | -webkit-box-shadow: 0 0 6px #f8b9b7; 748 | -moz-box-shadow: 0 0 6px #f8b9b7; 749 | box-shadow: 0 0 6px #f8b9b7; 750 | } 751 | form .clearfix.error .input-prepend .add-on, form .clearfix.error .input-append .add-on { 752 | color: #b94a48; 753 | background-color: #fce6e6; 754 | border-color: #b94a48; 755 | } 756 | form .clearfix.warning > label, form .clearfix.warning .help-block, form .clearfix.warning .help-inline { 757 | color: #c09853; 758 | } 759 | form .clearfix.warning input, form .clearfix.warning textarea { 760 | color: #c09853; 761 | border-color: #ccae64; 762 | } 763 | form .clearfix.warning input:focus, form .clearfix.warning textarea:focus { 764 | border-color: #be9a3f; 765 | -webkit-box-shadow: 0 0 6px #e5d6b1; 766 | -moz-box-shadow: 0 0 6px #e5d6b1; 767 | box-shadow: 0 0 6px #e5d6b1; 768 | } 769 | form .clearfix.warning .input-prepend .add-on, form .clearfix.warning .input-append .add-on { 770 | color: #c09853; 771 | background-color: #d2b877; 772 | border-color: #c09853; 773 | } 774 | form .clearfix.success > label, form .clearfix.success .help-block, form .clearfix.success .help-inline { 775 | color: #468847; 776 | } 777 | form .clearfix.success input, form .clearfix.success textarea { 778 | color: #468847; 779 | border-color: #57a957; 780 | } 781 | form .clearfix.success input:focus, form .clearfix.success textarea:focus { 782 | border-color: #458845; 783 | -webkit-box-shadow: 0 0 6px #9acc9a; 784 | -moz-box-shadow: 0 0 6px #9acc9a; 785 | box-shadow: 0 0 6px #9acc9a; 786 | } 787 | form .clearfix.success .input-prepend .add-on, form .clearfix.success .input-append .add-on { 788 | color: #468847; 789 | background-color: #bcddbc; 790 | border-color: #468847; 791 | } 792 | .input-mini, 793 | input.mini, 794 | textarea.mini, 795 | select.mini { 796 | width: 60px; 797 | } 798 | .input-small, 799 | input.small, 800 | textarea.small, 801 | select.small { 802 | width: 90px; 803 | } 804 | .input-medium, 805 | input.medium, 806 | textarea.medium, 807 | select.medium { 808 | width: 150px; 809 | } 810 | .input-large, 811 | input.large, 812 | textarea.large, 813 | select.large { 814 | width: 210px; 815 | } 816 | .input-xlarge, 817 | input.xlarge, 818 | textarea.xlarge, 819 | select.xlarge { 820 | width: 270px; 821 | } 822 | .input-xxlarge, 823 | input.xxlarge, 824 | textarea.xxlarge, 825 | select.xxlarge { 826 | width: 530px; 827 | } 828 | textarea.xxlarge { 829 | overflow-y: auto; 830 | } 831 | input.span1, textarea.span1 { 832 | display: inline-block; 833 | float: none; 834 | width: 30px; 835 | margin-left: 0; 836 | } 837 | input.span2, textarea.span2 { 838 | display: inline-block; 839 | float: none; 840 | width: 90px; 841 | margin-left: 0; 842 | } 843 | input.span3, textarea.span3 { 844 | display: inline-block; 845 | float: none; 846 | width: 150px; 847 | margin-left: 0; 848 | } 849 | input.span4, textarea.span4 { 850 | display: inline-block; 851 | float: none; 852 | width: 210px; 853 | margin-left: 0; 854 | } 855 | input.span5, textarea.span5 { 856 | display: inline-block; 857 | float: none; 858 | width: 270px; 859 | margin-left: 0; 860 | } 861 | input.span6, textarea.span6 { 862 | display: inline-block; 863 | float: none; 864 | width: 330px; 865 | margin-left: 0; 866 | } 867 | input.span7, textarea.span7 { 868 | display: inline-block; 869 | float: none; 870 | width: 390px; 871 | margin-left: 0; 872 | } 873 | input.span8, textarea.span8 { 874 | display: inline-block; 875 | float: none; 876 | width: 450px; 877 | margin-left: 0; 878 | } 879 | input.span9, textarea.span9 { 880 | display: inline-block; 881 | float: none; 882 | width: 510px; 883 | margin-left: 0; 884 | } 885 | input.span10, textarea.span10 { 886 | display: inline-block; 887 | float: none; 888 | width: 570px; 889 | margin-left: 0; 890 | } 891 | input.span11, textarea.span11 { 892 | display: inline-block; 893 | float: none; 894 | width: 630px; 895 | margin-left: 0; 896 | } 897 | input.span12, textarea.span12 { 898 | display: inline-block; 899 | float: none; 900 | width: 690px; 901 | margin-left: 0; 902 | } 903 | input.span13, textarea.span13 { 904 | display: inline-block; 905 | float: none; 906 | width: 750px; 907 | margin-left: 0; 908 | } 909 | input.span14, textarea.span14 { 910 | display: inline-block; 911 | float: none; 912 | width: 810px; 913 | margin-left: 0; 914 | } 915 | input.span15, textarea.span15 { 916 | display: inline-block; 917 | float: none; 918 | width: 870px; 919 | margin-left: 0; 920 | } 921 | input.span16, textarea.span16 { 922 | display: inline-block; 923 | float: none; 924 | width: 930px; 925 | margin-left: 0; 926 | } 927 | input[disabled], 928 | select[disabled], 929 | textarea[disabled], 930 | input[readonly], 931 | select[readonly], 932 | textarea[readonly] { 933 | background-color: #f5f5f5; 934 | border-color: #ddd; 935 | cursor: not-allowed; 936 | } 937 | .actions { 938 | background: #f5f5f5; 939 | margin-top: 18px; 940 | margin-bottom: 18px; 941 | padding: 17px 20px 18px 150px; 942 | border-top: 1px solid #ddd; 943 | -webkit-border-radius: 0 0 3px 3px; 944 | -moz-border-radius: 0 0 3px 3px; 945 | border-radius: 0 0 3px 3px; 946 | } 947 | .actions .secondary-action { 948 | float: right; 949 | } 950 | .actions .secondary-action a { 951 | line-height: 30px; 952 | } 953 | .actions .secondary-action a:hover { 954 | text-decoration: underline; 955 | } 956 | .help-inline, .help-block { 957 | font-size: 13px; 958 | line-height: 18px; 959 | color: #bfbfbf; 960 | } 961 | .help-inline { 962 | padding-left: 5px; 963 | *position: relative; 964 | /* IE6-7 */ 965 | 966 | *top: -5px; 967 | /* IE6-7 */ 968 | 969 | } 970 | .help-block { 971 | display: block; 972 | max-width: 600px; 973 | } 974 | .inline-inputs { 975 | color: #808080; 976 | } 977 | .inline-inputs span { 978 | padding: 0 2px 0 1px; 979 | } 980 | .input-prepend input, .input-append input { 981 | -webkit-border-radius: 0 3px 3px 0; 982 | -moz-border-radius: 0 3px 3px 0; 983 | border-radius: 0 3px 3px 0; 984 | } 985 | .input-prepend .add-on, .input-append .add-on { 986 | position: relative; 987 | background: #f5f5f5; 988 | border: 1px solid #ccc; 989 | z-index: 2; 990 | float: left; 991 | display: block; 992 | width: auto; 993 | min-width: 16px; 994 | height: 18px; 995 | padding: 4px 4px 4px 5px; 996 | margin-right: -1px; 997 | font-weight: normal; 998 | line-height: 18px; 999 | color: #bfbfbf; 1000 | text-align: center; 1001 | text-shadow: 0 1px 0 #ffffff; 1002 | -webkit-border-radius: 3px 0 0 3px; 1003 | -moz-border-radius: 3px 0 0 3px; 1004 | border-radius: 3px 0 0 3px; 1005 | } 1006 | .input-prepend .active, .input-append .active { 1007 | background: #a9dba9; 1008 | border-color: #46a546; 1009 | } 1010 | .input-prepend .add-on { 1011 | *margin-top: 1px; 1012 | /* IE6-7 */ 1013 | 1014 | } 1015 | .input-append input { 1016 | float: left; 1017 | -webkit-border-radius: 3px 0 0 3px; 1018 | -moz-border-radius: 3px 0 0 3px; 1019 | border-radius: 3px 0 0 3px; 1020 | } 1021 | .input-append .add-on { 1022 | -webkit-border-radius: 0 3px 3px 0; 1023 | -moz-border-radius: 0 3px 3px 0; 1024 | border-radius: 0 3px 3px 0; 1025 | margin-right: 0; 1026 | margin-left: -1px; 1027 | } 1028 | .inputs-list { 1029 | margin: 0 0 5px; 1030 | width: 100%; 1031 | } 1032 | .inputs-list li { 1033 | display: block; 1034 | padding: 0; 1035 | width: 100%; 1036 | } 1037 | .inputs-list label { 1038 | display: block; 1039 | float: none; 1040 | width: auto; 1041 | padding: 0; 1042 | margin-left: 20px; 1043 | line-height: 18px; 1044 | text-align: left; 1045 | white-space: normal; 1046 | } 1047 | .inputs-list label strong { 1048 | color: #808080; 1049 | } 1050 | .inputs-list label small { 1051 | font-size: 11px; 1052 | font-weight: normal; 1053 | } 1054 | .inputs-list .inputs-list { 1055 | margin-left: 25px; 1056 | margin-bottom: 10px; 1057 | padding-top: 0; 1058 | } 1059 | .inputs-list:first-child { 1060 | padding-top: 6px; 1061 | } 1062 | .inputs-list li + li { 1063 | padding-top: 2px; 1064 | } 1065 | .inputs-list input[type=radio], .inputs-list input[type=checkbox] { 1066 | margin-bottom: 0; 1067 | margin-left: -20px; 1068 | float: left; 1069 | } 1070 | .form-stacked { 1071 | padding-left: 20px; 1072 | } 1073 | .form-stacked fieldset { 1074 | padding-top: 9px; 1075 | } 1076 | .form-stacked legend { 1077 | padding-left: 0; 1078 | } 1079 | .form-stacked label { 1080 | display: block; 1081 | float: none; 1082 | width: auto; 1083 | font-weight: bold; 1084 | text-align: left; 1085 | line-height: 20px; 1086 | padding-top: 0; 1087 | } 1088 | .form-stacked .clearfix { 1089 | margin-bottom: 9px; 1090 | } 1091 | .form-stacked .clearfix div.input { 1092 | margin-left: 0; 1093 | } 1094 | .form-stacked .inputs-list { 1095 | margin-bottom: 0; 1096 | } 1097 | .form-stacked .inputs-list li { 1098 | padding-top: 0; 1099 | } 1100 | .form-stacked .inputs-list li label { 1101 | font-weight: normal; 1102 | padding-top: 0; 1103 | } 1104 | .form-stacked div.clearfix.error { 1105 | padding-top: 10px; 1106 | padding-bottom: 10px; 1107 | padding-left: 10px; 1108 | margin-top: 0; 1109 | margin-left: -10px; 1110 | } 1111 | .form-stacked .actions { 1112 | margin-left: -20px; 1113 | padding-left: 20px; 1114 | } 1115 | /* 1116 | * Tables.less 1117 | * Tables for, you guessed it, tabular data 1118 | * ---------------------------------------- */ 1119 | table { 1120 | width: 100%; 1121 | margin-bottom: 18px; 1122 | padding: 0; 1123 | font-size: 13px; 1124 | border-collapse: collapse; 1125 | } 1126 | table th, table td { 1127 | padding: 10px 10px 9px; 1128 | line-height: 18px; 1129 | text-align: left; 1130 | } 1131 | table th { 1132 | padding-top: 9px; 1133 | font-weight: bold; 1134 | vertical-align: middle; 1135 | } 1136 | table td { 1137 | vertical-align: top; 1138 | border-top: 1px solid #ddd; 1139 | } 1140 | table tbody th { 1141 | border-top: 1px solid #ddd; 1142 | vertical-align: top; 1143 | } 1144 | .condensed-table th, .condensed-table td { 1145 | padding: 5px 5px 4px; 1146 | } 1147 | .bordered-table { 1148 | border: 1px solid #ddd; 1149 | border-collapse: separate; 1150 | *border-collapse: collapse; 1151 | /* IE7, collapse table to remove spacing */ 1152 | 1153 | -webkit-border-radius: 4px; 1154 | -moz-border-radius: 4px; 1155 | border-radius: 4px; 1156 | } 1157 | .bordered-table th + th, .bordered-table td + td, .bordered-table th + td { 1158 | border-left: 1px solid #ddd; 1159 | } 1160 | .bordered-table thead tr:first-child th:first-child, .bordered-table tbody tr:first-child td:first-child { 1161 | -webkit-border-radius: 4px 0 0 0; 1162 | -moz-border-radius: 4px 0 0 0; 1163 | border-radius: 4px 0 0 0; 1164 | } 1165 | .bordered-table thead tr:first-child th:last-child, .bordered-table tbody tr:first-child td:last-child { 1166 | -webkit-border-radius: 0 4px 0 0; 1167 | -moz-border-radius: 0 4px 0 0; 1168 | border-radius: 0 4px 0 0; 1169 | } 1170 | .bordered-table tbody tr:last-child td:first-child { 1171 | -webkit-border-radius: 0 0 0 4px; 1172 | -moz-border-radius: 0 0 0 4px; 1173 | border-radius: 0 0 0 4px; 1174 | } 1175 | .bordered-table tbody tr:last-child td:last-child { 1176 | -webkit-border-radius: 0 0 4px 0; 1177 | -moz-border-radius: 0 0 4px 0; 1178 | border-radius: 0 0 4px 0; 1179 | } 1180 | table .span1 { 1181 | width: 20px; 1182 | } 1183 | table .span2 { 1184 | width: 60px; 1185 | } 1186 | table .span3 { 1187 | width: 100px; 1188 | } 1189 | table .span4 { 1190 | width: 140px; 1191 | } 1192 | table .span5 { 1193 | width: 180px; 1194 | } 1195 | table .span6 { 1196 | width: 220px; 1197 | } 1198 | table .span7 { 1199 | width: 260px; 1200 | } 1201 | table .span8 { 1202 | width: 300px; 1203 | } 1204 | table .span9 { 1205 | width: 340px; 1206 | } 1207 | table .span10 { 1208 | width: 380px; 1209 | } 1210 | table .span11 { 1211 | width: 420px; 1212 | } 1213 | table .span12 { 1214 | width: 460px; 1215 | } 1216 | table .span13 { 1217 | width: 500px; 1218 | } 1219 | table .span14 { 1220 | width: 540px; 1221 | } 1222 | table .span15 { 1223 | width: 580px; 1224 | } 1225 | table .span16 { 1226 | width: 620px; 1227 | } 1228 | .zebra-striped tbody tr:nth-child(odd) td, .zebra-striped tbody tr:nth-child(odd) th { 1229 | background-color: #f9f9f9; 1230 | } 1231 | .zebra-striped tbody tr:hover td, .zebra-striped tbody tr:hover th { 1232 | background-color: #f5f5f5; 1233 | } 1234 | table .header { 1235 | cursor: pointer; 1236 | } 1237 | table .header:after { 1238 | content: ""; 1239 | float: right; 1240 | margin-top: 7px; 1241 | border-width: 0 4px 4px; 1242 | border-style: solid; 1243 | border-color: #000 transparent; 1244 | visibility: hidden; 1245 | } 1246 | table .headerSortUp, table .headerSortDown { 1247 | background-color: rgba(141, 192, 219, 0.25); 1248 | text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); 1249 | } 1250 | table .header:hover:after { 1251 | visibility: visible; 1252 | } 1253 | table .headerSortDown:after, table .headerSortDown:hover:after { 1254 | visibility: visible; 1255 | filter: alpha(opacity=60); 1256 | -khtml-opacity: 0.6; 1257 | -moz-opacity: 0.6; 1258 | opacity: 0.6; 1259 | } 1260 | table .headerSortUp:after { 1261 | border-bottom: none; 1262 | border-left: 4px solid transparent; 1263 | border-right: 4px solid transparent; 1264 | border-top: 4px solid #000; 1265 | visibility: visible; 1266 | -webkit-box-shadow: none; 1267 | -moz-box-shadow: none; 1268 | box-shadow: none; 1269 | filter: alpha(opacity=60); 1270 | -khtml-opacity: 0.6; 1271 | -moz-opacity: 0.6; 1272 | opacity: 0.6; 1273 | } 1274 | table .blue { 1275 | color: #049cdb; 1276 | border-bottom-color: #049cdb; 1277 | } 1278 | table .headerSortUp.blue, table .headerSortDown.blue { 1279 | background-color: #ade6fe; 1280 | } 1281 | table .green { 1282 | color: #46a546; 1283 | border-bottom-color: #46a546; 1284 | } 1285 | table .headerSortUp.green, table .headerSortDown.green { 1286 | background-color: #cdeacd; 1287 | } 1288 | table .red { 1289 | color: #9d261d; 1290 | border-bottom-color: #9d261d; 1291 | } 1292 | table .headerSortUp.red, table .headerSortDown.red { 1293 | background-color: #f4c8c5; 1294 | } 1295 | table .yellow { 1296 | color: #ffc40d; 1297 | border-bottom-color: #ffc40d; 1298 | } 1299 | table .headerSortUp.yellow, table .headerSortDown.yellow { 1300 | background-color: #fff6d9; 1301 | } 1302 | table .orange { 1303 | color: #f89406; 1304 | border-bottom-color: #f89406; 1305 | } 1306 | table .headerSortUp.orange, table .headerSortDown.orange { 1307 | background-color: #fee9cc; 1308 | } 1309 | table .purple { 1310 | color: #7a43b6; 1311 | border-bottom-color: #7a43b6; 1312 | } 1313 | table .headerSortUp.purple, table .headerSortDown.purple { 1314 | background-color: #e2d5f0; 1315 | } 1316 | /* Patterns.less 1317 | * Repeatable UI elements outside the base styles provided from the scaffolding 1318 | * ---------------------------------------------------------------------------- */ 1319 | .topbar { 1320 | height: 40px; 1321 | position: fixed; 1322 | top: 0; 1323 | left: 0; 1324 | right: 0; 1325 | z-index: 10000; 1326 | overflow: visible; 1327 | } 1328 | .topbar a { 1329 | color: #bfbfbf; 1330 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 1331 | } 1332 | .topbar h3 a:hover, .topbar .brand:hover, .topbar ul .active > a { 1333 | background-color: #333; 1334 | background-color: rgba(255, 255, 255, 0.05); 1335 | color: #ffffff; 1336 | text-decoration: none; 1337 | } 1338 | .topbar h3 { 1339 | position: relative; 1340 | } 1341 | .topbar h3 a, .topbar .brand { 1342 | float: left; 1343 | display: block; 1344 | padding: 8px 20px 12px; 1345 | margin-left: -20px; 1346 | color: #ffffff; 1347 | font-size: 20px; 1348 | font-weight: 200; 1349 | line-height: 1; 1350 | } 1351 | .topbar p { 1352 | margin: 0; 1353 | line-height: 40px; 1354 | } 1355 | .topbar p a:hover { 1356 | background-color: transparent; 1357 | color: #ffffff; 1358 | } 1359 | .topbar form { 1360 | float: left; 1361 | margin: 5px 0 0 0; 1362 | position: relative; 1363 | filter: alpha(opacity=100); 1364 | -khtml-opacity: 1; 1365 | -moz-opacity: 1; 1366 | opacity: 1; 1367 | } 1368 | .topbar form.pull-right { 1369 | float: right; 1370 | } 1371 | .topbar input { 1372 | background-color: #444; 1373 | background-color: rgba(255, 255, 255, 0.3); 1374 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 1375 | font-size: normal; 1376 | font-weight: 13px; 1377 | line-height: 1; 1378 | padding: 4px 9px; 1379 | color: #ffffff; 1380 | color: rgba(255, 255, 255, 0.75); 1381 | border: 1px solid #111; 1382 | -webkit-border-radius: 4px; 1383 | -moz-border-radius: 4px; 1384 | border-radius: 4px; 1385 | -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0px rgba(255, 255, 255, 0.25); 1386 | -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0px rgba(255, 255, 255, 0.25); 1387 | box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0px rgba(255, 255, 255, 0.25); 1388 | -webkit-transition: none; 1389 | -moz-transition: none; 1390 | -ms-transition: none; 1391 | -o-transition: none; 1392 | transition: none; 1393 | } 1394 | .topbar input:-moz-placeholder { 1395 | color: #e6e6e6; 1396 | } 1397 | .topbar input::-webkit-input-placeholder { 1398 | color: #e6e6e6; 1399 | } 1400 | .topbar input:hover { 1401 | background-color: #bfbfbf; 1402 | background-color: rgba(255, 255, 255, 0.5); 1403 | color: #ffffff; 1404 | } 1405 | .topbar input:focus, .topbar input.focused { 1406 | outline: 0; 1407 | background-color: #ffffff; 1408 | color: #404040; 1409 | text-shadow: 0 1px 0 #ffffff; 1410 | border: 0; 1411 | padding: 5px 10px; 1412 | -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); 1413 | -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); 1414 | box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); 1415 | } 1416 | .topbar-inner, .topbar .fill { 1417 | background-color: #222; 1418 | background-color: #222222; 1419 | background-repeat: repeat-x; 1420 | background-image: -khtml-gradient(linear, left top, left bottom, from(#333333), to(#222222)); 1421 | background-image: -moz-linear-gradient(top, #333333, #222222); 1422 | background-image: -ms-linear-gradient(top, #333333, #222222); 1423 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #333333), color-stop(100%, #222222)); 1424 | background-image: -webkit-linear-gradient(top, #333333, #222222); 1425 | background-image: -o-linear-gradient(top, #333333, #222222); 1426 | background-image: linear-gradient(top, #333333, #222222); 1427 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0); 1428 | -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); 1429 | -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); 1430 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); 1431 | } 1432 | .topbar div > ul, .nav { 1433 | display: block; 1434 | float: left; 1435 | margin: 0 10px 0 0; 1436 | position: relative; 1437 | left: 0; 1438 | } 1439 | .topbar div > ul > li, .nav > li { 1440 | display: block; 1441 | float: left; 1442 | } 1443 | .topbar div > ul a, .nav a { 1444 | display: block; 1445 | float: none; 1446 | padding: 10px 10px 11px; 1447 | line-height: 19px; 1448 | text-decoration: none; 1449 | } 1450 | .topbar div > ul a:hover, .nav a:hover { 1451 | color: #ffffff; 1452 | text-decoration: none; 1453 | } 1454 | .topbar div > ul .active > a, .nav .active > a { 1455 | background-color: #222; 1456 | background-color: rgba(0, 0, 0, 0.5); 1457 | } 1458 | .topbar div > ul.secondary-nav, .nav.secondary-nav { 1459 | float: right; 1460 | margin-left: 10px; 1461 | margin-right: 0; 1462 | } 1463 | .topbar div > ul.secondary-nav .menu-dropdown, 1464 | .nav.secondary-nav .menu-dropdown, 1465 | .topbar div > ul.secondary-nav .dropdown-menu, 1466 | .nav.secondary-nav .dropdown-menu { 1467 | right: 0; 1468 | border: 0; 1469 | } 1470 | .topbar div > ul a.menu:hover, 1471 | .nav a.menu:hover, 1472 | .topbar div > ul li.open .menu, 1473 | .nav li.open .menu, 1474 | .topbar div > ul .dropdown-toggle:hover, 1475 | .nav .dropdown-toggle:hover, 1476 | .topbar div > ul .dropdown.open .dropdown-toggle, 1477 | .nav .dropdown.open .dropdown-toggle { 1478 | background: #444; 1479 | background: rgba(255, 255, 255, 0.05); 1480 | } 1481 | .topbar div > ul .menu-dropdown, 1482 | .nav .menu-dropdown, 1483 | .topbar div > ul .dropdown-menu, 1484 | .nav .dropdown-menu { 1485 | background-color: #333; 1486 | } 1487 | .topbar div > ul .menu-dropdown a.menu, 1488 | .nav .menu-dropdown a.menu, 1489 | .topbar div > ul .dropdown-menu a.menu, 1490 | .nav .dropdown-menu a.menu, 1491 | .topbar div > ul .menu-dropdown .dropdown-toggle, 1492 | .nav .menu-dropdown .dropdown-toggle, 1493 | .topbar div > ul .dropdown-menu .dropdown-toggle, 1494 | .nav .dropdown-menu .dropdown-toggle { 1495 | color: #ffffff; 1496 | } 1497 | .topbar div > ul .menu-dropdown a.menu.open, 1498 | .nav .menu-dropdown a.menu.open, 1499 | .topbar div > ul .dropdown-menu a.menu.open, 1500 | .nav .dropdown-menu a.menu.open, 1501 | .topbar div > ul .menu-dropdown .dropdown-toggle.open, 1502 | .nav .menu-dropdown .dropdown-toggle.open, 1503 | .topbar div > ul .dropdown-menu .dropdown-toggle.open, 1504 | .nav .dropdown-menu .dropdown-toggle.open { 1505 | background: #444; 1506 | background: rgba(255, 255, 255, 0.05); 1507 | } 1508 | .topbar div > ul .menu-dropdown li a, 1509 | .nav .menu-dropdown li a, 1510 | .topbar div > ul .dropdown-menu li a, 1511 | .nav .dropdown-menu li a { 1512 | color: #999; 1513 | text-shadow: 0 1px 0 rgba(0, 0, 0, 0.5); 1514 | } 1515 | .topbar div > ul .menu-dropdown li a:hover, 1516 | .nav .menu-dropdown li a:hover, 1517 | .topbar div > ul .dropdown-menu li a:hover, 1518 | .nav .dropdown-menu li a:hover { 1519 | background-color: #191919; 1520 | background-repeat: repeat-x; 1521 | background-image: -khtml-gradient(linear, left top, left bottom, from(#292929), to(#191919)); 1522 | background-image: -moz-linear-gradient(top, #292929, #191919); 1523 | background-image: -ms-linear-gradient(top, #292929, #191919); 1524 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #292929), color-stop(100%, #191919)); 1525 | background-image: -webkit-linear-gradient(top, #292929, #191919); 1526 | background-image: -o-linear-gradient(top, #292929, #191919); 1527 | background-image: linear-gradient(top, #292929, #191919); 1528 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#292929', endColorstr='#191919', GradientType=0); 1529 | color: #ffffff; 1530 | } 1531 | .topbar div > ul .menu-dropdown .active a, 1532 | .nav .menu-dropdown .active a, 1533 | .topbar div > ul .dropdown-menu .active a, 1534 | .nav .dropdown-menu .active a { 1535 | color: #ffffff; 1536 | } 1537 | .topbar div > ul .menu-dropdown .divider, 1538 | .nav .menu-dropdown .divider, 1539 | .topbar div > ul .dropdown-menu .divider, 1540 | .nav .dropdown-menu .divider { 1541 | background-color: #222; 1542 | border-color: #444; 1543 | } 1544 | .topbar ul .menu-dropdown li a, .topbar ul .dropdown-menu li a { 1545 | padding: 4px 15px; 1546 | } 1547 | li.menu, .dropdown { 1548 | position: relative; 1549 | } 1550 | a.menu:after, .dropdown-toggle:after { 1551 | width: 0; 1552 | height: 0; 1553 | display: inline-block; 1554 | content: "↓"; 1555 | text-indent: -99999px; 1556 | vertical-align: top; 1557 | margin-top: 8px; 1558 | margin-left: 4px; 1559 | border-left: 4px solid transparent; 1560 | border-right: 4px solid transparent; 1561 | border-top: 4px solid #ffffff; 1562 | filter: alpha(opacity=50); 1563 | -khtml-opacity: 0.5; 1564 | -moz-opacity: 0.5; 1565 | opacity: 0.5; 1566 | } 1567 | .menu-dropdown, .dropdown-menu { 1568 | background-color: #ffffff; 1569 | float: left; 1570 | display: none; 1571 | position: absolute; 1572 | top: 40px; 1573 | z-index: 900; 1574 | min-width: 160px; 1575 | max-width: 220px; 1576 | _width: 160px; 1577 | margin-left: 0; 1578 | margin-right: 0; 1579 | padding: 6px 0; 1580 | zoom: 1; 1581 | border-color: #999; 1582 | border-color: rgba(0, 0, 0, 0.2); 1583 | border-style: solid; 1584 | border-width: 0 1px 1px; 1585 | -webkit-border-radius: 0 0 6px 6px; 1586 | -moz-border-radius: 0 0 6px 6px; 1587 | border-radius: 0 0 6px 6px; 1588 | -webkit-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); 1589 | -moz-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); 1590 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); 1591 | -webkit-background-clip: padding-box; 1592 | -moz-background-clip: padding-box; 1593 | background-clip: padding-box; 1594 | } 1595 | .menu-dropdown li, .dropdown-menu li { 1596 | float: none; 1597 | display: block; 1598 | background-color: none; 1599 | } 1600 | .menu-dropdown .divider, .dropdown-menu .divider { 1601 | height: 1px; 1602 | margin: 5px 0; 1603 | overflow: hidden; 1604 | background-color: #eee; 1605 | border-bottom: 1px solid #ffffff; 1606 | } 1607 | .topbar .dropdown-menu a, .dropdown-menu a { 1608 | display: block; 1609 | padding: 4px 15px; 1610 | clear: both; 1611 | font-weight: normal; 1612 | line-height: 18px; 1613 | color: #808080; 1614 | text-shadow: 0 1px 0 #ffffff; 1615 | } 1616 | .topbar .dropdown-menu a:hover, 1617 | .dropdown-menu a:hover, 1618 | .topbar .dropdown-menu a.hover, 1619 | .dropdown-menu a.hover { 1620 | background-color: #dddddd; 1621 | background-repeat: repeat-x; 1622 | background-image: -khtml-gradient(linear, left top, left bottom, from(#eeeeee), to(#dddddd)); 1623 | background-image: -moz-linear-gradient(top, #eeeeee, #dddddd); 1624 | background-image: -ms-linear-gradient(top, #eeeeee, #dddddd); 1625 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #eeeeee), color-stop(100%, #dddddd)); 1626 | background-image: -webkit-linear-gradient(top, #eeeeee, #dddddd); 1627 | background-image: -o-linear-gradient(top, #eeeeee, #dddddd); 1628 | background-image: linear-gradient(top, #eeeeee, #dddddd); 1629 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#dddddd', GradientType=0); 1630 | color: #404040; 1631 | text-decoration: none; 1632 | -webkit-box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.025), inset 0 -1px rgba(0, 0, 0, 0.025); 1633 | -moz-box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.025), inset 0 -1px rgba(0, 0, 0, 0.025); 1634 | box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.025), inset 0 -1px rgba(0, 0, 0, 0.025); 1635 | } 1636 | .open .menu, 1637 | .dropdown.open .menu, 1638 | .open .dropdown-toggle, 1639 | .dropdown.open .dropdown-toggle { 1640 | color: #ffffff; 1641 | background: #ccc; 1642 | background: rgba(0, 0, 0, 0.3); 1643 | } 1644 | .open .menu-dropdown, 1645 | .dropdown.open .menu-dropdown, 1646 | .open .dropdown-menu, 1647 | .dropdown.open .dropdown-menu { 1648 | display: block; 1649 | } 1650 | .tabs, .pills { 1651 | margin: 0 0 18px; 1652 | padding: 0; 1653 | list-style: none; 1654 | zoom: 1; 1655 | } 1656 | .tabs:before, 1657 | .pills:before, 1658 | .tabs:after, 1659 | .pills:after { 1660 | display: table; 1661 | content: ""; 1662 | zoom: 1; 1663 | } 1664 | .tabs:after, .pills:after { 1665 | clear: both; 1666 | } 1667 | .tabs > li, .pills > li { 1668 | float: left; 1669 | } 1670 | .tabs > li > a, .pills > li > a { 1671 | display: block; 1672 | } 1673 | .tabs { 1674 | border-color: #ddd; 1675 | border-style: solid; 1676 | border-width: 0 0 1px; 1677 | } 1678 | .tabs > li { 1679 | position: relative; 1680 | margin-bottom: -1px; 1681 | } 1682 | .tabs > li > a { 1683 | padding: 0 15px; 1684 | margin-right: 2px; 1685 | line-height: 34px; 1686 | border: 1px solid transparent; 1687 | -webkit-border-radius: 4px 4px 0 0; 1688 | -moz-border-radius: 4px 4px 0 0; 1689 | border-radius: 4px 4px 0 0; 1690 | } 1691 | .tabs > li > a:hover { 1692 | text-decoration: none; 1693 | background-color: #eee; 1694 | border-color: #eee #eee #ddd; 1695 | } 1696 | .tabs .active > a, .tabs .active > a:hover { 1697 | color: #808080; 1698 | background-color: #ffffff; 1699 | border: 1px solid #ddd; 1700 | border-bottom-color: transparent; 1701 | cursor: default; 1702 | } 1703 | .tabs .menu-dropdown, .tabs .dropdown-menu { 1704 | top: 35px; 1705 | border-width: 1px; 1706 | -webkit-border-radius: 0 6px 6px 6px; 1707 | -moz-border-radius: 0 6px 6px 6px; 1708 | border-radius: 0 6px 6px 6px; 1709 | } 1710 | .tabs a.menu:after, .tabs .dropdown-toggle:after { 1711 | border-top-color: #999; 1712 | margin-top: 15px; 1713 | margin-left: 5px; 1714 | } 1715 | .tabs li.open.menu .menu, .tabs .open.dropdown .dropdown-toggle { 1716 | border-color: #999; 1717 | } 1718 | .tabs li.open a.menu:after, .tabs .dropdown.open .dropdown-toggle:after { 1719 | border-top-color: #555; 1720 | } 1721 | .pills a { 1722 | margin: 5px 3px 5px 0; 1723 | padding: 0 15px; 1724 | line-height: 30px; 1725 | text-shadow: 0 1px 1px #ffffff; 1726 | -webkit-border-radius: 15px; 1727 | -moz-border-radius: 15px; 1728 | border-radius: 15px; 1729 | } 1730 | .pills a:hover { 1731 | color: #ffffff; 1732 | text-decoration: none; 1733 | text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25); 1734 | background-color: #00438a; 1735 | } 1736 | .pills .active a { 1737 | color: #ffffff; 1738 | text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25); 1739 | background-color: #0069d6; 1740 | } 1741 | .pills-vertical > li { 1742 | float: none; 1743 | } 1744 | .tab-content > .tab-pane, 1745 | .pill-content > .pill-pane, 1746 | .tab-content > div, 1747 | .pill-content > div { 1748 | display: none; 1749 | } 1750 | .tab-content > .active, .pill-content > .active { 1751 | display: block; 1752 | } 1753 | .breadcrumb { 1754 | padding: 7px 14px; 1755 | margin: 0 0 18px; 1756 | background-color: #f5f5f5; 1757 | background-repeat: repeat-x; 1758 | background-image: -khtml-gradient(linear, left top, left bottom, from(#ffffff), to(#f5f5f5)); 1759 | background-image: -moz-linear-gradient(top, #ffffff, #f5f5f5); 1760 | background-image: -ms-linear-gradient(top, #ffffff, #f5f5f5); 1761 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ffffff), color-stop(100%, #f5f5f5)); 1762 | background-image: -webkit-linear-gradient(top, #ffffff, #f5f5f5); 1763 | background-image: -o-linear-gradient(top, #ffffff, #f5f5f5); 1764 | background-image: linear-gradient(top, #ffffff, #f5f5f5); 1765 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0); 1766 | border: 1px solid #ddd; 1767 | -webkit-border-radius: 3px; 1768 | -moz-border-radius: 3px; 1769 | border-radius: 3px; 1770 | -webkit-box-shadow: inset 0 1px 0 #ffffff; 1771 | -moz-box-shadow: inset 0 1px 0 #ffffff; 1772 | box-shadow: inset 0 1px 0 #ffffff; 1773 | } 1774 | .breadcrumb li { 1775 | display: inline; 1776 | text-shadow: 0 1px 0 #ffffff; 1777 | } 1778 | .breadcrumb .divider { 1779 | padding: 0 5px; 1780 | color: #bfbfbf; 1781 | } 1782 | .breadcrumb .active a { 1783 | color: #404040; 1784 | } 1785 | .hero-unit { 1786 | background-color: #f5f5f5; 1787 | margin-bottom: 30px; 1788 | padding: 60px; 1789 | -webkit-border-radius: 6px; 1790 | -moz-border-radius: 6px; 1791 | border-radius: 6px; 1792 | } 1793 | .hero-unit h1 { 1794 | margin-bottom: 0; 1795 | font-size: 60px; 1796 | line-height: 1; 1797 | letter-spacing: -1px; 1798 | } 1799 | .hero-unit p { 1800 | font-size: 18px; 1801 | font-weight: 200; 1802 | line-height: 27px; 1803 | } 1804 | footer { 1805 | margin-top: 17px; 1806 | padding-top: 17px; 1807 | border-top: 1px solid #eee; 1808 | } 1809 | .page-header { 1810 | margin-bottom: 17px; 1811 | border-bottom: 1px solid #ddd; 1812 | -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); 1813 | -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); 1814 | box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); 1815 | } 1816 | .page-header h1 { 1817 | margin-bottom: 8px; 1818 | } 1819 | .btn.danger, 1820 | .alert-message.danger, 1821 | .btn.danger:hover, 1822 | .alert-message.danger:hover, 1823 | .btn.error, 1824 | .alert-message.error, 1825 | .btn.error:hover, 1826 | .alert-message.error:hover, 1827 | .btn.success, 1828 | .alert-message.success, 1829 | .btn.success:hover, 1830 | .alert-message.success:hover, 1831 | .btn.info, 1832 | .alert-message.info, 1833 | .btn.info:hover, 1834 | .alert-message.info:hover { 1835 | color: #ffffff; 1836 | } 1837 | .btn .close, .alert-message .close { 1838 | font-family: Arial, sans-serif; 1839 | line-height: 18px; 1840 | } 1841 | .btn.danger, 1842 | .alert-message.danger, 1843 | .btn.error, 1844 | .alert-message.error { 1845 | background-color: #c43c35; 1846 | background-repeat: repeat-x; 1847 | background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35)); 1848 | background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); 1849 | background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35); 1850 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35)); 1851 | background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); 1852 | background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); 1853 | background-image: linear-gradient(top, #ee5f5b, #c43c35); 1854 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0); 1855 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 1856 | border-color: #c43c35 #c43c35 #882a25; 1857 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 1858 | } 1859 | .btn.success, .alert-message.success { 1860 | background-color: #57a957; 1861 | background-repeat: repeat-x; 1862 | background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957)); 1863 | background-image: -moz-linear-gradient(top, #62c462, #57a957); 1864 | background-image: -ms-linear-gradient(top, #62c462, #57a957); 1865 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957)); 1866 | background-image: -webkit-linear-gradient(top, #62c462, #57a957); 1867 | background-image: -o-linear-gradient(top, #62c462, #57a957); 1868 | background-image: linear-gradient(top, #62c462, #57a957); 1869 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0); 1870 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 1871 | border-color: #57a957 #57a957 #3d773d; 1872 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 1873 | } 1874 | .btn.info, .alert-message.info { 1875 | background-color: #339bb9; 1876 | background-repeat: repeat-x; 1877 | background-image: -khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9)); 1878 | background-image: -moz-linear-gradient(top, #5bc0de, #339bb9); 1879 | background-image: -ms-linear-gradient(top, #5bc0de, #339bb9); 1880 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9)); 1881 | background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9); 1882 | background-image: -o-linear-gradient(top, #5bc0de, #339bb9); 1883 | background-image: linear-gradient(top, #5bc0de, #339bb9); 1884 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0); 1885 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 1886 | border-color: #339bb9 #339bb9 #22697d; 1887 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 1888 | } 1889 | .btn { 1890 | cursor: pointer; 1891 | display: inline-block; 1892 | background-color: #e6e6e6; 1893 | background-repeat: no-repeat; 1894 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6)); 1895 | background-image: -webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); 1896 | background-image: -moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6); 1897 | background-image: -ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); 1898 | background-image: -o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); 1899 | background-image: linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); 1900 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0); 1901 | padding: 5px 14px 6px; 1902 | text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); 1903 | color: #333; 1904 | font-size: 13px; 1905 | line-height: normal; 1906 | border: 1px solid #ccc; 1907 | border-bottom-color: #bbb; 1908 | -webkit-border-radius: 4px; 1909 | -moz-border-radius: 4px; 1910 | border-radius: 4px; 1911 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); 1912 | -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); 1913 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); 1914 | -webkit-transition: 0.1s linear all; 1915 | -moz-transition: 0.1s linear all; 1916 | -ms-transition: 0.1s linear all; 1917 | -o-transition: 0.1s linear all; 1918 | transition: 0.1s linear all; 1919 | } 1920 | .btn:hover { 1921 | background-position: 0 -15px; 1922 | color: #333; 1923 | text-decoration: none; 1924 | } 1925 | .btn:focus { 1926 | outline: 1px dotted #666; 1927 | } 1928 | .btn.primary { 1929 | color: #ffffff; 1930 | background-color: #0064cd; 1931 | background-repeat: repeat-x; 1932 | background-image: -khtml-gradient(linear, left top, left bottom, from(#049cdb), to(#0064cd)); 1933 | background-image: -moz-linear-gradient(top, #049cdb, #0064cd); 1934 | background-image: -ms-linear-gradient(top, #049cdb, #0064cd); 1935 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #049cdb), color-stop(100%, #0064cd)); 1936 | background-image: -webkit-linear-gradient(top, #049cdb, #0064cd); 1937 | background-image: -o-linear-gradient(top, #049cdb, #0064cd); 1938 | background-image: linear-gradient(top, #049cdb, #0064cd); 1939 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#049cdb', endColorstr='#0064cd', GradientType=0); 1940 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 1941 | border-color: #0064cd #0064cd #003f81; 1942 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 1943 | } 1944 | .btn.active, .btn:active { 1945 | -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); 1946 | -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); 1947 | box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); 1948 | } 1949 | .btn.disabled { 1950 | cursor: default; 1951 | background-image: none; 1952 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 1953 | filter: alpha(opacity=65); 1954 | -khtml-opacity: 0.65; 1955 | -moz-opacity: 0.65; 1956 | opacity: 0.65; 1957 | -webkit-box-shadow: none; 1958 | -moz-box-shadow: none; 1959 | box-shadow: none; 1960 | } 1961 | .btn[disabled] { 1962 | cursor: default; 1963 | background-image: none; 1964 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 1965 | filter: alpha(opacity=65); 1966 | -khtml-opacity: 0.65; 1967 | -moz-opacity: 0.65; 1968 | opacity: 0.65; 1969 | -webkit-box-shadow: none; 1970 | -moz-box-shadow: none; 1971 | box-shadow: none; 1972 | } 1973 | .btn.large { 1974 | font-size: 15px; 1975 | line-height: normal; 1976 | padding: 9px 14px 9px; 1977 | -webkit-border-radius: 6px; 1978 | -moz-border-radius: 6px; 1979 | border-radius: 6px; 1980 | } 1981 | .btn.small { 1982 | padding: 7px 9px 7px; 1983 | font-size: 11px; 1984 | } 1985 | :root .alert-message, :root .btn { 1986 | border-radius: 0 \0; 1987 | } 1988 | button.btn::-moz-focus-inner, input[type=submit].btn::-moz-focus-inner { 1989 | padding: 0; 1990 | border: 0; 1991 | } 1992 | .close { 1993 | float: right; 1994 | color: #000000; 1995 | font-size: 20px; 1996 | font-weight: bold; 1997 | line-height: 13.5px; 1998 | text-shadow: 0 1px 0 #ffffff; 1999 | filter: alpha(opacity=25); 2000 | -khtml-opacity: 0.25; 2001 | -moz-opacity: 0.25; 2002 | opacity: 0.25; 2003 | } 2004 | .close:hover { 2005 | color: #000000; 2006 | text-decoration: none; 2007 | filter: alpha(opacity=40); 2008 | -khtml-opacity: 0.4; 2009 | -moz-opacity: 0.4; 2010 | opacity: 0.4; 2011 | } 2012 | .alert-message { 2013 | position: relative; 2014 | padding: 7px 15px; 2015 | margin-bottom: 18px; 2016 | color: #404040; 2017 | background-color: #eedc94; 2018 | background-repeat: repeat-x; 2019 | background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1), to(#eedc94)); 2020 | background-image: -moz-linear-gradient(top, #fceec1, #eedc94); 2021 | background-image: -ms-linear-gradient(top, #fceec1, #eedc94); 2022 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1), color-stop(100%, #eedc94)); 2023 | background-image: -webkit-linear-gradient(top, #fceec1, #eedc94); 2024 | background-image: -o-linear-gradient(top, #fceec1, #eedc94); 2025 | background-image: linear-gradient(top, #fceec1, #eedc94); 2026 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1', endColorstr='#eedc94', GradientType=0); 2027 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 2028 | border-color: #eedc94 #eedc94 #e4c652; 2029 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 2030 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); 2031 | border-width: 1px; 2032 | border-style: solid; 2033 | -webkit-border-radius: 4px; 2034 | -moz-border-radius: 4px; 2035 | border-radius: 4px; 2036 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25); 2037 | -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25); 2038 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25); 2039 | } 2040 | .alert-message .close { 2041 | margin-top: 1px; 2042 | *margin-top: 0; 2043 | } 2044 | .alert-message a { 2045 | font-weight: bold; 2046 | color: #404040; 2047 | } 2048 | .alert-message.danger p a, 2049 | .alert-message.error p a, 2050 | .alert-message.success p a, 2051 | .alert-message.info p a { 2052 | color: #ffffff; 2053 | } 2054 | .alert-message h5 { 2055 | line-height: 18px; 2056 | } 2057 | .alert-message p { 2058 | margin-bottom: 0; 2059 | } 2060 | .alert-message div { 2061 | margin-top: 5px; 2062 | margin-bottom: 2px; 2063 | line-height: 28px; 2064 | } 2065 | .alert-message .btn { 2066 | -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.25); 2067 | -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.25); 2068 | box-shadow: 0 1px 0 rgba(255, 255, 255, 0.25); 2069 | } 2070 | .alert-message.block-message { 2071 | background-image: none; 2072 | background-color: #fdf5d9; 2073 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 2074 | padding: 14px; 2075 | border-color: #fceec1; 2076 | -webkit-box-shadow: none; 2077 | -moz-box-shadow: none; 2078 | box-shadow: none; 2079 | } 2080 | .alert-message.block-message ul, .alert-message.block-message p { 2081 | margin-right: 30px; 2082 | } 2083 | .alert-message.block-message ul { 2084 | margin-bottom: 0; 2085 | } 2086 | .alert-message.block-message li { 2087 | color: #404040; 2088 | } 2089 | .alert-message.block-message .alert-actions { 2090 | margin-top: 5px; 2091 | } 2092 | .alert-message.block-message.error, .alert-message.block-message.success, .alert-message.block-message.info { 2093 | color: #404040; 2094 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); 2095 | } 2096 | .alert-message.block-message.error { 2097 | background-color: #fddfde; 2098 | border-color: #fbc7c6; 2099 | } 2100 | .alert-message.block-message.success { 2101 | background-color: #d1eed1; 2102 | border-color: #bfe7bf; 2103 | } 2104 | .alert-message.block-message.info { 2105 | background-color: #ddf4fb; 2106 | border-color: #c6edf9; 2107 | } 2108 | .alert-message.block-message.danger p a, 2109 | .alert-message.block-message.error p a, 2110 | .alert-message.block-message.success p a, 2111 | .alert-message.block-message.info p a { 2112 | color: #404040; 2113 | } 2114 | .pagination { 2115 | height: 36px; 2116 | margin: 18px 0; 2117 | } 2118 | .pagination ul { 2119 | float: left; 2120 | margin: 0; 2121 | border: 1px solid #ddd; 2122 | border: 1px solid rgba(0, 0, 0, 0.15); 2123 | -webkit-border-radius: 3px; 2124 | -moz-border-radius: 3px; 2125 | border-radius: 3px; 2126 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); 2127 | -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); 2128 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); 2129 | } 2130 | .pagination li { 2131 | display: inline; 2132 | } 2133 | .pagination a { 2134 | float: left; 2135 | padding: 0 14px; 2136 | line-height: 34px; 2137 | border-right: 1px solid; 2138 | border-right-color: #ddd; 2139 | border-right-color: rgba(0, 0, 0, 0.15); 2140 | *border-right-color: #ddd; 2141 | /* IE6-7 */ 2142 | 2143 | text-decoration: none; 2144 | } 2145 | .pagination a:hover, .pagination .active a { 2146 | background-color: #c7eefe; 2147 | } 2148 | .pagination .disabled a, .pagination .disabled a:hover { 2149 | background-color: transparent; 2150 | color: #bfbfbf; 2151 | } 2152 | .pagination .next a { 2153 | border: 0; 2154 | } 2155 | .well { 2156 | background-color: #f5f5f5; 2157 | margin-bottom: 20px; 2158 | padding: 19px; 2159 | min-height: 20px; 2160 | border: 1px solid #eee; 2161 | border: 1px solid rgba(0, 0, 0, 0.05); 2162 | -webkit-border-radius: 4px; 2163 | -moz-border-radius: 4px; 2164 | border-radius: 4px; 2165 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); 2166 | -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); 2167 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); 2168 | } 2169 | .well blockquote { 2170 | border-color: #ddd; 2171 | border-color: rgba(0, 0, 0, 0.15); 2172 | } 2173 | .modal-backdrop { 2174 | background-color: #000000; 2175 | position: fixed; 2176 | top: 0; 2177 | left: 0; 2178 | right: 0; 2179 | bottom: 0; 2180 | z-index: 10000; 2181 | } 2182 | .modal-backdrop.fade { 2183 | opacity: 0; 2184 | } 2185 | .modal-backdrop, .modal-backdrop.fade.in { 2186 | filter: alpha(opacity=80); 2187 | -khtml-opacity: 0.8; 2188 | -moz-opacity: 0.8; 2189 | opacity: 0.8; 2190 | } 2191 | .modal { 2192 | position: fixed; 2193 | top: 50%; 2194 | left: 50%; 2195 | z-index: 11000; 2196 | width: 560px; 2197 | margin: -250px 0 0 -280px; 2198 | background-color: #ffffff; 2199 | border: 1px solid #999; 2200 | border: 1px solid rgba(0, 0, 0, 0.3); 2201 | *border: 1px solid #999; 2202 | /* IE6-7 */ 2203 | 2204 | -webkit-border-radius: 6px; 2205 | -moz-border-radius: 6px; 2206 | border-radius: 6px; 2207 | -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); 2208 | -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); 2209 | box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); 2210 | -webkit-background-clip: padding-box; 2211 | -moz-background-clip: padding-box; 2212 | background-clip: padding-box; 2213 | } 2214 | .modal .close { 2215 | margin-top: 7px; 2216 | } 2217 | .modal.fade { 2218 | -webkit-transition: opacity .3s linear, top .3s ease-out; 2219 | -moz-transition: opacity .3s linear, top .3s ease-out; 2220 | -ms-transition: opacity .3s linear, top .3s ease-out; 2221 | -o-transition: opacity .3s linear, top .3s ease-out; 2222 | transition: opacity .3s linear, top .3s ease-out; 2223 | top: -25%; 2224 | } 2225 | .modal.fade.in { 2226 | top: 50%; 2227 | } 2228 | .modal-header { 2229 | border-bottom: 1px solid #eee; 2230 | padding: 5px 15px; 2231 | } 2232 | .modal-body { 2233 | padding: 15px; 2234 | } 2235 | .modal-body form { 2236 | margin-bottom: 0; 2237 | } 2238 | .modal-footer { 2239 | background-color: #f5f5f5; 2240 | padding: 14px 15px 15px; 2241 | border-top: 1px solid #ddd; 2242 | -webkit-border-radius: 0 0 6px 6px; 2243 | -moz-border-radius: 0 0 6px 6px; 2244 | border-radius: 0 0 6px 6px; 2245 | -webkit-box-shadow: inset 0 1px 0 #ffffff; 2246 | -moz-box-shadow: inset 0 1px 0 #ffffff; 2247 | box-shadow: inset 0 1px 0 #ffffff; 2248 | zoom: 1; 2249 | margin-bottom: 0; 2250 | } 2251 | .modal-footer:before, .modal-footer:after { 2252 | display: table; 2253 | content: ""; 2254 | zoom: 1; 2255 | } 2256 | .modal-footer:after { 2257 | clear: both; 2258 | } 2259 | .modal-footer .btn { 2260 | float: right; 2261 | margin-left: 5px; 2262 | } 2263 | .modal .popover, .modal .twipsy { 2264 | z-index: 12000; 2265 | } 2266 | .twipsy { 2267 | display: block; 2268 | position: absolute; 2269 | visibility: visible; 2270 | padding: 5px; 2271 | font-size: 11px; 2272 | z-index: 1000; 2273 | filter: alpha(opacity=80); 2274 | -khtml-opacity: 0.8; 2275 | -moz-opacity: 0.8; 2276 | opacity: 0.8; 2277 | } 2278 | .twipsy.fade.in { 2279 | filter: alpha(opacity=80); 2280 | -khtml-opacity: 0.8; 2281 | -moz-opacity: 0.8; 2282 | opacity: 0.8; 2283 | } 2284 | .twipsy.above .twipsy-arrow { 2285 | bottom: 0; 2286 | left: 50%; 2287 | margin-left: -5px; 2288 | border-left: 5px solid transparent; 2289 | border-right: 5px solid transparent; 2290 | border-top: 5px solid #000000; 2291 | } 2292 | .twipsy.left .twipsy-arrow { 2293 | top: 50%; 2294 | right: 0; 2295 | margin-top: -5px; 2296 | border-top: 5px solid transparent; 2297 | border-bottom: 5px solid transparent; 2298 | border-left: 5px solid #000000; 2299 | } 2300 | .twipsy.below .twipsy-arrow { 2301 | top: 0; 2302 | left: 50%; 2303 | margin-left: -5px; 2304 | border-left: 5px solid transparent; 2305 | border-right: 5px solid transparent; 2306 | border-bottom: 5px solid #000000; 2307 | } 2308 | .twipsy.right .twipsy-arrow { 2309 | top: 50%; 2310 | left: 0; 2311 | margin-top: -5px; 2312 | border-top: 5px solid transparent; 2313 | border-bottom: 5px solid transparent; 2314 | border-right: 5px solid #000000; 2315 | } 2316 | .twipsy-inner { 2317 | padding: 3px 8px; 2318 | background-color: #000000; 2319 | color: white; 2320 | text-align: center; 2321 | max-width: 200px; 2322 | text-decoration: none; 2323 | -webkit-border-radius: 4px; 2324 | -moz-border-radius: 4px; 2325 | border-radius: 4px; 2326 | } 2327 | .twipsy-arrow { 2328 | position: absolute; 2329 | width: 0; 2330 | height: 0; 2331 | } 2332 | .popover { 2333 | position: absolute; 2334 | top: 0; 2335 | left: 0; 2336 | z-index: 1000; 2337 | padding: 5px; 2338 | display: none; 2339 | } 2340 | .popover.above .arrow { 2341 | bottom: 0; 2342 | left: 50%; 2343 | margin-left: -5px; 2344 | border-left: 5px solid transparent; 2345 | border-right: 5px solid transparent; 2346 | border-top: 5px solid #000000; 2347 | } 2348 | .popover.right .arrow { 2349 | top: 50%; 2350 | left: 0; 2351 | margin-top: -5px; 2352 | border-top: 5px solid transparent; 2353 | border-bottom: 5px solid transparent; 2354 | border-right: 5px solid #000000; 2355 | } 2356 | .popover.below .arrow { 2357 | top: 0; 2358 | left: 50%; 2359 | margin-left: -5px; 2360 | border-left: 5px solid transparent; 2361 | border-right: 5px solid transparent; 2362 | border-bottom: 5px solid #000000; 2363 | } 2364 | .popover.left .arrow { 2365 | top: 50%; 2366 | right: 0; 2367 | margin-top: -5px; 2368 | border-top: 5px solid transparent; 2369 | border-bottom: 5px solid transparent; 2370 | border-left: 5px solid #000000; 2371 | } 2372 | .popover .arrow { 2373 | position: absolute; 2374 | width: 0; 2375 | height: 0; 2376 | } 2377 | .popover .inner { 2378 | background: #000000; 2379 | background: rgba(0, 0, 0, 0.8); 2380 | padding: 3px; 2381 | overflow: hidden; 2382 | width: 280px; 2383 | -webkit-border-radius: 6px; 2384 | -moz-border-radius: 6px; 2385 | border-radius: 6px; 2386 | -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); 2387 | -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); 2388 | box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); 2389 | } 2390 | .popover .title { 2391 | background-color: #f5f5f5; 2392 | padding: 9px 15px; 2393 | line-height: 1; 2394 | -webkit-border-radius: 3px 3px 0 0; 2395 | -moz-border-radius: 3px 3px 0 0; 2396 | border-radius: 3px 3px 0 0; 2397 | border-bottom: 1px solid #eee; 2398 | } 2399 | .popover .content { 2400 | background-color: #ffffff; 2401 | padding: 14px; 2402 | -webkit-border-radius: 0 0 3px 3px; 2403 | -moz-border-radius: 0 0 3px 3px; 2404 | border-radius: 0 0 3px 3px; 2405 | -webkit-background-clip: padding-box; 2406 | -moz-background-clip: padding-box; 2407 | background-clip: padding-box; 2408 | } 2409 | .popover .content p, .popover .content ul, .popover .content ol { 2410 | margin-bottom: 0; 2411 | } 2412 | .fade { 2413 | -webkit-transition: opacity 0.15s linear; 2414 | -moz-transition: opacity 0.15s linear; 2415 | -ms-transition: opacity 0.15s linear; 2416 | -o-transition: opacity 0.15s linear; 2417 | transition: opacity 0.15s linear; 2418 | opacity: 0; 2419 | } 2420 | .fade.in { 2421 | opacity: 1; 2422 | } 2423 | .label { 2424 | padding: 1px 3px 2px; 2425 | font-size: 9.75px; 2426 | font-weight: bold; 2427 | color: #ffffff; 2428 | text-transform: uppercase; 2429 | white-space: nowrap; 2430 | background-color: #bfbfbf; 2431 | -webkit-border-radius: 3px; 2432 | -moz-border-radius: 3px; 2433 | border-radius: 3px; 2434 | } 2435 | .label.important { 2436 | background-color: #c43c35; 2437 | } 2438 | .label.warning { 2439 | background-color: #f89406; 2440 | } 2441 | .label.success { 2442 | background-color: #46a546; 2443 | } 2444 | .label.notice { 2445 | background-color: #62cffc; 2446 | } 2447 | .media-grid { 2448 | margin-left: -20px; 2449 | margin-bottom: 0; 2450 | zoom: 1; 2451 | } 2452 | .media-grid:before, .media-grid:after { 2453 | display: table; 2454 | content: ""; 2455 | zoom: 1; 2456 | } 2457 | .media-grid:after { 2458 | clear: both; 2459 | } 2460 | .media-grid li { 2461 | display: inline; 2462 | } 2463 | .media-grid a { 2464 | float: left; 2465 | padding: 4px; 2466 | margin: 0 0 18px 20px; 2467 | border: 1px solid #ddd; 2468 | -webkit-border-radius: 4px; 2469 | -moz-border-radius: 4px; 2470 | border-radius: 4px; 2471 | -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); 2472 | -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); 2473 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); 2474 | } 2475 | .media-grid a img { 2476 | display: block; 2477 | } 2478 | .media-grid a:hover { 2479 | border-color: #0069d6; 2480 | -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); 2481 | -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); 2482 | box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); 2483 | } 2484 | /* PatternsXtra.less 2485 | * Repeatable UI elements outside the base styles provided from the scaffolding 2486 | * ---------------------------------------------------------------------------- */ 2487 | .btn.icon:before { 2488 | font-family: "IconicStrokeRegular"; 2489 | font-size: 14px; 2490 | line-height: normal; 2491 | color: #000000; 2492 | content: ""; 2493 | position: relative; 2494 | float: left; 2495 | margin: 0 0.55em 0 -0.25em; 2496 | } 2497 | .btn.icon.alternative:before { 2498 | color: #ffffff; 2499 | } 2500 | .btn.icon.right:before { 2501 | float: right; 2502 | margin: 0 -0.25em 0 0.5em; 2503 | } 2504 | .btn.icon.large:before { 2505 | font-size: 16px; 2506 | } 2507 | .btn.icon.small:before { 2508 | font-size: 13px; 2509 | } 2510 | .btn.icon.arrowup:before { 2511 | content: "0"; 2512 | } 2513 | .btn.icon.arrowdown:before { 2514 | content: "1"; 2515 | } 2516 | .btn.icon.arrowleft:before { 2517 | content: "2"; 2518 | } 2519 | .btn.icon.arrowright:before { 2520 | content: "3"; 2521 | } 2522 | .btn.icon.approve:before { 2523 | content: "4"; 2524 | } 2525 | .btn.icon.remove:before { 2526 | content: "5"; 2527 | } 2528 | .btn.icon.log:before { 2529 | content: "6"; 2530 | } 2531 | .btn.icon.calendar:before { 2532 | content: "7"; 2533 | } 2534 | .btn.icon.chat:before { 2535 | content: "8"; 2536 | } 2537 | .btn.icon.clock:before { 2538 | content: "9"; 2539 | } 2540 | .btn.icon.settings:before { 2541 | content: "a"; 2542 | } 2543 | .btn.icon.comment:before { 2544 | content: "b"; 2545 | } 2546 | .btn.icon.fork:before { 2547 | content: "c"; 2548 | } 2549 | .btn.icon.like:before { 2550 | content: "d"; 2551 | } 2552 | .btn.icon.home:before { 2553 | content: "e"; 2554 | } 2555 | .btn.icon.key:before { 2556 | content: "f"; 2557 | } 2558 | .btn.icon.lock:before { 2559 | content: "g"; 2560 | } 2561 | .btn.icon.unlock:before { 2562 | content: "h"; 2563 | } 2564 | .btn.icon.loop:before { 2565 | content: "i"; 2566 | } 2567 | .btn.icon.search:before { 2568 | content: "j"; 2569 | } 2570 | .btn.icon.mail:before { 2571 | content: "k"; 2572 | } 2573 | .btn.icon.move:before { 2574 | content: "l"; 2575 | } 2576 | .btn.icon.edit:before { 2577 | content: "m"; 2578 | } 2579 | .btn.icon.pin:before { 2580 | content: "n"; 2581 | } 2582 | .btn.icon.add:before { 2583 | content: "o"; 2584 | } 2585 | .btn.icon.reload:before { 2586 | content: "p"; 2587 | } 2588 | .btn.icon.rss:before { 2589 | content: "q"; 2590 | } 2591 | .btn.icon.tag:before { 2592 | content: "r"; 2593 | } 2594 | .btn.icon.trash:before { 2595 | content: "s"; 2596 | } 2597 | .btn.icon.favorite:before { 2598 | content: "t"; 2599 | } 2600 | .btn.icon.user:before { 2601 | content: "u"; 2602 | } 2603 | .btn.icon.minus:before { 2604 | content: "v"; 2605 | } 2606 | .btn.icon.upload:before { 2607 | content: "w"; 2608 | } 2609 | .btn.icon.transfer:before { 2610 | content: "x"; 2611 | } 2612 | .btn.icon.stop:before { 2613 | content: "y"; 2614 | } 2615 | .btn.icon.movie:before { 2616 | content: "z"; 2617 | } 2618 | .btn.icon.pause:before { 2619 | content: "A"; 2620 | } 2621 | .btn.icon.play:before { 2622 | content: "B"; 2623 | } 2624 | .btn.icon.new-window:before { 2625 | content: "C"; 2626 | } 2627 | .btn.icon.link:before { 2628 | content: "D"; 2629 | } 2630 | .btn.icon.iinfo:before { 2631 | content: "E"; 2632 | } 2633 | .btn.icon.image:before { 2634 | content: "F"; 2635 | } 2636 | .btn.icon.folder:before { 2637 | content: "G"; 2638 | } 2639 | .btn.icon.fullscreen:before { 2640 | content: "H"; 2641 | } 2642 | .btn.icon.fullexit:before { 2643 | content: "I"; 2644 | } 2645 | .btn.icon.share:before { 2646 | content: "J"; 2647 | } 2648 | .btn.icon.undo:before { 2649 | content: "K"; 2650 | } 2651 | .btn.icon.quote:before { 2652 | content: "L"; 2653 | } 2654 | .btn.icon.printer:before { 2655 | content: "M"; 2656 | } 2657 | @-webkit-keyframes progress-bar-stripes { 2658 | from { 2659 | background-position: 0 0; 2660 | } 2661 | to { 2662 | background-position: 40px 0; 2663 | } 2664 | } 2665 | @-moz-keyframes progress-bar-stripes { 2666 | from { 2667 | background-position: 0 0; 2668 | } 2669 | to { 2670 | background-position: 40px 0; 2671 | } 2672 | } 2673 | @keyframes progress-bar-stripes { 2674 | from { 2675 | background-position: 0 0; 2676 | } 2677 | to { 2678 | background-position: 40px 0; 2679 | } 2680 | } 2681 | .progress { 2682 | overflow: hidden; 2683 | height: 15px; 2684 | margin-top: 1px; 2685 | background-color: #f7f7f7; 2686 | background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); 2687 | background-image: -ms-linear-gradient(top, #f5f5f5, #f9f9f9); 2688 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9)); 2689 | background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9); 2690 | background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9); 2691 | background-image: linear-gradient(top, #f5f5f5, #f9f9f9); 2692 | background-repeat: repeat-x; 2693 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#f9f9f9', GradientType=0); 2694 | -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); 2695 | -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); 2696 | box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); 2697 | -webkit-border-radius: 4px; 2698 | -moz-border-radius: 4px; 2699 | border-radius: 4px; 2700 | } 2701 | .progress .bar { 2702 | width: 0%; 2703 | height: 15px; 2704 | color: #ffffff; 2705 | font-size: 12px; 2706 | text-align: center; 2707 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 2708 | background-color: #0e90d2; 2709 | background-image: -moz-linear-gradient(top, #149bdf, #0480be); 2710 | background-image: -ms-linear-gradient(top, #149bdf, #0480be); 2711 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be)); 2712 | background-image: -webkit-linear-gradient(top, #149bdf, #0480be); 2713 | background-image: -o-linear-gradient(top, #149bdf, #0480be); 2714 | background-image: linear-gradient(top, #149bdf, #0480be); 2715 | background-repeat: repeat-x; 2716 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#149bdf', endColorstr='#0480be', GradientType=0); 2717 | -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); 2718 | -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); 2719 | box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); 2720 | -webkit-box-sizing: border-box; 2721 | -moz-box-sizing: border-box; 2722 | box-sizing: border-box; 2723 | -webkit-transition: width 0.6s ease; 2724 | -moz-transition: width 0.6s ease; 2725 | -ms-transition: width 0.6s ease; 2726 | -o-transition: width 0.6s ease; 2727 | transition: width 0.6s ease; 2728 | } 2729 | .progress-striped .bar { 2730 | background-color: #62c462; 2731 | background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); 2732 | background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); 2733 | background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); 2734 | background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); 2735 | background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); 2736 | background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); 2737 | -webkit-background-size: 40px 40px; 2738 | -moz-background-size: 40px 40px; 2739 | -o-background-size: 40px 40px; 2740 | background-size: 40px 40px; 2741 | } 2742 | .progress.active .bar { 2743 | -webkit-animation: progress-bar-stripes 2s linear infinite; 2744 | -moz-animation: progress-bar-stripes 2s linear infinite; 2745 | animation: progress-bar-stripes 2s linear infinite; 2746 | } 2747 | .progress-danger .bar { 2748 | background-color: #dd514c; 2749 | background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); 2750 | background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35); 2751 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35)); 2752 | background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); 2753 | background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); 2754 | background-image: linear-gradient(top, #ee5f5b, #c43c35); 2755 | background-repeat: repeat-x; 2756 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0); 2757 | } 2758 | .progress-danger.progress-striped .bar { 2759 | background-color: #ee5f5b; 2760 | background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); 2761 | background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); 2762 | background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); 2763 | background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); 2764 | background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); 2765 | background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); 2766 | } 2767 | .progress-success .bar { 2768 | background-color: #5eb95e; 2769 | background-image: -moz-linear-gradient(top, #62c462, #57a957); 2770 | background-image: -ms-linear-gradient(top, #62c462, #57a957); 2771 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957)); 2772 | background-image: -webkit-linear-gradient(top, #62c462, #57a957); 2773 | background-image: -o-linear-gradient(top, #62c462, #57a957); 2774 | background-image: linear-gradient(top, #62c462, #57a957); 2775 | background-repeat: repeat-x; 2776 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0); 2777 | } 2778 | .progress-success.progress-striped .bar { 2779 | background-color: #62c462; 2780 | background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); 2781 | background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); 2782 | background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); 2783 | background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); 2784 | background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); 2785 | background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); 2786 | } 2787 | .progress-info .bar { 2788 | background-color: #4bb1cf; 2789 | background-image: -moz-linear-gradient(top, #5bc0de, #339bb9); 2790 | background-image: -ms-linear-gradient(top, #5bc0de, #339bb9); 2791 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9)); 2792 | background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9); 2793 | background-image: -o-linear-gradient(top, #5bc0de, #339bb9); 2794 | background-image: linear-gradient(top, #5bc0de, #339bb9); 2795 | background-repeat: repeat-x; 2796 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0); 2797 | } 2798 | .progress-info.progress-striped .bar { 2799 | background-color: #5bc0de; 2800 | background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); 2801 | background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); 2802 | background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); 2803 | background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); 2804 | background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); 2805 | background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); 2806 | } 2807 | -------------------------------------------------------------------------------- /public/css/styles.css: -------------------------------------------------------------------------------- 1 | 2 | /* Override some defaults */ 3 | html, body { 4 | background-color: #eee; 5 | } 6 | body { 7 | padding-top: 40px; /* 40px to make the container go all the way to the bottom of the topbar */ 8 | } 9 | .container > footer p { 10 | text-align: center; /* center align it with the container */ 11 | } 12 | .container { 13 | width: 940px; /* downsize our container to make the content feel a bit tighter and more cohesive. NOTE: this removes two full columns from the grid, meaning you only go to 14 columns and not 16. */ 14 | } 15 | 16 | /* The white background content wrapper */ 17 | .container > .content { 18 | background-color: #fff; 19 | padding: 20px; 20 | margin: 0 -20px; /* negative indent the amount of the padding to maintain the grid system */ 21 | border-radius: 0 0 6px 6px; 22 | box-shadow: 0 1px 2px rgba(0,0,0,.15); 23 | } 24 | 25 | /* Page header tweaks */ 26 | .page-header { 27 | background-color: #f5f5f5; 28 | padding: 20px 20px 10px; 29 | margin: -20px -20px 20px; 30 | } 31 | 32 | /* Give a quick and non-cross-browser friendly divider */ 33 | .content .span4 { 34 | margin-left: 0; 35 | padding-left: 19px; 36 | border-left: 1px solid #eee; 37 | } 38 | 39 | .topbar .btn { 40 | border: 0; 41 | } 42 | 43 | .nickname { 44 | color: #2288CA; 45 | } 46 | 47 | .scrollcontent { 48 | height: 500px; 49 | overflow-y: scroll; 50 | } 51 | 52 | .write-area { 53 | box-sizing: border-box; 54 | width: 100%; 55 | resize: none; 56 | } 57 | 58 | .userlist { 59 | padding-left: 13px; 60 | } 61 | 62 | #upload-modal { 63 | 64 | } 65 | 66 | #fileList { 67 | height: 160px; 68 | width: 525px; 69 | overflow-y: scroll; 70 | margin: 0; 71 | padding: 0; 72 | list-style: none; 73 | } 74 | 75 | .fileitem { 76 | margin: 9px 0 0 0; 77 | } 78 | 79 | .filenamefield { 80 | text-size: 12px; 81 | width: 320px; 82 | padding-right: 10px; 83 | overflow-x: hidden; 84 | } 85 | 86 | .fileprogressfield { 87 | width: 110px; 88 | top: 0px 89 | } 90 | 91 | .filestatusfield { 92 | width: 70px; 93 | text-align: center; 94 | } 95 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tug/express-chat/9e36091927d61fbf6e0e41895b81c3bdf73ad43b/public/favicon.ico -------------------------------------------------------------------------------- /public/images/bubble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tug/express-chat/9e36091927d61fbf6e0e41895b81c3bdf73ad43b/public/images/bubble.png -------------------------------------------------------------------------------- /public/images/stripe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tug/express-chat/9e36091927d61fbf6e0e41895b81c3bdf73ad43b/public/images/stripe.png -------------------------------------------------------------------------------- /public/js/app.js: -------------------------------------------------------------------------------- 1 | 2 | $(document).ready(function() { 3 | 4 | var roompath = document.location.pathname; 5 | var roomid = roompath.replace('/r/', ''); 6 | var nextmsgnum = 1; 7 | 8 | function datePrefix(dateObj) { 9 | dateObj = dateObj || new Date(); 10 | return ''+date('j M Y, g:i:s a', (dateObj.getTime()/1000))+' '; 11 | } 12 | 13 | var app = { 14 | 15 | ROOMID : roomid, 16 | MAX_MSG_LEN : 3000, 17 | MAX_USR_LEN : 50, 18 | MAX_ROOMID_LEN : 64, 19 | msgCount : 0, 20 | 21 | users : [], 22 | username : 'Anonymous', 23 | 24 | messageBox : $('#message'), 25 | nameBox : $('#name'), 26 | messagesBox : $('#messages'), 27 | usersBox : $('#users'), 28 | submitMessageButton : $('#submitMessageButton'), 29 | renameButton : $('#renameButton'), 30 | 31 | showWelcomeMessage: function() { 32 | app.addMessageToUl("----------------------------------------------"); 33 | app.showSystemMessage({ body : "Welcome on the room. You are known as "+htmlentities(app.username)+"."}); 34 | }, 35 | 36 | addMessageToUl: function(msg) { 37 | var preDiv = app.messagesBox.parent().get(0); 38 | var atBottom = (preDiv.scrollHeight - preDiv.scrollTop) == preDiv.clientHeight; 39 | app.messagesBox.append('
    • '+msg+'
    • '); 40 | if(atBottom) { 41 | preDiv.scrollTop = app.messagesBox.get(0).scrollHeight; 42 | } 43 | }, 44 | 45 | showMessage: function(msg) { 46 | var msgStr = datePrefix(msg.date); 47 | msgStr += ''+htmlentities('<'+msg.username+'>')+' '; 48 | msgStr += linkify(htmlentities(msg.body, 'ENT_NOQUOTES')); 49 | app.addMessageToUl(msgStr); 50 | }, 51 | 52 | showSystemMessage: function(msg) { 53 | app.addMessageToUl(datePrefix(msg.date) + '* ' + msg.body); 54 | }, 55 | 56 | setUsers: function(newusers) { 57 | if(newusers.constructor === Array) { 58 | app.users = newusers; 59 | app.refreshUserList(); 60 | } 61 | }, 62 | 63 | userJoined: function(newuser) { 64 | if(newuser != null) { 65 | app.users.push(newuser); 66 | app.refreshUserList(); 67 | app.showSystemMessage({ body: newuser+" joined the room." }); 68 | } 69 | }, 70 | 71 | userLeft: function(olduser) { 72 | for(var i=0; i b.toLowerCase()}); 105 | app.usersBox.empty(); 106 | $.each(allusers, function(i, usr) { 107 | app.usersBox.append('
    • ' + htmlentities(usr) + '
    • '); 108 | }); 109 | } 110 | 111 | } 112 | 113 | runChatClient(app); 114 | 115 | }) 116 | 117 | -------------------------------------------------------------------------------- /public/js/socket.chat.js: -------------------------------------------------------------------------------- 1 | 2 | function runChatClient(app) { 3 | 4 | var client = io.connect('/chat'); 5 | 6 | client.on('new message', addMessage); 7 | client.on('new messages', addMessages); 8 | 9 | client.on('ready', app.showWelcomeMessage); 10 | client.on('users', app.setUsers); 11 | client.on('user joined', app.userJoined); 12 | client.on('user left', app.userLeft); 13 | client.on('user renamed', app.userRenamed); 14 | 15 | client.on('connect', function () { 16 | client.emit('join room', app.ROOMID, app.msgCount, function(err, name) { 17 | if(err) console.log(err); 18 | app.username = name; 19 | }); 20 | }); 21 | 22 | function addMessage(msg) { 23 | if(msg.date != null) { 24 | msg.date = new Date(msg.date); 25 | } 26 | if(msg.body != null) { 27 | app.showMessage(msg); 28 | } 29 | } 30 | 31 | function addMessages(messages) { 32 | if(messages && messages.constructor === Array) { 33 | messages.forEach(addMessage); 34 | } 35 | } 36 | 37 | 38 | /* ---- bind ui events ---- */ 39 | 40 | function bindEnter(component, callback) { 41 | component.keyup(function(e) { 42 | var code = (e.keyCode ? e.keyCode : e.which); 43 | if(code == 13) { // ENTER 44 | $(this).blur(); 45 | callback(); 46 | } 47 | }).keydown(function(e) { 48 | var code = (e.keyCode ? e.keyCode : e.which); 49 | if (code == 13) { 50 | e.preventDefault(); 51 | } 52 | }); 53 | } 54 | 55 | app.submitMessageButton.click(sendMessageHandler); 56 | app.renameButton.click(renameHandler); 57 | bindEnter(app.messageBox, sendMessageHandler); 58 | bindEnter(app.nameBox, renameHandler); 59 | 60 | function sendMessageHandler() { 61 | var msg = app.messageBox.val(); 62 | if(msg && msg != '\n') { 63 | if(msg.length > app.MAX_MSG_LEN) { 64 | msg = msg.substr(0, app.MAX_MSG_LEN); 65 | } 66 | sendMessage(msg); 67 | } 68 | app.messageBox.val(''); 69 | } 70 | 71 | function renameHandler() { 72 | var name = app.nameBox.val(); 73 | if(name && name != app.username) { 74 | changeUsername(name); 75 | } 76 | } 77 | 78 | function sendMessage(message) { 79 | client.emit("message", message); 80 | } 81 | 82 | function changeUsername(newname) { 83 | if(newname) { 84 | if(newname > app.MAX_USR_LEN) newname = newname.substr(0, app.MAX_USR_LEN); 85 | client.emit("change name", newname, function(err, name) { 86 | if(err) { 87 | alert(err); 88 | } else { 89 | app.userRenamed({ oldname: app.username, newname: newname }); 90 | } 91 | }); 92 | } 93 | } 94 | 95 | } 96 | 97 | -------------------------------------------------------------------------------- /public/lib/phpjs.js: -------------------------------------------------------------------------------- 1 | 2 | function readableSize(size) { 3 | if(size == null) return 'unknown'; 4 | var units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; 5 | var i = 0; 6 | while(size >= 1024) { 7 | size /= 1024; 8 | ++i; 9 | } 10 | return size.toFixed(1) + ' ' + units[i]; 11 | } 12 | 13 | function linkify(inputText) { 14 | var replaceText, replacePattern1, replacePattern2, replacePattern3; 15 | 16 | //URLs starting with http://, https://, or ftp:// 17 | replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim; 18 | replacedText = inputText.replace(replacePattern1, '$1'); 19 | 20 | //URLs starting with "www." (without // before it, or it'd re-link the ones done above). 21 | replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim; 22 | replacedText = replacedText.replace(replacePattern2, '$1$2'); 23 | 24 | return replacedText 25 | } 26 | 27 | function htmlentities (string, quote_style, charset, double_encode) { 28 | // http://kevin.vanzonneveld.net 29 | // + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 30 | // + revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 31 | // + improved by: nobbler 32 | // + tweaked by: Jack 33 | // + bugfixed by: Onno Marsman 34 | // + revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 35 | // + bugfixed by: Brett Zamir (http://brett-zamir.me) 36 | // + input by: Ratheous 37 | // + improved by: Rafał Kukawski (http://blog.kukawski.pl) 38 | // + improved by: Dj (http://phpjs.org/functions/htmlentities:425#comment_134018) 39 | // - depends on: get_html_translation_table 40 | // * example 1: htmlentities('Kevin & van Zonneveld'); 41 | // * returns 1: 'Kevin & van Zonneveld' 42 | // * example 2: htmlentities("foo'bar","ENT_QUOTES"); 43 | // * returns 2: 'foo'bar' 44 | var hash_map = this.get_html_translation_table('HTML_ENTITIES', quote_style), 45 | symbol = ''; 46 | string = string == null ? '' : string + ''; 47 | 48 | if (!hash_map) { 49 | return false; 50 | } 51 | 52 | if (quote_style && quote_style === 'ENT_QUOTES') { 53 | hash_map["'"] = '''; 54 | } 55 | 56 | if (!!double_encode || double_encode == null) { 57 | for (symbol in hash_map) { 58 | if (hash_map.hasOwnProperty(symbol)) { 59 | string = string.split(symbol).join(hash_map[symbol]); 60 | } 61 | } 62 | } else { 63 | string = string.replace(/([\s\S]*?)(&(?:#\d+|#x[\da-f]+|[a-zA-Z][\da-z]*);|$)/g, function (ignore, text, entity) { 64 | for (symbol in hash_map) { 65 | if (hash_map.hasOwnProperty(symbol)) { 66 | text = text.split(symbol).join(hash_map[symbol]); 67 | } 68 | } 69 | 70 | return text + entity; 71 | }); 72 | } 73 | 74 | return string; 75 | } 76 | 77 | 78 | function get_html_translation_table (table, quote_style) { 79 | // http://kevin.vanzonneveld.net 80 | // + original by: Philip Peterson 81 | // + revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 82 | // + bugfixed by: noname 83 | // + bugfixed by: Alex 84 | // + bugfixed by: Marco 85 | // + bugfixed by: madipta 86 | // + improved by: KELAN 87 | // + improved by: Brett Zamir (http://brett-zamir.me) 88 | // + bugfixed by: Brett Zamir (http://brett-zamir.me) 89 | // + input by: Frank Forte 90 | // + bugfixed by: T.Wild 91 | // + input by: Ratheous 92 | // % note: It has been decided that we're not going to add global 93 | // % note: dependencies to php.js, meaning the constants are not 94 | // % note: real constants, but strings instead. Integers are also supported if someone 95 | // % note: chooses to create the constants themselves. 96 | // * example 1: get_html_translation_table('HTML_SPECIALCHARS'); 97 | // * returns 1: {'"': '"', '&': '&', '<': '<', '>': '>'} 98 | var entities = {}, 99 | hash_map = {}, 100 | decimal; 101 | var constMappingTable = {}, 102 | constMappingQuoteStyle = {}; 103 | var useTable = {}, 104 | useQuoteStyle = {}; 105 | 106 | // Translate arguments 107 | constMappingTable[0] = 'HTML_SPECIALCHARS'; 108 | constMappingTable[1] = 'HTML_ENTITIES'; 109 | constMappingQuoteStyle[0] = 'ENT_NOQUOTES'; 110 | constMappingQuoteStyle[2] = 'ENT_COMPAT'; 111 | constMappingQuoteStyle[3] = 'ENT_QUOTES'; 112 | 113 | useTable = !isNaN(table) ? constMappingTable[table] : table ? table.toUpperCase() : 'HTML_SPECIALCHARS'; 114 | useQuoteStyle = !isNaN(quote_style) ? constMappingQuoteStyle[quote_style] : quote_style ? quote_style.toUpperCase() : 'ENT_COMPAT'; 115 | 116 | if (useTable !== 'HTML_SPECIALCHARS' && useTable !== 'HTML_ENTITIES') { 117 | throw new Error("Table: " + useTable + ' not supported'); 118 | // return false; 119 | } 120 | 121 | entities['38'] = '&'; 122 | if (useTable === 'HTML_ENTITIES') { 123 | entities['160'] = ' '; 124 | entities['161'] = '¡'; 125 | entities['162'] = '¢'; 126 | entities['163'] = '£'; 127 | entities['164'] = '¤'; 128 | entities['165'] = '¥'; 129 | entities['166'] = '¦'; 130 | entities['167'] = '§'; 131 | entities['168'] = '¨'; 132 | entities['169'] = '©'; 133 | entities['170'] = 'ª'; 134 | entities['171'] = '«'; 135 | entities['172'] = '¬'; 136 | entities['173'] = '­'; 137 | entities['174'] = '®'; 138 | entities['175'] = '¯'; 139 | entities['176'] = '°'; 140 | entities['177'] = '±'; 141 | entities['178'] = '²'; 142 | entities['179'] = '³'; 143 | entities['180'] = '´'; 144 | entities['181'] = 'µ'; 145 | entities['182'] = '¶'; 146 | entities['183'] = '·'; 147 | entities['184'] = '¸'; 148 | entities['185'] = '¹'; 149 | entities['186'] = 'º'; 150 | entities['187'] = '»'; 151 | entities['188'] = '¼'; 152 | entities['189'] = '½'; 153 | entities['190'] = '¾'; 154 | entities['191'] = '¿'; 155 | entities['192'] = 'À'; 156 | entities['193'] = 'Á'; 157 | entities['194'] = 'Â'; 158 | entities['195'] = 'Ã'; 159 | entities['196'] = 'Ä'; 160 | entities['197'] = 'Å'; 161 | entities['198'] = 'Æ'; 162 | entities['199'] = 'Ç'; 163 | entities['200'] = 'È'; 164 | entities['201'] = 'É'; 165 | entities['202'] = 'Ê'; 166 | entities['203'] = 'Ë'; 167 | entities['204'] = 'Ì'; 168 | entities['205'] = 'Í'; 169 | entities['206'] = 'Î'; 170 | entities['207'] = 'Ï'; 171 | entities['208'] = 'Ð'; 172 | entities['209'] = 'Ñ'; 173 | entities['210'] = 'Ò'; 174 | entities['211'] = 'Ó'; 175 | entities['212'] = 'Ô'; 176 | entities['213'] = 'Õ'; 177 | entities['214'] = 'Ö'; 178 | entities['215'] = '×'; 179 | entities['216'] = 'Ø'; 180 | entities['217'] = 'Ù'; 181 | entities['218'] = 'Ú'; 182 | entities['219'] = 'Û'; 183 | entities['220'] = 'Ü'; 184 | entities['221'] = 'Ý'; 185 | entities['222'] = 'Þ'; 186 | entities['223'] = 'ß'; 187 | entities['224'] = 'à'; 188 | entities['225'] = 'á'; 189 | entities['226'] = 'â'; 190 | entities['227'] = 'ã'; 191 | entities['228'] = 'ä'; 192 | entities['229'] = 'å'; 193 | entities['230'] = 'æ'; 194 | entities['231'] = 'ç'; 195 | entities['232'] = 'è'; 196 | entities['233'] = 'é'; 197 | entities['234'] = 'ê'; 198 | entities['235'] = 'ë'; 199 | entities['236'] = 'ì'; 200 | entities['237'] = 'í'; 201 | entities['238'] = 'î'; 202 | entities['239'] = 'ï'; 203 | entities['240'] = 'ð'; 204 | entities['241'] = 'ñ'; 205 | entities['242'] = 'ò'; 206 | entities['243'] = 'ó'; 207 | entities['244'] = 'ô'; 208 | entities['245'] = 'õ'; 209 | entities['246'] = 'ö'; 210 | entities['247'] = '÷'; 211 | entities['248'] = 'ø'; 212 | entities['249'] = 'ù'; 213 | entities['250'] = 'ú'; 214 | entities['251'] = 'û'; 215 | entities['252'] = 'ü'; 216 | entities['253'] = 'ý'; 217 | entities['254'] = 'þ'; 218 | entities['255'] = 'ÿ'; 219 | } 220 | 221 | if (useQuoteStyle !== 'ENT_NOQUOTES') { 222 | entities['34'] = '"'; 223 | } 224 | if (useQuoteStyle === 'ENT_QUOTES') { 225 | entities['39'] = '''; 226 | } 227 | entities['60'] = '<'; 228 | entities['62'] = '>'; 229 | 230 | 231 | // ascii decimals to real symbols 232 | for (decimal in entities) { 233 | if (entities.hasOwnProperty(decimal)) { 234 | hash_map[String.fromCharCode(decimal)] = entities[decimal]; 235 | } 236 | } 237 | 238 | return hash_map; 239 | } 240 | 241 | 242 | function date (format, timestamp) { 243 | // http://kevin.vanzonneveld.net 244 | // + original by: Carlos R. L. Rodrigues (http://www.jsfromhell.com) 245 | // + parts by: Peter-Paul Koch (http://www.quirksmode.org/js/beat.html) 246 | // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 247 | // + improved by: MeEtc (http://yass.meetcweb.com) 248 | // + improved by: Brad Touesnard 249 | // + improved by: Tim Wiel 250 | // + improved by: Bryan Elliott 251 | // 252 | // + improved by: Brett Zamir (http://brett-zamir.me) 253 | // + improved by: David Randall 254 | // + input by: Brett Zamir (http://brett-zamir.me) 255 | // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 256 | // + improved by: Brett Zamir (http://brett-zamir.me) 257 | // + improved by: Brett Zamir (http://brett-zamir.me) 258 | // + improved by: Theriault 259 | // + derived from: gettimeofday 260 | // + input by: majak 261 | // + bugfixed by: majak 262 | // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 263 | // + input by: Alex 264 | // + bugfixed by: Brett Zamir (http://brett-zamir.me) 265 | // + improved by: Theriault 266 | // + improved by: Brett Zamir (http://brett-zamir.me) 267 | // + improved by: Theriault 268 | // + improved by: Thomas Beaucourt (http://www.webapp.fr) 269 | // + improved by: JT 270 | // + improved by: Theriault 271 | // + improved by: Rafał Kukawski (http://blog.kukawski.pl) 272 | // + bugfixed by: omid (http://phpjs.org/functions/380:380#comment_137122) 273 | // + input by: Martin 274 | // + input by: Alex Wilson 275 | // + bugfixed by: Chris (http://www.devotis.nl/) 276 | // % note 1: Uses global: php_js to store the default timezone 277 | // % note 2: Although the function potentially allows timezone info (see notes), it currently does not set 278 | // % note 2: per a timezone specified by date_default_timezone_set(). Implementers might use 279 | // % note 2: this.php_js.currentTimezoneOffset and this.php_js.currentTimezoneDST set by that function 280 | // % note 2: in order to adjust the dates in this function (or our other date functions!) accordingly 281 | // * example 1: date('H:m:s \\m \\i\\s \\m\\o\\n\\t\\h', 1062402400); 282 | // * returns 1: '09:09:40 m is month' 283 | // * example 2: date('F j, Y, g:i a', 1062462400); 284 | // * returns 2: 'September 2, 2003, 2:26 am' 285 | // * example 3: date('Y W o', 1062462400); 286 | // * returns 3: '2003 36 2003' 287 | // * example 4: x = date('Y m d', (new Date()).getTime()/1000); 288 | // * example 4: (x+'').length == 10 // 2009 01 09 289 | // * returns 4: true 290 | // * example 5: date('W', 1104534000); 291 | // * returns 5: '53' 292 | // * example 6: date('B t', 1104534000); 293 | // * returns 6: '999 31' 294 | // * example 7: date('W U', 1293750000.82); // 2010-12-31 295 | // * returns 7: '52 1293750000' 296 | // * example 8: date('W', 1293836400); // 2011-01-01 297 | // * returns 8: '52' 298 | // * example 9: date('W Y-m-d', 1293974054); // 2011-01-02 299 | // * returns 9: '52 2011-01-02' 300 | var that = this, 301 | jsdate, f, formatChr = /\\?([a-z])/gi, 302 | formatChrCb, 303 | // Keep this here (works, but for code commented-out 304 | // below for file size reasons) 305 | //, tal= [], 306 | _pad = function (n, c) { 307 | if ((n = n + '').length < c) { 308 | return new Array((++c) - n.length).join('0') + n; 309 | } 310 | return n; 311 | }, 312 | txt_words = ["Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Satur", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; 313 | formatChrCb = function (t, s) { 314 | return f[t] ? f[t]() : s; 315 | }; 316 | f = { 317 | // Day 318 | d: function () { // Day of month w/leading 0; 01..31 319 | return _pad(f.j(), 2); 320 | }, 321 | D: function () { // Shorthand day name; Mon...Sun 322 | return f.l().slice(0, 3); 323 | }, 324 | j: function () { // Day of month; 1..31 325 | return jsdate.getDate(); 326 | }, 327 | l: function () { // Full day name; Monday...Sunday 328 | return txt_words[f.w()] + 'day'; 329 | }, 330 | N: function () { // ISO-8601 day of week; 1[Mon]..7[Sun] 331 | return f.w() || 7; 332 | }, 333 | S: function () { // Ordinal suffix for day of month; st, nd, rd, th 334 | var j = f.j(); 335 | return j < 4 | j > 20 && ['st', 'nd', 'rd'][j%10 - 1] || 'th'; 336 | }, 337 | w: function () { // Day of week; 0[Sun]..6[Sat] 338 | return jsdate.getDay(); 339 | }, 340 | z: function () { // Day of year; 0..365 341 | var a = new Date(f.Y(), f.n() - 1, f.j()), 342 | b = new Date(f.Y(), 0, 1); 343 | return Math.round((a - b) / 864e5) + 1; 344 | }, 345 | 346 | // Week 347 | W: function () { // ISO-8601 week number 348 | var a = new Date(f.Y(), f.n() - 1, f.j() - f.N() + 3), 349 | b = new Date(a.getFullYear(), 0, 4); 350 | return _pad(1 + Math.round((a - b) / 864e5 / 7), 2); 351 | }, 352 | 353 | // Month 354 | F: function () { // Full month name; January...December 355 | return txt_words[6 + f.n()]; 356 | }, 357 | m: function () { // Month w/leading 0; 01...12 358 | return _pad(f.n(), 2); 359 | }, 360 | M: function () { // Shorthand month name; Jan...Dec 361 | return f.F().slice(0, 3); 362 | }, 363 | n: function () { // Month; 1...12 364 | return jsdate.getMonth() + 1; 365 | }, 366 | t: function () { // Days in month; 28...31 367 | return (new Date(f.Y(), f.n(), 0)).getDate(); 368 | }, 369 | 370 | // Year 371 | L: function () { // Is leap year?; 0 or 1 372 | var j = f.Y(); 373 | return j%4==0 & j%100!=0 | j%400==0; 374 | }, 375 | o: function () { // ISO-8601 year 376 | var n = f.n(), 377 | W = f.W(), 378 | Y = f.Y(); 379 | return Y + (n === 12 && W < 9 ? 1 : n === 1 && W > 9 ? -1 : 0); 380 | }, 381 | Y: function () { // Full year; e.g. 1980...2010 382 | return jsdate.getFullYear(); 383 | }, 384 | y: function () { // Last two digits of year; 00...99 385 | return (f.Y() + "").slice(-2); 386 | }, 387 | 388 | // Time 389 | a: function () { // am or pm 390 | return jsdate.getHours() > 11 ? "pm" : "am"; 391 | }, 392 | A: function () { // AM or PM 393 | return f.a().toUpperCase(); 394 | }, 395 | B: function () { // Swatch Internet time; 000..999 396 | var H = jsdate.getUTCHours() * 36e2, 397 | // Hours 398 | i = jsdate.getUTCMinutes() * 60, 399 | // Minutes 400 | s = jsdate.getUTCSeconds(); // Seconds 401 | return _pad(Math.floor((H + i + s + 36e2) / 86.4) % 1e3, 3); 402 | }, 403 | g: function () { // 12-Hours; 1..12 404 | return f.G() % 12 || 12; 405 | }, 406 | G: function () { // 24-Hours; 0..23 407 | return jsdate.getHours(); 408 | }, 409 | h: function () { // 12-Hours w/leading 0; 01..12 410 | return _pad(f.g(), 2); 411 | }, 412 | H: function () { // 24-Hours w/leading 0; 00..23 413 | return _pad(f.G(), 2); 414 | }, 415 | i: function () { // Minutes w/leading 0; 00..59 416 | return _pad(jsdate.getMinutes(), 2); 417 | }, 418 | s: function () { // Seconds w/leading 0; 00..59 419 | return _pad(jsdate.getSeconds(), 2); 420 | }, 421 | u: function () { // Microseconds; 000000-999000 422 | return _pad(jsdate.getMilliseconds() * 1000, 6); 423 | }, 424 | 425 | // Timezone 426 | e: function () { // Timezone identifier; e.g. Atlantic/Azores, ... 427 | // The following works, but requires inclusion of the very large 428 | // timezone_abbreviations_list() function. 429 | /* return that.date_default_timezone_get(); 430 | */ 431 | throw 'Not supported (see source code of date() for timezone on how to add support)'; 432 | }, 433 | I: function () { // DST observed?; 0 or 1 434 | // Compares Jan 1 minus Jan 1 UTC to Jul 1 minus Jul 1 UTC. 435 | // If they are not equal, then DST is observed. 436 | var a = new Date(f.Y(), 0), 437 | // Jan 1 438 | c = Date.UTC(f.Y(), 0), 439 | // Jan 1 UTC 440 | b = new Date(f.Y(), 6), 441 | // Jul 1 442 | d = Date.UTC(f.Y(), 6); // Jul 1 UTC 443 | return 0 + ((a - c) !== (b - d)); 444 | }, 445 | O: function () { // Difference to GMT in hour format; e.g. +0200 446 | var tzo = jsdate.getTimezoneOffset(), 447 | a = Math.abs(tzo); 448 | return (tzo > 0 ? "-" : "+") + _pad(Math.floor(a / 60) * 100 + a % 60, 4); 449 | }, 450 | P: function () { // Difference to GMT w/colon; e.g. +02:00 451 | var O = f.O(); 452 | return (O.substr(0, 3) + ":" + O.substr(3, 2)); 453 | }, 454 | T: function () { // Timezone abbreviation; e.g. EST, MDT, ... 455 | // The following works, but requires inclusion of the very 456 | // large timezone_abbreviations_list() function. 457 | /* var abbr = '', i = 0, os = 0, default = 0; 458 | if (!tal.length) { 459 | tal = that.timezone_abbreviations_list(); 460 | } 461 | if (that.php_js && that.php_js.default_timezone) { 462 | default = that.php_js.default_timezone; 463 | for (abbr in tal) { 464 | for (i=0; i < tal[abbr].length; i++) { 465 | if (tal[abbr][i].timezone_id === default) { 466 | return abbr.toUpperCase(); 467 | } 468 | } 469 | } 470 | } 471 | for (abbr in tal) { 472 | for (i = 0; i < tal[abbr].length; i++) { 473 | os = -jsdate.getTimezoneOffset() * 60; 474 | if (tal[abbr][i].offset === os) { 475 | return abbr.toUpperCase(); 476 | } 477 | } 478 | } 479 | */ 480 | return 'UTC'; 481 | }, 482 | Z: function () { // Timezone offset in seconds (-43200...50400) 483 | return -jsdate.getTimezoneOffset() * 60; 484 | }, 485 | 486 | // Full Date/Time 487 | c: function () { // ISO-8601 date. 488 | return 'Y-m-d\\TH:i:sP'.replace(formatChr, formatChrCb); 489 | }, 490 | r: function () { // RFC 2822 491 | return 'D, d M Y H:i:s O'.replace(formatChr, formatChrCb); 492 | }, 493 | U: function () { // Seconds since UNIX epoch 494 | return jsdate / 1000 | 0; 495 | } 496 | }; 497 | this.date = function (format, timestamp) { 498 | that = this; 499 | jsdate = (timestamp == null ? new Date() : // Not provided 500 | (timestamp instanceof Date) ? new Date(timestamp) : // JS Date() 501 | new Date(timestamp * 1000) // UNIX timestamp (auto-convert to int) 502 | ); 503 | return format.replace(formatChr, formatChrCb); 504 | }; 505 | return this.date(format, timestamp); 506 | } 507 | 508 | 509 | -------------------------------------------------------------------------------- /scripts/cleanMongodb.js: -------------------------------------------------------------------------------- 1 | 2 | var userconfig = require('../loadConfig'); 3 | 4 | require('../loadApp')(userconfig, function(err, app, model, config) { 5 | 6 | var Step = app.libs.Step; 7 | var db = model.mongo; 8 | 9 | dropCollections(db); 10 | 11 | function dropCollections(db) { 12 | db.collectionNames(function(err, names) { 13 | if(err || !names) { 14 | console.log("No collection found"); 15 | process.exit(0); 16 | } else { 17 | console.log("Removing collections", names); 18 | names = names.map(function(colObj) { return colObj.name.split('.').splice(1).join('.'); }); 19 | Step(function() { 20 | var group = this.group(); 21 | var groups = {}; 22 | names.forEach(function(name) { 23 | if(name.indexOf("system") != 0) { 24 | groups[name] = group(); 25 | db.collection(name, function(err, collection) { 26 | console.log("Removing all objects in "+collection.collectionName); 27 | collection.remove({}, groups[collection.collectionName]); 28 | }); 29 | } 30 | }); 31 | }, function() { 32 | process.exit(0); 33 | }); 34 | } 35 | }); 36 | } 37 | 38 | }); 39 | 40 | -------------------------------------------------------------------------------- /scripts/cleanRedis.js: -------------------------------------------------------------------------------- 1 | 2 | var userconfig = require('../loadConfig'); 3 | 4 | require('../loadApp')(userconfig, function(err, app, model, config) { 5 | 6 | var redisClient = model.redis.createClient(); 7 | flush(redisClient); 8 | 9 | function flush(redisClient) { 10 | redisClient.flushdb(function(err) { 11 | console.log(err || "ok"); 12 | process.exit(0); 13 | }); 14 | } 15 | 16 | }); -------------------------------------------------------------------------------- /scripts/createSecretRoom.js: -------------------------------------------------------------------------------- 1 | 2 | var args = process.argv.splice(2); 3 | if(args.length < 2) { 4 | console.log("Enter config file and room name as arguments"); 5 | process.exit(0); 6 | } 7 | 8 | var configFile = args[0]; 9 | var roomTitle = args[1]; 10 | 11 | var userconfig = require('../loadConfig'); 12 | 13 | require('../loadApp')(userconfig, function(err, app, model, config) { 14 | 15 | if(err) console.log(err); 16 | 17 | var Room = model.mongoose.model('Room'); 18 | var room = new Room({ _id: roomTitle, ispublic: false, title: roomTitle }); 19 | room.save(function(err) { 20 | if(err) { 21 | console.log(err.message); 22 | } else { 23 | var url = app.routes.url("chat.index", {"roomid": room.id }); 24 | console.log("room created at "+url); 25 | } 26 | process.exit(0); 27 | }); 28 | 29 | }); 30 | 31 | -------------------------------------------------------------------------------- /urls.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(app) { 3 | 4 | return { 5 | // [route, controller, method, pre-middleware(s), post-middleware(s) ] 6 | // WARNING : Controller name must be unique ! 7 | // Otherwise it won't be possible to find the corresponding route with the app.routes.url() method from `autoload` 8 | urls : [ 9 | ["/", "index.index", "get" ], 10 | ["/rooms/create", "index.createRoom", "post" ], 11 | ["/r/:roomid", "chat.index", "get" ], 12 | ], 13 | 14 | // [namespace, controller, method/event middleware] 15 | ios : [ 16 | ["/chat", "chat.socket", "connection", ["chat.authorizeSocket"] ], 17 | ] 18 | }; 19 | 20 | } 21 | 22 | --------------------------------------------------------------------------------