├── .gitignore ├── Jakefile.js ├── Makefile ├── README.textile ├── app.js ├── bin └── nodepad.js ├── helpers.js ├── models.js ├── package.json ├── public ├── images │ ├── glass.png │ └── gradient.gif ├── javascripts │ ├── backbone-min.js │ ├── documents.js │ ├── flash.js │ ├── json.js │ ├── resize.js │ └── underscore-min.js └── stylesheets │ ├── aristo │ ├── images │ │ ├── button_bg.png │ │ ├── datepicker.gif │ │ ├── icon_sprite.png │ │ ├── progress_bar.gif │ │ ├── slider_h_bg.gif │ │ ├── slider_handles.png │ │ ├── slider_v_bg.gif │ │ ├── tab_bg.gif │ │ ├── the_gradient.gif │ │ ├── ui-bg_diagonals-thick_18_b81900_40x40.png │ │ ├── ui-bg_diagonals-thick_20_666666_40x40.png │ │ ├── ui-bg_flat_10_000000_40x100.png │ │ ├── ui-bg_glass_100_f6f6f6_1x400.png │ │ ├── ui-bg_glass_100_fdf5ce_1x400.png │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ ├── ui-bg_gloss-wave_35_f6a828_500x100.png │ │ ├── ui-bg_highlight-soft_100_eeeeee_1x100.png │ │ ├── ui-bg_highlight-soft_75_ffe45c_1x100.png │ │ ├── ui-icons_222222_256x240.png │ │ ├── ui-icons_228ef1_256x240.png │ │ ├── ui-icons_ef8c08_256x240.png │ │ ├── ui-icons_ffd27a_256x240.png │ │ └── ui-icons_ffffff_256x240.png │ └── jquery-ui-1.8.5.custom.css │ ├── style.css │ └── style.styl ├── test ├── app.test.js └── helper.js └── views ├── 404.jade ├── 500.jade ├── documents ├── edit.jade ├── fields.jade ├── index.jade ├── new.jade └── show.jade ├── index.jade ├── layout.jade ├── mailer └── welcome.jade ├── sessions └── new.jade └── users ├── fields.jade └── new.jade /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw? 2 | .DS_Store 3 | tmp/* 4 | log/* 5 | node_modules/ 6 | -------------------------------------------------------------------------------- /Jakefile.js: -------------------------------------------------------------------------------- 1 | 2 | desc('Saves all documents to generate keywords'); 3 | task('index', [], function() { 4 | app = require('./app'); 5 | 6 | app.Document.find({}, function(err, documents) { 7 | documents.forEach(function(d) { 8 | console.log(d._id); 9 | d.save(); 10 | }); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | ./node_modules/.bin/mocha 3 | 4 | .PHONY: test 5 | -------------------------------------------------------------------------------- /README.textile: -------------------------------------------------------------------------------- 1 | h3. Nodepad 2 | 3 | This is a Node notepad, written for a tutorial series on "DailyJS":http://dailyjs.com. 4 | 5 | h3. Updates 6 | 7 | Follow the series on "DailyJS":http://dailyjs.com, "@dailyjs":http://twitter.com/dailyjs, or "@alex_young":http://twitter.com/alex_young. 8 | 9 | h3. Dependencies 10 | 11 | You need to install these on your system first: 12 | 13 | * "Node":http://nodejs.org/ 14 | * "npm":https://github.com/isaacs/npm/ 15 | * "MongoDB":http://www.mongodb.org/ 16 | 17 | These are the libraries that the project requires: 18 | 19 | * "Mongoose":https://github.com/LearnBoost/mongoose 20 | * "Express":http://expressjs.com/ 21 | * "Expresso":http://visionmedia.github.com/expresso/ 22 | * "Jade":http://jade-lang.com/ 23 | * "less":https://github.com/cloudhead/less.js 24 | 25 | See the package.json file for the versions of the dependencies I've tested with. 26 | 27 | On Mac OS Snow Leopard and Debian Lenny/Squeeze. 28 | 29 | h3. Dependency Installation 30 | 31 | Make sure you're running a mongo instance (type mongod if required). 32 | 33 |
34 | git clone git@github.com:alexyoung/nodepad.git nodepad
35 | cd nodepad
36 | npm install
37 | 
38 | node app.js
39 | 
40 | 41 | h3. Contributions 42 | 43 | * "luebken":https://github.com/luebken 44 | * "eirikurn":https://github.com/eirikurn 45 | * "@francoislaberge":http://twitter.com/francoislaberge 46 | 47 | h3. License (GPL) 48 | 49 | This program is free software: you can redistribute it and/or modify 50 | it under the terms of the GNU General Public License as published by 51 | the Free Software Foundation, either version 3 of the License, or 52 | (at your option) any later version. 53 | 54 | This program is distributed in the hope that it will be useful, 55 | but WITHOUT ANY WARRANTY; without even the implied warranty of 56 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 57 | GNU General Public License for more details. 58 | 59 | You should have received a copy of the GNU General Public License 60 | along with this program. If not, see "http://www.gnu.org/licenses/":http://www.gnu.org/licenses/. 61 | 62 | 63 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | connect = require('connect'), 3 | jade = require('jade'), 4 | app = module.exports = express.createServer(), 5 | mongoose = require('mongoose'), 6 | mongoStore = require('connect-mongodb'), 7 | mailer = require('mailer'), 8 | stylus = require('stylus'), 9 | markdown = require('markdown').markdown, 10 | connectTimeout = require('connect-timeout'), 11 | util = require('util'), 12 | path = require('path'), 13 | models = require('./models'), 14 | db, 15 | Document, 16 | User, 17 | LoginToken, 18 | Settings = { development: {}, test: {}, production: {} }, 19 | emails; 20 | 21 | function renderJadeFile(template, options) { 22 | var fn = jade.compile(template, options); 23 | return fn(options.locals); 24 | } 25 | 26 | emails = { 27 | send: function(template, mailOptions, templateOptions) { 28 | mailOptions.to = mailOptions.to; 29 | renderJadeFile(path.join(__dirname, 'views', 'mailer', template), templateOptions, function(err, text) { 30 | // Add the rendered Jade template to the mailOptions 31 | mailOptions.body = text; 32 | 33 | // Merge the app's mail options 34 | var keys = Object.keys(app.set('mailOptions')), 35 | k; 36 | for (var i = 0, len = keys.length; i < len; i++) { 37 | k = keys[i]; 38 | if (!mailOptions.hasOwnProperty(k)) 39 | mailOptions[k] = app.set('mailOptions')[k] 40 | } 41 | 42 | console.log('[SENDING MAIL]', util.inspect(mailOptions)); 43 | 44 | // Only send mails in production 45 | if (app.settings.env == 'production') { 46 | mailer.send(mailOptions, 47 | function(err, result) { 48 | if (err) { 49 | console.log(err); 50 | } 51 | } 52 | ); 53 | } 54 | }); 55 | }, 56 | 57 | sendWelcome: function(user) { 58 | this.send('welcome.jade', { to: user.email, subject: 'Welcome to Nodepad' }, { locals: { user: user } }); 59 | } 60 | }; 61 | 62 | app.helpers(require('./helpers.js').helpers); 63 | app.dynamicHelpers(require('./helpers.js').dynamicHelpers); 64 | 65 | app.configure('development', function() { 66 | app.set('db-uri', 'mongodb://localhost/nodepad-development'); 67 | app.use(express.errorHandler({ dumpExceptions: true })); 68 | app.set('view options', { 69 | pretty: true 70 | }); 71 | }); 72 | 73 | app.configure('test', function() { 74 | app.set('db-uri', 'mongodb://localhost/nodepad-test'); 75 | app.set('view options', { 76 | pretty: true 77 | }); 78 | }); 79 | 80 | app.configure('production', function() { 81 | app.set('db-uri', 'mongodb://localhost/nodepad-production'); 82 | }); 83 | 84 | app.configure(function() { 85 | app.set('views', __dirname + '/views'); 86 | app.use(express.favicon()); 87 | app.use(express.bodyParser()); 88 | app.use(express.cookieParser()); 89 | app.use(connectTimeout({ time: 10000 })); 90 | app.use(express.session({ store: mongoStore(app.set('db-uri')), secret: 'topsecret' })); 91 | app.use(express.logger({ format: '\x1b[1m:method\x1b[0m \x1b[33m:url\x1b[0m :response-time ms' })) 92 | app.use(express.methodOverride()); 93 | app.use(stylus.middleware({ src: __dirname + '/public' })); 94 | app.use(express.static(__dirname + '/public')); 95 | app.set('mailOptions', { 96 | host: 'localhost', 97 | port: '25', 98 | from: 'nodepad@example.com' 99 | }); 100 | }); 101 | 102 | models.defineModels(mongoose, function() { 103 | app.Document = Document = mongoose.model('Document'); 104 | app.User = User = mongoose.model('User'); 105 | app.LoginToken = LoginToken = mongoose.model('LoginToken'); 106 | db = mongoose.connect(app.set('db-uri')); 107 | }) 108 | 109 | function authenticateFromLoginToken(req, res, next) { 110 | var cookie = JSON.parse(req.cookies.logintoken); 111 | 112 | LoginToken.findOne({ email: cookie.email, 113 | series: cookie.series, 114 | token: cookie.token }, (function(err, token) { 115 | if (!token) { 116 | res.redirect('/sessions/new'); 117 | return; 118 | } 119 | 120 | User.findOne({ email: token.email }, function(err, user) { 121 | if (user) { 122 | req.session.user_id = user.id; 123 | req.currentUser = user; 124 | 125 | token.token = token.randomToken(); 126 | token.save(function() { 127 | res.cookie('logintoken', token.cookieValue, { expires: new Date(Date.now() + 2 * 604800000), path: '/' }); 128 | next(); 129 | }); 130 | } else { 131 | res.redirect('/sessions/new'); 132 | } 133 | }); 134 | })); 135 | } 136 | 137 | function loadUser(req, res, next) { 138 | if (req.session.user_id) { 139 | User.findById(req.session.user_id, function(err, user) { 140 | if (user) { 141 | req.currentUser = user; 142 | next(); 143 | } else { 144 | res.redirect('/sessions/new'); 145 | } 146 | }); 147 | } else if (req.cookies.logintoken) { 148 | authenticateFromLoginToken(req, res, next); 149 | } else { 150 | res.redirect('/sessions/new'); 151 | } 152 | } 153 | 154 | app.get('/', loadUser, function(req, res) { 155 | res.redirect('/documents') 156 | }); 157 | 158 | // Error handling 159 | function NotFound(msg) { 160 | this.name = 'NotFound'; 161 | Error.call(this, msg); 162 | Error.captureStackTrace(this, arguments.callee); 163 | } 164 | 165 | util.inherits(NotFound, Error); 166 | 167 | app.get('/404', function(req, res) { 168 | throw new NotFound; 169 | }); 170 | 171 | app.get('/500', function(req, res) { 172 | throw new Error('An expected error'); 173 | }); 174 | 175 | app.get('/bad', function(req, res) { 176 | unknownMethod(); 177 | }); 178 | 179 | app.error(function(err, req, res, next) { 180 | if (err instanceof NotFound) { 181 | res.render('404.jade', { status: 404 }); 182 | } else { 183 | next(err); 184 | } 185 | }); 186 | 187 | if (app.settings.env == 'production') { 188 | app.error(function(err, req, res) { 189 | res.render('500.jade', { 190 | status: 500, 191 | locals: { 192 | error: err 193 | } 194 | }); 195 | }); 196 | } 197 | 198 | // Document list 199 | app.get('/documents', loadUser, function(req, res) { 200 | Document.find({ user_id: req.currentUser.id }, 201 | [], { sort: ['title', 'descending'] }, 202 | function(err, documents) { 203 | documents = documents.map(function(d) { 204 | return { title: d.title, id: d._id }; 205 | }); 206 | res.render('documents/index.jade', { 207 | locals: { documents: documents, currentUser: req.currentUser } 208 | }); 209 | }); 210 | }); 211 | 212 | app.get('/documents.:format?', loadUser, function(req, res) { 213 | Document.find({ user_id: req.currentUser.id }, 214 | [], { sort: ['title', 'descending'] }, 215 | function(err, documents) { 216 | switch (req.params.format) { 217 | case 'json': 218 | res.send(documents.map(function(d) { 219 | return d.toObject(); 220 | })); 221 | break; 222 | 223 | default: 224 | res.send('Format not available', 400); 225 | } 226 | }); 227 | }); 228 | 229 | app.get('/documents/titles.json', loadUser, function(req, res) { 230 | Document.find({ user_id: req.currentUser.id }, 231 | [], { sort: ['title', 'descending'] }, 232 | function(err, documents) { 233 | res.send(documents.map(function(d) { 234 | return { title: d.title, id: d._id }; 235 | })); 236 | }); 237 | }); 238 | 239 | app.get('/documents/:id.:format?/edit', loadUser, function(req, res, next) { 240 | Document.findOne({ _id: req.params.id, user_id: req.currentUser.id }, function(err, d) { 241 | if (!d) return next(new NotFound('Document not found')); 242 | res.render('documents/edit.jade', { 243 | locals: { d: d, currentUser: req.currentUser } 244 | }); 245 | }); 246 | }); 247 | 248 | app.get('/documents/new', loadUser, function(req, res) { 249 | res.render('documents/new.jade', { 250 | locals: { d: new Document(), currentUser: req.currentUser } 251 | }); 252 | }); 253 | 254 | // Create document 255 | app.post('/documents.:format?', loadUser, function(req, res) { 256 | var d = new Document(req.body); 257 | d.user_id = req.currentUser.id; 258 | d.save(function() { 259 | switch (req.params.format) { 260 | case 'json': 261 | var data = d.toObject(); 262 | // TODO: Backbone requires 'id', but can I alias it? 263 | data.id = data._id; 264 | res.send(data); 265 | break; 266 | 267 | default: 268 | req.flash('info', 'Document created'); 269 | res.redirect('/documents'); 270 | } 271 | }); 272 | }); 273 | 274 | // Read document 275 | app.get('/documents/:id.:format?', loadUser, function(req, res, next) { 276 | Document.findOne({ _id: req.params.id, user_id: req.currentUser.id }, function(err, d) { 277 | if (!d) return next(new NotFound('Document not found')); 278 | 279 | switch (req.params.format) { 280 | case 'json': 281 | res.send(d.toObject()); 282 | break; 283 | 284 | case 'html': 285 | res.send(markdown.toHTML(d.data)); 286 | break; 287 | 288 | default: 289 | res.render('documents/show.jade', { 290 | locals: { d: d, currentUser: req.currentUser } 291 | }); 292 | } 293 | }); 294 | }); 295 | 296 | // Update document 297 | app.put('/documents/:id.:format?', loadUser, function(req, res, next) { 298 | Document.findOne({ _id: req.params.id, user_id: req.currentUser.id }, function(err, d) { 299 | if (!d) return next(new NotFound('Document not found')); 300 | d.title = req.body.title; 301 | d.data = req.body.data; 302 | 303 | d.save(function(err) { 304 | switch (req.params.format) { 305 | case 'json': 306 | res.send(d.toObject()); 307 | break; 308 | 309 | default: 310 | req.flash('info', 'Document updated'); 311 | res.redirect('/documents'); 312 | } 313 | }); 314 | }); 315 | }); 316 | 317 | // Delete document 318 | app.del('/documents/:id.:format?', loadUser, function(req, res, next) { 319 | Document.findOne({ _id: req.params.id, user_id: req.currentUser.id }, function(err, d) { 320 | if (!d) return next(new NotFound('Document not found')); 321 | 322 | d.remove(function() { 323 | switch (req.params.format) { 324 | case 'json': 325 | res.send('true'); 326 | break; 327 | 328 | default: 329 | req.flash('info', 'Document deleted'); 330 | res.redirect('/documents'); 331 | } 332 | }); 333 | }); 334 | }); 335 | 336 | // Users 337 | app.get('/users/new', function(req, res) { 338 | res.render('users/new.jade', { 339 | locals: { user: new User() } 340 | }); 341 | }); 342 | 343 | app.post('/users.:format?', function(req, res) { 344 | var user = new User(req.body.user); 345 | 346 | function userSaveFailed() { 347 | req.flash('error', 'Account creation failed'); 348 | res.render('users/new.jade', { 349 | locals: { user: user } 350 | }); 351 | } 352 | 353 | user.save(function(err) { 354 | if (err) return userSaveFailed(); 355 | 356 | req.flash('info', 'Your account has been created'); 357 | emails.sendWelcome(user); 358 | 359 | switch (req.params.format) { 360 | case 'json': 361 | res.send(user.toObject()); 362 | break; 363 | 364 | default: 365 | req.session.user_id = user.id; 366 | res.redirect('/documents'); 367 | } 368 | }); 369 | }); 370 | 371 | // Sessions 372 | app.get('/sessions/new', function(req, res) { 373 | res.render('sessions/new.jade', { 374 | locals: { user: new User() } 375 | }); 376 | }); 377 | 378 | app.post('/sessions', function(req, res) { 379 | User.findOne({ email: req.body.user.email }, function(err, user) { 380 | if (user && user.authenticate(req.body.user.password)) { 381 | req.session.user_id = user.id; 382 | 383 | // Remember me 384 | if (req.body.remember_me) { 385 | var loginToken = new LoginToken({ email: user.email }); 386 | loginToken.save(function() { 387 | res.cookie('logintoken', loginToken.cookieValue, { expires: new Date(Date.now() + 2 * 604800000), path: '/' }); 388 | res.redirect('/documents'); 389 | }); 390 | } else { 391 | res.redirect('/documents'); 392 | } 393 | } else { 394 | req.flash('error', 'Incorrect credentials'); 395 | res.redirect('/sessions/new'); 396 | } 397 | }); 398 | }); 399 | 400 | app.del('/sessions', loadUser, function(req, res) { 401 | if (req.session) { 402 | LoginToken.remove({ email: req.currentUser.email }, function() {}); 403 | res.clearCookie('logintoken'); 404 | req.session.destroy(function() {}); 405 | } 406 | res.redirect('/sessions/new'); 407 | }); 408 | 409 | // Search 410 | app.post('/search.:format?', loadUser, function(req, res) { 411 | Document.find({ user_id: req.currentUser.id, keywords: req.body.s }, 412 | function(err, documents) { 413 | console.log(documents); 414 | console.log(err); 415 | switch (req.params.format) { 416 | case 'json': 417 | res.send(documents.map(function(d) { 418 | return { title: d.title, id: d._id }; 419 | })); 420 | break; 421 | 422 | default: 423 | res.send('Format not available', 400); 424 | break; 425 | } 426 | }); 427 | }); 428 | 429 | if (!module.parent) { 430 | app.listen(3000, 'localhost', function() { 431 | console.log('Express server listening on port %d, environment: %s', app.address().port, app.settings.env) 432 | }); 433 | 434 | console.log('Using connect %s, Express %s, Jade %s', connect.version, express.version, jade.version); 435 | } 436 | -------------------------------------------------------------------------------- /bin/nodepad.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var app = require('../app.js'); 3 | app.listen(3000); 4 | 5 | -------------------------------------------------------------------------------- /helpers.js: -------------------------------------------------------------------------------- 1 | exports.helpers = { 2 | appName: 'Nodepad', 3 | version: '0.1', 4 | 5 | nameAndVersion: function(name, version) { 6 | return name + ' v' + version; 7 | } 8 | }; 9 | 10 | function FlashMessage(type, messages) { 11 | this.type = type; 12 | this.messages = typeof messages === 'string' ? [messages] : messages; 13 | } 14 | 15 | FlashMessage.prototype = { 16 | get icon() { 17 | switch (this.type) { 18 | case 'info': 19 | return 'ui-icon-info'; 20 | case 'error': 21 | return 'ui-icon-alert'; 22 | } 23 | }, 24 | 25 | get stateClass() { 26 | switch (this.type) { 27 | case 'info': 28 | return 'ui-state-highlight'; 29 | case 'error': 30 | return 'ui-state-error'; 31 | } 32 | }, 33 | 34 | toHTML: function() { 35 | return '
' + 36 | '
' + 37 | '

' + this.messages.join(', ') + '

' + 38 | '
' + 39 | '
'; 40 | } 41 | }; 42 | 43 | exports.dynamicHelpers = { 44 | flashMessages: function(req, res) { 45 | var html = ''; 46 | ['error', 'info'].forEach(function(type) { 47 | var messages = req.flash(type); 48 | if (messages.length > 0) { 49 | html += new FlashMessage(type, messages).toHTML(); 50 | } 51 | }); 52 | return html; 53 | } 54 | }; 55 | -------------------------------------------------------------------------------- /models.js: -------------------------------------------------------------------------------- 1 | var crypto = require('crypto'), 2 | Document, 3 | User, 4 | LoginToken; 5 | 6 | function extractKeywords(text) { 7 | if (!text) return []; 8 | 9 | return text. 10 | split(/\s+/). 11 | filter(function(v) { return v.length > 2; }). 12 | filter(function(v, i, a) { return a.lastIndexOf(v) === i; }); 13 | } 14 | 15 | function defineModels(mongoose, fn) { 16 | var Schema = mongoose.Schema, 17 | ObjectId = Schema.ObjectId; 18 | 19 | /** 20 | * Model: Document 21 | */ 22 | Document = new Schema({ 23 | 'title': { type: String, index: true }, 24 | 'data': String, 25 | 'tags': [String], 26 | 'keywords': [String], 27 | 'user_id': ObjectId 28 | }); 29 | 30 | Document.virtual('id') 31 | .get(function() { 32 | return this._id.toHexString(); 33 | }); 34 | 35 | Document.pre('save', function(next) { 36 | this.keywords = extractKeywords(this.data); 37 | next(); 38 | }); 39 | 40 | /** 41 | * Model: User 42 | */ 43 | function validatePresenceOf(value) { 44 | return value && value.length; 45 | } 46 | 47 | User = new Schema({ 48 | 'email': { type: String, validate: [validatePresenceOf, 'an email is required'], index: { unique: true } }, 49 | 'hashed_password': String, 50 | 'salt': String 51 | }); 52 | 53 | User.virtual('id') 54 | .get(function() { 55 | return this._id.toHexString(); 56 | }); 57 | 58 | User.virtual('password') 59 | .set(function(password) { 60 | this._password = password; 61 | this.salt = this.makeSalt(); 62 | this.hashed_password = this.encryptPassword(password); 63 | }) 64 | .get(function() { return this._password; }); 65 | 66 | User.method('authenticate', function(plainText) { 67 | return this.encryptPassword(plainText) === this.hashed_password; 68 | }); 69 | 70 | User.method('makeSalt', function() { 71 | return Math.round((new Date().valueOf() * Math.random())) + ''; 72 | }); 73 | 74 | User.method('encryptPassword', function(password) { 75 | return crypto.createHmac('sha1', this.salt).update(password).digest('hex'); 76 | }); 77 | 78 | User.pre('save', function(next) { 79 | if (!validatePresenceOf(this.password)) { 80 | next(new Error('Invalid password')); 81 | } else { 82 | next(); 83 | } 84 | }); 85 | 86 | /** 87 | * Model: LoginToken 88 | * 89 | * Used for session persistence. 90 | */ 91 | LoginToken = new Schema({ 92 | email: { type: String, index: true }, 93 | series: { type: String, index: true }, 94 | token: { type: String, index: true } 95 | }); 96 | 97 | LoginToken.method('randomToken', function() { 98 | return Math.round((new Date().valueOf() * Math.random())) + ''; 99 | }); 100 | 101 | LoginToken.pre('save', function(next) { 102 | // Automatically create the tokens 103 | this.token = this.randomToken(); 104 | 105 | if (this.isNew) 106 | this.series = this.randomToken(); 107 | 108 | next(); 109 | }); 110 | 111 | LoginToken.virtual('id') 112 | .get(function() { 113 | return this._id.toHexString(); 114 | }); 115 | 116 | LoginToken.virtual('cookieValue') 117 | .get(function() { 118 | return JSON.stringify({ email: this.email, token: this.token, series: this.series }); 119 | }); 120 | 121 | mongoose.model('Document', Document); 122 | mongoose.model('User', User); 123 | mongoose.model('LoginToken', LoginToken); 124 | 125 | fn(); 126 | } 127 | 128 | exports.defineModels = defineModels; 129 | 130 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodepad", 3 | "description": "A notepad written with Node", 4 | "version": "0.1.1", 5 | "homepage": "http://dailyjs.com", 6 | "author": "Alex R. Young (http://alexyoung.org)", 7 | "directories": { 8 | "public": "./public" 9 | }, 10 | "engines": { 11 | "node": ">= 0.4.0" 12 | }, 13 | "dependencies": { 14 | "express": "2.5.1", 15 | "connect": "1.8.1", 16 | "mongoose": "2.3.12", 17 | "stylus": "0.18.0", 18 | "jade": "0.18.0", 19 | "connect-mongodb": "1.1.1", 20 | "markdown": "0.3.1", 21 | "mailer": "0.6.7", 22 | "connect-timeout": "0.0.1" 23 | }, 24 | "devDependencies": { 25 | "tobi": "0.3.2", 26 | "mocha": "0.0.2" 27 | }, 28 | "bin": { 29 | "nodepad": "bin/nodepad.js" 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /public/images/glass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/images/glass.png -------------------------------------------------------------------------------- /public/images/gradient.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/images/gradient.gif -------------------------------------------------------------------------------- /public/javascripts/backbone-min.js: -------------------------------------------------------------------------------- 1 | // Backbone.js 0.5.3 2 | // (c) 2010 Jeremy Ashkenas, DocumentCloud Inc. 3 | // Backbone may be freely distributed under the MIT license. 4 | // For all details and documentation: 5 | // http://documentcloud.github.com/backbone 6 | (function(){var h=this,p=h.Backbone,e;e=typeof exports!=="undefined"?exports:h.Backbone={};e.VERSION="0.5.3";var f=h._;if(!f&&typeof require!=="undefined")f=require("underscore")._;var g=h.jQuery||h.Zepto;e.noConflict=function(){h.Backbone=p;return this};e.emulateHTTP=!1;e.emulateJSON=!1;e.Events={bind:function(a,b,c){var d=this._callbacks||(this._callbacks={});(d[a]||(d[a]=[])).push([b,c]);return this},unbind:function(a,b){var c;if(a){if(c=this._callbacks)if(b){c=c[a];if(!c)return this;for(var d= 7 | 0,e=c.length;d/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")},has:function(a){return this.attributes[a]!=null},set:function(a,b){b||(b={});if(!a)return this;if(a.attributes)a=a.attributes;var c=this.attributes,d=this._escapedAttributes;if(!b.silent&&this.validate&&!this._performValidation(a,b))return!1;if(this.idAttribute in a)this.id=a[this.idAttribute]; 10 | var e=this._changing;this._changing=!0;for(var g in a){var h=a[g];if(!f.isEqual(c[g],h))c[g]=h,delete d[g],this._changed=!0,b.silent||this.trigger("change:"+g,this,h,b)}!e&&!b.silent&&this._changed&&this.change(b);this._changing=!1;return this},unset:function(a,b){if(!(a in this.attributes))return this;b||(b={});var c={};c[a]=void 0;if(!b.silent&&this.validate&&!this._performValidation(c,b))return!1;delete this.attributes[a];delete this._escapedAttributes[a];a==this.idAttribute&&delete this.id;this._changed= 11 | !0;b.silent||(this.trigger("change:"+a,this,void 0,b),this.change(b));return this},clear:function(a){a||(a={});var b,c=this.attributes,d={};for(b in c)d[b]=void 0;if(!a.silent&&this.validate&&!this._performValidation(d,a))return!1;this.attributes={};this._escapedAttributes={};this._changed=!0;if(!a.silent){for(b in c)this.trigger("change:"+b,this,void 0,a);this.change(a)}return this},fetch:function(a){a||(a={});var b=this,c=a.success;a.success=function(d,e,f){if(!b.set(b.parse(d,f),a))return!1;c&& 12 | c(b,d)};a.error=i(a.error,b,a);return(this.sync||e.sync).call(this,"read",this,a)},save:function(a,b){b||(b={});if(a&&!this.set(a,b))return!1;var c=this,d=b.success;b.success=function(a,e,f){if(!c.set(c.parse(a,f),b))return!1;d&&d(c,a,f)};b.error=i(b.error,c,b);var f=this.isNew()?"create":"update";return(this.sync||e.sync).call(this,f,this,b)},destroy:function(a){a||(a={});if(this.isNew())return this.trigger("destroy",this,this.collection,a);var b=this,c=a.success;a.success=function(d){b.trigger("destroy", 13 | b,b.collection,a);c&&c(b,d)};a.error=i(a.error,b,a);return(this.sync||e.sync).call(this,"delete",this,a)},url:function(){var a=k(this.collection)||this.urlRoot||l();if(this.isNew())return a;return a+(a.charAt(a.length-1)=="/"?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this)},isNew:function(){return this.id==null},change:function(a){this.trigger("change",this,a);this._previousAttributes=f.clone(this.attributes);this._changed=!1},hasChanged:function(a){if(a)return this._previousAttributes[a]!= 14 | this.attributes[a];return this._changed},changedAttributes:function(a){a||(a=this.attributes);var b=this._previousAttributes,c=!1,d;for(d in a)f.isEqual(b[d],a[d])||(c=c||{},c[d]=a[d]);return c},previous:function(a){if(!a||!this._previousAttributes)return null;return this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},_performValidation:function(a,b){var c=this.validate(a);if(c)return b.error?b.error(this,c,b):this.trigger("error",this,c,b),!1;return!0}}); 15 | e.Collection=function(a,b){b||(b={});if(b.comparator)this.comparator=b.comparator;f.bindAll(this,"_onModelEvent","_removeReference");this._reset();a&&this.reset(a,{silent:!0});this.initialize.apply(this,arguments)};f.extend(e.Collection.prototype,e.Events,{model:e.Model,initialize:function(){},toJSON:function(){return this.map(function(a){return a.toJSON()})},add:function(a,b){if(f.isArray(a))for(var c=0,d=a.length;c').hide().appendTo("body")[0].contentWindow,this.navigate(a); 25 | this._hasPushState?g(window).bind("popstate",this.checkUrl):"onhashchange"in window&&!b?g(window).bind("hashchange",this.checkUrl):setInterval(this.checkUrl,this.interval);this.fragment=a;m=!0;a=window.location;b=a.pathname==this.options.root;if(this._wantsPushState&&!this._hasPushState&&!b)return this.fragment=this.getFragment(null,!0),window.location.replace(this.options.root+"#"+this.fragment),!0;else if(this._wantsPushState&&this._hasPushState&&b&&a.hash)this.fragment=a.hash.replace(j,""),window.history.replaceState({}, 26 | document.title,a.protocol+"//"+a.host+this.options.root+this.fragment);if(!this.options.silent)return this.loadUrl()},route:function(a,b){this.handlers.unshift({route:a,callback:b})},checkUrl:function(){var a=this.getFragment();a==this.fragment&&this.iframe&&(a=this.getFragment(this.iframe.location.hash));if(a==this.fragment||a==decodeURIComponent(this.fragment))return!1;this.iframe&&this.navigate(a);this.loadUrl()||this.loadUrl(window.location.hash)},loadUrl:function(a){var b=this.fragment=this.getFragment(a); 27 | return f.any(this.handlers,function(a){if(a.route.test(b))return a.callback(b),!0})},navigate:function(a,b){var c=(a||"").replace(j,"");if(!(this.fragment==c||this.fragment==decodeURIComponent(c))){if(this._hasPushState){var d=window.location;c.indexOf(this.options.root)!=0&&(c=this.options.root+c);this.fragment=c;window.history.pushState({},document.title,d.protocol+"//"+d.host+c)}else if(window.location.hash=this.fragment=c,this.iframe&&c!=this.getFragment(this.iframe.location.hash))this.iframe.document.open().close(), 28 | this.iframe.location.hash=c;b&&this.loadUrl(a)}}});e.View=function(a){this.cid=f.uniqueId("view");this._configure(a||{});this._ensureElement();this.delegateEvents();this.initialize.apply(this,arguments)};var u=/^(\S+)\s*(.*)$/,n=["model","collection","el","id","attributes","className","tagName"];f.extend(e.View.prototype,e.Events,{tagName:"div",$:function(a){return g(a,this.el)},initialize:function(){},render:function(){return this},remove:function(){g(this.el).remove();return this},make:function(a, 29 | b,c){a=document.createElement(a);b&&g(a).attr(b);c&&g(a).html(c);return a},delegateEvents:function(a){if(a||(a=this.events))for(var b in f.isFunction(a)&&(a=a.call(this)),g(this.el).unbind(".delegateEvents"+this.cid),a){var c=this[a[b]];if(!c)throw Error('Event "'+a[b]+'" does not exist');var d=b.match(u),e=d[1];d=d[2];c=f.bind(c,this);e+=".delegateEvents"+this.cid;d===""?g(this.el).bind(e,c):g(this.el).delegate(d,e,c)}},_configure:function(a){this.options&&(a=f.extend({},this.options,a));for(var b= 30 | 0,c=n.length;bShow All'); 260 | 261 | if (results.length === 0) { 262 | alert('No results found'); 263 | } else { 264 | for (var i = 0; i < results.length; i++) { 265 | var d = new Document(results[i]); 266 | appView.documentList.addDocument(d); 267 | } 268 | } 269 | }, 'json'); 270 | } 271 | }); 272 | 273 | AppView = Backbone.View.extend({ 274 | initialize: function() { 275 | this.documentList = new DocumentList(); 276 | this.searchView = new SearchView(); 277 | this.toolbar = new ListToolBar(); 278 | this.documentControls = new DocumentControls(); 279 | } 280 | }); 281 | 282 | var appView = new AppView(); 283 | window.Documents = Documents; 284 | window.appView = appView; 285 | 286 | $('#logout').click(function(e) { 287 | e.preventDefault(); 288 | if (confirm('Are you sure you want to log out?')) { 289 | var element = $(this), 290 | form = $('
'); 291 | form 292 | .attr({ 293 | method: 'POST', 294 | action: '/sessions' 295 | }) 296 | .hide() 297 | .append('') 298 | .find('input') 299 | .attr({ 300 | 'name': '_method', 301 | 'value': 'delete' 302 | }) 303 | .end() 304 | .appendTo('body') 305 | .submit(); 306 | } 307 | }); 308 | })(); 309 | -------------------------------------------------------------------------------- /public/javascripts/flash.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | function hideFlashMessages() { 3 | $(this).fadeOut(); 4 | } 5 | 6 | setTimeout(function() { 7 | $('.flash').each(hideFlashMessages); 8 | }, 5000); 9 | $('.flash').click(hideFlashMessages); 10 | })(); 11 | -------------------------------------------------------------------------------- /public/javascripts/json.js: -------------------------------------------------------------------------------- 1 | /* 2 | http://www.JSON.org/json2.js 3 | 2010-03-20 4 | 5 | Public Domain. 6 | 7 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 8 | 9 | See http://www.JSON.org/js.html 10 | 11 | 12 | This code should be minified before deployment. 13 | See http://javascript.crockford.com/jsmin.html 14 | 15 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 16 | NOT CONTROL. 17 | 18 | 19 | This file creates a global JSON object containing two methods: stringify 20 | and parse. 21 | 22 | JSON.stringify(value, replacer, space) 23 | value any JavaScript value, usually an object or array. 24 | 25 | replacer an optional parameter that determines how object 26 | values are stringified for objects. It can be a 27 | function or an array of strings. 28 | 29 | space an optional parameter that specifies the indentation 30 | of nested structures. If it is omitted, the text will 31 | be packed without extra whitespace. If it is a number, 32 | it will specify the number of spaces to indent at each 33 | level. If it is a string (such as '\t' or ' '), 34 | it contains the characters used to indent at each level. 35 | 36 | This method produces a JSON text from a JavaScript value. 37 | 38 | When an object value is found, if the object contains a toJSON 39 | method, its toJSON method will be called and the result will be 40 | stringified. A toJSON method does not serialize: it returns the 41 | value represented by the name/value pair that should be serialized, 42 | or undefined if nothing should be serialized. The toJSON method 43 | will be passed the key associated with the value, and this will be 44 | bound to the value 45 | 46 | For example, this would serialize Dates as ISO strings. 47 | 48 | Date.prototype.toJSON = function (key) { 49 | function f(n) { 50 | // Format integers to have at least two digits. 51 | return n < 10 ? '0' + n : n; 52 | } 53 | 54 | return this.getUTCFullYear() + '-' + 55 | f(this.getUTCMonth() + 1) + '-' + 56 | f(this.getUTCDate()) + 'T' + 57 | f(this.getUTCHours()) + ':' + 58 | f(this.getUTCMinutes()) + ':' + 59 | f(this.getUTCSeconds()) + 'Z'; 60 | }; 61 | 62 | You can provide an optional replacer method. It will be passed the 63 | key and value of each member, with this bound to the containing 64 | object. The value that is returned from your method will be 65 | serialized. If your method returns undefined, then the member will 66 | be excluded from the serialization. 67 | 68 | If the replacer parameter is an array of strings, then it will be 69 | used to select the members to be serialized. It filters the results 70 | such that only members with keys listed in the replacer array are 71 | stringified. 72 | 73 | Values that do not have JSON representations, such as undefined or 74 | functions, will not be serialized. Such values in objects will be 75 | dropped; in arrays they will be replaced with null. You can use 76 | a replacer function to replace those with JSON values. 77 | JSON.stringify(undefined) returns undefined. 78 | 79 | The optional space parameter produces a stringification of the 80 | value that is filled with line breaks and indentation to make it 81 | easier to read. 82 | 83 | If the space parameter is a non-empty string, then that string will 84 | be used for indentation. If the space parameter is a number, then 85 | the indentation will be that many spaces. 86 | 87 | Example: 88 | 89 | text = JSON.stringify(['e', {pluribus: 'unum'}]); 90 | // text is '["e",{"pluribus":"unum"}]' 91 | 92 | 93 | text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); 94 | // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' 95 | 96 | text = JSON.stringify([new Date()], function (key, value) { 97 | return this[key] instanceof Date ? 98 | 'Date(' + this[key] + ')' : value; 99 | }); 100 | // text is '["Date(---current time---)"]' 101 | 102 | 103 | JSON.parse(text, reviver) 104 | This method parses a JSON text to produce an object or array. 105 | It can throw a SyntaxError exception. 106 | 107 | The optional reviver parameter is a function that can filter and 108 | transform the results. It receives each of the keys and values, 109 | and its return value is used instead of the original value. 110 | If it returns what it received, then the structure is not modified. 111 | If it returns undefined then the member is deleted. 112 | 113 | Example: 114 | 115 | // Parse the text. Values that look like ISO date strings will 116 | // be converted to Date objects. 117 | 118 | myData = JSON.parse(text, function (key, value) { 119 | var a; 120 | if (typeof value === 'string') { 121 | a = 122 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 123 | if (a) { 124 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], 125 | +a[5], +a[6])); 126 | } 127 | } 128 | return value; 129 | }); 130 | 131 | myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { 132 | var d; 133 | if (typeof value === 'string' && 134 | value.slice(0, 5) === 'Date(' && 135 | value.slice(-1) === ')') { 136 | d = new Date(value.slice(5, -1)); 137 | if (d) { 138 | return d; 139 | } 140 | } 141 | return value; 142 | }); 143 | 144 | 145 | This is a reference implementation. You are free to copy, modify, or 146 | redistribute. 147 | */ 148 | 149 | /*jslint evil: true, strict: false */ 150 | 151 | /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, 152 | call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, 153 | getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, 154 | lastIndex, length, parse, prototype, push, replace, slice, stringify, 155 | test, toJSON, toString, valueOf 156 | */ 157 | 158 | 159 | // Create a JSON object only if one does not already exist. We create the 160 | // methods in a closure to avoid creating global variables. 161 | 162 | if (!this.JSON) { 163 | this.JSON = {}; 164 | } 165 | 166 | (function () { 167 | 168 | function f(n) { 169 | // Format integers to have at least two digits. 170 | return n < 10 ? '0' + n : n; 171 | } 172 | 173 | if (typeof Date.prototype.toJSON !== 'function') { 174 | 175 | Date.prototype.toJSON = function (key) { 176 | 177 | return isFinite(this.valueOf()) ? 178 | this.getUTCFullYear() + '-' + 179 | f(this.getUTCMonth() + 1) + '-' + 180 | f(this.getUTCDate()) + 'T' + 181 | f(this.getUTCHours()) + ':' + 182 | f(this.getUTCMinutes()) + ':' + 183 | f(this.getUTCSeconds()) + 'Z' : null; 184 | }; 185 | 186 | String.prototype.toJSON = 187 | Number.prototype.toJSON = 188 | Boolean.prototype.toJSON = function (key) { 189 | return this.valueOf(); 190 | }; 191 | } 192 | 193 | var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 194 | escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 195 | gap, 196 | indent, 197 | meta = { // table of character substitutions 198 | '\b': '\\b', 199 | '\t': '\\t', 200 | '\n': '\\n', 201 | '\f': '\\f', 202 | '\r': '\\r', 203 | '"' : '\\"', 204 | '\\': '\\\\' 205 | }, 206 | rep; 207 | 208 | 209 | function quote(string) { 210 | 211 | // If the string contains no control characters, no quote characters, and no 212 | // backslash characters, then we can safely slap some quotes around it. 213 | // Otherwise we must also replace the offending characters with safe escape 214 | // sequences. 215 | 216 | escapable.lastIndex = 0; 217 | return escapable.test(string) ? 218 | '"' + string.replace(escapable, function (a) { 219 | var c = meta[a]; 220 | return typeof c === 'string' ? c : 221 | '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 222 | }) + '"' : 223 | '"' + string + '"'; 224 | } 225 | 226 | 227 | function str(key, holder) { 228 | 229 | // Produce a string from holder[key]. 230 | 231 | var i, // The loop counter. 232 | k, // The member key. 233 | v, // The member value. 234 | length, 235 | mind = gap, 236 | partial, 237 | value = holder[key]; 238 | 239 | // If the value has a toJSON method, call it to obtain a replacement value. 240 | 241 | if (value && typeof value === 'object' && 242 | typeof value.toJSON === 'function') { 243 | value = value.toJSON(key); 244 | } 245 | 246 | // If we were called with a replacer function, then call the replacer to 247 | // obtain a replacement value. 248 | 249 | if (typeof rep === 'function') { 250 | value = rep.call(holder, key, value); 251 | } 252 | 253 | // What happens next depends on the value's type. 254 | 255 | switch (typeof value) { 256 | case 'string': 257 | return quote(value); 258 | 259 | case 'number': 260 | 261 | // JSON numbers must be finite. Encode non-finite numbers as null. 262 | 263 | return isFinite(value) ? String(value) : 'null'; 264 | 265 | case 'boolean': 266 | case 'null': 267 | 268 | // If the value is a boolean or null, convert it to a string. Note: 269 | // typeof null does not produce 'null'. The case is included here in 270 | // the remote chance that this gets fixed someday. 271 | 272 | return String(value); 273 | 274 | // If the type is 'object', we might be dealing with an object or an array or 275 | // null. 276 | 277 | case 'object': 278 | 279 | // Due to a specification blunder in ECMAScript, typeof null is 'object', 280 | // so watch out for that case. 281 | 282 | if (!value) { 283 | return 'null'; 284 | } 285 | 286 | // Make an array to hold the partial results of stringifying this object value. 287 | 288 | gap += indent; 289 | partial = []; 290 | 291 | // Is the value an array? 292 | 293 | if (Object.prototype.toString.apply(value) === '[object Array]') { 294 | 295 | // The value is an array. Stringify every element. Use null as a placeholder 296 | // for non-JSON values. 297 | 298 | length = value.length; 299 | for (i = 0; i < length; i += 1) { 300 | partial[i] = str(i, value) || 'null'; 301 | } 302 | 303 | // Join all of the elements together, separated with commas, and wrap them in 304 | // brackets. 305 | 306 | v = partial.length === 0 ? '[]' : 307 | gap ? '[\n' + gap + 308 | partial.join(',\n' + gap) + '\n' + 309 | mind + ']' : 310 | '[' + partial.join(',') + ']'; 311 | gap = mind; 312 | return v; 313 | } 314 | 315 | // If the replacer is an array, use it to select the members to be stringified. 316 | 317 | if (rep && typeof rep === 'object') { 318 | length = rep.length; 319 | for (i = 0; i < length; i += 1) { 320 | k = rep[i]; 321 | if (typeof k === 'string') { 322 | v = str(k, value); 323 | if (v) { 324 | partial.push(quote(k) + (gap ? ': ' : ':') + v); 325 | } 326 | } 327 | } 328 | } else { 329 | 330 | // Otherwise, iterate through all of the keys in the object. 331 | 332 | for (k in value) { 333 | if (Object.hasOwnProperty.call(value, k)) { 334 | v = str(k, value); 335 | if (v) { 336 | partial.push(quote(k) + (gap ? ': ' : ':') + v); 337 | } 338 | } 339 | } 340 | } 341 | 342 | // Join all of the member texts together, separated with commas, 343 | // and wrap them in braces. 344 | 345 | v = partial.length === 0 ? '{}' : 346 | gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + 347 | mind + '}' : '{' + partial.join(',') + '}'; 348 | gap = mind; 349 | return v; 350 | } 351 | } 352 | 353 | // If the JSON object does not yet have a stringify method, give it one. 354 | 355 | if (typeof JSON.stringify !== 'function') { 356 | JSON.stringify = function (value, replacer, space) { 357 | 358 | // The stringify method takes a value and an optional replacer, and an optional 359 | // space parameter, and returns a JSON text. The replacer can be a function 360 | // that can replace values, or an array of strings that will select the keys. 361 | // A default replacer method can be provided. Use of the space parameter can 362 | // produce text that is more easily readable. 363 | 364 | var i; 365 | gap = ''; 366 | indent = ''; 367 | 368 | // If the space parameter is a number, make an indent string containing that 369 | // many spaces. 370 | 371 | if (typeof space === 'number') { 372 | for (i = 0; i < space; i += 1) { 373 | indent += ' '; 374 | } 375 | 376 | // If the space parameter is a string, it will be used as the indent string. 377 | 378 | } else if (typeof space === 'string') { 379 | indent = space; 380 | } 381 | 382 | // If there is a replacer, it must be a function or an array. 383 | // Otherwise, throw an error. 384 | 385 | rep = replacer; 386 | if (replacer && typeof replacer !== 'function' && 387 | (typeof replacer !== 'object' || 388 | typeof replacer.length !== 'number')) { 389 | throw new Error('JSON.stringify'); 390 | } 391 | 392 | // Make a fake root object containing our value under the key of ''. 393 | // Return the result of stringifying the value. 394 | 395 | return str('', {'': value}); 396 | }; 397 | } 398 | 399 | 400 | // If the JSON object does not yet have a parse method, give it one. 401 | 402 | if (typeof JSON.parse !== 'function') { 403 | JSON.parse = function (text, reviver) { 404 | 405 | // The parse method takes a text and an optional reviver function, and returns 406 | // a JavaScript value if the text is a valid JSON text. 407 | 408 | var j; 409 | 410 | function walk(holder, key) { 411 | 412 | // The walk method is used to recursively walk the resulting structure so 413 | // that modifications can be made. 414 | 415 | var k, v, value = holder[key]; 416 | if (value && typeof value === 'object') { 417 | for (k in value) { 418 | if (Object.hasOwnProperty.call(value, k)) { 419 | v = walk(value, k); 420 | if (v !== undefined) { 421 | value[k] = v; 422 | } else { 423 | delete value[k]; 424 | } 425 | } 426 | } 427 | } 428 | return reviver.call(holder, key, value); 429 | } 430 | 431 | 432 | // Parsing happens in four stages. In the first stage, we replace certain 433 | // Unicode characters with escape sequences. JavaScript handles many characters 434 | // incorrectly, either silently deleting them, or treating them as line endings. 435 | 436 | text = String(text); 437 | cx.lastIndex = 0; 438 | if (cx.test(text)) { 439 | text = text.replace(cx, function (a) { 440 | return '\\u' + 441 | ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 442 | }); 443 | } 444 | 445 | // In the second stage, we run the text against regular expressions that look 446 | // for non-JSON patterns. We are especially concerned with '()' and 'new' 447 | // because they can cause invocation, and '=' because it can cause mutation. 448 | // But just to be safe, we want to reject all unexpected forms. 449 | 450 | // We split the second stage into 4 regexp operations in order to work around 451 | // crippling inefficiencies in IE's and Safari's regexp engines. First we 452 | // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we 453 | // replace all simple value tokens with ']' characters. Third, we delete all 454 | // open brackets that follow a colon or comma or that begin the text. Finally, 455 | // we look to see that the remaining characters are only whitespace or ']' or 456 | // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. 457 | 458 | if (/^[\],:{}\s]*$/. 459 | test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'). 460 | replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). 461 | replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { 462 | 463 | // In the third stage we use the eval function to compile the text into a 464 | // JavaScript structure. The '{' operator is subject to a syntactic ambiguity 465 | // in JavaScript: it can begin a block or an object literal. We wrap the text 466 | // in parens to eliminate the ambiguity. 467 | 468 | j = eval('(' + text + ')'); 469 | 470 | // In the optional fourth stage, we recursively walk the new structure, passing 471 | // each name/value pair to a reviver function for possible transformation. 472 | 473 | return typeof reviver === 'function' ? 474 | walk({'': j}, '') : j; 475 | } 476 | 477 | // If the text is not JSON parseable, then a SyntaxError is thrown. 478 | 479 | throw new SyntaxError('JSON.parse'); 480 | }; 481 | } 482 | }()); 483 | -------------------------------------------------------------------------------- /public/javascripts/resize.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | // Correct widths and heights based on window size 3 | function resize() { 4 | var height = $(window).height() - $('#header').height() - 1, 5 | width = $('.content').width(), 6 | ov = $('.outline-view'), 7 | ed = $('#editor'), 8 | toolbar = $('.toolbar'), 9 | divider = $('.content-divider'), 10 | content = $('.content'), 11 | controls = $('#controls'); 12 | 13 | $('#DocumentTitles').css({ height: height - toolbar.height() + 'px' }); 14 | ov.css({ height: height + 'px' }); 15 | toolbar.css({ width: ov.width() + 'px' }); 16 | 17 | content.css({ 18 | height: height - controls.height() + 'px', 19 | width: $('body').width() - ov.width() - divider.width() - 1 + 'px' 20 | }); 21 | 22 | divider.css({ height: height + 'px' }); 23 | 24 | ed.css({ 25 | width: content.width() + 'px', 26 | height: content.height() - 22 - $('input.title').height() + 'px' 27 | }).focus(); 28 | 29 | $('#controls').css({ 30 | width: content.width() + 'px' 31 | }); 32 | } 33 | 34 | $(window).resize(resize); 35 | $(window).focus(resize); 36 | resize(); 37 | })(); 38 | 39 | -------------------------------------------------------------------------------- /public/javascripts/underscore-min.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.1.5 2 | // (c) 2011 Jeremy Ashkenas, DocumentCloud Inc. 3 | // Underscore is freely distributable under the MIT license. 4 | // Portions of Underscore are inspired or borrowed from Prototype, 5 | // Oliver Steele's Functional, and John Resig's Micro-Templating. 6 | // For all details and documentation: 7 | // http://documentcloud.github.com/underscore 8 | (function(){var q=this,D=q._,n={},k=Array.prototype,o=Object.prototype,i=k.slice,E=k.unshift,F=o.toString,m=o.hasOwnProperty,s=k.forEach,t=k.map,u=k.reduce,v=k.reduceRight,w=k.filter,x=k.every,y=k.some,p=k.indexOf,z=k.lastIndexOf;o=Array.isArray;var G=Object.keys,A=Function.prototype.bind,c=function(a){return new l(a)};if(typeof module!=="undefined"&&module.exports){module.exports=c;c._=c}else q._=c;c.VERSION="1.1.5";var j=c.each=c.forEach=function(a,b,d){if(a!=null)if(s&&a.forEach===s)a.forEach(b, 9 | d);else if(c.isNumber(a.length))for(var e=0,f=a.length;e=e.computed&&(e={value:f,computed:g})});return e.value};c.min=function(a,b,d){if(!b&&c.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};j(a,function(f,g,h){g=b?b.call(d,f,g,h):f;gh?1:0}),"value")};c.sortedIndex= 14 | function(a,b,d){d=d||c.identity;for(var e=0,f=a.length;e>1;d(a[g])=0})})};c.zip=function(){for(var a=i.call(arguments),b=c.max(c.pluck(a,"length")),d=Array(b),e=0;e=0;d--)b=[a[d].apply(this,b)];return b[0]}};c.keys=G||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var b=[],d;for(d in a)if(m.call(a, 20 | d))b[b.length]=d;return b};c.values=function(a){return c.map(a,c.identity)};c.functions=c.methods=function(a){return c.filter(c.keys(a),function(b){return c.isFunction(a[b])}).sort()};c.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};c.defaults=function(a){j(i.call(arguments,1),function(b){for(var d in b)if(a[d]==null)a[d]=b[d]});return a};c.clone=function(a){return c.isArray(a)?a.slice():c.extend({},a)};c.tap=function(a,b){b(a);return a};c.isEqual=function(a, 21 | b){if(a===b)return true;var d=typeof a;if(d!=typeof b)return false;if(a==b)return true;if(!a&&b||a&&!b)return false;if(a._chain)a=a._wrapped;if(b._chain)b=b._wrapped;if(a.isEqual)return a.isEqual(b);if(c.isDate(a)&&c.isDate(b))return a.getTime()===b.getTime();if(c.isNaN(a)&&c.isNaN(b))return false;if(c.isRegExp(a)&&c.isRegExp(b))return a.source===b.source&&a.global===b.global&&a.ignoreCase===b.ignoreCase&&a.multiline===b.multiline;if(d!=="object")return false;if(a.length&&a.length!==b.length)return false; 22 | d=c.keys(a);var e=c.keys(b);if(d.length!=e.length)return false;for(var f in a)if(!(f in b)||!c.isEqual(a[f],b[f]))return false;return true};c.isEmpty=function(a){if(c.isArray(a)||c.isString(a))return a.length===0;for(var b in a)if(m.call(a,b))return false;return true};c.isElement=function(a){return!!(a&&a.nodeType==1)};c.isArray=o||function(a){return F.call(a)==="[object Array]"};c.isArguments=function(a){return!!(a&&m.call(a,"callee"))};c.isFunction=function(a){return!!(a&&a.constructor&&a.call&& 23 | a.apply)};c.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};c.isNumber=function(a){return!!(a===0||a&&a.toExponential&&a.toFixed)};c.isNaN=function(a){return a!==a};c.isBoolean=function(a){return a===true||a===false};c.isDate=function(a){return!!(a&&a.getTimezoneOffset&&a.setUTCFullYear)};c.isRegExp=function(a){return!!(a&&a.test&&a.exec&&(a.ignoreCase||a.ignoreCase===false))};c.isNull=function(a){return a===null};c.isUndefined=function(a){return a===void 0};c.noConflict=function(){q._= 24 | D;return this};c.identity=function(a){return a};c.times=function(a,b,d){for(var e=0;e/g,interpolate:/<%=([\s\S]+?)%>/g};c.template=function(a,b){var d=c.templateSettings;d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.interpolate, 25 | function(e,f){return"',"+f.replace(/\\'/g,"'")+",'"}).replace(d.evaluate||null,function(e,f){return"');"+f.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+"__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');";d=new Function("obj",d);return b?d(b):d};var l=function(a){this._wrapped=a};c.prototype=l.prototype;var r=function(a,b){return b?c(a).chain():a},I=function(a,b){l.prototype[a]=function(){var d=i.call(arguments);E.call(d,this._wrapped);return r(b.apply(c, 26 | d),this._chain)}};c.mixin(c);j(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var b=k[a];l.prototype[a]=function(){b.apply(this._wrapped,arguments);return r(this._wrapped,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];l.prototype[a]=function(){return r(b.apply(this._wrapped,arguments),this._chain)}});l.prototype.chain=function(){this._chain=true;return this};l.prototype.value=function(){return this._wrapped}})(); 27 | -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/button_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/button_bg.png -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/datepicker.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/datepicker.gif -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/icon_sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/icon_sprite.png -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/progress_bar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/progress_bar.gif -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/slider_h_bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/slider_h_bg.gif -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/slider_handles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/slider_handles.png -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/slider_v_bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/slider_v_bg.gif -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/tab_bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/tab_bg.gif -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/the_gradient.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/the_gradient.gif -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/ui-bg_diagonals-thick_18_b81900_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/ui-bg_diagonals-thick_18_b81900_40x40.png -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/ui-bg_diagonals-thick_20_666666_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/ui-bg_diagonals-thick_20_666666_40x40.png -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/ui-bg_flat_10_000000_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/ui-bg_flat_10_000000_40x100.png -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/ui-bg_glass_100_f6f6f6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/ui-bg_glass_100_f6f6f6_1x400.png -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/ui-bg_glass_100_fdf5ce_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/ui-bg_glass_100_fdf5ce_1x400.png -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/ui-bg_gloss-wave_35_f6a828_500x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/ui-bg_gloss-wave_35_f6a828_500x100.png -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/ui-bg_highlight-soft_100_eeeeee_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/ui-bg_highlight-soft_100_eeeeee_1x100.png -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/ui-bg_highlight-soft_75_ffe45c_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/ui-bg_highlight-soft_75_ffe45c_1x100.png -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/ui-icons_228ef1_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/ui-icons_228ef1_256x240.png -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/ui-icons_ef8c08_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/ui-icons_ef8c08_256x240.png -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/ui-icons_ffd27a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/ui-icons_ffd27a_256x240.png -------------------------------------------------------------------------------- /public/stylesheets/aristo/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/nodepad/68357e774e2c971b39f9dec343a5c1c763d76401/public/stylesheets/aristo/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /public/stylesheets/aristo/jquery-ui-1.8.5.custom.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Aristo for jQuery UI 3 | * Licensed under Creative Commons Attribution-Share Alike 3.0 with permission from 280 North and Pinvoke. 4 | */ 5 | 6 | /* 7 | * jQuery UI CSS Framework @VERSION 8 | * 9 | * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) 10 | * Dual licensed under the MIT or GPL Version 2 licenses. 11 | * http://jquery.org/license 12 | * 13 | * http://docs.jquery.com/UI/Theming/API 14 | */ 15 | 16 | /* Layout helpers 17 | ----------------------------------*/ 18 | .ui-helper-hidden { display: none; } 19 | .ui-helper-hidden-accessible { position: absolute; left: -99999999px; } 20 | .ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } 21 | .ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } 22 | .ui-helper-clearfix { display: inline-block; } 23 | /* required comment for clearfix to work in Opera \*/ 24 | * html .ui-helper-clearfix { height:1%; } 25 | .ui-helper-clearfix { display:block; } 26 | /* end clearfix */ 27 | .ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } 28 | 29 | 30 | /* Interaction Cues 31 | ----------------------------------*/ 32 | .ui-state-disabled { cursor: default !important; } 33 | 34 | 35 | /* Icons 36 | ----------------------------------*/ 37 | 38 | /* states and images */ 39 | .ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } 40 | 41 | 42 | /* Misc visuals 43 | ----------------------------------*/ 44 | 45 | /* Overlays */ 46 | .ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } 47 | 48 | 49 | /* 50 | * jQuery UI CSS Framework @VERSION 51 | * 52 | * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) 53 | * Dual licensed under the MIT or GPL Version 2 licenses. 54 | * http://jquery.org/license 55 | * 56 | * http://docs.jquery.com/UI/Theming/API 57 | * 58 | * To view and modify this theme, visit http://jqueryui.com/themeroller/?ctl=themeroller 59 | */ 60 | 61 | 62 | /* Component containers 63 | ----------------------------------*/ 64 | .ui-widget { font-family: Helvetica, Arial, sans-serif; outline: none;} 65 | .ui-widget a { outline: none; } 66 | .ui-widget .ui-widget { font-size: 1em; } 67 | .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Helvetica, Arial, sans-serif; font-size: 1em; } 68 | .ui-widget-content { border: 1px solid #dddddd; color: #333333; background: #FFFFFF; } 69 | .ui-widget-content a { color: #333333; } 70 | .ui-widget-header { border: 1px solid #8ab0c6; background: #a7cfe6; color: #ffffff; font-weight: bold; } 71 | .ui-widget-header a { color: #ffffff; } 72 | 73 | /* Interaction states 74 | ----------------------------------*/ 75 | .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #cccccc; background: #f6f6f6 url(images/ui-bg_glass_100_f6f6f6_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #5F83B9; } 76 | .ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #1c94c4; text-decoration: none; } 77 | .ui-state-hover, .ui-widget-content .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus { border: 1px solid #749aaf; background: #fdf5ce url(images/ui-bg_glass_100_fdf5ce_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #c77405; } 78 | .ui-state-hover a, .ui-state-hover a:hover { color: #c77405; text-decoration: none; } 79 | .ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #fbd850; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #eb8f00; } 80 | .ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #eb8f00; text-decoration: none; } 81 | .ui-widget :active { outline: none; } 82 | 83 | /* Interaction Cues 84 | ----------------------------------*/ 85 | .ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #d2dbf4; background: #f4f8fd; color: #0d2054; -moz-border-radius: 0px !important; -webkit-border-radius: 0px !important; border-radius: 0px !important; font-size: 11px; } 86 | .ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #0d2054; } 87 | .ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #e2d0d0; background: #fcf0f0; color: #280b0b; -moz-border-radius: 0px !important; -webkit-border-radius: 0px !important; border-radius: 0px !important; font-size: 11px; } 88 | .ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #280b0b; } 89 | .ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #280b0b; } 90 | .ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } 91 | .ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } 92 | .ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } 93 | .ui-state-highlight p, .ui-state-error p { margin: 8px 0px; padding: 1px 0px; } 94 | .ui-state-highlight .ui-icon, .ui-state-error .ui-icon { margin: -1px 8px 0px 0px !important; } 95 | 96 | /* Icons 97 | ----------------------------------*/ 98 | 99 | /* states and images */ 100 | .ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); } 101 | .ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } 102 | .ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); } 103 | .ui-state-default .ui-icon { background-image: url(images/ui-icons_222222_256x240.png); } 104 | .ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } 105 | .ui-state-active .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } 106 | .ui-state-highlight .ui-icon {background-image: url(images/ui-icons_228ef1_256x240.png); } 107 | .ui-state-error .ui-icon, .ui-state-error-text .ui-icon { background: url(images/icon_sprite.png) -16px 0px no-repeat !important; } 108 | 109 | /* positioning */ 110 | .ui-icon-carat-1-n { background-position: 0 0; } 111 | .ui-icon-carat-1-ne { background-position: -16px 0; } 112 | .ui-icon-carat-1-e { background-position: -32px 0; } 113 | .ui-icon-carat-1-se { background-position: -48px 0; } 114 | .ui-icon-carat-1-s { background-position: -64px 0; } 115 | .ui-icon-carat-1-sw { background-position: -80px 0; } 116 | .ui-icon-carat-1-w { background-position: -96px 0; } 117 | .ui-icon-carat-1-nw { background-position: -112px 0; } 118 | .ui-icon-carat-2-n-s { background-position: -128px 0; } 119 | .ui-icon-carat-2-e-w { background-position: -144px 0; } 120 | .ui-icon-triangle-1-n { background-position: 0 -16px; } 121 | .ui-icon-triangle-1-ne { background-position: -16px -16px; } 122 | .ui-icon-triangle-1-e { background-position: -32px -16px; } 123 | .ui-icon-triangle-1-se { background-position: -48px -16px; } 124 | .ui-icon-triangle-1-s { background-position: -64px -16px; } 125 | .ui-icon-triangle-1-sw { background-position: -80px -16px; } 126 | .ui-icon-triangle-1-w { background-position: -96px -16px; } 127 | .ui-icon-triangle-1-nw { background-position: -112px -16px; } 128 | .ui-icon-triangle-2-n-s { background-position: -128px -16px; } 129 | .ui-icon-triangle-2-e-w { background-position: -144px -16px; } 130 | .ui-icon-arrow-1-n { background-position: 0 -32px; } 131 | .ui-icon-arrow-1-ne { background-position: -16px -32px; } 132 | .ui-icon-arrow-1-e { background-position: -32px -32px; } 133 | .ui-icon-arrow-1-se { background-position: -48px -32px; } 134 | .ui-icon-arrow-1-s { background-position: -64px -32px; } 135 | .ui-icon-arrow-1-sw { background-position: -80px -32px; } 136 | .ui-icon-arrow-1-w { background-position: -96px -32px; } 137 | .ui-icon-arrow-1-nw { background-position: -112px -32px; } 138 | .ui-icon-arrow-2-n-s { background-position: -128px -32px; } 139 | .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } 140 | .ui-icon-arrow-2-e-w { background-position: -160px -32px; } 141 | .ui-icon-arrow-2-se-nw { background-position: -176px -32px; } 142 | .ui-icon-arrowstop-1-n { background-position: -192px -32px; } 143 | .ui-icon-arrowstop-1-e { background-position: -208px -32px; } 144 | .ui-icon-arrowstop-1-s { background-position: -224px -32px; } 145 | .ui-icon-arrowstop-1-w { background-position: -240px -32px; } 146 | .ui-icon-arrowthick-1-n { background-position: 0 -48px; } 147 | .ui-icon-arrowthick-1-ne { background-position: -16px -48px; } 148 | .ui-icon-arrowthick-1-e { background-position: -32px -48px; } 149 | .ui-icon-arrowthick-1-se { background-position: -48px -48px; } 150 | .ui-icon-arrowthick-1-s { background-position: -64px -48px; } 151 | .ui-icon-arrowthick-1-sw { background-position: -80px -48px; } 152 | .ui-icon-arrowthick-1-w { background-position: -96px -48px; } 153 | .ui-icon-arrowthick-1-nw { background-position: -112px -48px; } 154 | .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } 155 | .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } 156 | .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } 157 | .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } 158 | .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } 159 | .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } 160 | .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } 161 | .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } 162 | .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } 163 | .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } 164 | .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } 165 | .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } 166 | .ui-icon-arrowreturn-1-w { background-position: -64px -64px; } 167 | .ui-icon-arrowreturn-1-n { background-position: -80px -64px; } 168 | .ui-icon-arrowreturn-1-e { background-position: -96px -64px; } 169 | .ui-icon-arrowreturn-1-s { background-position: -112px -64px; } 170 | .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } 171 | .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } 172 | .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } 173 | .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } 174 | .ui-icon-arrow-4 { background-position: 0 -80px; } 175 | .ui-icon-arrow-4-diag { background-position: -16px -80px; } 176 | .ui-icon-extlink { background-position: -32px -80px; } 177 | .ui-icon-newwin { background-position: -48px -80px; } 178 | .ui-icon-refresh { background-position: -64px -80px; } 179 | .ui-icon-shuffle { background-position: -80px -80px; } 180 | .ui-icon-transfer-e-w { background-position: -96px -80px; } 181 | .ui-icon-transferthick-e-w { background-position: -112px -80px; } 182 | .ui-icon-folder-collapsed { background-position: 0 -96px; } 183 | .ui-icon-folder-open { background-position: -16px -96px; } 184 | .ui-icon-document { background-position: -32px -96px; } 185 | .ui-icon-document-b { background-position: -48px -96px; } 186 | .ui-icon-note { background-position: -64px -96px; } 187 | .ui-icon-mail-closed { background-position: -80px -96px; } 188 | .ui-icon-mail-open { background-position: -96px -96px; } 189 | .ui-icon-suitcase { background-position: -112px -96px; } 190 | .ui-icon-comment { background-position: -128px -96px; } 191 | .ui-icon-person { background-position: -144px -96px; } 192 | .ui-icon-print { background-position: -160px -96px; } 193 | .ui-icon-trash { background-position: -176px -96px; } 194 | .ui-icon-locked { background-position: -192px -96px; } 195 | .ui-icon-unlocked { background-position: -208px -96px; } 196 | .ui-icon-bookmark { background-position: -224px -96px; } 197 | .ui-icon-tag { background-position: -240px -96px; } 198 | .ui-icon-home { background-position: 0 -112px; } 199 | .ui-icon-flag { background-position: -16px -112px; } 200 | .ui-icon-calendar { background-position: -32px -112px; } 201 | .ui-icon-cart { background-position: -48px -112px; } 202 | .ui-icon-pencil { background-position: -64px -112px; } 203 | .ui-icon-clock { background-position: -80px -112px; } 204 | .ui-icon-disk { background-position: -96px -112px; } 205 | .ui-icon-calculator { background-position: -112px -112px; } 206 | .ui-icon-zoomin { background-position: -128px -112px; } 207 | .ui-icon-zoomout { background-position: -144px -112px; } 208 | .ui-icon-search { background-position: -160px -112px; } 209 | .ui-icon-wrench { background-position: -176px -112px; } 210 | .ui-icon-gear { background-position: -192px -112px; } 211 | .ui-icon-heart { background-position: -208px -112px; } 212 | .ui-icon-star { background-position: -224px -112px; } 213 | .ui-icon-link { background-position: -240px -112px; } 214 | .ui-icon-cancel { background-position: 0 -128px; } 215 | .ui-icon-plus { background-position: -16px -128px; } 216 | .ui-icon-plusthick { background-position: -32px -128px; } 217 | .ui-icon-minus { background-position: -48px -128px; } 218 | .ui-icon-minusthick { background-position: -64px -128px; } 219 | .ui-icon-close { background-position: -80px -128px; } 220 | .ui-icon-closethick { background-position: -96px -128px; } 221 | .ui-icon-key { background-position: -112px -128px; } 222 | .ui-icon-lightbulb { background-position: -128px -128px; } 223 | .ui-icon-scissors { background-position: -144px -128px; } 224 | .ui-icon-clipboard { background-position: -160px -128px; } 225 | .ui-icon-copy { background-position: -176px -128px; } 226 | .ui-icon-contact { background-position: -192px -128px; } 227 | .ui-icon-image { background-position: -208px -128px; } 228 | .ui-icon-video { background-position: -224px -128px; } 229 | .ui-icon-script { background-position: -240px -128px; } 230 | .ui-icon-alert { background-position: 0 -144px; } 231 | .ui-icon-info { background: url(images/icon_sprite.png) 0px 0px no-repeat !important; } 232 | .ui-icon-notice { background-position: -32px -144px; } 233 | .ui-icon-help { background-position: -48px -144px; } 234 | .ui-icon-check { background-position: -64px -144px; } 235 | .ui-icon-bullet { background-position: -80px -144px; } 236 | .ui-icon-radio-off { background-position: -96px -144px; } 237 | .ui-icon-radio-on { background-position: -112px -144px; } 238 | .ui-icon-pin-w { background-position: -128px -144px; } 239 | .ui-icon-pin-s { background-position: -144px -144px; } 240 | .ui-icon-play { background-position: 0 -160px; } 241 | .ui-icon-pause { background-position: -16px -160px; } 242 | .ui-icon-seek-next { background-position: -32px -160px; } 243 | .ui-icon-seek-prev { background-position: -48px -160px; } 244 | .ui-icon-seek-end { background-position: -64px -160px; } 245 | .ui-icon-seek-start { background-position: -80px -160px; } 246 | /* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ 247 | .ui-icon-seek-first { background-position: -80px -160px; } 248 | .ui-icon-stop { background-position: -96px -160px; } 249 | .ui-icon-eject { background-position: -112px -160px; } 250 | .ui-icon-volume-off { background-position: -128px -160px; } 251 | .ui-icon-volume-on { background-position: -144px -160px; } 252 | .ui-icon-power { background-position: 0 -176px; } 253 | .ui-icon-signal-diag { background-position: -16px -176px; } 254 | .ui-icon-signal { background-position: -32px -176px; } 255 | .ui-icon-battery-0 { background-position: -48px -176px; } 256 | .ui-icon-battery-1 { background-position: -64px -176px; } 257 | .ui-icon-battery-2 { background-position: -80px -176px; } 258 | .ui-icon-battery-3 { background-position: -96px -176px; } 259 | .ui-icon-circle-plus { background-position: 0 -192px; } 260 | .ui-icon-circle-minus { background-position: -16px -192px; } 261 | .ui-icon-circle-close { background-position: -32px -192px; } 262 | .ui-icon-circle-triangle-e { background-position: -48px -192px; } 263 | .ui-icon-circle-triangle-s { background-position: -64px -192px; } 264 | .ui-icon-circle-triangle-w { background-position: -80px -192px; } 265 | .ui-icon-circle-triangle-n { background-position: -96px -192px; } 266 | .ui-icon-circle-arrow-e { background-position: -112px -192px; } 267 | .ui-icon-circle-arrow-s { background-position: -128px -192px; } 268 | .ui-icon-circle-arrow-w { background-position: -144px -192px; } 269 | .ui-icon-circle-arrow-n { background-position: -160px -192px; } 270 | .ui-icon-circle-zoomin { background-position: -176px -192px; } 271 | .ui-icon-circle-zoomout { background-position: -192px -192px; } 272 | .ui-icon-circle-check { background-position: -208px -192px; } 273 | .ui-icon-circlesmall-plus { background-position: 0 -208px; } 274 | .ui-icon-circlesmall-minus { background-position: -16px -208px; } 275 | .ui-icon-circlesmall-close { background-position: -32px -208px; } 276 | .ui-icon-squaresmall-plus { background-position: -48px -208px; } 277 | .ui-icon-squaresmall-minus { background-position: -64px -208px; } 278 | .ui-icon-squaresmall-close { background-position: -80px -208px; } 279 | .ui-icon-grip-dotted-vertical { background-position: 0 -224px; } 280 | .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } 281 | .ui-icon-grip-solid-vertical { background-position: -32px -224px; } 282 | .ui-icon-grip-solid-horizontal { background-position: -48px -224px; } 283 | .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } 284 | .ui-icon-grip-diagonal-se { background-position: -80px -224px; } 285 | 286 | 287 | /* Misc visuals 288 | ----------------------------------*/ 289 | 290 | /* Corner radius */ 291 | .ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; } 292 | .ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; } 293 | .ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; } 294 | .ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } 295 | .ui-corner-top { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; } 296 | .ui-corner-bottom { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } 297 | .ui-corner-right { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } 298 | .ui-corner-left { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; } 299 | .ui-corner-all { -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; } 300 | 301 | /* Overlays */ 302 | .ui-widget-overlay { background: #222d3f; opacity: .70; filter:Alpha(Opacity=70); } 303 | .ui-widget-shadow { margin: -5px 0 0 -5px; padding: 5px; background: #000000 url(images/ui-bg_flat_10_000000_40x100.png) 50% 50% repeat-x; opacity: .20;filter:Alpha(Opacity=20); -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; } 304 | 305 | /* 306 | * jQuery UI Resizable @VERSION 307 | * 308 | * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) 309 | * Dual licensed under the MIT or GPL Version 2 licenses. 310 | * http://jquery.org/license 311 | * 312 | * http://docs.jquery.com/UI/Resizable#theming 313 | */ 314 | .ui-resizable { position: relative;} 315 | .ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;} 316 | .ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; } 317 | .ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; } 318 | .ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; } 319 | .ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; } 320 | .ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; } 321 | .ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; } 322 | .ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; } 323 | .ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; } 324 | .ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;} 325 | 326 | /* 327 | * jQuery UI Selectable @VERSION 328 | * 329 | * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) 330 | * Dual licensed under the MIT or GPL Version 2 licenses. 331 | * http://jquery.org/license 332 | * 333 | * http://docs.jquery.com/UI/Selectable#theming 334 | */ 335 | .ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; } 336 | /* 337 | * jQuery UI Accordion @VERSION 338 | * 339 | * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) 340 | * Dual licensed under the MIT or GPL Version 2 licenses. 341 | * http://jquery.org/license 342 | * 343 | * http://docs.jquery.com/UI/Accordion#theming 344 | */ 345 | /* IE/Win - Fix animation bug - #4615 */ 346 | .ui-accordion { width: 100%; } 347 | .ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; background: url(images/button_bg.png) repeat-x; } 348 | .ui-accordion .ui-accordion-header .ui-state-default { background-position: 0px 0px; } 349 | .ui-accordion .ui-accordion-header.ui-state-active { background-position: 0px -33px; border-color: #749aaf !important; } 350 | .ui-accordion .ui-accordion-header.ui-state-hover, .ui-accordion h3.ui-state-default { border-color: #aaaaaa; } 351 | .ui-accordion .ui-accordion-header.ui-state-active a { color:#1c4257; } 352 | .ui-accordion .ui-accordion-header .ui-icon { background: url(images/icon_sprite.png); } 353 | .ui-accordion .ui-accordion-header.ui-state-active .ui-icon { background-position: 0px -64px; } 354 | .ui-accordion .ui-accordion-header.ui-state-default .ui-icon { background-position: -16px -80px; } 355 | .ui-accordion .ui-accordion-li-fix { display: inline; } 356 | .ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; } 357 | .ui-accordion .ui-accordion-header a { display: block; font-size: 12px; padding: .5em .5em .5em .7em; font-weight: bold; color:#4f4f4f; text-shadow: 0px 1px 0px rgba(255,255,255,0.7); } 358 | .ui-accordion-icons .ui-accordion-header a { padding-left: 24px; } 359 | .ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -7px; } 360 | .ui-accordion .ui-accordion-content { background: #f8fcfe; padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; font-size: 11px; border-color: #749aaf; } 361 | .ui-accordion .ui-accordion-content-active { display: block; } 362 | .ui-accordion .ui-accordion-header, .ui-accordion .ui-accordion-content { -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; } 363 | .ui-accordion .ui-state-active { } 364 | 365 | /* 366 | * jQuery UI Autocomplete @VERSION 367 | * 368 | * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) 369 | * Dual licensed under the MIT or GPL Version 2 licenses. 370 | * http://jquery.org/license 371 | * 372 | * http://docs.jquery.com/UI/Autocomplete#theming 373 | */ 374 | .ui-autocomplete { position: absolute; z-index: 2 !important; cursor: default; background: #FFFFFF; border: 0px none !important; padding: 0px !important; -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; -moz-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); -webkit-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); } 375 | .ui-autocomplete-loading { background: white url('images/ui-anim_basic_16x16.gif') right center no-repeat; } 376 | /* workarounds */ 377 | * html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ 378 | 379 | /* 380 | * jQuery UI Menu @VERSION 381 | * 382 | * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) 383 | * Dual licensed under the MIT or GPL Version 2 licenses. 384 | * http://jquery.org/license 385 | * 386 | * http://docs.jquery.com/UI/Menu#theming 387 | */ 388 | .ui-menu { 389 | list-style:none; 390 | padding: 2px; 391 | margin: 0; 392 | display:block; 393 | float: left; 394 | } 395 | .ui-menu .ui-menu { 396 | margin-top: -3px; 397 | } 398 | .ui-menu .ui-menu-item { 399 | margin:0; 400 | padding: 0; 401 | zoom: 1; 402 | float: left; 403 | clear: left; 404 | width: 100%; 405 | } 406 | .ui-menu .ui-menu-item a { 407 | text-decoration:none; 408 | display:block; 409 | border: 0px none; 410 | padding:.2em .4em; 411 | line-height:1.5; 412 | -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; 413 | zoom:1; 414 | } 415 | .ui-menu .ui-menu-item a.ui-state-hover, 416 | .ui-menu .ui-menu-item a.ui-state-active { 417 | background: #5f83b9; 418 | color: #FFFFFF; 419 | text-shadow: 0px 1px 1px #234386; 420 | font-weight: normal; 421 | margin: -1px; 422 | } 423 | 424 | /* 425 | * jQuery UI Button @VERSION 426 | * 427 | * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) 428 | * Dual licensed under the MIT or GPL Version 2 licenses. 429 | * http://jquery.org/license 430 | * 431 | * http://docs.jquery.com/UI/Button#theming 432 | */ 433 | .ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; border: 0px none !important; -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; } /* the overflow property removes extra width in IE */ 434 | .ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */ 435 | button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */ 436 | .ui-button-icons-only { width: 3em; } 437 | button.ui-button-icons-only { width: 3.2em; } 438 | .ui-button span { -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; border: 1px solid; } 439 | 440 | 441 | /* === INPUT:SUBMIT BUG FIX === */ 442 | input.ui-button { background: url(images/button_bg.png) 0px 0px repeat-x !important; color: #4f4f4f; -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; border: 1px solid #b6b6b6; outline: none; } 443 | input.ui-button:hover { background: url(images/button_bg.png) 0px 0px repeat-x !important; color: #313131; border-color: #9d9d9d; -moz-box-shadow: 0 0 6px rgba(0, 0, 0, 0.3); -webkit-box-shadow: 0px 0px 8px rgba(212,212,212,1); box-shadow: 0px 0px 8px rgba(212,212,212,1); } 444 | input.ui-button:active { background: url(images/button_bg.png) 0px bottom repeat-x !important; color: #4f4f4f; border-color: #b6b6b6; } 445 | 446 | /* === IE6 AND IE7 BUTTON WIDTH FIX === */ 447 | .ui-button { *display: inline !important; } 448 | 449 | .ui-state-default .ui-button-text { background: url(images/button_bg.png) 0px 0px repeat-x !important; color: #4f4f4f; border-color: #b6b6b6; } 450 | .ui-state-hover .ui-button-text { background: url(images/button_bg.png) 0px 0px repeat-x !important; color: #313131; border-color: #9d9d9d; -moz-box-shadow: 0 0 6px rgba(0, 0, 0, 0.3); -webkit-box-shadow: 0px 0px 8px rgba(212,212,212,1); box-shadow: 0px 0px 8px rgba(212,212,212,1); } 451 | .ui-state-active .ui-button-text { background: url(images/button_bg.png) 0px bottom repeat-x !important; color: #4f4f4f; border-color: #b6b6b6; } 452 | 453 | /*button text element */ 454 | .ui-button .ui-button-text { display: block; line-height: 1.4; font-weight: bold; font-size: 14px; text-shadow: 0px 1px 0px rgba(255,255,255,0.8); } 455 | .ui-button-text-only .ui-button-text { padding: 5px 12px; } 456 | .ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: 5px; text-indent: -9999999px; } 457 | .ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: 5px 12px 5px 25px; } 458 | .ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: 5px 12px 5px 25px;; } 459 | .ui-button-text-icons .ui-button-text { padding-right: 1.8em; } 460 | /* no icon support for input elements, provide padding by default */ 461 | input.ui-button { padding: .4em 1em; } 462 | 463 | /*button icon element(s) */ 464 | .ui-button .ui-icon { border: 0px none; } 465 | .ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; margin-left: 6px; } 466 | .ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; } 467 | .ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { } 468 | .ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } 469 | .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } 470 | 471 | /*button sets*/ 472 | .ui-buttonset { margin-right: 7px; } 473 | .ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; } 474 | .ui-buttonset, .ui-buttonset span { -moz-border-radius: 0px !important; -webkit-border-radius: 0px !important; border-radius: 0px !important; } 475 | .ui-corner-left .ui-button-text { -moz-border-radius-topleft: 4px !important; -webkit-border-top-left-radius: 4px !important; border-top-left-radius: 4px !important; -moz-border-radius-bottomleft: 4px !important; -webkit-border-bottom-left-radius: 4px !important; border-bottom-left-radius: 4px !important; } 476 | .ui-corner-right .ui-button-text { -moz-border-radius-topright: 4px !important; -webkit-border-top-right-radius: 4px !important; border-top-right-radius: 4px !important; -moz-border-radius-bottomright: 4px !important; -webkit-border-bottom-right-radius: 4px !important; border-bottom-right-radius: 4px !important; } 477 | .ui-buttonset .ui-state-active .ui-button-text { cursor: default; background: url(images/button_bg.png) 0px -33px repeat-x !important; color: #1c4257; border-color: #7096ab; -moz-box-shadow: none !important; -webkit-box-shadow: none !important; box-shadow: none !important; } 478 | 479 | /* workarounds */ 480 | button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */ 481 | 482 | /* 483 | * jQuery UI Dialog @VERSION 484 | * 485 | * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) 486 | * Dual licensed under the MIT or GPL Version 2 licenses. 487 | * http://jquery.org/license 488 | * 489 | * http://docs.jquery.com/UI/Dialog#theming 490 | */ 491 | .ui-dialog { position: absolute; padding: 0; width: 300px; overflow: hidden; background: #FFFFFF; -moz-box-shadow: 0px 5px 8px rgba(0,0,0,0.8); -webkit-box-shadow: 0px 5px 8px rgba(0,0,0,0.8); box-shadow: 0px 5px 8px rgba(0,0,0,0.8); } 492 | .ui-dialog .ui-dialog-titlebar { padding: .5em 1em .3em; position: relative; border-width: 0px 0px 1px 0px; border-color: #979797; background: url(images/the_gradient.gif) 0px 0px repeat-x; -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; } 493 | .ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .2em 0; font-size: 13px; color: #000000; text-shadow: 0px 1px 0px rgba(255,255,255,0.8); } 494 | .ui-dialog .ui-dialog-titlebar-close { position: absolute; right: 6px; top: 50%; width: 16px; margin: -9px 0 0 0; height: 16px; } 495 | .ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; background: url(images/icon_sprite.png) 0px -16px no-repeat; } 496 | .ui-dialog-titlebar .ui-state-hover { -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; border: 0px none; background: transparent; } 497 | .ui-dialog .ui-dialog-titlebar-close.ui-state-hover span { background-position: -16px -16px ; } 498 | .ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; } 499 | .ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; font-size: 12px; } 500 | .ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; } 501 | .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; } 502 | .ui-dialog .ui-dialog-buttonpane button { float: right; margin: .5em .4em .5em 0; cursor: pointer; padding: .2em .6em .3em .6em; line-height: 1.4em; width:auto; overflow:visible; background: transparent !important; border: 0px none; } 503 | .ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; } 504 | .ui-draggable .ui-dialog-titlebar { cursor: move; } 505 | 506 | /* 507 | * jQuery UI Slider @VERSION 508 | * 509 | * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) 510 | * Dual licensed under the MIT or GPL Version 2 licenses. 511 | * http://jquery.org/license 512 | * 513 | * http://docs.jquery.com/UI/Slider#theming 514 | */ 515 | .ui-slider { position: relative; text-align: left; border: 0px none; } 516 | .ui-state-focus .ui-slider-handle { border: 0px none; } 517 | .ui-slider .ui-slider-handle { background: url(images/slider_handles.png) 0px -23px no-repeat; position: absolute; z-index: 2; width: 23px; height: 23px; cursor: pointer; } 518 | .ui-slider .ui-state-hover { background-position: 0px 0px !important; } 519 | .ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; } 520 | .ui-slider .ui-state-default { border: 0px none; } 521 | 522 | .ui-slider-horizontal { height: 5px; background: url(images/slider_h_bg.gif) 0px 0px repeat-x;} 523 | .ui-slider-horizontal .ui-slider-handle { top: -9px; margin-left: -12px; } 524 | .ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; background: url(images/slider_h_bg.gif) 0px -5px repeat-x; } 525 | .ui-slider-horizontal .ui-slider-range-min { left: 0; } 526 | .ui-slider-horizontal .ui-slider-range-max { right: 0; } 527 | 528 | .ui-slider-vertical { width: 5px; height: 100px; background: url(images/slider_v_bg.gif) -5px 0px repeat-y; } 529 | .ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: -.6em; margin-bottom: -.6em; } 530 | .ui-slider-vertical .ui-slider-range { left: 0; width: 100%; background: url(images/slider_v_bg.gif) 0px 0px repeat-y; } 531 | .ui-slider-vertical .ui-slider-range-min { bottom: 0; } 532 | .ui-slider-vertical .ui-slider-range-max { top: 0; } 533 | 534 | /* 535 | * jQuery UI Tabs @VERSION 536 | * 537 | * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) 538 | * Dual licensed under the MIT or GPL Version 2 licenses. 539 | * http://jquery.org/license 540 | * 541 | * http://docs.jquery.com/UI/Tabs#theming 542 | */ 543 | .ui-tabs { background: #FFFFFF; position: relative; padding: .2em; zoom: 1; -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; border: 0px none; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ 544 | .ui-tabs .ui-tabs-nav { border-color: #a8a8a8; border-width: 0px 0px 1px 0px; margin: 0; padding: 0; background: transparent; -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; } 545 | .ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; -moz-border-radius-topleft: 3px; -webkit-border-top-left-radius: 3px; border-top-left-radius: 3px; -moz-border-radius-topright: 3px; -webkit-border-top-right-radius: 3px; border-top-right-radius: 3px; } 546 | .ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; font-size: 12px; } 547 | .ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; } 548 | .ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; } 549 | .ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ 550 | .ui-tabs .ui-tabs-panel { display: block; border: 0; padding: 1em 1.4em; background: none; font-size: 12px; border-color: #a8a8a8; border-width: 0px 1px 1px 1px; border-style: solid; -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px;} 551 | .ui-tabs .ui-tabs-hide { display: none !important; } 552 | .ui-tabs .ui-tabs-nav li.ui-state-default { background: #cccccc url(images/button_bg.png) 0px 0px repeat-x; border-color: #a8a8a8; } 553 | .ui-tabs .ui-tabs-nav li.ui-state-default a { color: #4f4f4f !important; text-shadow: 0px 1px 0px rgba(255,255,255,0.8); } 554 | .ui-tabs .ui-tabs-nav li.ui-state-active { background: #FFFFFF; } 555 | .ui-tabs-panel .ui-button { border-width: 0px; background: transparent; } 556 | 557 | /* 558 | * jQuery UI Datepicker @VERSION 559 | * 560 | * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) 561 | * Dual licensed under the MIT or GPL Version 2 licenses. 562 | * http://jquery.org/license 563 | * 564 | * http://docs.jquery.com/UI/Datepicker#theming 565 | */ 566 | .ui-datepicker { width: 17em; padding: .2em .2em 0; background: #FFFFFF url(images/datepicker.gif) left top repeat-x; -moz-box-shadow: 0px 5px 10px rgba(0,0,0,0.8); -webkit-box-shadow: 0px 5px 10px rgba(0,0,0,0.8); box-shadow: 0px 5px 10px rgba(0,0,0,0.8); } 567 | .ui-datepicker .ui-datepicker-header { position:relative; padding:2px 0px 6px 0px; background: transparent; -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; border: 0px none; } 568 | .ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 6px; width: 16px; height: 16px; border: 0px none; cursor: pointer; } 569 | .ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; } 570 | .ui-datepicker .ui-datepicker-prev { left:2px; } 571 | .ui-datepicker .ui-datepicker-next { right:2px; } 572 | .ui-datepicker .ui-datepicker-header .ui-state-hover { background: transparent; border: 0px none; } 573 | .ui-datepicker .ui-datepicker-prev span { background-position: 0px -32px !important; } 574 | .ui-datepicker .ui-datepicker-next span { background-position: -16px -32px !important; } 575 | .ui-datepicker .ui-datepicker-prev-hover span { background-position: 0px -48px !important; } 576 | .ui-datepicker .ui-datepicker-next-hover span { background-position: -16px -48px !important; } 577 | .ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; background: url(images/icon_sprite.png) no-repeat; } 578 | .ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; font-size: 12px; color: #000000; text-shadow: 0px 1px 0px rgba(255,255,255,0.8); } 579 | .ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } 580 | .ui-datepicker select.ui-datepicker-month-year {width: 100%;} 581 | .ui-datepicker select.ui-datepicker-month, 582 | .ui-datepicker select.ui-datepicker-year { width: 49%;} 583 | .ui-datepicker table {width: 100%; font-size: 10px; border-collapse: collapse; margin: 0 0 .4em; } 584 | .ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } 585 | .ui-datepicker td { border: 0; padding: 1px; } 586 | .ui-datepicker td span, .ui-datepicker td a { display: block; padding: 2px 3px 3px; text-align: right; text-decoration: none; } 587 | .ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } 588 | .ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } 589 | .ui-datepicker-buttonpane button { background: url(images/button_bg.png) 0px 0px repeat-x !important; color: #4f4f4f !important; border-color: #b6b6b6 !important; font-weight: bold !important; font-size: 12px; text-shadow: 0px 1px 0px rgba(255,255,255,0.8); } 590 | .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } 591 | .ui-datepicker .ui-datepicker-calendar a { background: transparent; border: 0px none; } 592 | .ui-datepicker .ui-datepicker-calendar .ui-state-active { } 593 | .ui-datepicker .ui-datepicker-calendar a.ui-state-hover { color: #1c4257; } 594 | .ui-datepicker .ui-datepicker-current-day .ui-state-default { background: #5f83b9; color: #FFFFFF !important; text-shadow: 0px 1px 1px #234386; font-weight: bold; } 595 | 596 | /* with multiple calendars */ 597 | .ui-datepicker.ui-datepicker-multi { width:auto; } 598 | .ui-datepicker-multi .ui-datepicker-group { float:left; } 599 | .ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } 600 | .ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } 601 | .ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } 602 | .ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } 603 | .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } 604 | .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } 605 | .ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } 606 | .ui-datepicker-row-break { clear:both; width:100%; } 607 | 608 | /* RTL support */ 609 | .ui-datepicker-rtl { direction: rtl; } 610 | .ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } 611 | .ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } 612 | .ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } 613 | .ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } 614 | .ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } 615 | .ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } 616 | .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } 617 | .ui-datepicker-rtl .ui-datepicker-group { float:right; } 618 | .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } 619 | .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } 620 | 621 | /* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ 622 | .ui-datepicker-cover { 623 | display: none; /*sorry for IE5*/ 624 | display/**/: block; /*sorry for IE5*/ 625 | position: absolute; /*must have*/ 626 | z-index: -1; /*must have*/ 627 | filter: mask(); /*must have*/ 628 | top: -4px; /*must have*/ 629 | left: -4px; /*must have*/ 630 | width: 200px; /*must have*/ 631 | height: 200px; /*must have*/ 632 | } 633 | 634 | /* 635 | * jQuery UI Progressbar @VERSION 636 | * 637 | * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) 638 | * Dual licensed under the MIT or GPL Version 2 licenses. 639 | * http://jquery.org/license 640 | * 641 | * http://docs.jquery.com/UI/Progressbar#theming 642 | */ 643 | .ui-progressbar { height: 12px; text-align: left; background: url(images/progress_bar.gif) 0px -14px repeat-x; } 644 | .ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; background: url(images/progress_bar.gif) 0px 0px repeat-x; } 645 | -------------------------------------------------------------------------------- /public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 0; 3 | margin: 0; 4 | font: 14px "Lucida Grande", "Helvetica Nueue", Arial, sans-serif; 5 | } 6 | h1, 7 | h2, 8 | h3, 9 | h4, 10 | h5, 11 | h6, 12 | p, 13 | ul, 14 | li, 15 | div, 16 | form { 17 | margin: 0; 18 | padding: 0; 19 | } 20 | a { 21 | outline: none; 22 | -moz-outline-style: none; 23 | } 24 | #header { 25 | width: 100%; 26 | height: 35px; 27 | list-style-type: none; 28 | margin: 0; 29 | padding: 0; 30 | background: #f6f6f6 url("/images/gradient.gif") repeat-x 50% 50%; 31 | border-bottom: 1px solid #999; 32 | } 33 | #header ul { 34 | list-style-type: none; 35 | padding: 8px; 36 | } 37 | #header h1 a { 38 | font-size: 18px; 39 | } 40 | #header ul li { 41 | float: left; 42 | } 43 | #header ul li.right { 44 | float: right; 45 | } 46 | #header a { 47 | font-size: 13px; 48 | font-weight: bold; 49 | text-decoration: none; 50 | color: #333; 51 | text-shadow: #f0f0f0 0 1px 0; 52 | } 53 | .outline-view { 54 | position: absolute; 55 | width: 250px; 56 | background-color: #dfe3ea; 57 | } 58 | #DocumentTitles { 59 | overflow: auto; 60 | } 61 | .outline-view ul li { 62 | margin: 0; 63 | padding: 0; 64 | } 65 | .outline-view ul a { 66 | font-size: 13px; 67 | color: #000; 68 | text-decoration: none; 69 | display: block; 70 | padding: 7px 10px; 71 | } 72 | .outline-view ul li.selected a { 73 | color: #fff; 74 | font-weight: bold; 75 | text-shadow: #666 0 1px 1px; 76 | } 77 | .outline-view ul .selected { 78 | font-weight: bold; 79 | color: #fff; 80 | background-color: #8897ba; 81 | background: -webkit-gradient(linear, left top, left bottom, from(#b2bed7), to(#8897ba)); 82 | background: -moz-linear-gradient(top, #b2bed7, #8897ba); 83 | } 84 | .outline-view ahover { 85 | background-color: #f0f0f0 !important; 86 | } 87 | .outline-view a { 88 | outline: none; 89 | } 90 | .content-divider { 91 | position: absolute; 92 | left: 250px; 93 | background-color: #999; 94 | width: 2px; 95 | margin: 0; 96 | padding: 0; 97 | cursor: e-resize; 98 | cursor: col-resize; 99 | } 100 | .content { 101 | position: absolute; 102 | left: 251px; 103 | margin: 0; 104 | padding: 0; 105 | overflow: auto; 106 | overflow-x: hidden; 107 | } 108 | ul.toolbar { 109 | list-style-type: none; 110 | margin: 0; 111 | padding: 0; 112 | background: #f6f6f6 url("/images/glass.png") repeat-x 50% 50%; 113 | height: 30px; 114 | border-top: 1px solid #c5c5c5; 115 | position: absolute; 116 | left: 0; 117 | bottom: 0; 118 | } 119 | ul.toolbar li { 120 | float: left; 121 | border-right: 1px solid #c5c5c5; 122 | } 123 | ul.toolbar li.right { 124 | float: right; 125 | border-left: 1px solid #c5c5c5; 126 | } 127 | ul.toolbar a { 128 | display: block; 129 | padding: 6px 10px; 130 | font-size: 14px; 131 | border: 1px solid #f5f4f5; 132 | color: #666; 133 | font-weight: bold !important; 134 | width: 14px; 135 | text-align: center; 136 | font-size: 15px; 137 | line-height: 16px; 138 | } 139 | ul.toolbar a { 140 | text-shadow: #fff 0 1px 0; 141 | text-decoration: none; 142 | } 143 | ul.toolbar a:active { 144 | background-color: #666; 145 | color: #fff; 146 | } 147 | #editor-container { 148 | float: right; 149 | width: 100%; 150 | } 151 | #editor, 152 | #editor-container input.title { 153 | border: none; 154 | outline: none; 155 | font-size: 108%; 156 | float: right; 157 | clear: both; 158 | border-bottom: 1px solid #999; 159 | margin: 0; 160 | width: 100%; 161 | padding: 5px 0; 162 | } 163 | #editor-container input:focus { 164 | background-color: #ffc; 165 | } 166 | #editor-container input.title { 167 | float: left; 168 | padding-left: 4px; 169 | margin-left: 1px; 170 | } 171 | #controls { 172 | position: absolute; 173 | left: 252px; 174 | bottom: 0; 175 | height: 30px; 176 | border-top: 1px solid #c5c5c5; 177 | background: #f6f6f6 url("/images/glass.png") repeat-x 50% 50%; 178 | } 179 | #controls a { 180 | font-size: 13px; 181 | width: 44px; 182 | } 183 | #html-container { 184 | padding: 10px; 185 | background-color: #f6f6f6; 186 | font-size: 110%; 187 | } 188 | #html-container p, 189 | #html-container ul, 190 | #html-container ol, 191 | #html-container li, 192 | #html-container h1, 193 | #html-container h2, 194 | #html-container h3, 195 | #html-container h4, 196 | #html-container h5, 197 | #html-container h6 { 198 | margin: 10px 0; 199 | } 200 | #html-container li { 201 | margin-left: 20px; 202 | } 203 | #html-button.active { 204 | background-color: #dfe3ea; 205 | } 206 | .page { 207 | padding: 10px; 208 | } 209 | .page h2 { 210 | margin-bottom: 10px; 211 | } 212 | .page p { 213 | margin: 10px 0; 214 | } 215 | .flash { 216 | position: absolute; 217 | top: 0; 218 | bottom: 0; 219 | z-index: 1001; 220 | width: 100%; 221 | opacity: 0.75; 222 | background-color: #111; 223 | } 224 | .flash span { 225 | float: left; 226 | margin-right: 0.7em; 227 | } 228 | .flash .ui-corner-all { 229 | width: 300px; 230 | margin: 50px auto 0 auto; 231 | padding: 0 5px; 232 | opacity: 0.9; 233 | font-weight: bold; 234 | -moz-box-shadow: 0 0 8px #f6f6f6; 235 | -webkit-box-shadow: 0 0 8px #f6f6f6; 236 | box-shadow: 0 0 8px #f6f6f6; 237 | } 238 | form.users { 239 | background-color: #f0f0f0; 240 | border: 1px solid #999; 241 | float: left; 242 | padding: 0 10px 10px 10px; 243 | } 244 | form.users div { 245 | padding: 10px 0 0 0; 246 | float: left; 247 | clear: left; 248 | } 249 | form.users label { 250 | width: 140px; 251 | float: left; 252 | clear: right; 253 | color: #666; 254 | font-weight: bold; 255 | } 256 | form.users input[type=submit] { 257 | margin-left: 140px; 258 | clear: both; 259 | } 260 | form.search { 261 | margin-right: 10px; 262 | } 263 | #show-all { 264 | color: #999; 265 | } 266 | -------------------------------------------------------------------------------- /public/stylesheets/style.styl: -------------------------------------------------------------------------------- 1 | active-colour = #dfe3ea 2 | highlight-colour = #c5c5c5 3 | faded-white = #f0f0f0 4 | light-grey = #f6f6f6 5 | medium-grey = #999 6 | dark-grey = #666 7 | black = #111 8 | selected-colour = #8897ba 9 | light-selection-colour = #ffc 10 | 11 | grey-border() 12 | border-top 1px solid highlight-colour 13 | 14 | glass-background() 15 | background light-grey url('/images/glass.png') repeat-x 50% 50% 16 | 17 | light-shadow() 18 | text-shadow faded-white 0 1px 0 19 | 20 | dark-shadow() 21 | text-shadow dark-grey 0 1px 1px 22 | 23 | centre-shadow(size, colour) 24 | -moz-box-shadow 0 0 size colour 25 | -webkit-box-shadow 0 0 size colour 26 | box-shadow 0 0 size colour 27 | 28 | body 29 | padding 0 30 | margin 0 31 | font 14px "Lucida Grande", "Helvetica Nueue", Arial, sans-serif 32 | 33 | h1, h2, h3, h4, h5, h6, p, ul, li, div, form 34 | margin 0 35 | padding 0 36 | 37 | a 38 | outline none 39 | -moz-outline-style none 40 | 41 | #header 42 | width 100% 43 | height 35px 44 | list-style-type none 45 | margin 0 46 | padding 0 47 | background light-grey url('/images/gradient.gif') repeat-x 50% 50% 48 | border-bottom 1px solid medium-grey 49 | 50 | #header ul 51 | list-style-type none 52 | padding 8px 53 | 54 | #header h1 a 55 | font-size 18px 56 | 57 | #header ul li 58 | float left 59 | 60 | #header ul li.right 61 | float right 62 | 63 | #header a 64 | font-size 13px 65 | font-weight bold 66 | text-decoration none 67 | color #333 68 | light-shadow() 69 | 70 | .outline-view 71 | position absolute 72 | width 250px 73 | background-color active-colour 74 | 75 | #DocumentTitles 76 | overflow auto 77 | 78 | .outline-view ul li 79 | margin 0 80 | padding 0 81 | 82 | .outline-view ul a 83 | font-size 13px 84 | color #000 85 | text-decoration none 86 | display block 87 | padding 7px 10px 88 | 89 | .outline-view ul li.selected a 90 | color #fff 91 | font-weight bold 92 | dark-shadow() 93 | 94 | .outline-view ul .selected 95 | font-weight bold 96 | color #fff 97 | background-color selected-colour 98 | background -webkit-gradient(linear, left top, left bottom, from(#b2bed7), to(selected-colour)) 99 | background -moz-linear-gradient(top, #b2bed7, selected-colour) 100 | 101 | .outline-view ahover 102 | background-color faded-white !important 103 | 104 | .outline-view a 105 | outline none 106 | 107 | .content-divider 108 | position absolute 109 | left 250px 110 | background-color medium-grey 111 | width 2px 112 | margin 0 113 | padding 0 114 | cursor e-resize 115 | cursor col-resize 116 | 117 | .content 118 | position absolute 119 | left 251px 120 | margin 0 121 | padding 0 122 | overflow auto 123 | overflow-x hidden 124 | 125 | ul.toolbar 126 | list-style-type none 127 | margin 0 128 | padding 0 129 | glass-background() 130 | height 30px 131 | grey-border() 132 | position absolute 133 | left 0 134 | bottom 0 135 | 136 | ul.toolbar li 137 | float left 138 | border-right 1px solid highlight-colour 139 | 140 | ul.toolbar li.right 141 | float right 142 | border-left 1px solid highlight-colour 143 | 144 | ul.toolbar a 145 | display block 146 | padding 6px 10px 147 | font-size 14px 148 | border 1px solid #F5F4F5 149 | color dark-grey 150 | font-weight bold !important 151 | width 14px 152 | text-align center 153 | font-size 15px 154 | line-height 16px 155 | 156 | ul.toolbar a 157 | text-shadow #fff 0 1px 0 158 | text-decoration none 159 | 160 | ul.toolbar a:active 161 | background-color dark-grey 162 | color white 163 | 164 | #editor-container 165 | float right 166 | width 100% 167 | 168 | #editor, #editor-container input.title 169 | border none 170 | outline none 171 | font-size 108% 172 | float right 173 | clear both 174 | border-bottom 1px solid medium-grey 175 | margin 0 176 | width 100% 177 | padding 5px 0 178 | 179 | #editor-container input:focus 180 | background-color light-selection-colour 181 | 182 | #editor-container input.title 183 | float left 184 | padding-left 4px 185 | margin-left 1px 186 | 187 | #controls 188 | position absolute 189 | left 252px 190 | bottom 0 191 | height 30px 192 | grey-border() 193 | glass-background() 194 | 195 | #controls a 196 | font-size 13px 197 | width 44px 198 | 199 | #html-container 200 | padding 10px 201 | background-color light-grey 202 | font-size 110% 203 | 204 | #html-container p, 205 | #html-container ul, 206 | #html-container ol, 207 | #html-container li, 208 | #html-container h1, 209 | #html-container h2, 210 | #html-container h3, 211 | #html-container h4, 212 | #html-container h5, 213 | #html-container h6 214 | margin 10px 0 215 | 216 | #html-container li 217 | margin-left 20px 218 | 219 | #html-button.active 220 | background-color active-colour 221 | 222 | .page 223 | padding 10px 224 | 225 | .page h2 226 | margin-bottom 10px 227 | 228 | .page p 229 | margin 10px 0 230 | 231 | .flash 232 | position absolute 233 | top 0 234 | bottom 0 235 | z-index 1001 236 | width 100% 237 | opacity 0.75 238 | background-color black 239 | 240 | .flash span 241 | float left 242 | margin-right .7em 243 | 244 | .flash .ui-corner-all 245 | width 300px 246 | margin 50px auto 0 auto 247 | padding 0 5px 248 | opacity 0.9 249 | font-weight bold 250 | centre-shadow(8px, light-grey) 251 | 252 | form.users 253 | background-color faded-white 254 | border 1px solid medium-grey 255 | float left 256 | padding 0 10px 10px 10px 257 | 258 | form.users div 259 | padding 10px 0 0 0 260 | float left 261 | clear left 262 | 263 | form.users label 264 | width 140px 265 | float left 266 | clear right 267 | color dark-grey 268 | font-weight bold 269 | 270 | form.users input[type=submit] 271 | margin-left 140px 272 | clear both 273 | 274 | form.search 275 | margin-right 10px 276 | 277 | #show-all 278 | color medium-grey 279 | -------------------------------------------------------------------------------- /test/app.test.js: -------------------------------------------------------------------------------- 1 | var app = require(__dirname + '/../app'), 2 | assert = require('assert'), 3 | tobi = require('tobi'), 4 | testHelper = require('./helper'), 5 | browser = tobi.createBrowser(app); 6 | 7 | describe('Sign in', function() { 8 | before(function(done) { 9 | testHelper.clear([app.User], function() { 10 | var user = new app.User({'email' : 'alex@example.com', 'password' : 'test' }); 11 | user.save(done); 12 | console.log('done'); 13 | }); 14 | }); 15 | 16 | after(function(done) { 17 | app.close(); 18 | done(); 19 | }); 20 | 21 | it('should allow valid users to sign in', function(done) { 22 | // FIXME: tobi doesn't seem to work at the moment 23 | browser.get('/sessions/new', function(res, $) { 24 | console.log('got / page'); 25 | // Fill email, password and submit form 26 | $('form#login') 27 | .fill('user[email]', 'alex@example.com') 28 | .fill('user[password]', 'test') 29 | .submit(function(res, $) { 30 | console.log('form submitted'); 31 | // Form submitted, new page loaded. 32 | assert.equal(browser.text('#header a.destroy'), 'Log Out'); 33 | testHelper.end(); 34 | done(); 35 | }); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/helper.js: -------------------------------------------------------------------------------- 1 | // Force test environment 2 | process.env.NODE_ENV = 'test'; 3 | var state = { 4 | models: [] 5 | }; 6 | 7 | function prepare(models, next) { 8 | var modelCount = models.length; 9 | models.forEach(function(model) { 10 | modelCount--; 11 | model.find({}, function(err, records) { 12 | var count = records.length; 13 | records.forEach(function(result) { 14 | result.remove(); 15 | count--; 16 | }); 17 | if (count === 0 && modelCount === 0) next(); 18 | }); 19 | }); 20 | }; 21 | 22 | module.exports = { 23 | clear: function(models, next) { 24 | prepare(models, next); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /views/404.jade: -------------------------------------------------------------------------------- 1 | .page 2 | h2 Page Not Found 3 | p That page could not be found. 4 | -------------------------------------------------------------------------------- /views/500.jade: -------------------------------------------------------------------------------- 1 | .page 2 | h2 Error 3 | p Something went wrong with the application. 4 | h3 Error Details 5 | pre #{error} 6 | -------------------------------------------------------------------------------- /views/documents/edit.jade: -------------------------------------------------------------------------------- 1 | h2 Edit Document 2 | p 3 | a.button(href='/documents') 4 | ← All Documents 5 | form(method='post', action='/documents/' + d.id) 6 | input(name='d[id]', value=d.id, type='hidden') 7 | input(name='_method', value='PUT', type='hidden') 8 | !=partial('fields', { locals: { d: d } }) 9 | 10 | -------------------------------------------------------------------------------- /views/documents/fields.jade: -------------------------------------------------------------------------------- 1 | div 2 | label Title: 3 | input(name='d[title]', value=d.title || '') 4 | div 5 | label Note: 6 | div 7 | textarea(name='d[data]')= d.data 8 | div 9 | input(type='submit', value='Save') 10 | 11 | -------------------------------------------------------------------------------- /views/documents/index.jade: -------------------------------------------------------------------------------- 1 | #left.outline-view 2 | #DocumentTitles 3 | ul#document-list 4 | li#document-row-template(style='display: none') 5 | a(id='document_{{ id }}') {{ title }} 6 | 7 | ul.toolbar 8 | li 9 | a#create-document(href='/documents/new') 10 | + 11 | li 12 | a#delete-document(href='#') 13 | - 14 | 15 | .content-divider 16 | 17 | .content 18 | #html-container(style='display: none') 19 | #editor-container 20 | div.title 21 | input.title(name='d[title]')= d.title 22 | textarea#editor(name='d[data]') 23 | 24 | ul#controls.toolbar 25 | li 26 | a#save-button(href='#') Save 27 | li 28 | a#html-button(href='#') HTML 29 | 30 | -------------------------------------------------------------------------------- /views/documents/new.jade: -------------------------------------------------------------------------------- 1 | .page 2 | h2 New Document 3 | p 4 | a.button(href='/documents') ← All Documents 5 | form(method='post', action='/documents') 6 | !=partial('fields', { locals: { d: d } }) 7 | 8 | -------------------------------------------------------------------------------- /views/documents/show.jade: -------------------------------------------------------------------------------- 1 | h2=d.title 2 | a.button(href='/documents/' + d.id + '/edit') Edit 3 | p 4 | a.button(href='/documents') 5 | ← All Documents 6 | p=d.data 7 | -------------------------------------------------------------------------------- /views/index.jade: -------------------------------------------------------------------------------- 1 | h1= title 2 | p Welcome to #{title} -------------------------------------------------------------------------------- /views/layout.jade: -------------------------------------------------------------------------------- 1 | !!! 2 | html 3 | head 4 | title= 'Nodepad' 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | link(rel='stylesheet', href='/stylesheets/aristo/jquery-ui-1.8.5.custom.css') 7 | script(type='text/javascript', src='/javascripts/json.js') 8 | script(type='text/javascript', src='https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js') 9 | script(type='text/javascript', src='https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.7/jquery-ui.min.js') 10 | script(type='text/javascript', src='/javascripts/underscore-min.js') 11 | script(type='text/javascript', src='/javascripts/backbone-min.js') 12 | body 13 | #container 14 | #header 15 | ul 16 | li 17 | h1 18 | a(href='/') #{nameAndVersion(appName, version)} 19 | - if (typeof currentUser !== 'undefined') 20 | li.right 21 | a#logout(href='/sessions') Log Out 22 | li.right 23 | form.search(action='/search') 24 | input(name='s', value='Search') 25 | !{flashMessages} 26 | != body 27 | script(type='text/javascript', src='/javascripts/flash.js') 28 | - if (typeof documents !== 'undefined') 29 | script(type='text/javascript', src='/javascripts/documents.js') 30 | script(type='text/javascript', src='/javascripts/resize.js') 31 | script(type='text/javascript') 32 | $(function() { 33 | if (typeof Documents !== 'undefined') 34 | Documents.reset(!{JSON.stringify(documents)}); 35 | }); 36 | 37 | -------------------------------------------------------------------------------- /views/mailer/welcome.jade: -------------------------------------------------------------------------------- 1 | | Dear #{user.email}, 2 | | 3 | | Welcome to Nodepad! 4 | | 5 | | Regards, 6 | | 7 | | The Nodepad Mail Robot 8 | 9 | -------------------------------------------------------------------------------- /views/sessions/new.jade: -------------------------------------------------------------------------------- 1 | .page 2 | h2 Log In 3 | p 4 | a.button(href='/users/new') Create an account → 5 | form.users#login(method='post', action='/sessions') 6 | !=partial('../users/fields', { locals: { user: user } }) 7 | div 8 | label(for='remember_me') Remember me: 9 | input#remember_me(type='checkbox', name='remember_me') 10 | div 11 | input(type='submit', value='Log In') 12 | 13 | -------------------------------------------------------------------------------- /views/users/fields.jade: -------------------------------------------------------------------------------- 1 | div 2 | label Email: 3 | input(name='user[email]', value=user.email || '') 4 | 5 | div 6 | label Password: 7 | input(name='user[password]', value=user.password || '', type='password') 8 | 9 | -------------------------------------------------------------------------------- /views/users/new.jade: -------------------------------------------------------------------------------- 1 | .page 2 | h2 Register 3 | 4 | form.users(method='post', action='/users') 5 | !=partial('fields', { locals: { user: user } }) 6 | div 7 | input(type='submit', value='Register') 8 | span \ or 9 | a(href='/sessions/new') Log In 10 | 11 | --------------------------------------------------------------------------------