├── .gitignore ├── Contribute.md ├── LICENSE.md ├── app.js ├── config ├── chat.js └── express.js ├── controllers ├── dashboard.js ├── gambits.js ├── knowledge.js ├── plugins.js ├── replies.js └── topics.js ├── lib └── slack.js ├── package.json ├── plugins ├── animals.js ├── bot.js ├── cnet.js ├── define.js ├── math.js ├── time.js ├── user.js └── weather.js ├── public ├── css │ ├── app.css │ ├── bootstrap-theme.css │ ├── bootstrap-theme.css.map │ ├── bootstrap-theme.min.css │ ├── bootstrap.css │ ├── bootstrap.css.map │ ├── bootstrap.min.css │ ├── callout.css │ ├── chat.css │ ├── ie9.css │ ├── sweet-alert.css │ └── typeahead.css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── img │ ├── gambit.png │ └── realtime.png └── js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── icanhas.min.js │ ├── npm.js │ ├── sortable.min.js │ ├── sweet-alert.js │ ├── sweet-alert.min.js │ ├── typeahead.js │ └── vivagraph.js ├── readme.md └── views ├── _layout.jade ├── docs.jade ├── gambits ├── get.jade └── index.jade ├── import.jade ├── includes ├── _chat.jade ├── _gambitform.jade ├── _head.jade └── _knoledgemenu.jade ├── index.jade ├── knowledge ├── bot.jade ├── graph.jade ├── index.jade ├── user.jade └── world.jade ├── replies ├── get.jade └── index.jade ├── settings.jade └── topics ├── get.jade └── index.jade /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/* 3 | testbot/* 4 | botfacts/* 5 | test/* 6 | *.log 7 | logs/* 8 | systemDB/* 9 | uploads/* 10 | plugins/wizard.js 11 | superscript-editor.iml 12 | .idea -------------------------------------------------------------------------------- /Contribute.md: -------------------------------------------------------------------------------- 1 | # Contribute.md 2 | 3 | ## Request for contributions 4 | 5 | Thanks for taking the time to help make SuperScript Editor better. Here are some guidlines that will highly increase the chances of your fix or feature request from being accepted. 6 | 7 | ### Bug Fixes 8 | 9 | If you find a bug you would like fixed. Open up a [ticket](https://github.com/silentrob/superscript-editor/issues/new) with a detailed description of the bug and the expected behaviour. If you would like to fix the problem yourself please do the following steps. 10 | 11 | 1. Fork it. 12 | 2. Create a branch (`git checkout -b fix-for-that-thing`) 13 | 3. Commit a failing test (`git commit -am "adds a failing test to demonstrate that thing"`) 14 | 3. Commit a fix that makes the test pass (`git commit -am "fixes that thing"`) 15 | 4. Push to the branch (`git push origin fix-for-that-thing`) 16 | 5. Open a [Pull Request](https://github.com/silentrob/superscript-editor/pulls) 17 | 18 | Please keep your branch up to date by rebasing upstream changes from master. 19 | 20 | ### New Functionality 21 | 22 | If you wish to add new functionality to superscript, that is super cool, lets chat about what that feature is and how best it could be implemented. You can use [gitter](https://gitter.im/silentrob/superscript), [twitter](https://twitter.com/rob_ellis) email or Issues to engague on new features. Its best if you explaing the problem you are trying to solve, not just the solution you want. You may also submit a pull request with the steps above. -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright © 2014-2015 Rob Ellis 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | ”Software”), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | // The main application script, ties everything together. 2 | 3 | var mongoose = require('mongoose'); 4 | var sfact = require("sfacts"); 5 | var ss = require("superscript"); 6 | 7 | var express = require('express'); 8 | var app = express(); 9 | 10 | var appServer = require('http').Server(app); 11 | var io = require('socket.io')(appServer); 12 | 13 | var port = process.env.PORT || 3000; 14 | var dbName = process.env.BOT || "testbot"; 15 | var dbHost = process.env.DBHOST || "localhost"; 16 | 17 | var config = { db: 'mongodb://' + dbHost + '/' + dbName }; 18 | var options = { server: { socketOptions: { keepAlive: 1 } } }; 19 | var factSystem = sfact.create(dbName); 20 | 21 | var findOrCreate = require('mongoose-findorcreate'); 22 | var settingsSchema = new mongoose.Schema({ key: String, value: String }); 23 | settingsSchema.plugin(findOrCreate); 24 | Settings = mongoose.model('Settings', settingsSchema); 25 | 26 | mongoose.connect(config.db, options, function(err){ 27 | if (err) console.log("Error connecting to the MongoDB --", err); 28 | }); 29 | 30 | var botOptions = { 31 | mongoose : mongoose, 32 | factSystem: factSystem, 33 | editMode : true 34 | }; 35 | 36 | app.projectName = dbName; 37 | var conn = mongoose.connection; 38 | 39 | conn.once('open', function() { 40 | 41 | var models = require('superscript/lib/topics/index')(mongoose, factSystem); 42 | 43 | require('./config/express')(app); 44 | 45 | new ss(botOptions, function(err, botInstance){ 46 | require('./config/chat')(io, botInstance, models); 47 | 48 | var dashRoutes = require('./controllers/dashboard')(models, botInstance, app.projectName); 49 | var gambitRoute = require('./controllers/gambits')(models, botInstance); 50 | var topicsRoute = require('./controllers/topics')(models, botInstance); 51 | var repliesRoute = require('./controllers/replies')(models, botInstance); 52 | var pluginsRoute = require('./controllers/plugins')(models, botInstance); 53 | var knowledgeRoute = require('./controllers/knowledge')(models, botInstance); 54 | 55 | // General routs 56 | app.get('/', dashRoutes.index); 57 | app.get('/docs', dashRoutes.docs); 58 | app.get('/settings', dashRoutes.settings); 59 | app.post('/settings/slack', dashRoutes.postSlack); 60 | app.get('/import', dashRoutes.load); 61 | app.post('/import', dashRoutes.postdata); 62 | 63 | // Plugin JSON 64 | app.get('/plugins', pluginsRoute.getJSON); 65 | 66 | app.get('/knowledge', knowledgeRoute.index); 67 | app.get('/knowledge/graph', knowledgeRoute.graph); 68 | app.get('/knowledge/user', knowledgeRoute.user); 69 | app.get('/knowledge/bot', knowledgeRoute.bot); 70 | app.get('/knowledge/world', knowledgeRoute.world); 71 | app.get('/knowledge/concept/:name', knowledgeRoute.concept); 72 | app.get('/knowledge/world/filter', knowledgeRoute.filter); 73 | 74 | app.post('/knowledge/bot', knowledgeRoute.addBot); 75 | app.post('/knowledge/world', knowledgeRoute.addWorld); 76 | 77 | app.delete('/knowledge/user', knowledgeRoute.userDelete); 78 | app.delete('/knowledge/bot', knowledgeRoute.botDelete); 79 | app.delete('/knowledge/world', knowledgeRoute.worldDelete); 80 | 81 | app.post('/knowledge/bot/import',knowledgeRoute.botImport); 82 | app.post('/knowledge/world/import',knowledgeRoute.worldImport); 83 | app.post('/knowledge/concepts/import', knowledgeRoute.worldImport); 84 | 85 | // Gambits CRUD and nested replies 86 | app.get('/gambits', gambitRoute.index); 87 | app.post('/gambits', gambitRoute.post); 88 | 89 | app.get('/replies', repliesRoute.index); 90 | app.get('/replies/:id', repliesRoute.show); 91 | app.delete('/replies/:id', repliesRoute.delete); 92 | 93 | app.post('/gambits/quick', gambitRoute.quickPost); 94 | app.post('/gambits/:id', gambitRoute.post); 95 | 96 | app.get('/gambits/new', gambitRoute.new); 97 | app.put('/gambits/:id', gambitRoute.update); 98 | app.get('/gambits/:id', gambitRoute.show); 99 | app.delete('/gambits/:id', gambitRoute.delete); 100 | 101 | app.get('/gambits/:id/replies', gambitRoute.replies); 102 | 103 | app.post('/gambits/:id/reply', gambitRoute.reply); 104 | app.delete('/gambits/:id/reply', gambitRoute.deleteReply); 105 | app.put('/gambits/:id/reply/:rid', gambitRoute.updateReply); 106 | app.post('/gambits/:id/test', gambitRoute.test); 107 | 108 | // Topics 109 | app.get('/topics', topicsRoute.index); 110 | app.post('/topics', topicsRoute.post); 111 | app.get('/topics/:id', topicsRoute.show); 112 | app.put('/topics/:id', topicsRoute.update); 113 | app.delete('/topics/:id', topicsRoute.delete); 114 | app.post('/topics/:id/test', topicsRoute.test); 115 | app.post('/topics/:id/sort', topicsRoute.sort); 116 | app.post('/topics/:id/reorder', topicsRoute.reorder); 117 | }); 118 | 119 | var server = appServer.listen(port, function () { 120 | var host = server.address().address; 121 | var port = server.address().port; 122 | console.log('\n\n\tSuperScript Community Editor.\n\tListening at http://%s:%s\n\tBot Name: %s\n\n\tSwitch or create a new bot by starting `BOT= node app.js`\n\n', host, port, dbName); 123 | }); 124 | }); 125 | 126 | module.exports = app; 127 | -------------------------------------------------------------------------------- /config/chat.js: -------------------------------------------------------------------------------- 1 | module.exports = function(io, bot, models) { 2 | io.on('connection', function(socket) { 3 | var user_id = "testUser"; 4 | socket.emit('chat message', {string:'Welcome to the real-time editor.'}); 5 | 6 | socket.on('chat message', function(msg){ 7 | // Emit the message back first 8 | bot.reply(user_id, msg.trim(), function(err, resObj){ 9 | models.topic.findOne({name:resObj.topicName}, function(err, topic){ 10 | if (!topic) { 11 | console.log(err); 12 | } else { 13 | resObj.topicId = topic._id; 14 | } 15 | 16 | socket.emit('chat message', resObj); 17 | 18 | }); 19 | }); 20 | }); 21 | }); 22 | } -------------------------------------------------------------------------------- /config/express.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var express = require('express'); 7 | var compression = require('compression'); 8 | var morgan = require('morgan'); 9 | var winston = require('winston'); 10 | var cookieParser = require('cookie-parser'); 11 | var cookieSession = require('cookie-session'); 12 | var bodyParser = require('body-parser'); 13 | var methodOverride = require('method-override'); 14 | var csrf = require('csurf'); 15 | var multer = require('multer'); 16 | 17 | var flash = require('connect-flash'); 18 | var helpers = require('view-helpers'); 19 | var pkg = require('../package.json'); 20 | 21 | var env = process.env.NODE_ENV || 'development'; 22 | 23 | /** 24 | * Expose 25 | */ 26 | 27 | module.exports = function (app) { 28 | 29 | app.use(compression({ threshold: 512 })); 30 | 31 | // Static files middleware 32 | app.use(express.static('./public')); 33 | 34 | // Use winston on production 35 | var log; 36 | if (env !== 'development') { 37 | log = { 38 | stream: { 39 | write: function (message, encoding) { 40 | winston.info(message); 41 | } 42 | } 43 | }; 44 | } else { 45 | log = 'dev'; 46 | } 47 | 48 | // Don't log during tests 49 | // Logging middleware 50 | if (env !== 'test') app.use(morgan(log)); 51 | 52 | // set views path, template engine and default layout 53 | app.set('view engine', 'jade'); 54 | app.set('views', './views'); 55 | 56 | // expose package.json to views 57 | app.use(function (req, res, next) { 58 | res.locals.projectName = app.projectName; 59 | res.locals.pkg = pkg; 60 | res.locals.env = env; 61 | next(); 62 | }); 63 | 64 | // bodyParser should be above methodOverride 65 | app.use(bodyParser.urlencoded({ 66 | extended: true 67 | })); 68 | 69 | app.use(bodyParser.json()); 70 | app.use(multer({ 71 | dest: './uploads/', 72 | rename: function (fieldname, filename) { 73 | return filename.replace(/\W+/g, '-').toLowerCase() + Date.now(); 74 | } 75 | })); 76 | 77 | app.use(methodOverride(function (req, res) { 78 | if (req.body && typeof req.body === 'object' && '_method' in req.body) { 79 | // look in urlencoded POST bodies and delete it 80 | var method = req.body._method; 81 | delete req.body._method; 82 | return method; 83 | } 84 | })); 85 | 86 | // cookieParser should be above session 87 | app.use(cookieParser()); 88 | app.use(cookieSession({ secret: 'secret' })); 89 | 90 | // connect flash for flash messages - should be declared after sessions 91 | app.use(flash()); 92 | 93 | app.use(function(req, res, next){ 94 | res.locals.success_messages = req.flash('success'); 95 | res.locals.error_messages = req.flash('error'); 96 | next(); 97 | }); 98 | 99 | // should be declared after session and flash 100 | app.use(helpers(pkg.name)); 101 | }; 102 | -------------------------------------------------------------------------------- /controllers/dashboard.js: -------------------------------------------------------------------------------- 1 | var exec = require('child_process').exec; 2 | var slackClient; 3 | module.exports = function(models, bot, project) { 4 | 5 | return { 6 | index: function(req, res) { 7 | models.topic.find({}, null, {sort:{name:1}}, function(err, topics) { 8 | res.render('index', { title: 'SuperScript Community Editor', topics: topics }); 9 | }); 10 | }, 11 | 12 | settings: function(req, res) { 13 | Settings.find({key:'token'}, function(e,r){ 14 | var token = (r && r.length !== 0) ? r[0].value : ""; 15 | res.render('settings', {token:token}); 16 | }); 17 | }, 18 | 19 | postSlack: function(req, res) { 20 | if (req.body.token !== "") { 21 | var prop = {key:'token', value: req.body.token }; 22 | Settings.findOrCreate({key:'token'}, prop, function(err, item, isNew){ 23 | item.save(function(err, result){ 24 | 25 | if (req.body.start) { 26 | evars = {}; 27 | for(key in process.env) { 28 | evars[key] = process.env[key]; 29 | } 30 | evars.TOKEN = req.body.token; 31 | evars.PROJECT = project; 32 | 33 | if (slackClient) { 34 | req.flash('error', 'Slack Client Already Running'); 35 | res.redirect('back'); 36 | } else { 37 | slackClient = exec('node ./lib/slack.js', {env: evars}, 38 | function (error, stdout, stderr) { 39 | console.log('stdout: ' + stdout); 40 | console.log('stderr: ' + stderr); 41 | if (error !== null) { 42 | console.log('exec error: ' + error); 43 | } 44 | req.flash('success', 'Slack Client Enabled'); 45 | res.redirect('back'); 46 | }); 47 | } 48 | 49 | } else { 50 | 51 | if (slackClient) { 52 | slackClient.kill('SIGKILL'); 53 | slackClient = null; 54 | req.flash('success', 'Slack Client Disabled'); 55 | } 56 | res.redirect('back'); 57 | } 58 | }); 59 | }); 60 | } else { 61 | res.flash("error"); 62 | res.redirect('back'); 63 | } 64 | }, 65 | 66 | docs: function(req, res) { 67 | res.render('docs'); 68 | }, 69 | 70 | load: function(req, res) { 71 | res.render('import'); 72 | }, 73 | 74 | postdata: function(req, res) { 75 | if (req.files && req.files.file && req.files.file.path) { 76 | models.importer(req.files.file.path, function(){ 77 | req.flash("success","Data file Imported"); 78 | res.redirect("/"); 79 | }); 80 | } else { 81 | req.flash("error","Missing Data file."); 82 | res.redirect("back"); 83 | } 84 | } 85 | 86 | }; 87 | }; 88 | -------------------------------------------------------------------------------- /controllers/gambits.js: -------------------------------------------------------------------------------- 1 | var _ = require("underscore"); 2 | 3 | // Code to count how many capture groups 4 | // var num_groups = (new RegExp(arbitrary_regex.toString() + '|')).exec('').length - 1; 5 | // alert(num_groups); 6 | 7 | // var num_groups = (new RegExp(re.toString() + '|')).exec('').length - 1; 8 | 9 | module.exports = function(models, bot) { 10 | return { 11 | index : function(req, res) { 12 | models.topic.find({}, function(err, topics){ 13 | models.gambit.find({}).populate('replies').exec(function(err, gambits){ 14 | res.render('gambits/index', {gambits: gambits, topics:topics }); 15 | }); 16 | }); 17 | }, 18 | 19 | // Add a reply to a gambit 20 | deleteReply: function(req, res) { 21 | 22 | var remMe = {$pull: {replies: req.body.replyId }}; 23 | models.gambit.findByIdAndUpdate(req.params.id, remMe, function(err, me) { 24 | // We should also delete the reply object and not leave it orphaned. 25 | models.reply.find({ id: req.body.replyId }).remove().exec(); 26 | res.sendStatus(200); 27 | }); 28 | }, 29 | 30 | // Update an existing reply 31 | updateReply: function(req, res) { 32 | var props = { 33 | reply: req.body.reply, 34 | filter: req.body.filter 35 | }; 36 | models.reply.findByIdAndUpdate(req.params.rid, props, function(err, me) { 37 | res.sendStatus(200); 38 | }); 39 | }, 40 | 41 | reply: function(req, res) { 42 | var properties = { 43 | reply: req.body.reply, 44 | filter: req.body.filter 45 | }; 46 | 47 | models.reply.create(properties, function(err,rep){ 48 | rep.save(function(){ 49 | var addMe = {$addToSet: {replies: rep._id}}; 50 | models.gambit.findByIdAndUpdate(req.params.id, addMe, function(err, me) { 51 | res.status(201).send(rep); 52 | }); 53 | }); 54 | }); 55 | }, 56 | 57 | // JSON endpoint to fetch all replies 58 | replies: function(req, res) { 59 | return models.gambit.findById(req.params.id).populate('replies').exec(function(err, gambit){ 60 | if (gambit && gambit.replies) { 61 | res.json(gambit.replies); 62 | } else { 63 | res.json([]); 64 | } 65 | }); 66 | }, 67 | 68 | update: function(req, res) { 69 | 70 | var updateGambitModel = function() { 71 | models.gambit.findById(req.params.id, function(err, me) { 72 | me.input = req.body.input; 73 | me.isQuestion = (req.body.isQuestion == "on") ? true : false; 74 | me.qType = req.body.qType; 75 | me.filter = req.body.filter; 76 | 77 | me.save(function() { 78 | req.flash('success', 'Gambit updated.'); 79 | res.redirect('/gambits/' + req.params.id); 80 | }); 81 | }); 82 | }; 83 | 84 | if (req.body.topicId && req.body.topicId !== req.body.topic) { 85 | models.topic.update({},{ $pull: { gambits: req.params.id }}, {multi:true}, function(err, res) { 86 | models.topic.update({_id: req.body.topic},{ $addToSet:{ gambits: req.params.id }}, function(err, res) { 87 | updateGambitModel(); 88 | }); 89 | }); 90 | } else { 91 | updateGambitModel(); 92 | } 93 | }, 94 | 95 | // Test a gambit against input 96 | test: function(req, res) { 97 | return models.gambit.findById(req.params.id, function (err, gambit) { 98 | bot.message(req.body.phrase, function(err, messageObj){ 99 | gambit.doesMatch(messageObj, function(err, result){ 100 | res.json(result); 101 | }); 102 | }); 103 | }); 104 | }, 105 | 106 | quickPost: function(req, res) { 107 | if (!req.body.input || !req.body.reply) { 108 | res.json({error:'Missing Input or Reply'}); 109 | } else { 110 | 111 | var isQuestion = (req.body.isQuestion == "on") ? true : false; 112 | var gambitParams = { 113 | input: req.body.input, 114 | isQuestion: isQuestion, 115 | qType: req.body.qType 116 | }; 117 | var gambit = new models.gambit(gambitParams); 118 | var replyParams = { 119 | reply: req.body.reply 120 | }; 121 | 122 | models.reply.create(replyParams, function(err, reply1){ 123 | // if (err) return res.json(err); 124 | 125 | gambit.replies.addToSet(reply1._id); 126 | gambit.save(function(err) { 127 | 128 | if (req.body.topicId === "" && req.body.replyId === "") { 129 | models.topic.findOrCreate({name:'random'}, function(err, topic) { 130 | var remMe = {$addToSet: {gambits: gambit._id }}; 131 | topic.update(remMe, function(err){ 132 | // topic.sortGambits(); 133 | res.json({success:true}); 134 | }); 135 | }); 136 | } else if (req.body.topicId) { 137 | models.topic.findById(req.body.topicId, function(err, topic) { 138 | var remMe = {$addToSet: {gambits: gambit._id }}; 139 | topic.update(remMe, function(err){ 140 | topic.sortGambits(); 141 | res.json({success:true}); 142 | }); 143 | }); 144 | } else if (req.body.replyId) { 145 | models.reply.findById(req.body.replyId, function(err, reply2) { 146 | var remMe = {$addToSet: {gambits: gambit._id }}; 147 | reply2.update(remMe, function(err){ res.json({success:true}); }); 148 | }); 149 | } 150 | }); 151 | }); 152 | 153 | } 154 | }, 155 | 156 | 157 | post: function(req, res) { 158 | if (req.body.input === "") { 159 | req.flash('error', 'Input is Required.'); 160 | res.redirect('back'); 161 | } else { 162 | 163 | var isQuestion = (req.body.isQuestion == "on") ? true : false; 164 | var gambitParams = { 165 | input: req.body.input, 166 | isQuestion: isQuestion, 167 | qType: req.body.qType 168 | }; 169 | 170 | if (req.params.id) { 171 | gambitParams._id = req.params.id; 172 | } 173 | 174 | var gambit = new models.gambit(gambitParams); 175 | 176 | if (req.body.reply && req.body.reply !== "") { 177 | var replyParams = { 178 | reply: req.body.reply 179 | }; 180 | 181 | models.reply.create(replyParams, function(err,reply){ 182 | gambit.replies.addToSet(reply._id); 183 | gambit.save(function(err){ 184 | req.flash('success', 'Gambit Created'); 185 | res.redirect('/gambits/' + gambit._id); 186 | }); 187 | }); 188 | 189 | } else { 190 | // No Reply Obj.. Just trigger 191 | gambit.save(function(err) { 192 | // If we have a RepyId this gambit should be added to that parent 193 | if (req.body.replyId) { 194 | var addMe = {$addToSet: {gambits: gambit._id}}; 195 | models.reply.findByIdAndUpdate(req.body.replyId, addMe, function(err, me) { 196 | req.flash('success', 'Gambit Created'); 197 | res.redirect('/gambits/' + gambit._id); 198 | }); 199 | 200 | } else if (req.body.topicId) { 201 | var addMe = {$addToSet: {gambits: gambit._id}}; 202 | models.topic.findByIdAndUpdate(req.body.topicId, addMe, function(err, me) { 203 | req.flash('success', 'Gambit Created'); 204 | res.redirect('/gambits/' + gambit._id); 205 | }); 206 | } else { 207 | req.flash('success', 'Gambit Created'); 208 | res.redirect('/gambits/' + gambit._id); 209 | } 210 | }); 211 | } 212 | } 213 | }, 214 | 215 | delete: function (req, res) { 216 | return models.gambit.findById(req.params.id, function (err, item) { 217 | if (!item) { 218 | return res.sendStatus(410); 219 | } else { 220 | return item.remove(function (err) { 221 | return res.redirect('/gambits'); 222 | }); 223 | } 224 | }); 225 | }, 226 | 227 | show: function(req, res) { 228 | 229 | // TODO - This should be a view-helper or called via XHR 230 | return models.topic.find({}).sort('name').select('_id name').exec(function(err, allTopics){ 231 | 232 | return models.gambit.findById(req.params.id).exec(function(error, gambit) { 233 | return models.topic.findOne({ gambits: gambit._id }) 234 | .exec(function(error, topic) { 235 | 236 | return models.reply.findOne({gambits: req.params.id}) 237 | .populate('gambits', null, null) 238 | .exec(function(error, Reply){ 239 | // Lets go up one more too 240 | if (Reply) { 241 | return models.gambit.findOne({replies: Reply._id}) 242 | .populate('replies', null, null) 243 | .exec(function(err, parentGambit){ 244 | 245 | res.render('gambits/get', { 246 | gambit: gambit, 247 | topic: null, 248 | reply: Reply, 249 | parent: parentGambit, 250 | topics: allTopics}); 251 | } 252 | ); 253 | } else { 254 | res.render('gambits/get', { 255 | gambit: gambit, 256 | topic: topic, 257 | reply: null, 258 | parent: null, 259 | topics: allTopics}); 260 | } 261 | } 262 | ); 263 | } 264 | ); 265 | }); 266 | 267 | }); 268 | }, 269 | 270 | // Render a new gambit form 271 | // We create a gambit object but it is not saved. 272 | // TODO - We have topics, lets fetch them 273 | new: function(req, res) { 274 | if (req.query.replyId) { 275 | return models.gambit.findOne({replies: req.query.replyId}) 276 | .populate('replies', null, {_id: req.query.replyId}) 277 | .exec(function(error, parentGambit) { 278 | var gambit = new models.gambit(); 279 | gambit.input = req.query.input || ""; 280 | 281 | res.render('gambits/get', { 282 | isNew:true, 283 | gambit: gambit, 284 | topic: {}, 285 | topics:[], 286 | parent: parentGambit }); 287 | } 288 | ); 289 | } else if (req.query.topicId) { 290 | 291 | return models.topic.findOne({_id: req.query.topicId}) 292 | .exec(function(error, parentTopic) { 293 | var gambit = new models.gambit(); 294 | gambit.input = req.query.input || ""; 295 | res.render('gambits/get', { 296 | isNew:true, 297 | gambit: gambit, 298 | topic: {}, 299 | topics:[], 300 | parent: parentTopic }); 301 | } 302 | ); 303 | } else { 304 | res.flash("Gambits need to be attached to a topic or reply", "error"); 305 | res.redirect("back"); 306 | } 307 | } 308 | }; 309 | }; 310 | -------------------------------------------------------------------------------- /controllers/knowledge.js: -------------------------------------------------------------------------------- 1 | var _ = require("underscore"); 2 | 3 | module.exports = function(models, bot) { 4 | 5 | return { 6 | index: function(req, res) { 7 | bot.factSystem.db.get({predicate: 'isa', object: 'concept' }, function(err, items){ 8 | res.render('knowledge/index',{concepts:items}); 9 | }); 10 | }, 11 | 12 | graph: function(req, res) { 13 | bot.factSystem.db.get({limit: 2000, offset: 0}, function(err, items){ 14 | var data1 = items.map(function(el, index){ 15 | return el.subject; 16 | }); 17 | 18 | var data2 = items.map(function(el, index){ 19 | return el.object; 20 | }); 21 | 22 | var data = _.unique(data1.concat(data2)); 23 | 24 | // Gen IDs 25 | var data3 = []; 26 | data3 = data.map(function(item, index){ 27 | return {id:'xx_'+index, name:item}; 28 | }); 29 | 30 | // nodes 31 | var nodes = data3.map(function(item, index){ 32 | return {data: item}; 33 | }); 34 | 35 | // Edges 36 | var edges = items.map(function(item, index){ 37 | var sourceID, targetID; 38 | for(var i = 0; i < data3.length; i++) { 39 | if (data3[i].name === item.subject) { 40 | sourceID = data3[i].id; 41 | } 42 | } 43 | 44 | for(var i = 0; i < data3.length; i++) { 45 | if (data3[i].name === item.object) { 46 | targetID = data3[i].id; 47 | } 48 | } 49 | 50 | return { data: { source: sourceID, target: targetID } }; 51 | }); 52 | 53 | res.render('knowledge/graph', {nodes: nodes, edges: edges}); 54 | }); 55 | }, 56 | 57 | filter: function(req, res) { 58 | params = {}; 59 | for (var x in req.query) { 60 | if (req.query[x] !== "") { 61 | params[x] = req.query[x]; 62 | } 63 | } 64 | 65 | bot.factSystem.db.get(params, function(err, items) { 66 | res.render('knowledge/world', {concepts:items, params:params}); 67 | }); 68 | }, 69 | 70 | concept: function(req, res) { 71 | bot.factSystem.db.get({subject:req.params.name}, function(err, data){ 72 | res.render('knowledge/index',{concept: data}); 73 | }); 74 | }, 75 | 76 | user: function(req, res) { 77 | // TODO: Change user by req. 78 | bot.getUser('testUser', function(err, user) { 79 | if (user) { 80 | user.memory.db.get({}, function(err, items) { 81 | res.render('knowledge/user',{concepts:items}); 82 | }); 83 | } else { 84 | res.render('knowledge/user',{concepts:[]}); 85 | } 86 | }); 87 | }, 88 | 89 | userDelete: function(req, res) { 90 | var triple = { subject: req.body.s, predicate: req.body.p, object: req.body.o }; 91 | 92 | bot.getUser('testUser', function(err, user) { 93 | if (user) { 94 | user.memory.db.del(triple, function(err) { 95 | res.sendStatus(200); 96 | }); 97 | } else { 98 | res.sendStatus(200); 99 | } 100 | }); 101 | }, 102 | 103 | 104 | bot: function(req, res) { 105 | bot.memory.db.get({}, function(err, items) { 106 | res.render('knowledge/bot', {concepts:items}); 107 | }); 108 | }, 109 | 110 | addBot: function(req, res) { 111 | var s = req.body.subject; 112 | var p = req.body.predicate; 113 | var v = req.body.object; 114 | 115 | if (s && p && v) { 116 | bot.memory.create(req.body.subject, req.body.predicate, req.body.object, false , function(err, items) { 117 | req.flash('success', 'Fact Created'); 118 | res.redirect('back'); 119 | }); 120 | } else { 121 | req.flash('error', 'Missing Value'); 122 | res.redirect('back'); 123 | } 124 | }, 125 | 126 | botDelete: function(req, res) { 127 | var triple = { subject: req.body.s, predicate: req.body.p, object: req.body.o }; 128 | bot.memory.db.del(triple, function(err) { 129 | res.sendStatus(200); 130 | }); 131 | }, 132 | 133 | botImport: function(req, res) { 134 | bot.memory.loadFile([req.files.file.path], function(){ 135 | res.redirect('back'); 136 | }); 137 | }, 138 | 139 | world: function(req, res) { 140 | bot.factSystem.db.get({limit: 100, offset: 0}, function(err, items) { 141 | res.render('knowledge/world', {concepts:items, params: {}}); 142 | }); 143 | }, 144 | 145 | addWorld: function(req, res) { 146 | 147 | var s = req.body.subject; 148 | var p = req.body.predicate; 149 | var v = req.body.object; 150 | 151 | if (s && p && v) { 152 | bot.factSystem.create(req.body.subject, req.body.predicate, req.body.object, false , function(err, items) { 153 | req.flash('success', 'Fact Created'); 154 | res.redirect('back'); 155 | }); 156 | } else { 157 | req.flash('error', 'Missing Value'); 158 | res.redirect('back'); 159 | } 160 | }, 161 | 162 | worldImport: function(req, res) { 163 | bot.factSystem.loadFile([req.files.file.path], function(){ 164 | res.redirect('back'); 165 | }); 166 | }, 167 | 168 | worldDelete: function(req, res) { 169 | var triple = { subject: req.body.s, predicate: req.body.p, object: req.body.o }; 170 | bot.factSystem.db.del(triple, function(err) { 171 | res.sendStatus(200); 172 | }); 173 | }, 174 | 175 | }; 176 | }; 177 | -------------------------------------------------------------------------------- /controllers/plugins.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(models, bot) { 3 | 4 | return { 5 | getJSON: function(req, res) { 6 | res.json(Object.keys(bot.getPlugins())); 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /controllers/replies.js: -------------------------------------------------------------------------------- 1 | var _ = require("underscore"); 2 | var async = require("async"); 3 | 4 | module.exports = function(models) { 5 | return { 6 | index : function(req, res) { 7 | models.reply.find({}, function(err, replies) { 8 | res.render('replies/index', {replies:replies}); 9 | }); 10 | }, 11 | 12 | show : function(req, res) { 13 | return models.reply.findById(req.params.id).populate('gambits').exec(function(error, replies) { 14 | var iter = function (gambit, cb) { 15 | Gambit.populate(gambit, { path: 'replies' }, cb); 16 | }; 17 | 18 | async.each(replies.gambits, iter, function done(err) { 19 | res.render('replies/get', {replies:replies}); 20 | }); 21 | }); 22 | }, 23 | 24 | delete: function (req, res) { 25 | return models.reply.findById(req.params.id, function (err, item) { 26 | if (!item) { 27 | return res.sendStatus(410); 28 | } else { 29 | return item.remove(function (err) { 30 | return res.sendStatus(200); 31 | }); 32 | } 33 | }); 34 | }, 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /controllers/topics.js: -------------------------------------------------------------------------------- 1 | var _ = require("underscore"); 2 | var async = require("async"); 3 | 4 | module.exports = function(models, bot) { 5 | return { 6 | index : function(req, res) { 7 | models.topic.find({}, null, {sort:{name:1}}, function(err, topics){ 8 | res.render('topics/index', {topics: topics }); 9 | }); 10 | }, 11 | 12 | reorder: function(req, res) { 13 | models.topic.findById(req.params.id, function (err, topic) { 14 | if (req.body.ids.length === topic.gambits.length) { 15 | topic.gambits = req.body.ids; 16 | topic.save(); 17 | } 18 | return res.sendStatus(200); 19 | }); 20 | }, 21 | 22 | post: function(req, res) { 23 | var topicName = req.body.name.toLowerCase(); 24 | topicName = topicName.replace(/\s/g,"_"); 25 | 26 | var keywords = req.body.keywords.split(","); 27 | var system = (req.body.system == "on") ? true : false; 28 | 29 | if (topicName !== "") { 30 | new models.topic({name: topicName, keywords: keywords, system: system, keep:true }).save(function(err){ 31 | if (req.body.name !== topicName) { 32 | req.flash('success', 'Gambit Created, but we changed the name.'); 33 | } else { 34 | req.flash('success', 'Gambit Created'); 35 | } 36 | 37 | res.redirect('/topics'); 38 | }); 39 | } else { 40 | req.flash('error', 'Topic Name is required.'); 41 | res.redirect("/topics"); 42 | } 43 | }, 44 | 45 | update: function(req, res) { 46 | var topicName = req.body.name.toLowerCase(); 47 | topicName = topicName.replace(/\s/g,"_"); 48 | 49 | var keywords = req.body.keywords.split(","); 50 | var system = (req.body.system == "on") ? true : false; 51 | var keep = (req.body.keep == "on") ? true : false; 52 | 53 | if (topicName !== "") { 54 | models.topic.findById(req.params.id, function (err, topic) { 55 | topic.keywords = keywords; 56 | topic.name = topicName; 57 | topic.keep = keep; 58 | topic.system = system; 59 | topic.save(function(){ 60 | req.flash('success', 'Gambit Created'); 61 | res.redirect('back'); 62 | }); 63 | }); 64 | } else { 65 | req.flash('error', 'Topic Name is required.'); 66 | res.redirect("back"); 67 | } 68 | }, 69 | 70 | 71 | delete: function (req, res) { 72 | return models.topic.findById(req.params.id, function (err, item) { 73 | if (!item) { 74 | return res.sendStatus(410); 75 | } else { 76 | return item.remove(function (err) { 77 | return res.sendStatus(200); 78 | }); 79 | } 80 | }); 81 | }, 82 | 83 | sort: function(req, res) { 84 | models.topic.findById(req.params.id, function(err, topic) { 85 | topic.sortGambits(function() { 86 | res.redirect('back'); 87 | }); 88 | }); 89 | }, 90 | 91 | show: function(req, res) { 92 | 93 | models.topic.findById(req.params.id, function(err, topic) { 94 | 95 | var iter = function (gambit, cb) { 96 | Reply.populate(gambit, { path: 'replies' }, cb); 97 | }; 98 | 99 | models.topic.findById(req.params.id).populate('gambits').exec(function(error, topic) { 100 | 101 | async.each(topic.gambits, iter, function done(err) { 102 | // We bring in all the gambits so we can add them to the topic. 103 | models.gambit.find({},'_id, input', function(error, gambits) { 104 | res.render('topics/get', {topic: topic, gambits:gambits }); 105 | }); 106 | }); 107 | }); 108 | 109 | }); 110 | 111 | 112 | }, 113 | 114 | // Test a topic against input 115 | test: function(req, res) { 116 | return models.topic.findById(req.params.id, function (err, topic) { 117 | bot.message(req.body.phrase, function(err, messageObj){ 118 | topic.doesMatch(messageObj, function(err, result){ 119 | res.json(result); 120 | }); 121 | }); 122 | }); 123 | }, 124 | }; 125 | }; 126 | -------------------------------------------------------------------------------- /lib/slack.js: -------------------------------------------------------------------------------- 1 | 2 | var token = process.env.TOKEN; 3 | var factDB = process.env.PROJECT; 4 | var topicDB = process.env.PROJECT; 5 | var dbHost = process.env.DBHOST || "localhost"; 6 | 7 | // This is the main Bot interface 8 | var superscript = require("superscript"); 9 | var mongoose = require("mongoose"); 10 | var facts = require("sfacts"); 11 | var Slack = require("slack-client"); 12 | 13 | mongoose.connect('mongodb://' + dbHost + '/' + topicDB); 14 | 15 | var factSystem = facts.explore("botfacts"); 16 | var TopicSystem = require("superscript/lib/topics/index")(mongoose, factDB); 17 | 18 | // How should we reply to the user? 19 | // direct - sents a DM 20 | // atReply - sents a channel message with @username 21 | // public sends a channel reply with no username 22 | var replyType = "atReply"; 23 | 24 | var atReplyRE = /<@(.*?)>/; 25 | var options = {}; 26 | options['factSystem'] = factSystem; 27 | options['mongoose'] = mongoose; 28 | 29 | var slack = new Slack(token, true, true); 30 | 31 | var botHandle = function(err, bot) { 32 | slack.login() 33 | 34 | slack.on('error', function(error) { 35 | console.error("Error:"); 36 | console.log(error) 37 | }) 38 | 39 | slack.on('open', function(){ 40 | console.log("Welcome to Slack. You are %s of %s", slack.self.name, slack.team.name); 41 | }) 42 | 43 | slack.on('close', function() { 44 | console.warn("Disconnected"); 45 | }) 46 | 47 | slack.on('message', function(data) { 48 | receiveData(slack, bot, data); 49 | }); 50 | } 51 | 52 | var receiveData = function(slack, bot, data) { 53 | 54 | // Fetch the user who sent the message; 55 | var user = data._client.users[data.user]; 56 | var channel; 57 | var messageData = data.toJSON(); 58 | var message = ""; 59 | 60 | if (messageData && messageData.text) { 61 | message = "" + messageData.text.trim(); 62 | } 63 | 64 | 65 | var match = message.match(atReplyRE) 66 | 67 | // Are they talking to us? 68 | if (match && match[1] === slack.self.id) { 69 | 70 | message = message.replace(atReplyRE, '').trim(); 71 | if (message[0] == ':') { 72 | message = message.substring(1).trim(); 73 | } 74 | 75 | bot.reply(user.name, message, function(err, reply){ 76 | // We reply back direcly to the user 77 | 78 | switch (replyType) { 79 | case "direct": 80 | channel = slack.getChannelGroupOrDMByName(user.name); 81 | break; 82 | case "atReply": 83 | reply.string = "@" + user.name + " " + reply.string; 84 | channel = slack.getChannelGroupOrDMByID(messageData.channel); 85 | break; 86 | case "public": 87 | channel = slack.getChannelGroupOrDMByID(messageData.channel); 88 | break 89 | 90 | } 91 | if (reply.string) { 92 | channel.send(reply.string); 93 | } 94 | 95 | }); 96 | 97 | } else if (messageData.channel[0] == "D") { 98 | bot.reply(user.name, message, function(err, reply){ 99 | channel = slack.getChannelGroupOrDMByName(user.name); 100 | if (reply.string) { 101 | channel.send(reply.string); 102 | } 103 | }); 104 | } else { 105 | console.log("Ignoring...", messageData) 106 | } 107 | } 108 | 109 | // Main entry point 110 | new superscript(options, function(err, botInstance){ 111 | botHandle(null, botInstance); 112 | }); 113 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "superscript-editor", 3 | "version": "0.1.7", 4 | "description": "A community editor for creating Chatter bots with SuperScript", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "node app", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Rob Ellis", 11 | "licenses": [ 12 | { 13 | "type": "MIT", 14 | "url": "https://raw.github.com/silentrob/superscript-editor/master/LICENSE" 15 | } 16 | ], 17 | "dependencies": { 18 | "async": "^0.9.0", 19 | "body-parser": "^1.11.0", 20 | "compression": "^1.4.0", 21 | "config": "^1.10.0", 22 | "connect-flash": "^0.1.1", 23 | "cookie-parser": "^1.3.3", 24 | "cookie-session": "^1.1.0", 25 | "csurf": "^1.6.6", 26 | "cytoscape": "^2.4.1", 27 | "express": "^4.11.2", 28 | "jade": "^1.9.1", 29 | "method-override": "^2.3.1", 30 | "mongoose": "~3.8.22", 31 | "mongoose-findorcreate": "^0.1.2", 32 | "morgan": "^1.5.1", 33 | "multer": "^0.1.7", 34 | "sfacts": "0.0.9", 35 | "slack-client": "^1.4.0", 36 | "socket.io": "^1.3.3", 37 | "superscript": "latest", 38 | "underscore": "^1.7.0", 39 | "view-helpers": "^0.1.5", 40 | "winston": "^1.0.0", 41 | "debug": "^2.1.2", 42 | "moment": "^2.9.0", 43 | "request": "^2.53.0", 44 | "roman-numerals": "^0.3.2" 45 | }, 46 | "devDependencies": { 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /plugins/animals.js: -------------------------------------------------------------------------------- 1 | var _ = require("underscore"); 2 | 3 | exports.aGender = function(gender, thing, cb) { 4 | this.facts.db.get({predicate:gender, object:thing}, function(e,r){ 5 | if (!_.isEmpty(r)) { 6 | cb(null, r[0].subject); 7 | } else { 8 | cb(null,""); 9 | } 10 | }); 11 | } 12 | 13 | exports.aBaby = function(thing, cb) { 14 | cb(null, thing); 15 | } 16 | 17 | exports.aLocation = function(thing, cb) { 18 | // cb(null, thing); 19 | this.facts.db.get({predicate:"habitat", object:thing}, function(e,r){ 20 | if (!_.isEmpty(r)) { 21 | var str = "A " + thing + " lives in a " + r[0].subject; 22 | cb(null, str); 23 | } else { 24 | cb(null,""); 25 | } 26 | }); 27 | } 28 | 29 | 30 | exports.aGroup = function(thing, cb) { 31 | cb(null, thing); 32 | } -------------------------------------------------------------------------------- /plugins/bot.js: -------------------------------------------------------------------------------- 1 | var _ = require("underscore"); 2 | var Utils = require("superscript/lib/utils"); 3 | 4 | exports.getFavorite = function(thing, cb) { 5 | var that = this; 6 | var message = this.message; 7 | var suggest = ""; 8 | var facts = that.facts.db; 9 | var userfacts = that.user.memory.db; 10 | var botfacts = that.botfacts.db; 11 | 12 | var cleanThing = thing.toLowerCase(); 13 | cleanThing = cleanThing.replace(/\s/g,"_"); 14 | 15 | botfacts.get({subject:'favorite', predicate: cleanThing}, function(err, list) { 16 | if (!_.isEmpty(list)) { 17 | var favThing = Utils.pickItem(list); 18 | favThing = favThing.object.replace(/_/g, " "); 19 | cb(null, "My favorite " + thing + " is " + favThing + "."); 20 | } else { 21 | // Quibble can handle this. 22 | cb(null, ""); 23 | } 24 | }); 25 | }; 26 | -------------------------------------------------------------------------------- /plugins/cnet.js: -------------------------------------------------------------------------------- 1 | 2 | var debug = require("debug")("Reason Plugin"); 3 | var history = require("superscript/lib/history"); 4 | var Utils = require("superscript/lib/utils"); 5 | var wd = require("superscript/lib/wordnet"); 6 | var _ = require("underscore"); 7 | var moment = require("moment"); 8 | 9 | exports.usedFor = function(cb) { 10 | var that = this; 11 | this.cnet.usedForForward(that.message.nouns[0], function(e,r){ 12 | if (!_.isEmpty(r)) { 13 | var res = (r) ? Utils.makeSentense(r[0].sentense) : ""; 14 | cb(null, res); 15 | } else { 16 | cb(null,""); 17 | } 18 | }); 19 | } 20 | 21 | exports.putA = function(cb) { 22 | var that = this; 23 | var thing = (that.message.entities[0]) ? that.message.entities[0] : that.message.nouns[0]; 24 | var userfacts = that.user.memory.db; 25 | 26 | if (thing) { 27 | this.cnet.putConcept(thing, function(e, putThing){ 28 | if (putThing) { 29 | cb(null, Utils.makeSentense(Utils.indefiniteArticlerize(putThing))); 30 | } else { 31 | cb(null, ""); 32 | } 33 | }); 34 | } 35 | } 36 | 37 | exports.isA = function(cb) { 38 | var that = this; 39 | var thing = (that.message.entities[0]) ? that.message.entities[0] : that.message.nouns[0]; 40 | var userfacts = that.user.memory.db; 41 | var userID = that.user.name; 42 | 43 | if (thing) { 44 | this.cnet.isAForward(thing, function(e,r){ 45 | if (!_.isEmpty(r)) { 46 | var res = (r) ? Utils.makeSentense(r[0].sentense) : ""; 47 | cb(null, res); 48 | } else { 49 | // Lets try wordnet 50 | wd.define(thing, function(err, result){ 51 | if (err) { 52 | cb(null, ""); 53 | } else { 54 | cb(null, result); 55 | } 56 | }); 57 | } 58 | }); 59 | } else { 60 | var thing = ""; 61 | // my x is adj => what is adj 62 | if (that.message.adverbs[0]) { 63 | thing = that.message.adverbs[0]; 64 | } else { 65 | thing = that.message.adjectives[0]; 66 | } 67 | userfacts.get({object:thing, predicate: userID}, function(err, list) { 68 | if (!_.isEmpty(list)){ 69 | // Because it came from userID it must be his 70 | cb(null, "You said your " + list[0].subject + " is " + thing + "."); 71 | } else { 72 | // find example of thing? 73 | cb(null, ""); 74 | } 75 | }); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /plugins/define.js: -------------------------------------------------------------------------------- 1 | var wd = require("superscript/lib/wordnet"); 2 | 3 | exports.wordnetDefine = function(cb) { 4 | var args = Array.prototype.slice.call(arguments); 5 | var word; 6 | 7 | if (args.length == 2) { 8 | word = args[0]; 9 | } else { 10 | word = this.message.words.pop(); 11 | } 12 | 13 | wd.define(word, function(err, result){ 14 | if (err) { 15 | cb(null,""); 16 | } else { 17 | cb(null, "The definition of " + word + " is " + result); 18 | } 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /plugins/math.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Math functions for 4 | - evaluating expressions 5 | - converting functions 6 | - sequence functions 7 | */ 8 | 9 | var math = require("superscript/lib/math"); 10 | var roman = require('roman-numerals'); 11 | var debug = require("debug")("mathPlugin"); 12 | var Utils = require("superscript/lib/utils"); 13 | 14 | exports.evaluateExpression = function(cb) { 15 | if (this.message.numericExp || (this.message.halfNumericExp && this.user.prevAns)) { 16 | var answer = math.parse(this.message.cwords, this.user.prevAns); 17 | if (answer) { 18 | this.user.prevAns = answer; 19 | var suggestedReply = "I think it is " + answer; 20 | } else { 21 | var suggestedReply = "What do I look like, a computer?"; 22 | } 23 | cb(null, suggestedReply); 24 | } else { 25 | cb(true, ""); 26 | } 27 | } 28 | 29 | exports.numToRoman = function(cb) { 30 | suggest = "I think it is " + roman.toRoman(this.message.numbers[0]); 31 | cb(null, suggest); 32 | } 33 | 34 | exports.numToHex = function(cb) { 35 | suggest = "I think it is " + parseInt(this.message.numbers[0], 10).toString(16); 36 | cb(null, suggest); 37 | } 38 | 39 | exports.numToBinary = function(cb) { 40 | var suggest = "I think it is " + parseInt(this.message.numbers[0], 10).toString(2); 41 | cb(null, suggest); 42 | } 43 | 44 | 45 | exports.numMissing = function(cb) { 46 | // What number are missing 1, 3, 5, 7 47 | if (this.message.lemWords.indexOf("missing") != -1 && this.message.numbers.length != 0) { 48 | var numArray = this.message.numbers.sort(); 49 | var mia = []; 50 | for(var i = 1; i < numArray.length; i++) { 51 | if(numArray[i] - numArray[i-1] != 1) { 52 | var x = numArray[i] - numArray[i-1]; 53 | var j = 1; 54 | while (j < x) { 55 | mia.push(parseFloat(numArray[i-1])+j); 56 | j++; 57 | } 58 | } 59 | } 60 | var s = mia.sort(function(a, b){return a-b}); 61 | cb(null, "I think it is " + s.join(" ")); 62 | } else { 63 | cb(true, ""); 64 | } 65 | } 66 | 67 | // Sequence 68 | exports.numSequence = function(cb) { 69 | if (this.message.lemWords.indexOf("sequence") != -1 && this.message.numbers.length != 0) { 70 | debug("Finding the next number in the series") 71 | var numArray = this.message.numbers.map(function(item){return parseInt(item)}) 72 | numArray = numArray.sort(function(a, b){return a-b}); 73 | 74 | if (math.arithGeo(numArray) == "Arithmetic") { 75 | for(var i = 1; i < numArray.length; i++) { 76 | var x = numArray[i] - numArray[i-1]; 77 | } 78 | suggest = "I think it is " + (parseInt(numArray.pop()) + x); 79 | } else if (math.arithGeo(numArray) == "Geometric") { 80 | var a = numArray[1]; 81 | var r = a / numArray[0]; 82 | suggest = "I think it is " + numArray.pop() * r; 83 | } 84 | 85 | cb(null, suggest); 86 | } else { 87 | cb(true, ""); 88 | } 89 | } 90 | 91 | // returns the state of a number 92 | exports.evenOdd = function(cb) { 93 | var message = this.message; 94 | 95 | if (message.numbers.length == 1) { 96 | var num = message.numbers[0]; 97 | if (num % 2 == 0) { 98 | cb(null,"It is even."); 99 | } else { 100 | cb(null,"It is odd."); 101 | } 102 | 103 | } else { 104 | cb(null,"Which number ?"); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /plugins/time.js: -------------------------------------------------------------------------------- 1 | var moment = require("moment"); 2 | var COEFF = 1000 * 60 * 5; 3 | 4 | var getSeason = function() { 5 | 6 | var now = moment(); 7 | now.dayOfYear() 8 | var doy = now.dayOfYear(); 9 | 10 | if (doy > 80 && doy < 172) { 11 | return "spring"; 12 | } else if (doy > 172 && doy < 266) { 13 | return "summer" 14 | } else if (doy > 266 && doy < 357) { 15 | return "fall" 16 | } else if ( doy < 80 || doy > 357) { 17 | return "winter"; 18 | } 19 | } 20 | 21 | exports.getDOW = function(cb) { 22 | cb(null, moment().format("dddd")); 23 | } 24 | 25 | exports.getDate = function(cb) { 26 | cb(null, moment().format("ddd, MMMM Do")); 27 | } 28 | 29 | exports.getDateTomorrow = function(cb) { 30 | var date = moment().add(1,'d').format("ddd, MMMM Do"); 31 | cb(null, date); 32 | } 33 | 34 | exports.getSeason = function(cb) { 35 | var date = moment().add(1,'d').format("ddd, MMMM Do"); 36 | cb(null, getSeason()); 37 | } 38 | 39 | exports.getTime = function(cb) { 40 | var date = new Date(); 41 | var rounded = new Date(Math.round(date.getTime() / COEFF) * COEFF); 42 | var time = moment(rounded).format("h:mm"); 43 | // TODO, dont say this every time. 44 | cb(null, "I'm in Vancouver, so time is " + time + " here."); 45 | } 46 | 47 | exports.getTimeOfDay = function(cb) { 48 | var date = new Date(); 49 | var rounded = new Date(Math.round(date.getTime() / COEFF) * COEFF); 50 | var time = moment(rounded).format("H") 51 | var tod 52 | if (time < 12) { 53 | tod = "morning" 54 | } else if (time < 17) { 55 | tod = "afternoon" 56 | } else { 57 | tod = "evening" 58 | } 59 | 60 | cb(null, tod); 61 | } 62 | 63 | exports.getDayOfWeek = function(cb) { 64 | cb(null, moment().format("dddd")); 65 | } 66 | 67 | exports.getMonth = function(cb) { 68 | var reply = ""; 69 | if (this.message.words.indexOf("next") != -1) { 70 | reply = moment().add(1,'M').format("MMMM"); 71 | } else if (this.message.words.indexOf("previous") != -1) { 72 | reply = moment().subtract(1,'M').format("MMMM"); 73 | } else if (this.message.words.indexOf("first") != -1) { 74 | reply = "January"; 75 | } else if (this.message.words.indexOf("last") != -1) { 76 | reply = "December"; 77 | } else { 78 | var reply = moment().format("MMMM"); 79 | } 80 | cb(null, reply); 81 | } 82 | -------------------------------------------------------------------------------- /plugins/user.js: -------------------------------------------------------------------------------- 1 | var debug = require("debug")("UserFacts"); 2 | var _ = require("underscore"); 3 | var Utils = require("superscript/lib/utils"); 4 | 5 | exports.save = function(key, value, cb) { 6 | var memory = this.user.memory; 7 | var userId = this.user.id; 8 | 9 | memory.db.get({subject:key, predicate: userId }, function(err, results) { 10 | if (!_.isEmpty(results)) { 11 | memory.db.del(results[0], function(){ 12 | memory.db.put({subject:key, predicate: userId, object: value}, function(){ 13 | cb(null,""); 14 | }); 15 | }); 16 | } else { 17 | memory.db.put({subject:key, predicate: userId, object: value}, function(err){ 18 | cb(null, ""); 19 | }); 20 | } 21 | }); 22 | } 23 | 24 | exports.get = function(key, cb) { 25 | 26 | var memory = this.user.memory; 27 | var userId = this.user.id; 28 | 29 | debug("getVar", key, userId); 30 | 31 | memory.db.get({subject:key, predicate: userId}, function resultHandle(err, res){ 32 | if (res && res.length != 0) { 33 | cb(err, res[0].object); 34 | } else { 35 | cb(err, null); 36 | } 37 | }); 38 | } 39 | 40 | exports.createUserFact = function(s,v,o,cb) { 41 | 42 | if (s != "undefined" && v != "undefined" && o != "undefined") { 43 | this.user.memory.create(s,v,o,false, function(){ 44 | cb(null,""); 45 | }); 46 | } else { 47 | debug("Possible Error with fact", this.message.raw); 48 | cb(null,"") 49 | } 50 | 51 | } 52 | 53 | 54 | // What does my dad like to play? 55 | exports.resolveUserFact = function(subject, verb, cb) { 56 | var subject = subject.replace(/\s/g,"_").toLowerCase(); 57 | 58 | console.log("resolveUserFact", subject, verb); 59 | var memory = this.user.memory; 60 | memory.db.get({subject:subject, predicate:verb}, function(err, result){ 61 | if (!_.isEmpty(result)) { 62 | cb(null, result[0].object); 63 | } else { 64 | memory.db.get({object:subject, predicate:verb}, function(err, result){ 65 | if (!_.isEmpty(result)) { 66 | cb(null, result[0].subject); 67 | } else { 68 | cb(null,""); 69 | } 70 | }); 71 | } 72 | }); 73 | } 74 | 75 | 76 | // We check to see if we know the name in the message object 77 | exports.known = function(bool, cb) { 78 | var memory = this.user.memory; 79 | var name = (this.message.names && !_.isEmpty(this.message.names)) ? this.message.names[0] : ""; 80 | memory.db.get({subject:name.toLowerCase()}, function resultHandle(err, res1){ 81 | memory.db.get({object:name.toLowerCase()}, function resultHandle(err, res2){ 82 | if (_.isEmpty(res1) && _.isEmpty(res2)) { 83 | cb(null, (bool == "false") ? true : false) 84 | } else { 85 | cb(null, (bool == "true") ? true : false) 86 | } 87 | }); 88 | }); 89 | } 90 | 91 | 92 | exports.inTopic = function(topic, cb) { 93 | if (topic == this.user.currentTopic) { 94 | cb(null, "true"); 95 | } else { 96 | cb(null, "false"); 97 | } 98 | } -------------------------------------------------------------------------------- /plugins/weather.js: -------------------------------------------------------------------------------- 1 | var request = require("request"); 2 | exports.weather = function(cb) { 3 | 4 | if (this.message.names) { 5 | var location = this.message.names[0] 6 | 7 | request.get("http://api.openweathermap.org/data/2.5/find?q="+location+"&type=like&units=metric&mode=json", function(err, res, body) { 8 | var results = JSON.parse(body); 9 | if (results.list.length != 0) { 10 | console.log(JSON.stringify(results.list, null, 2)) 11 | cb(null, "It is " + results.list[0]['weather'][0]['description']); 12 | } else { 13 | cb(null, "I'm not near a window."); 14 | } 15 | }); 16 | } else { 17 | cb(null, "I'm not near a window."); 18 | } 19 | } 20 | 21 | 22 | exports.temp = function(cb) { 23 | 24 | if (this.message.names) { 25 | var location = this.message.names[0] 26 | 27 | request.get("http://api.openweathermap.org/data/2.5/find?q="+location+"&type=like&units=metric&mode=json", function(err, res, body) { 28 | var results = JSON.parse(body); 29 | if (results.list.length != 0) { 30 | cb(null, "It is about " + results.list[0]['main'].temp); 31 | } else { 32 | cb(null, "I'm not near a window."); 33 | } 34 | }); 35 | } else { 36 | cb(null, "I'm not near a window."); 37 | } 38 | } -------------------------------------------------------------------------------- /public/css/app.css: -------------------------------------------------------------------------------- 1 | svg { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | 6 | .bg-warning, .bg-success { 7 | padding:15px; 8 | } 9 | 10 | .select-group-control { 11 | border-top-left-radius: 0px; 12 | border-bottom-left-radius: 0px; 13 | -webkit-appearance: none; 14 | } 15 | 16 | .btn-file { 17 | position: relative; 18 | overflow: hidden; 19 | } 20 | .btn-file input[type=file] { 21 | position: absolute; 22 | top: 0; 23 | right: 0; 24 | min-width: 100%; 25 | min-height: 100%; 26 | font-size: 100px; 27 | text-align: right; 28 | filter: alpha(opacity=0); 29 | opacity: 0; 30 | outline: none; 31 | background: white; 32 | cursor: inherit; 33 | display: block; 34 | } 35 | 36 | .hilite { 37 | background-color: yellow; 38 | } 39 | 40 | .sortable > li { 41 | list-style: none; 42 | border: 1px solid #CCC; 43 | /*background: #efefef;*/ 44 | /*color: #1C94C4;*/ 45 | margin: 5px; 46 | padding: 5px; 47 | } -------------------------------------------------------------------------------- /public/css/bootstrap-theme.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.2 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | .btn-default, 8 | .btn-primary, 9 | .btn-success, 10 | .btn-info, 11 | .btn-warning, 12 | .btn-danger { 13 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); 14 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 15 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 16 | } 17 | .btn-default:active, 18 | .btn-primary:active, 19 | .btn-success:active, 20 | .btn-info:active, 21 | .btn-warning:active, 22 | .btn-danger:active, 23 | .btn-default.active, 24 | .btn-primary.active, 25 | .btn-success.active, 26 | .btn-info.active, 27 | .btn-warning.active, 28 | .btn-danger.active { 29 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 30 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 31 | } 32 | .btn-default .badge, 33 | .btn-primary .badge, 34 | .btn-success .badge, 35 | .btn-info .badge, 36 | .btn-warning .badge, 37 | .btn-danger .badge { 38 | text-shadow: none; 39 | } 40 | .btn:active, 41 | .btn.active { 42 | background-image: none; 43 | } 44 | .btn-default { 45 | text-shadow: 0 1px 0 #fff; 46 | background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); 47 | background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); 48 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); 49 | background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); 50 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); 51 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 52 | background-repeat: repeat-x; 53 | border-color: #dbdbdb; 54 | border-color: #ccc; 55 | } 56 | .btn-default:hover, 57 | .btn-default:focus { 58 | background-color: #e0e0e0; 59 | background-position: 0 -15px; 60 | } 61 | .btn-default:active, 62 | .btn-default.active { 63 | background-color: #e0e0e0; 64 | border-color: #dbdbdb; 65 | } 66 | .btn-default.disabled, 67 | .btn-default:disabled, 68 | .btn-default[disabled] { 69 | background-color: #e0e0e0; 70 | background-image: none; 71 | } 72 | .btn-primary { 73 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); 74 | background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); 75 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); 76 | background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); 77 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); 78 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 79 | background-repeat: repeat-x; 80 | border-color: #245580; 81 | } 82 | .btn-primary:hover, 83 | .btn-primary:focus { 84 | background-color: #265a88; 85 | background-position: 0 -15px; 86 | } 87 | .btn-primary:active, 88 | .btn-primary.active { 89 | background-color: #265a88; 90 | border-color: #245580; 91 | } 92 | .btn-primary.disabled, 93 | .btn-primary:disabled, 94 | .btn-primary[disabled] { 95 | background-color: #265a88; 96 | background-image: none; 97 | } 98 | .btn-success { 99 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); 100 | background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); 101 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); 102 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); 103 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); 104 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 105 | background-repeat: repeat-x; 106 | border-color: #3e8f3e; 107 | } 108 | .btn-success:hover, 109 | .btn-success:focus { 110 | background-color: #419641; 111 | background-position: 0 -15px; 112 | } 113 | .btn-success:active, 114 | .btn-success.active { 115 | background-color: #419641; 116 | border-color: #3e8f3e; 117 | } 118 | .btn-success.disabled, 119 | .btn-success:disabled, 120 | .btn-success[disabled] { 121 | background-color: #419641; 122 | background-image: none; 123 | } 124 | .btn-info { 125 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 126 | background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 127 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); 128 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); 129 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); 130 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 131 | background-repeat: repeat-x; 132 | border-color: #28a4c9; 133 | } 134 | .btn-info:hover, 135 | .btn-info:focus { 136 | background-color: #2aabd2; 137 | background-position: 0 -15px; 138 | } 139 | .btn-info:active, 140 | .btn-info.active { 141 | background-color: #2aabd2; 142 | border-color: #28a4c9; 143 | } 144 | .btn-info.disabled, 145 | .btn-info:disabled, 146 | .btn-info[disabled] { 147 | background-color: #2aabd2; 148 | background-image: none; 149 | } 150 | .btn-warning { 151 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 152 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 153 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); 154 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); 155 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); 156 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 157 | background-repeat: repeat-x; 158 | border-color: #e38d13; 159 | } 160 | .btn-warning:hover, 161 | .btn-warning:focus { 162 | background-color: #eb9316; 163 | background-position: 0 -15px; 164 | } 165 | .btn-warning:active, 166 | .btn-warning.active { 167 | background-color: #eb9316; 168 | border-color: #e38d13; 169 | } 170 | .btn-warning.disabled, 171 | .btn-warning:disabled, 172 | .btn-warning[disabled] { 173 | background-color: #eb9316; 174 | background-image: none; 175 | } 176 | .btn-danger { 177 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 178 | background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 179 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); 180 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); 181 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); 182 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 183 | background-repeat: repeat-x; 184 | border-color: #b92c28; 185 | } 186 | .btn-danger:hover, 187 | .btn-danger:focus { 188 | background-color: #c12e2a; 189 | background-position: 0 -15px; 190 | } 191 | .btn-danger:active, 192 | .btn-danger.active { 193 | background-color: #c12e2a; 194 | border-color: #b92c28; 195 | } 196 | .btn-danger.disabled, 197 | .btn-danger:disabled, 198 | .btn-danger[disabled] { 199 | background-color: #c12e2a; 200 | background-image: none; 201 | } 202 | .thumbnail, 203 | .img-thumbnail { 204 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 205 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 206 | } 207 | .dropdown-menu > li > a:hover, 208 | .dropdown-menu > li > a:focus { 209 | background-color: #e8e8e8; 210 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 211 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 212 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 213 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 214 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 215 | background-repeat: repeat-x; 216 | } 217 | .dropdown-menu > .active > a, 218 | .dropdown-menu > .active > a:hover, 219 | .dropdown-menu > .active > a:focus { 220 | background-color: #2e6da4; 221 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 222 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 223 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 224 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 225 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 226 | background-repeat: repeat-x; 227 | } 228 | .navbar-default { 229 | background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); 230 | background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); 231 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); 232 | background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); 233 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); 234 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 235 | background-repeat: repeat-x; 236 | border-radius: 4px; 237 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 238 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 239 | } 240 | .navbar-default .navbar-nav > .open > a, 241 | .navbar-default .navbar-nav > .active > a { 242 | background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); 243 | background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); 244 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); 245 | background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); 246 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); 247 | background-repeat: repeat-x; 248 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 249 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 250 | } 251 | .navbar-brand, 252 | .navbar-nav > li > a { 253 | text-shadow: 0 1px 0 rgba(255, 255, 255, .25); 254 | } 255 | .navbar-inverse { 256 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); 257 | background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); 258 | background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); 259 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); 260 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); 261 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 262 | background-repeat: repeat-x; 263 | } 264 | .navbar-inverse .navbar-nav > .open > a, 265 | .navbar-inverse .navbar-nav > .active > a { 266 | background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); 267 | background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); 268 | background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); 269 | background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); 270 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); 271 | background-repeat: repeat-x; 272 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 273 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 274 | } 275 | .navbar-inverse .navbar-brand, 276 | .navbar-inverse .navbar-nav > li > a { 277 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); 278 | } 279 | .navbar-static-top, 280 | .navbar-fixed-top, 281 | .navbar-fixed-bottom { 282 | border-radius: 0; 283 | } 284 | @media (max-width: 767px) { 285 | .navbar .navbar-nav .open .dropdown-menu > .active > a, 286 | .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, 287 | .navbar .navbar-nav .open .dropdown-menu > .active > a:focus { 288 | color: #fff; 289 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 290 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 291 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 292 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 293 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 294 | background-repeat: repeat-x; 295 | } 296 | } 297 | .alert { 298 | text-shadow: 0 1px 0 rgba(255, 255, 255, .2); 299 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 300 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 301 | } 302 | .alert-success { 303 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 304 | background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 305 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); 306 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); 307 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); 308 | background-repeat: repeat-x; 309 | border-color: #b2dba1; 310 | } 311 | .alert-info { 312 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 313 | background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 314 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); 315 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); 316 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); 317 | background-repeat: repeat-x; 318 | border-color: #9acfea; 319 | } 320 | .alert-warning { 321 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 322 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 323 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); 324 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); 325 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); 326 | background-repeat: repeat-x; 327 | border-color: #f5e79e; 328 | } 329 | .alert-danger { 330 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 331 | background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 332 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); 333 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); 334 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); 335 | background-repeat: repeat-x; 336 | border-color: #dca7a7; 337 | } 338 | .progress { 339 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 340 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 341 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); 342 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); 343 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); 344 | background-repeat: repeat-x; 345 | } 346 | .progress-bar { 347 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); 348 | background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); 349 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); 350 | background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); 351 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); 352 | background-repeat: repeat-x; 353 | } 354 | .progress-bar-success { 355 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); 356 | background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); 357 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); 358 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); 359 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); 360 | background-repeat: repeat-x; 361 | } 362 | .progress-bar-info { 363 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 364 | background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 365 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); 366 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); 367 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); 368 | background-repeat: repeat-x; 369 | } 370 | .progress-bar-warning { 371 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 372 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 373 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); 374 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); 375 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); 376 | background-repeat: repeat-x; 377 | } 378 | .progress-bar-danger { 379 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); 380 | background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); 381 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); 382 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); 383 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); 384 | background-repeat: repeat-x; 385 | } 386 | .progress-bar-striped { 387 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 388 | background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 389 | background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 390 | } 391 | .list-group { 392 | border-radius: 4px; 393 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 394 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 395 | } 396 | .list-group-item.active, 397 | .list-group-item.active:hover, 398 | .list-group-item.active:focus { 399 | text-shadow: 0 -1px 0 #286090; 400 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); 401 | background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); 402 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); 403 | background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); 404 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); 405 | background-repeat: repeat-x; 406 | border-color: #2b669a; 407 | } 408 | .list-group-item.active .badge, 409 | .list-group-item.active:hover .badge, 410 | .list-group-item.active:focus .badge { 411 | text-shadow: none; 412 | } 413 | .panel { 414 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 415 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 416 | } 417 | .panel-default > .panel-heading { 418 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 419 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 420 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 421 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 422 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 423 | background-repeat: repeat-x; 424 | } 425 | .panel-primary > .panel-heading { 426 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 427 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 428 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 429 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 430 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 431 | background-repeat: repeat-x; 432 | } 433 | .panel-success > .panel-heading { 434 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 435 | background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 436 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); 437 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); 438 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); 439 | background-repeat: repeat-x; 440 | } 441 | .panel-info > .panel-heading { 442 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 443 | background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 444 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); 445 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); 446 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); 447 | background-repeat: repeat-x; 448 | } 449 | .panel-warning > .panel-heading { 450 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 451 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 452 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); 453 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); 454 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); 455 | background-repeat: repeat-x; 456 | } 457 | .panel-danger > .panel-heading { 458 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 459 | background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 460 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); 461 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); 462 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); 463 | background-repeat: repeat-x; 464 | } 465 | .well { 466 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 467 | background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 468 | background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); 469 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); 470 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); 471 | background-repeat: repeat-x; 472 | border-color: #dcdcdc; 473 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 474 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 475 | } 476 | /*# sourceMappingURL=bootstrap-theme.css.map */ 477 | -------------------------------------------------------------------------------- /public/css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.2 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default:disabled,.btn-default[disabled]{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary:disabled,.btn-primary[disabled]{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success:disabled,.btn-success[disabled]{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info:disabled,.btn-info[disabled]{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning:disabled,.btn-warning[disabled]{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger:disabled,.btn-danger[disabled]{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} -------------------------------------------------------------------------------- /public/css/callout.css: -------------------------------------------------------------------------------- 1 | .bs-callout { 2 | padding: 20px; 3 | margin: 20px 0; 4 | border: 1px solid #eee; 5 | border-left-width: 5px; 6 | border-radius: 3px; 7 | } 8 | .bs-callout h4 { 9 | margin-top: 0; 10 | margin-bottom: 5px; 11 | } 12 | .bs-callout p:last-child { 13 | margin-bottom: 0; 14 | } 15 | .bs-callout code { 16 | border-radius: 3px; 17 | } 18 | .bs-callout+.bs-callout { 19 | margin-top: -5px; 20 | } 21 | .bs-callout-default { 22 | border-left-color: #777; 23 | } 24 | .bs-callout-default h4 { 25 | color: #777; 26 | } 27 | .bs-callout-primary { 28 | border-left-color: #428bca; 29 | } 30 | .bs-callout-primary h4 { 31 | color: #428bca; 32 | } 33 | .bs-callout-success { 34 | border-left-color: #5cb85c; 35 | } 36 | .bs-callout-success h4 { 37 | color: #5cb85c; 38 | } 39 | .bs-callout-danger { 40 | border-left-color: #d9534f; 41 | } 42 | .bs-callout-danger h4 { 43 | color: #d9534f; 44 | } 45 | .bs-callout-warning { 46 | border-left-color: #f0ad4e; 47 | } 48 | .bs-callout-warning h4 { 49 | color: #f0ad4e; 50 | } 51 | .bs-callout-info { 52 | border-left-color: #5bc0de; 53 | } 54 | .bs-callout-info h4 { 55 | color: #5bc0de; 56 | } -------------------------------------------------------------------------------- /public/css/chat.css: -------------------------------------------------------------------------------- 1 | .chat 2 | { 3 | list-style: none; 4 | margin: 0; 5 | padding: 0; 6 | } 7 | 8 | .chat li 9 | { 10 | margin-bottom: 10px; 11 | padding-bottom: 5px; 12 | border-bottom: 1px dotted #B3A9A9; 13 | } 14 | 15 | .chat li.left .chat-body 16 | { 17 | margin-left: 60px; 18 | } 19 | 20 | .chat li.right .chat-body 21 | { 22 | margin-right: 60px; 23 | } 24 | 25 | 26 | .chat li .chat-body p 27 | { 28 | margin: 0; 29 | color: #777777; 30 | } 31 | 32 | .panel .slidedown .glyphicon, .chat .glyphicon 33 | { 34 | margin-right: 5px; 35 | } 36 | 37 | .panel-body 38 | { 39 | overflow-y: scroll; 40 | height: 350px; 41 | } 42 | 43 | ::-webkit-scrollbar-track 44 | { 45 | -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); 46 | background-color: #F5F5F5; 47 | } 48 | 49 | ::-webkit-scrollbar 50 | { 51 | width: 12px; 52 | background-color: #F5F5F5; 53 | } 54 | 55 | ::-webkit-scrollbar-thumb 56 | { 57 | -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3); 58 | background-color: #555; 59 | } 60 | -------------------------------------------------------------------------------- /public/css/ie9.css: -------------------------------------------------------------------------------- 1 | /* Internet Explorer 9 has some special quirks that are fixed here */ 2 | /* The icons are not animated. */ 3 | /* This file is automatically merged into sweet-alert.min.js through Gulp */ 4 | 5 | /* Error icon */ 6 | .sweet-alert .icon.error .line.left { 7 | -ms-transform: rotate(45deg)\9; 8 | } 9 | .sweet-alert .icon.error .line.right { 10 | -ms-transform: rotate(-45deg)\9; 11 | } 12 | 13 | 14 | /* Success icon */ 15 | .sweet-alert .icon.success { 16 | border-color: transparent\9; 17 | } 18 | .sweet-alert .icon.success .line.tip { 19 | -ms-transform: rotate(45deg)\9; 20 | } 21 | .sweet-alert .icon.success .line.long { 22 | -ms-transform: rotate(-45deg)\9; 23 | } -------------------------------------------------------------------------------- /public/css/sweet-alert.css: -------------------------------------------------------------------------------- 1 | .sweet-overlay{background-color:#000;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=40)";background-color:rgba(0,0,0,.4);position:fixed;left:0;right:0;top:0;bottom:0;display:none;z-index:10000}.sweet-alert{background-color:#fff;font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;width:478px;padding:17px;border-radius:5px;text-align:center;position:fixed;left:50%;top:50%;margin-left:-256px;margin-top:-200px;overflow:hidden;display:none;z-index:99999}@media all and (max-width:540px){.sweet-alert{width:auto;margin-left:0;margin-right:0;left:15px;right:15px}}.sweet-alert h2{color:#575757;font-size:30px;text-align:center;font-weight:600;text-transform:none;position:relative;margin:25px 0;padding:0;line-height:40px;display:block}.sweet-alert p{color:#797979;font-size:16px;font-weight:300;position:relative;text-align:inherit;float:none;margin:0;padding:0;line-height:normal}.sweet-alert button{background-color:#AEDEF4;color:#fff;border:none;box-shadow:none;font-size:17px;font-weight:500;-webkit-border-radius:4px;border-radius:5px;padding:10px 32px;margin:26px 5px 0;cursor:pointer}.sweet-alert button:focus{outline:0;box-shadow:0 0 2px rgba(128,179,235,.5),inset 0 0 0 1px rgba(0,0,0,.05)}.sweet-alert button:hover{background-color:#a1d9f2}.sweet-alert button:active{background-color:#81ccee}.sweet-alert button.cancel{background-color:#D0D0D0}.sweet-alert button.cancel:hover{background-color:#c8c8c8}.sweet-alert button.cancel:active{background-color:#b6b6b6}.sweet-alert button.cancel:focus{box-shadow:rgba(197,205,211,.8) 0 0 2px,rgba(0,0,0,.0470588) 0 0 0 1px inset!important}.sweet-alert button::-moz-focus-inner{border:0}.sweet-alert[data-has-cancel-button=false] button{box-shadow:none!important}.sweet-alert .icon{width:80px;height:80px;border:4px solid gray;-webkit-border-radius:40px;border-radius:50%;margin:20px auto;padding:0;position:relative;box-sizing:content-box}.sweet-alert .icon.error{border-color:#F27474}.sweet-alert .icon.error .x-mark{position:relative;display:block}.sweet-alert .icon.error .line{position:absolute;height:5px;width:47px;background-color:#F27474;display:block;top:37px;border-radius:2px}.sweet-alert .icon.error .line.left{-webkit-transform:rotate(45deg);transform:rotate(45deg);left:17px}.sweet-alert .icon.error .line.right{-webkit-transform:rotate(-45deg);transform:rotate(-45deg);right:16px}.sweet-alert .icon.warning{border-color:#F8BB86}.sweet-alert .icon.warning .body{position:absolute;width:5px;height:47px;left:50%;top:10px;-webkit-border-radius:2px;border-radius:2px;margin-left:-2px;background-color:#F8BB86}.sweet-alert .icon.warning .dot{position:absolute;width:7px;height:7px;-webkit-border-radius:50%;border-radius:50%;margin-left:-3px;left:50%;bottom:10px;background-color:#F8BB86}.sweet-alert .icon.info{border-color:#C9DAE1}.sweet-alert .icon.info::before{content:"";position:absolute;width:5px;height:29px;left:50%;bottom:17px;border-radius:2px;margin-left:-2px;background-color:#C9DAE1}.sweet-alert .icon.info::after{content:"";position:absolute;width:7px;height:7px;border-radius:50%;margin-left:-3px;top:19px;background-color:#C9DAE1}.sweet-alert .icon.success{border-color:#A5DC86}.sweet-alert .icon.success::after,.sweet-alert .icon.success::before{content:'';position:absolute;width:60px;height:120px;background:#fff;-webkit-transform:rotate(45deg);transform:rotate(45deg)}.sweet-alert .icon.success::before{-webkit-border-radius:120px 0 0 120px;border-radius:120px 0 0 120px;top:-7px;left:-33px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);-webkit-transform-origin:60px 60px;transform-origin:60px 60px}.sweet-alert .icon.success::after{-webkit-border-radius:0 120px 120px 0;border-radius:0 120px 120px 0;top:-11px;left:30px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);-webkit-transform-origin:0 60px;transform-origin:0 60px}.sweet-alert .icon.success .placeholder{width:80px;height:80px;border:4px solid rgba(165,220,134,.2);-webkit-border-radius:40px;border-radius:50%;box-sizing:content-box;position:absolute;left:-4px;top:-4px;z-index:2}.sweet-alert .icon.success .fix{width:5px;height:90px;background-color:#fff;position:absolute;left:28px;top:8px;z-index:1;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.sweet-alert .icon.success .line{height:5px;background-color:#A5DC86;display:block;border-radius:2px;position:absolute;z-index:2}.sweet-alert .icon.success .line.tip{width:25px;left:14px;top:46px;-webkit-transform:rotate(45deg);transform:rotate(45deg)}.sweet-alert .icon.success .line.long{width:47px;right:8px;top:38px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.sweet-alert .icon.custom{background-size:contain;border-radius:0;border:none;background-position:center center;background-repeat:no-repeat}@-webkit-keyframes showSweetAlert{0%{transform:scale(.7);-webkit-transform:scale(.7)}45%{transform:scale(1.05);-webkit-transform:scale(1.05)}80%{transform:scale(.95);-webkit-tranform:scale(.95)}100%{transform:scale(1);-webkit-transform:scale(1)}}@keyframes showSweetAlert{0%{transform:scale(.7);-webkit-transform:scale(.7)}45%{transform:scale(1.05);-webkit-transform:scale(1.05)}80%{transform:scale(.95);-webkit-tranform:scale(.95)}100%{transform:scale(1);-webkit-transform:scale(1)}}@-webkit-keyframes hideSweetAlert{0%{transform:scale(1);-webkit-transform:scale(1)}100%{transform:scale(.5);-webkit-transform:scale(.5)}}@keyframes hideSweetAlert{0%{transform:scale(1);-webkit-transform:scale(1)}100%{transform:scale(.5);-webkit-transform:scale(.5)}}.showSweetAlert{-webkit-animation:showSweetAlert .3s;animation:showSweetAlert .3s}.showSweetAlert[data-animation=none]{-webkit-animation:none;animation:none}.hideSweetAlert{-webkit-animation:hideSweetAlert .2s;animation:hideSweetAlert .2s}.hideSweetAlert[data-animation=none]{-webkit-animation:none;animation:none}@-webkit-keyframes animateSuccessTip{0%,54%{width:0;left:1px;top:19px}70%{width:50px;left:-8px;top:37px}84%{width:17px;left:21px;top:48px}100%{width:25px;left:14px;top:45px}}@keyframes animateSuccessTip{0%,54%{width:0;left:1px;top:19px}70%{width:50px;left:-8px;top:37px}84%{width:17px;left:21px;top:48px}100%{width:25px;left:14px;top:45px}}@-webkit-keyframes animateSuccessLong{0%,65%{width:0;right:46px;top:54px}84%{width:55px;right:0;top:35px}100%{width:47px;right:8px;top:38px}}@keyframes animateSuccessLong{0%,65%{width:0;right:46px;top:54px}84%{width:55px;right:0;top:35px}100%{width:47px;right:8px;top:38px}}@-webkit-keyframes rotatePlaceholder{0%,5%{transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}100%,12%{transform:rotate(-405deg);-webkit-transform:rotate(-405deg)}}@keyframes rotatePlaceholder{0%,5%{transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}100%,12%{transform:rotate(-405deg);-webkit-transform:rotate(-405deg)}}.animateSuccessTip{-webkit-animation:animateSuccessTip .75s;animation:animateSuccessTip .75s}.animateSuccessLong{-webkit-animation:animateSuccessLong .75s;animation:animateSuccessLong .75s}.icon.success.animate::after{-webkit-animation:rotatePlaceholder 4.25s ease-in;animation:rotatePlaceholder 4.25s ease-in}@-webkit-keyframes animateErrorIcon{0%{transform:rotateX(100deg);-webkit-transform:rotateX(100deg);opacity:0}100%{transform:rotateX(0deg);-webkit-transform:rotateX(0deg);opacity:1}}@keyframes animateErrorIcon{0%{transform:rotateX(100deg);-webkit-transform:rotateX(100deg);opacity:0}100%{transform:rotateX(0deg);-webkit-transform:rotateX(0deg);opacity:1}}.animateErrorIcon{-webkit-animation:animateErrorIcon .5s;animation:animateErrorIcon .5s}@-webkit-keyframes animateXMark{0%,50%{transform:scale(.4);-webkit-transform:scale(.4);margin-top:26px;opacity:0}80%{transform:scale(1.15);-webkit-transform:scale(1.15);margin-top:-6px}100%{transform:scale(1);-webkit-transform:scale(1);margin-top:0;opacity:1}}@keyframes animateXMark{0%,50%{transform:scale(.4);-webkit-transform:scale(.4);margin-top:26px;opacity:0}80%{transform:scale(1.15);-webkit-transform:scale(1.15);margin-top:-6px}100%{transform:scale(1);-webkit-transform:scale(1);margin-top:0;opacity:1}}.animateXMark{-webkit-animation:animateXMark .5s;animation:animateXMark .5s}@-webkit-keyframes pulseWarning{0%{border-color:#F8D486}100%{border-color:#F8BB86}}@keyframes pulseWarning{0%{border-color:#F8D486}100%{border-color:#F8BB86}}.pulseWarning{-webkit-animation:pulseWarning .75s infinite alternate;animation:pulseWarning .75s infinite alternate}@-webkit-keyframes pulseWarningIns{0%{background-color:#F8D486}100%{background-color:#F8BB86}}@keyframes pulseWarningIns{0%{background-color:#F8D486}100%{background-color:#F8BB86}}.pulseWarningIns{-webkit-animation:pulseWarningIns .75s infinite alternate;animation:pulseWarningIns .75s infinite alternate} -------------------------------------------------------------------------------- /public/css/typeahead.css: -------------------------------------------------------------------------------- 1 | span.twitter-typeahead .tt-dropdown-menu { 2 | position: absolute; 3 | top: 100%; 4 | left: 0; 5 | z-index: 1000; 6 | display: none; 7 | float: left; 8 | min-width: 160px; 9 | padding: 5px 0; 10 | margin: 2px 0 0; 11 | list-style: none; 12 | font-size: 14px; 13 | text-align: left; 14 | background-color: #ffffff; 15 | border: 1px solid #cccccc; 16 | border: 1px solid rgba(0, 0, 0, 0.15); 17 | border-radius: 4px; 18 | -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); 19 | box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); 20 | background-clip: padding-box; 21 | } 22 | span.twitter-typeahead .tt-suggestion > p { 23 | display: block; 24 | padding: 3px 20px; 25 | margin-bottom: 0px; 26 | clear: both; 27 | font-weight: normal; 28 | line-height: 1.42857143; 29 | color: #333333; 30 | white-space: nowrap; 31 | } 32 | span.twitter-typeahead .tt-suggestion > p:hover, 33 | span.twitter-typeahead .tt-suggestion > p:focus { 34 | color: #ffffff; 35 | text-decoration: none; 36 | outline: 0; 37 | background-color: #428bca; 38 | } 39 | span.twitter-typeahead .tt-suggestion.tt-cursor { 40 | color: #ffffff; 41 | background-color: #428bca; 42 | } 43 | span.twitter-typeahead { 44 | width: 100%; 45 | } 46 | .input-group span.twitter-typeahead { 47 | display: block !important; 48 | } 49 | .input-group span.twitter-typeahead .tt-dropdown-menu { 50 | top: 32px !important; 51 | } 52 | .input-group.input-group-lg span.twitter-typeahead .tt-dropdown-menu { 53 | top: 44px !important; 54 | } 55 | .input-group.input-group-sm span.twitter-typeahead .tt-dropdown-menu { 56 | top: 28px !important; 57 | } 58 | -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silentrob/superscript-editor/cb836db3112ae9639b8918b405b61cd253025e11/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silentrob/superscript-editor/cb836db3112ae9639b8918b405b61cd253025e11/public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silentrob/superscript-editor/cb836db3112ae9639b8918b405b61cd253025e11/public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silentrob/superscript-editor/cb836db3112ae9639b8918b405b61cd253025e11/public/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /public/img/gambit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silentrob/superscript-editor/cb836db3112ae9639b8918b405b61cd253025e11/public/img/gambit.png -------------------------------------------------------------------------------- /public/img/realtime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silentrob/superscript-editor/cb836db3112ae9639b8918b405b61cd253025e11/public/img/realtime.png -------------------------------------------------------------------------------- /public/js/icanhas.min.js: -------------------------------------------------------------------------------- 1 | (function(){var r,v=Object.prototype.toString;Array.isArray=Array.isArray||function(a){return"[object Array]"==v.call(a)};var s=String.prototype.trim,l;if(s)l=function(a){return null==a?"":s.call(a)};else{var n,p;/\S/.test("\u00a0")?(n=/^[\s\xA0]+/,p=/[\s\xA0]+$/):(n=/^\s+/,p=/\s+$/);l=function(a){return null==a?"":a.toString().replace(n,"").replace(p,"")}}var w={"&":"&","<":"<",">":">",'"':""","'":"'"},t={},u=function(){};u.prototype={otag:"{{",ctag:"}}",pragmas:{},buffer:[],pragmas_implemented:{"IMPLICIT-ITERATOR":!0}, 2 | context:{},render:function(a,c,b,d){d||(this.context=c,this.buffer=[]);if(this.includes("",a)){a=this.render_pragmas(a);var e=this.render_section(a,c,b);!1===e&&(e=this.render_tags(a,c,b,d));if(d)return e;this.sendLines(e)}else{if(d)return a;this.send(a)}},send:function(a){""!==a&&this.buffer.push(a)},sendLines:function(a){if(a){a=a.split("\n");for(var c=0;c|&|\\{|%)?([^#\\^]+?)\\1?"+b+"+","g")})},h=g(),j=function(a,d,f){switch(d){case "!":return"";case "=":return e.set_delimiters(f),h=g(),"";case ">":return e.render_partial(f, 6 | c,b);case "{":case "&":return e.find(f,c);default:return a=e.find(f,c),String(a).replace(/&(?!\w+;)|[<>"']/g,function(a){return w[a]||a})}};a=a.split("\n");for(var f=0;f');f.find(d.handle).mousedown(function(){g=!0}).mouseup(function(){g=!1}),a(this).data("items",d.items),c=c.add(i),d.connectWith&&a(d.connectWith).add(this).data("connectWith",d.connectWith),f.attr("draggable","true").on("dragstart.h5s",function(c){if(d.handle&&!g)return!1;g=!1;var e=c.originalEvent.dataTransfer;e.effectAllowed="move",e.setData("Text","dummy"),h=(b=a(this)).addClass("sortable-dragging").index()}).on("dragend.h5s",function(){b.removeClass("sortable-dragging").show(),c.detach(),h!=b.index()&&f.parent().trigger("sortupdate",{item:b}),b=null}).not("a[href], img").on("selectstart.h5s",function(){return this.dragDrop&&this.dragDrop(),!1}).end().add([this,i]).on("dragover.h5s dragenter.h5s drop.h5s",function(e){return!f.is(b)&&d.connectWith!==a(b).parent().data("connectWith")?!0:e.type=="drop"?(e.stopPropagation(),c.filter(":visible").after(b),!1):(e.preventDefault(),e.originalEvent.dataTransfer.dropEffect="move",f.is(this)?(d.forcePlaceholderSize&&i.height(b.outerHeight()),b.hide(),a(this)[i.index()= 0) { 60 | newClass = newClass.replace(' ' + className + ' ', ' '); 61 | } 62 | elem.className = newClass.replace(/^\s+|\s+$/g, ''); 63 | } 64 | }, 65 | escapeHtml = function(str) { 66 | var div = document.createElement('div'); 67 | div.appendChild(document.createTextNode(str)); 68 | return div.innerHTML; 69 | }, 70 | _show = function(elem) { 71 | elem.style.opacity = ''; 72 | elem.style.display = 'block'; 73 | }, 74 | show = function(elems) { 75 | if (elems && !elems.length) { 76 | return _show(elems); 77 | } 78 | for (var i = 0; i < elems.length; ++i) { 79 | _show(elems[i]); 80 | } 81 | }, 82 | _hide = function(elem) { 83 | elem.style.opacity = ''; 84 | elem.style.display = 'none'; 85 | }, 86 | hide = function(elems) { 87 | if (elems && !elems.length) { 88 | return _hide(elems); 89 | } 90 | for (var i = 0; i < elems.length; ++i) { 91 | _hide(elems[i]); 92 | } 93 | }, 94 | isDescendant = function(parent, child) { 95 | var node = child.parentNode; 96 | while (node !== null) { 97 | if (node === parent) { 98 | return true; 99 | } 100 | node = node.parentNode; 101 | } 102 | return false; 103 | }, 104 | getTopMargin = function(elem) { 105 | elem.style.left = '-9999px'; 106 | elem.style.display = 'block'; 107 | 108 | var height = elem.clientHeight, 109 | padding; 110 | if (typeof getComputedStyle !== "undefined") { /* IE 8 */ 111 | padding = parseInt(getComputedStyle(elem).getPropertyValue('padding'), 10); 112 | } else { 113 | padding = parseInt(elem.currentStyle.padding); 114 | } 115 | 116 | elem.style.left = ''; 117 | elem.style.display = 'none'; 118 | return ('-' + parseInt(height / 2 + padding) + 'px'); 119 | }, 120 | fadeIn = function(elem, interval) { 121 | if (+elem.style.opacity < 1) { 122 | interval = interval || 16; 123 | elem.style.opacity = 0; 124 | elem.style.display = 'block'; 125 | var last = +new Date(); 126 | var tick = function() { 127 | elem.style.opacity = +elem.style.opacity + (new Date() - last) / 100; 128 | last = +new Date(); 129 | 130 | if (+elem.style.opacity < 1) { 131 | setTimeout(tick, interval); 132 | } 133 | }; 134 | tick(); 135 | } 136 | elem.style.display = 'block'; //fallback IE8 137 | }, 138 | fadeOut = function(elem, interval) { 139 | interval = interval || 16; 140 | elem.style.opacity = 1; 141 | var last = +new Date(); 142 | var tick = function() { 143 | elem.style.opacity = +elem.style.opacity - (new Date() - last) / 100; 144 | last = +new Date(); 145 | 146 | if (+elem.style.opacity > 0) { 147 | setTimeout(tick, interval); 148 | } else { 149 | elem.style.display = 'none'; 150 | } 151 | }; 152 | tick(); 153 | }, 154 | fireClick = function(node) { 155 | // Taken from http://www.nonobtrusive.com/2011/11/29/programatically-fire-crossbrowser-click-event-with-javascript/ 156 | // Then fixed for today's Chrome browser. 157 | if (typeof MouseEvent === 'function') { 158 | // Up-to-date approach 159 | var mevt = new MouseEvent('click', { 160 | view: window, 161 | bubbles: false, 162 | cancelable: true 163 | }); 164 | node.dispatchEvent(mevt); 165 | } else if ( document.createEvent ) { 166 | // Fallback 167 | var evt = document.createEvent('MouseEvents'); 168 | evt.initEvent('click', false, false); 169 | node.dispatchEvent(evt); 170 | } else if( document.createEventObject ) { 171 | node.fireEvent('onclick') ; 172 | } else if (typeof node.onclick === 'function' ) { 173 | node.onclick(); 174 | } 175 | }, 176 | stopEventPropagation = function(e) { 177 | // In particular, make sure the space bar doesn't scroll the main window. 178 | if (typeof e.stopPropagation === 'function') { 179 | e.stopPropagation(); 180 | e.preventDefault(); 181 | } else if (window.event && window.event.hasOwnProperty('cancelBubble')) { 182 | window.event.cancelBubble = true; 183 | } 184 | }; 185 | 186 | // Remember state in cases where opening and handling a modal will fiddle with it. 187 | var previousActiveElement, 188 | previousDocumentClick, 189 | previousWindowKeyDown, 190 | lastFocusedButton; 191 | 192 | 193 | /* 194 | * Add modal + overlay to DOM 195 | */ 196 | 197 | var sweetAlertInitialize = function() { 198 | var sweetHTML = '

Title

Text

', 199 | sweetWrap = document.createElement('div'); 200 | 201 | sweetWrap.innerHTML = sweetHTML; 202 | 203 | // Append elements to body 204 | while (sweetWrap.firstChild) { 205 | document.body.appendChild(sweetWrap.firstChild); 206 | } 207 | }; 208 | 209 | 210 | /* 211 | * Global sweetAlert function 212 | */ 213 | var sweetAlert, swal; 214 | 215 | sweetAlert = swal = function() { 216 | var customizations = arguments[0]; 217 | 218 | /* 219 | * Use argument if defined or default value from params object otherwise. 220 | * Supports the case where a default value is boolean true and should be 221 | * overridden by a corresponding explicit argument which is boolean false. 222 | */ 223 | function argumentOrDefault(key) { 224 | var args = customizations; 225 | 226 | if (typeof args[key] !== 'undefined') { 227 | return args[key]; 228 | } else { 229 | return defaultParams[key]; 230 | } 231 | } 232 | 233 | if (arguments[0] === undefined) { 234 | logStr('SweetAlert expects at least 1 attribute!'); 235 | return false; 236 | } 237 | 238 | var params = extend({}, defaultParams); 239 | 240 | switch (typeof arguments[0]) { 241 | 242 | // Ex: swal("Hello", "Just testing", "info"); 243 | case 'string': 244 | params.title = arguments[0]; 245 | params.text = arguments[1] || ''; 246 | params.type = arguments[2] || ''; 247 | 248 | break; 249 | 250 | // Ex: swal({title:"Hello", text: "Just testing", type: "info"}); 251 | case 'object': 252 | if (arguments[0].title === undefined) { 253 | logStr('Missing "title" argument!'); 254 | return false; 255 | } 256 | 257 | params.title = arguments[0].title; 258 | 259 | var availableCustoms = [ 260 | 'text', 261 | 'type', 262 | 'customClass', 263 | 'allowOutsideClick', 264 | 'showConfirmButton', 265 | 'showCancelButton', 266 | 'closeOnConfirm', 267 | 'closeOnCancel', 268 | 'timer', 269 | 'confirmButtonColor', 270 | 'cancelButtonText', 271 | 'imageUrl', 272 | 'imageSize', 273 | 'html', 274 | 'animation', 275 | 'allowEscapeKey']; 276 | 277 | // It would be nice to just use .forEach here, but IE8... :( 278 | var numCustoms = availableCustoms.length; 279 | for (var customIndex = 0; customIndex < numCustoms; customIndex++) { 280 | var customName = availableCustoms[customIndex]; 281 | params[customName] = argumentOrDefault(customName); 282 | } 283 | 284 | // Show "Confirm" instead of "OK" if cancel button is visible 285 | params.confirmButtonText = (params.showCancelButton) ? 'Confirm' : defaultParams.confirmButtonText; 286 | params.confirmButtonText = argumentOrDefault('confirmButtonText'); 287 | 288 | // Function to call when clicking on cancel/OK 289 | params.doneFunction = arguments[1] || null; 290 | 291 | break; 292 | 293 | default: 294 | logStr('Unexpected type of argument! Expected "string" or "object", got ' + typeof arguments[0]); 295 | return false; 296 | 297 | } 298 | 299 | setParameters(params); 300 | fixVerticalPosition(); 301 | openModal(); 302 | 303 | 304 | // Modal interactions 305 | var modal = getModal(); 306 | 307 | // Mouse interactions 308 | var onButtonEvent = function(event) { 309 | var e = event || window.event; 310 | var target = e.target || e.srcElement, 311 | targetedConfirm = (target.className.indexOf("confirm") !== -1), 312 | modalIsVisible = hasClass(modal, 'visible'), 313 | doneFunctionExists = (params.doneFunction && modal.getAttribute('data-has-done-function') === 'true'); 314 | 315 | switch (e.type) { 316 | case ("mouseover"): 317 | if (targetedConfirm) { 318 | target.style.backgroundColor = colorLuminance(params.confirmButtonColor, -0.04); 319 | } 320 | break; 321 | case ("mouseout"): 322 | if (targetedConfirm) { 323 | target.style.backgroundColor = params.confirmButtonColor; 324 | } 325 | break; 326 | case ("mousedown"): 327 | if (targetedConfirm) { 328 | target.style.backgroundColor = colorLuminance(params.confirmButtonColor, -0.14); 329 | } 330 | break; 331 | case ("mouseup"): 332 | if (targetedConfirm) { 333 | target.style.backgroundColor = colorLuminance(params.confirmButtonColor, -0.04); 334 | } 335 | break; 336 | case ("focus"): 337 | var $confirmButton = modal.querySelector('button.confirm'), 338 | $cancelButton = modal.querySelector('button.cancel'); 339 | 340 | if (targetedConfirm) { 341 | $cancelButton.style.boxShadow = 'none'; 342 | } else { 343 | $confirmButton.style.boxShadow = 'none'; 344 | } 345 | break; 346 | case ("click"): 347 | if (targetedConfirm && doneFunctionExists && modalIsVisible) { // Clicked "confirm" 348 | 349 | params.doneFunction(true); 350 | 351 | if (params.closeOnConfirm) { 352 | sweetAlert.close(); 353 | } 354 | } else if (doneFunctionExists && modalIsVisible) { // Clicked "cancel" 355 | 356 | // Check if callback function expects a parameter (to track cancel actions) 357 | var functionAsStr = String(params.doneFunction).replace(/\s/g, ''); 358 | var functionHandlesCancel = functionAsStr.substring(0, 9) === "function(" && functionAsStr.substring(9, 10) !== ")"; 359 | 360 | if (functionHandlesCancel) { 361 | params.doneFunction(false); 362 | } 363 | 364 | if (params.closeOnCancel) { 365 | sweetAlert.close(); 366 | } 367 | } else { 368 | sweetAlert.close(); 369 | } 370 | 371 | break; 372 | } 373 | }; 374 | 375 | var $buttons = modal.querySelectorAll('button'); 376 | for (var i = 0; i < $buttons.length; i++) { 377 | $buttons[i].onclick = onButtonEvent; 378 | $buttons[i].onmouseover = onButtonEvent; 379 | $buttons[i].onmouseout = onButtonEvent; 380 | $buttons[i].onmousedown = onButtonEvent; 381 | //$buttons[i].onmouseup = onButtonEvent; 382 | $buttons[i].onfocus = onButtonEvent; 383 | } 384 | 385 | // Remember the current document.onclick event. 386 | previousDocumentClick = document.onclick; 387 | document.onclick = function(event) { 388 | var e = event || window.event; 389 | var target = e.target || e.srcElement; 390 | 391 | var clickedOnModal = (modal === target), 392 | clickedOnModalChild = isDescendant(modal, target), 393 | modalIsVisible = hasClass(modal, 'visible'), 394 | outsideClickIsAllowed = modal.getAttribute('data-allow-ouside-click') === 'true'; 395 | 396 | if (!clickedOnModal && !clickedOnModalChild && modalIsVisible && outsideClickIsAllowed) { 397 | sweetAlert.close(); 398 | } 399 | }; 400 | 401 | 402 | // Keyboard interactions 403 | var $okButton = modal.querySelector('button.confirm'), 404 | $cancelButton = modal.querySelector('button.cancel'), 405 | $modalButtons = modal.querySelectorAll('button[tabindex]'); 406 | 407 | 408 | function handleKeyDown(event) { 409 | var e = event || window.event; 410 | var keyCode = e.keyCode || e.which; 411 | 412 | if ([9,13,32,27].indexOf(keyCode) === -1) { 413 | // Don't do work on keys we don't care about. 414 | return; 415 | } 416 | 417 | var $targetElement = e.target || e.srcElement; 418 | 419 | var btnIndex = -1; // Find the button - note, this is a nodelist, not an array. 420 | for (var i = 0; i < $modalButtons.length; i++) { 421 | if ($targetElement === $modalButtons[i]) { 422 | btnIndex = i; 423 | break; 424 | } 425 | } 426 | 427 | if (keyCode === 9) { 428 | // TAB 429 | if (btnIndex === -1) { 430 | // No button focused. Jump to the confirm button. 431 | $targetElement = $okButton; 432 | } else { 433 | // Cycle to the next button 434 | if (btnIndex === $modalButtons.length - 1) { 435 | $targetElement = $modalButtons[0]; 436 | } else { 437 | $targetElement = $modalButtons[btnIndex + 1]; 438 | } 439 | } 440 | 441 | stopEventPropagation(e); 442 | $targetElement.focus(); 443 | setFocusStyle($targetElement, params.confirmButtonColor); // TODO 444 | 445 | } else { 446 | if (keyCode === 13 || keyCode === 32) { 447 | if (btnIndex === -1) { 448 | // ENTER/SPACE clicked outside of a button. 449 | $targetElement = $okButton; 450 | } else { 451 | // Do nothing - let the browser handle it. 452 | $targetElement = undefined; 453 | } 454 | } else if (keyCode === 27 && params.allowEscapeKey === true) { 455 | $targetElement = $cancelButton; 456 | } else { 457 | // Fallback - let the browser handle it. 458 | $targetElement = undefined; 459 | } 460 | 461 | if ($targetElement !== undefined) { 462 | fireClick($targetElement, e); 463 | } 464 | } 465 | } 466 | 467 | previousWindowKeyDown = window.onkeydown; 468 | 469 | window.onkeydown = handleKeyDown; 470 | 471 | function handleOnBlur(event) { 472 | var e = event || window.event; 473 | var $targetElement = e.target || e.srcElement, 474 | $focusElement = e.relatedTarget, 475 | modalIsVisible = hasClass(modal, 'visible'); 476 | 477 | if (modalIsVisible) { 478 | var btnIndex = -1; // Find the button - note, this is a nodelist, not an array. 479 | 480 | if ($focusElement !== null) { 481 | // If we picked something in the DOM to focus to, let's see if it was a button. 482 | for (var i = 0; i < $modalButtons.length; i++) { 483 | if ($focusElement === $modalButtons[i]) { 484 | btnIndex = i; 485 | break; 486 | } 487 | } 488 | 489 | if (btnIndex === -1) { 490 | // Something in the dom, but not a visible button. Focus back on the button. 491 | $targetElement.focus(); 492 | } 493 | } else { 494 | // Exiting the DOM (e.g. clicked in the URL bar); 495 | lastFocusedButton = $targetElement; 496 | } 497 | } 498 | } 499 | 500 | $okButton.onblur = handleOnBlur; 501 | $cancelButton.onblur = handleOnBlur; 502 | 503 | window.onfocus = function() { 504 | // When the user has focused away and focused back from the whole window. 505 | window.setTimeout(function() { 506 | // Put in a timeout to jump out of the event sequence. Calling focus() in the event 507 | // sequence confuses things. 508 | if (lastFocusedButton !== undefined) { 509 | lastFocusedButton.focus(); 510 | lastFocusedButton = undefined; 511 | } 512 | }, 0); 513 | }; 514 | }; 515 | 516 | 517 | /* 518 | * Set default params for each popup 519 | * @param {Object} userParams 520 | */ 521 | sweetAlert.setDefaults = swal.setDefaults = function(userParams) { 522 | if (!userParams) { 523 | throw new Error('userParams is required'); 524 | } 525 | if (typeof userParams !== 'object') { 526 | throw new Error('userParams has to be a object'); 527 | } 528 | 529 | extend(defaultParams, userParams); 530 | }; 531 | 532 | 533 | /* 534 | * Set type, text and actions on modal 535 | */ 536 | 537 | function setParameters(params) { 538 | var modal = getModal(); 539 | 540 | var $title = modal.querySelector('h2'), 541 | $text = modal.querySelector('p'), 542 | $cancelBtn = modal.querySelector('button.cancel'), 543 | $confirmBtn = modal.querySelector('button.confirm'); 544 | 545 | // Title 546 | $title.innerHTML = (params.html) ? params.title : escapeHtml(params.title).split("\n").join("
"); 547 | 548 | // Text 549 | $text.innerHTML = (params.html) ? params.text : escapeHtml(params.text || '').split("\n").join("
"); 550 | 551 | if (params.text) { 552 | show($text); 553 | } 554 | 555 | //Custom Class 556 | if (params.customClass) { 557 | addClass(modal, params.customClass); 558 | modal.setAttribute('data-custom-class', params.customClass); 559 | } else { 560 | // Find previously set classes and remove them 561 | var customClass = modal.getAttribute('data-custom-class'); 562 | removeClass(modal, customClass); 563 | modal.setAttribute('data-custom-class', ""); 564 | } 565 | 566 | // Icon 567 | hide(modal.querySelectorAll('.sa-icon')); 568 | if (params.type && !isIE8()) { 569 | var validType = false; 570 | for (var i = 0; i < alertTypes.length; i++) { 571 | if (params.type === alertTypes[i]) { 572 | validType = true; 573 | break; 574 | } 575 | } 576 | if (!validType) { 577 | logStr('Unknown alert type: ' + params.type); 578 | return false; 579 | } 580 | var $icon = modal.querySelector('.sa-icon.' + 'sa-' + params.type); 581 | show($icon); 582 | 583 | // Animate icon 584 | switch (params.type) { 585 | case "success": 586 | addClass($icon, 'animate'); 587 | addClass($icon.querySelector('.sa-tip'), 'animateSuccessTip'); 588 | addClass($icon.querySelector('.sa-long'), 'animateSuccessLong'); 589 | break; 590 | case "error": 591 | addClass($icon, 'animateErrorIcon'); 592 | addClass($icon.querySelector('.sa-x-mark'), 'animateXMark'); 593 | break; 594 | case "warning": 595 | addClass($icon, 'pulseWarning'); 596 | addClass($icon.querySelector('.sa-body'), 'pulseWarningIns'); 597 | addClass($icon.querySelector('.sa-dot'), 'pulseWarningIns'); 598 | break; 599 | } 600 | } 601 | 602 | // Custom image 603 | if (params.imageUrl) { 604 | var $customIcon = modal.querySelector('.sa-icon.sa-custom'); 605 | 606 | $customIcon.style.backgroundImage = 'url(' + params.imageUrl + ')'; 607 | show($customIcon); 608 | 609 | var _imgWidth = 80, 610 | _imgHeight = 80; 611 | 612 | if (params.imageSize) { 613 | var dimensions = params.imageSize.toString().split('x'); 614 | var imgWidth = dimensions[0]; 615 | var imgHeight = dimensions[1]; 616 | 617 | if (!imgWidth || !imgHeight) { 618 | logStr("Parameter imageSize expects value with format WIDTHxHEIGHT, got " + params.imageSize); 619 | } else { 620 | _imgWidth = imgWidth; 621 | _imgHeight = imgHeight; 622 | } 623 | } 624 | $customIcon.setAttribute('style', $customIcon.getAttribute('style') + 'width:' + _imgWidth + 'px; height:' + _imgHeight + 'px'); 625 | } 626 | 627 | // Show cancel button? 628 | modal.setAttribute('data-has-cancel-button', params.showCancelButton); 629 | if (params.showCancelButton) { 630 | $cancelBtn.style.display = 'inline-block'; 631 | } else { 632 | hide($cancelBtn); 633 | } 634 | 635 | // Show confirm button? 636 | modal.setAttribute('data-has-confirm-button', params.showConfirmButton); 637 | if (params.showConfirmButton) { 638 | $confirmBtn.style.display = 'inline-block'; 639 | } else { 640 | hide($confirmBtn); 641 | } 642 | 643 | // Edit text on cancel and confirm buttons 644 | if (params.cancelButtonText) { 645 | $cancelBtn.innerHTML = escapeHtml(params.cancelButtonText); 646 | } 647 | if (params.confirmButtonText) { 648 | $confirmBtn.innerHTML = escapeHtml(params.confirmButtonText); 649 | } 650 | 651 | // Set confirm button to selected background color 652 | $confirmBtn.style.backgroundColor = params.confirmButtonColor; 653 | 654 | // Set box-shadow to default focused button 655 | setFocusStyle($confirmBtn, params.confirmButtonColor); 656 | 657 | // Allow outside click? 658 | modal.setAttribute('data-allow-ouside-click', params.allowOutsideClick); 659 | 660 | // Done-function 661 | var hasDoneFunction = (params.doneFunction) ? true : false; 662 | modal.setAttribute('data-has-done-function', hasDoneFunction); 663 | 664 | // Prevent modal from animating 665 | if (!params.animation){ 666 | modal.setAttribute('data-animation', 'none'); 667 | } else{ 668 | modal.setAttribute('data-animation', 'pop'); 669 | } 670 | 671 | // Close timer 672 | modal.setAttribute('data-timer', params.timer); 673 | } 674 | 675 | 676 | /* 677 | * Set hover, active and focus-states for buttons (source: http://www.sitepoint.com/javascript-generate-lighter-darker-color) 678 | */ 679 | 680 | function colorLuminance(hex, lum) { 681 | // Validate hex string 682 | hex = String(hex).replace(/[^0-9a-f]/gi, ''); 683 | if (hex.length < 6) { 684 | hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]; 685 | } 686 | lum = lum || 0; 687 | 688 | // Convert to decimal and change luminosity 689 | var rgb = "#", c, i; 690 | for (i = 0; i < 3; i++) { 691 | c = parseInt(hex.substr(i*2,2), 16); 692 | c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16); 693 | rgb += ("00"+c).substr(c.length); 694 | } 695 | 696 | return rgb; 697 | } 698 | 699 | function extend(a, b){ 700 | for (var key in b) { 701 | if (b.hasOwnProperty(key)) { 702 | a[key] = b[key]; 703 | } 704 | } 705 | 706 | return a; 707 | } 708 | 709 | function hexToRgb(hex) { 710 | var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); 711 | return result ? parseInt(result[1], 16) + ', ' + parseInt(result[2], 16) + ', ' + parseInt(result[3], 16) : null; 712 | } 713 | 714 | // Add box-shadow style to button (depending on its chosen bg-color) 715 | function setFocusStyle($button, bgColor) { 716 | var rgbColor = hexToRgb(bgColor); 717 | $button.style.boxShadow = '0 0 2px rgba(' + rgbColor +', 0.8), inset 0 0 0 1px rgba(0, 0, 0, 0.05)'; 718 | } 719 | 720 | 721 | // Animation when opening modal 722 | function openModal() { 723 | var modal = getModal(); 724 | fadeIn(getOverlay(), 10); 725 | show(modal); 726 | addClass(modal, 'showSweetAlert'); 727 | removeClass(modal, 'hideSweetAlert'); 728 | 729 | previousActiveElement = document.activeElement; 730 | var $okButton = modal.querySelector('button.confirm'); 731 | $okButton.focus(); 732 | 733 | setTimeout(function() { 734 | addClass(modal, 'visible'); 735 | }, 500); 736 | 737 | var timer = modal.getAttribute('data-timer'); 738 | 739 | if (timer !== "null" && timer !== "") { 740 | modal.timeout = setTimeout(function() { 741 | sweetAlert.close(); 742 | }, timer); 743 | } 744 | } 745 | 746 | 747 | // Aninmation when closing modal 748 | sweetAlert.close = swal.close = function() { 749 | var modal = getModal(); 750 | fadeOut(getOverlay(), 5); 751 | fadeOut(modal, 5); 752 | removeClass(modal, 'showSweetAlert'); 753 | addClass(modal, 'hideSweetAlert'); 754 | removeClass(modal, 'visible'); 755 | 756 | 757 | // Reset icon animations 758 | 759 | var $successIcon = modal.querySelector('.sa-icon.sa-success'); 760 | removeClass($successIcon, 'animate'); 761 | removeClass($successIcon.querySelector('.sa-tip'), 'animateSuccessTip'); 762 | removeClass($successIcon.querySelector('.sa-long'), 'animateSuccessLong'); 763 | 764 | var $errorIcon = modal.querySelector('.sa-icon.sa-error'); 765 | removeClass($errorIcon, 'animateErrorIcon'); 766 | removeClass($errorIcon.querySelector('.sa-x-mark'), 'animateXMark'); 767 | 768 | var $warningIcon = modal.querySelector('.sa-icon.sa-warning'); 769 | removeClass($warningIcon, 'pulseWarning'); 770 | removeClass($warningIcon.querySelector('.sa-body'), 'pulseWarningIns'); 771 | removeClass($warningIcon.querySelector('.sa-dot'), 'pulseWarningIns'); 772 | 773 | 774 | // Reset the page to its previous state 775 | window.onkeydown = previousWindowKeyDown; 776 | document.onclick = previousDocumentClick; 777 | if (previousActiveElement) { 778 | previousActiveElement.focus(); 779 | } 780 | lastFocusedButton = undefined; 781 | clearTimeout(modal.timeout); 782 | }; 783 | 784 | 785 | /* 786 | * Set "margin-top"-property on modal based on its computed height 787 | */ 788 | 789 | function fixVerticalPosition() { 790 | var modal = getModal(); 791 | 792 | modal.style.marginTop = getTopMargin(getModal()); 793 | } 794 | 795 | // If browser is Internet Explorer 8 796 | function isIE8() { 797 | if (window.attachEvent && !window.addEventListener) { 798 | return true; 799 | } else { 800 | return false; 801 | } 802 | } 803 | 804 | // Error messages for developers 805 | function logStr(string) { 806 | if (window.console) { // IE... 807 | window.console.log("SweetAlert: " + string); 808 | } 809 | } 810 | 811 | if (typeof define === 'function' && define.amd) { 812 | define(function() { return sweetAlert; }); 813 | } else if (typeof module !== 'undefined' && module.exports) { 814 | module.exports = sweetAlert; 815 | } else if (typeof window !== 'undefined') { 816 | window.sweetAlert = window.swal = sweetAlert; 817 | } 818 | 819 | })(window, document); -------------------------------------------------------------------------------- /public/js/sweet-alert.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t,n){function o(e){var t=x(),n=t.querySelector("h2"),o=t.querySelector("p"),a=t.querySelector("button.cancel"),r=t.querySelector("button.confirm");if(n.innerHTML=e.html?e.title:E(e.title).split("\n").join("
"),o.innerHTML=e.html?e.text:E(e.text||"").split("\n").join("
"),e.text&&A(o),e.customClass)T(t,e.customClass),t.setAttribute("data-custom-class",e.customClass);else{var s=t.getAttribute("data-custom-class");B(t,s),t.setAttribute("data-custom-class","")}if(O(t.querySelectorAll(".sa-icon")),e.type&&!u()){for(var c=!1,l=0;lo;o++)n=parseInt(e.substr(2*o,2),16),n=Math.round(Math.min(Math.max(0,n+n*t),255)).toString(16),a+=("00"+n).substr(n.length);return a}function r(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e}function s(e){var t=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);return t?parseInt(t[1],16)+", "+parseInt(t[2],16)+", "+parseInt(t[3],16):null}function i(e,t){var n=s(t);e.style.boxShadow="0 0 2px rgba("+n+", 0.8), inset 0 0 0 1px rgba(0, 0, 0, 0.05)"}function c(){var e=x();H(k(),10),A(e),T(e,"showSweetAlert"),B(e,"hideSweetAlert"),d=t.activeElement;var n=e.querySelector("button.confirm");n.focus(),setTimeout(function(){T(e,"visible")},500);var o=e.getAttribute("data-timer");"null"!==o&&""!==o&&(e.timeout=setTimeout(function(){v.close()},o))}function l(){var e=x();e.style.marginTop=D(x())}function u(){return e.attachEvent&&!e.addEventListener?!0:!1}function f(t){e.console&&e.console.log("SweetAlert: "+t)}var d,m,p,y,v,b,g=".sweet-alert",w=".sweet-overlay",h=["error","warning","info","success"],S={title:"",text:"",type:null,allowOutsideClick:!1,showConfirmButton:!0,showCancelButton:!1,closeOnConfirm:!0,closeOnCancel:!0,confirmButtonText:"OK",confirmButtonColor:"#AEDEF4",cancelButtonText:"Cancel",imageUrl:null,imageSize:null,timer:null,customClass:"",html:!1,animation:!0,allowEscapeKey:!0},x=function(){var e=t.querySelector(g);return e||(j(),e=x()),e},k=function(){return t.querySelector(w)},C=function(e,t){return new RegExp(" "+t+" ").test(" "+e.className+" ")},T=function(e,t){C(e,t)||(e.className+=" "+t)},B=function(e,t){var n=" "+e.className.replace(/[\t\r\n]/g," ")+" ";if(C(e,t)){for(;n.indexOf(" "+t+" ")>=0;)n=n.replace(" "+t+" "," ");e.className=n.replace(/^\s+|\s+$/g,"")}},E=function(e){var n=t.createElement("div");return n.appendChild(t.createTextNode(e)),n.innerHTML},q=function(e){e.style.opacity="",e.style.display="block"},A=function(e){if(e&&!e.length)return q(e);for(var t=0;t0?setTimeout(o,t):e.style.display="none"};o()},N=function(n){if("function"==typeof MouseEvent){var o=new MouseEvent("click",{view:e,bubbles:!1,cancelable:!0});n.dispatchEvent(o)}else if(t.createEvent){var a=t.createEvent("MouseEvents");a.initEvent("click",!1,!1),n.dispatchEvent(a)}else t.createEventObject?n.fireEvent("onclick"):"function"==typeof n.onclick&&n.onclick()},P=function(t){"function"==typeof t.stopPropagation?(t.stopPropagation(),t.preventDefault()):e.event&&e.event.hasOwnProperty("cancelBubble")&&(e.event.cancelBubble=!0)},j=function(){var e='

Title

Text

',n=t.createElement("div");for(n.innerHTML=e;n.firstChild;)t.body.appendChild(n.firstChild)};v=b=function(){function s(e){var t=b;return"undefined"!=typeof t[e]?t[e]:S[e]}function u(t){var o=t||e.event,a=o.keyCode||o.which;if(-1!==[9,13,32,27].indexOf(a)){for(var r=o.target||o.srcElement,s=-1,c=0;ck;k++){var T=w[k];g[T]=s(T)}g.confirmButtonText=g.showCancelButton?"Confirm":S.confirmButtonText,g.confirmButtonText=s("confirmButtonText"),g.doneFunction=arguments[1]||null;break;default:return f('Unexpected type of argument! Expected "string" or "object", got '+typeof arguments[0]),!1}o(g),l(),c();for(var B=x(),E=function(t){var n=t||e.event,o=n.target||n.srcElement,r=-1!==o.className.indexOf("confirm"),s=C(B,"visible"),i=g.doneFunction&&"true"===B.getAttribute("data-has-done-function");switch(n.type){case"mouseover":r&&(o.style.backgroundColor=a(g.confirmButtonColor,-.04));break;case"mouseout":r&&(o.style.backgroundColor=g.confirmButtonColor);break;case"mousedown":r&&(o.style.backgroundColor=a(g.confirmButtonColor,-.14));break;case"mouseup":r&&(o.style.backgroundColor=a(g.confirmButtonColor,-.04));break;case"focus":var c=B.querySelector("button.confirm"),l=B.querySelector("button.cancel");r?l.style.boxShadow="none":c.style.boxShadow="none";break;case"click":if(r&&i&&s)g.doneFunction(!0),g.closeOnConfirm&&v.close();else if(i&&s){var u=String(g.doneFunction).replace(/\s/g,""),f="function("===u.substring(0,9)&&")"!==u.substring(9,10);f&&g.doneFunction(!1),g.closeOnCancel&&v.close()}else v.close()}},q=B.querySelectorAll("button"),A=0;Asuperscriptjs.com -------------------------------------------------------------------------------- /views/gambits/get.jade: -------------------------------------------------------------------------------- 1 | extends ../_layout.jade 2 | block content 3 | .container 4 | .row 5 | .page-header 6 | if (parent && parent.replies) 7 | h2 Gambit in reply to '#{parent.input}' >> '#{parent.replies[0].reply}' 8 | else if (parent && parent.gambits) 9 | h2 Gambit in topic '#{parent.name}' 10 | else 11 | h2 Gambit 12 | 13 | - if (success_messages.length != 0) 14 | p.bg-success= success_messages 15 | 16 | //- code 17 | //- pre= gambit 18 | //- pre= topic 19 | //- pre= parent 20 | //- pre= reply 21 | 22 | .row 23 | - if (error_messages.length != 0) 24 | p.bg-warning= error_messages 25 | 26 | - if (gambit.input == gambit.trigger && /(~\w+)/.test(gambit.input)) 27 | p.clearfix.pull-right.label.label-warning Concept Not Expanded 28 | 29 | form.form-horizontal#js-main(action="/gambits/#{gambit._id}", method="post") 30 | if (isNew === true && parent && parent.replies) 31 | input(type="hidden", name="replyId", value="#{parent.replies[0]._id}") 32 | else if (isNew === true && parent && parent.gambits) 33 | input(type="hidden", name="topicId", value="#{parent._id}") 34 | else 35 | input(type="hidden", id="putMethod" name="_method", value="put") 36 | if (topic) 37 | input(type="hidden", name="topicId", value="#{topic._id}") 38 | 39 | .form-group 40 | label.col-sm-2.control-label(for='formName') Input 41 | .col-sm-8 42 | input#formName.form-control.input-lg(type='text', name="input", placeholder='Input', value='#{gambit.input||""}') 43 | .col-sm-2 44 | input.form-control.input-lg(type='text', name="filter", placeholder='Filter Function', value='#{gambit.filter||""}') 45 | .form-group 46 | .col-sm-offset-2.col-sm-10 47 | .checkbox 48 | label 49 | if gambit.isQuestion 50 | input#js-isQuestion(type='checkbox', name="isQuestion", checked="true") 51 | | Match Questions 52 | else 53 | input#js-isQuestion(type='checkbox', name="isQuestion") 54 | | Match Questions 55 | 56 | .form-group.js-questionType 57 | label.col-sm-2.control-label(for='reply') Question Type 58 | .col-xs-3 59 | select#qtype.form-control(name="qType") 60 | optgroup(label="Question Types") 61 | //- option(value="") All Question Types 62 | if gambit.qType == "WH" 63 | option(value="WH" selected="") Question word 64 | else 65 | option(value="WH") Question word 66 | 67 | if gambit.qType == "YN" 68 | option(value="YN" selected="") Yes/No 69 | else 70 | option(value="YN" ) Yes/No 71 | 72 | if gambit.qType == "TG" 73 | option(value="TG" selected="") Tag Question 74 | else 75 | option(value="TG") Tag Question 76 | 77 | if gambit.qType == "CH" 78 | option(value="CH" selected="") Choice Question 79 | else 80 | option(value="CH") Choice Question 81 | 82 | optgroup(label="Answer Types") 83 | 84 | if gambit.qSubType == "ABBR" 85 | option(value="ABBR" selected="") Abbreviation 86 | else 87 | option(value="ABBR") Abbreviation 88 | 89 | if gambit.qSubType == "ENTY" 90 | option(value="ENTY" selected="") Entities 91 | else 92 | option(value="ENTY") Entities 93 | 94 | if gambit.qSubType == "DESC" 95 | option(value="DESC" selected="") Description 96 | else 97 | option(value="DESC") Description 98 | 99 | if gambit.qSubType == "HUM" 100 | option(value="HUM" selected="") Human 101 | else 102 | option(value="HUM") Human 103 | 104 | if gambit.qSubType == "NUM" 105 | option(value="NUM" selected="") Number 106 | else 107 | option(value="NUM") Number 108 | 109 | if gambit.qSubType == "LOC" 110 | option(value="LOC" selected="") Location 111 | else 112 | option(value="LOC") Location 113 | 114 | //- ABBR - abbreviation 115 | //- abb - abbreviation 116 | //- exp - expression abbreviated 117 | //- ENTY - entities 118 | //- animal - animals 119 | //- body - organs of body 120 | //- color - colors 121 | //- creative - inventions, books and other creative pieces 122 | //- currency - currency names 123 | //- event - events 124 | //- food - food 125 | //- instrument - musical instrument 126 | //- lang - languages 127 | //- letter - letters like a-z 128 | //- other - other entities 129 | //- plant - plants 130 | //- product - products 131 | //- religion - religions 132 | //- sport - sports 133 | //- substance - elements and substances 134 | //- symbol - symbols and signs 135 | //- technique - techniques and methods 136 | //- term - equivalent terms 137 | //- vehicle - vehicles 138 | //- word - words with a special property 139 | //- DESC - description and abstract concepts 140 | //- def - definition of sth. 141 | //- desc - description of sth. 142 | //- manner - manner of an action 143 | //- reason - reasons 144 | //- HUM - human beings 145 | //- group - a group or organization of persons 146 | //- ind - an individual 147 | //- title - title of a person 148 | //- desc - description of a person 149 | //- LOC - locations 150 | //- city - cities 151 | //- country - countries 152 | //- mountain - mountains 153 | //- other - other locations 154 | //- state - states 155 | //- NUM - numeric values 156 | //- code - postcodes, phone number or other codes 157 | //- count - number of sth. 158 | //- expression - numeric mathmatical expression 159 | //- date - dates 160 | //- distance - linear measures 161 | //- money - prices 162 | //- order - ranks 163 | //- other - other numbers 164 | //- period - the lasting time of sth. 165 | //- percent - fractions 166 | //- speed - speed 167 | //- temp - temperature 168 | //- size - size, area and volume 169 | //- weight - weight 170 | 171 | #replyGroup 172 | 173 | if (topic) 174 | .form-group 175 | label.col-sm-2.control-label Topic 176 | .col-xs-3 177 | select#topics.form-control(name="topic") 178 | each t in topics 179 | if (String(t._id) === String(topic._id)) 180 | option(value="#{t._id}" selected="selected")= topic.name 181 | else 182 | option(value="#{t._id}")= t.name 183 | 184 | .form-group 185 | .col-sm-offset-2.col-sm-10 186 | if isNew 187 | button.btn.btn-default(type='submit') Create Gambit 188 | else 189 | button.btn.btn-default(type='submit') Update Gambit 190 | | 191 | input#js-del.btn.btn-danger(type="button" name="delete" value="Delete Gambit") 192 | 193 | 194 | // Gambits should only belong in one topic. 195 | if (reply && parent || topic) 196 | .row 197 | .page-header 198 | h3 This Gambit apprars in: 199 | 200 | if reply && parent 201 | b Reply: 202 | a(href="/gambits/#{parent._id}")=reply.reply 203 | else 204 | b Topic: 205 | a(href="/topics/#{topic._id}")=topic.name 206 | 207 | if (!isNew) 208 | .row 209 | .page-header 210 | h3 Test Gambit 211 | 212 | form.form-horizontal.js-submitTest 213 | .form-group 214 | label.col-sm-2.control-label(for='testPhrase') Test phrase 215 | .col-sm-10 216 | input#testPhrase.form-control.input-lg(type='text', name="phrase", placeholder='Test Phrase') 217 | 218 | .form-group 219 | .col-sm-offset-2.col-sm-10 220 | a.js-testPhrase.btn.btn-default Test Gambit 221 | 222 | script. 223 | $('.js-submitTest').submit(function(e){ 224 | e.preventDefault(); 225 | $('.js-testPhrase').trigger('click'); 226 | }); 227 | 228 | // Check to see if we can match 229 | $('.js-testPhrase').click(function(e){ 230 | e.preventDefault; 231 | var string = $("#testPhrase").val(); 232 | 233 | $.ajax({ 234 | url: '/gambits/#{gambit._id}/test', 235 | type: 'POST', 236 | data: { 237 | phrase: string 238 | }, 239 | success: function(result) { 240 | var stars = []; 241 | 242 | if (result) { 243 | for (var i = 1; i <= result.length;i++){ 244 | stars.push(result[i]); 245 | } 246 | if (stars.length != 0) { 247 | swal("Tested", "Yay found matches\n" + stars, "success"); 248 | } else { 249 | swal("Tested", "Yay it matches","success"); 250 | } 251 | } else { 252 | swal("Tested", "'" + string + "' does not match", "error"); 253 | } 254 | } 255 | }); 256 | }); 257 | 258 | var loadPlugins = function() { 259 | var plugins = new Bloodhound({ 260 | datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'), 261 | queryTokenizer: Bloodhound.tokenizers.whitespace, 262 | limit: 10, 263 | prefetch: { 264 | url: '/plugins', 265 | filter: function(list) { 266 | return $.map(list, function(plugin) { return { name: plugin }; }); 267 | } 268 | } 269 | }); 270 | 271 | // kicks off the loading/processing of `local` and `prefetch` 272 | plugins.initialize(); 273 | $('.typeahead').typeahead(null, { 274 | name: 'plugins', 275 | displayKey: 'name', 276 | source: plugins.ttAdapter() 277 | }); 278 | } 279 | 280 | $.get("/gambits/#{gambit._id}/replies", function(replies){ 281 | for (var i = 0; i < replies.length; i++) { 282 | var reply = ich.reply(replies[i]); 283 | $('#replyGroup').append(reply); 284 | } 285 | 286 | var reply = ich.reply({}); 287 | $('#replyGroup').append(reply); 288 | loadPlugins(); 289 | }); 290 | 291 | $("#js-del").on('click', function(e) { 292 | e.preventDefault(); 293 | $("#putMethod").attr("value", "delete"); 294 | $("#js-main").trigger("submit"); 295 | }); 296 | 297 | // Edit Reply 298 | $("#replyGroup").on({ 299 | click:function(e){ 300 | e.preventDefault(); 301 | var $group = $(this).parents('.js-reply-group'); 302 | var replyId = $group.data('id'); 303 | var replyString = $group.find('.reply').val(); 304 | //- var filterFunction = $group.find('.filter').val(); 305 | var filterFunction = $group.find('.typeahead').typeahead('val'); 306 | 307 | if (replyString === "") { 308 | swal("Ooops...", "Reply can not be empty", "error"); 309 | } else { 310 | $.ajax({ 311 | url: '/gambits/#{gambit._id}/reply/' + replyId, 312 | type: 'PUT', 313 | data: { 314 | reply: replyString, 315 | filter: filterFunction 316 | }, 317 | success: function(result) { 318 | swal("Sweet", "Reply has been updated", "success"); 319 | } 320 | }); 321 | } 322 | } 323 | }, ".js-replyEdit"); 324 | 325 | // Add Reply 326 | $("#replyGroup").on({ 327 | click:function(e){ 328 | e.preventDefault(); 329 | 330 | // Check to make sure the gambit is not new. 331 | if ($('#putMethod').length != 0) { 332 | $group = $(this).parents('.js-reply-group'); 333 | var replyString = $group.find('.reply').val(); 334 | var filterFunction = $group.find('.filter').val(); 335 | 336 | if (replyString === "") { 337 | swal("Ooops...", "Reply can not be empty", "error"); 338 | } else { 339 | $.ajax({ 340 | url: '/gambits/#{gambit._id}/reply', 341 | type: 'POST', 342 | data: { 343 | reply: replyString, 344 | filter: filterFunction 345 | }, 346 | success: function(result) { 347 | console.log(result) 348 | var reply = ich.reply(result); 349 | $group.before(reply); 350 | $group.find('input').val(""); 351 | } 352 | }); 353 | } 354 | } else { 355 | swal("Ooops...", "We need to create this gambit before adding a reply.\nHit 'Create Gambit' below.", "error"); 356 | } 357 | } 358 | }, ".js-replyAdd"); 359 | 360 | // Remove Reply 361 | $("#replyGroup").on({ 362 | click:function(e){ 363 | e.preventDefault(); 364 | var $group = $(this).parents('.js-reply-group'); 365 | var replyId = $group.data('id') 366 | swal({ 367 | title: "Are you sure?", 368 | text: "You will not be able to recover this reply!", 369 | type: "warning", 370 | showCancelButton: true, 371 | confirmButtonColor: "#DD6B55", 372 | confirmButtonText: "Yes, delete it!", 373 | closeOnConfirm: true 374 | }, function(){ 375 | $.ajax({ 376 | url: '/gambits/#{gambit._id}/reply', 377 | type: 'DELETE', 378 | data: { 379 | replyId: replyId 380 | }, 381 | success: function(result) { 382 | $group[0].remove(); 383 | } 384 | }); 385 | }); 386 | } 387 | }, ".js-replyDelete"); 388 | 389 | // Remove Reply 390 | $("#replyGroup").on({ 391 | click:function(e){ 392 | e.preventDefault(); 393 | 394 | } 395 | }, ".js-replyExpand"); 396 | 397 | if (!$('input#js-isQuestion').is(':checked')) { 398 | $('.js-questionType').addClass('hidden'); 399 | } 400 | 401 | $("#js-isQuestion").click(function(e){ 402 | $('.js-questionType').toggleClass('hidden'); 403 | }); 404 | 405 | 406 | script(id="reply" type="text/html"). 407 |
408 | 409 | 410 |
411 | 412 |
413 | 414 |
415 | 416 |
417 |
418 | {{#reply}} 419 | 420 | 422 | 423 | 425 | 426 |   427 | 428 | 430 | 431 | {{/reply}} 432 | 433 | {{^reply}} 434 | 435 | 437 | {{/reply}} 438 | 439 |
440 |
441 | 442 | 443 | -------------------------------------------------------------------------------- /views/gambits/index.jade: -------------------------------------------------------------------------------- 1 | extends ../_layout.jade 2 | block content 3 | .container 4 | .row 5 | .page-header 6 | h1 Gambit List 7 | p Gambits are the primary building block of the system. They have two parts, a Trigger, and Reply. 8 | 9 | - if (success_messages.length != 0) 10 | p.bg-success= success_messages 11 | 12 | table.table.table-bordered.table-condensed 13 | thead 14 | tr 15 | th(style="width:25px") Q 16 | th Input 17 | th FirstReply 18 | th(style="width:25px") RC 19 | th Action 20 | 21 | - for (var i = 0; i < gambits.length; i++) 22 | tr 23 | td= (gambits[i].isQuestion) ? "?":"+" 24 | td 25 | a(href="/gambits/#{gambits[i]._id}")= gambits[i].input 26 | td= (gambits[i].replies.length >= 1)? gambits[i].replies[0].reply : "" 27 | td= gambits[i].replies.length 28 | td 29 | a(data-id="#{gambits[i]._id}").js-del.btn.btn-danger.btn-xs Delete 30 | script. 31 | 32 | $(".js-del").click(function(e){ 33 | var id = $(e.target).data('id'); 34 | swal({ 35 | title: "Are you sure?", 36 | text: "You will not be able to recover this gambit!", 37 | type: "warning", 38 | showCancelButton: true, 39 | confirmButtonColor: "#DD6B55", 40 | confirmButtonText: "Yes, delete it!", 41 | closeOnConfirm: true 42 | }, function(){ 43 | $.ajax({ 44 | url: '/gambits/' + id, 45 | type: 'DELETE', 46 | success: function(result) { 47 | $(e.target).parents('tr').remove(); 48 | //- swal("Deleted!", "Gone", "success"); 49 | } 50 | }); 51 | }); 52 | }); 53 | 54 | -------------------------------------------------------------------------------- /views/import.jade: -------------------------------------------------------------------------------- 1 | extends ./_layout.jade 2 | block content 3 | .container 4 | .row 5 | .page-header 6 | h1 Import existing project 7 | .row 8 | 9 | - if (error_messages.length != 0) 10 | p.bg-warning= error_messages 11 | 12 | form(action="/import" method="post" enctype="multipart/form-data") 13 | .form-group 14 | label(for='datafile') Data File 15 | input#datafile(type='file' name="file") 16 | p.help-block Import an existing application file that has been pre-parsed. 17 | button.btn.btn-default(type='submit') Import File 18 | 19 | -------------------------------------------------------------------------------- /views/includes/_chat.jade: -------------------------------------------------------------------------------- 1 | .container 2 | .row 3 | .col-md-6 4 | .panel.panel-primary 5 | .panel-heading 6 | span.glyphicon.glyphicon-comment 7 | | Chat 8 | 9 | .panel-body 10 | ul.chat 11 | 12 | .panel-footer 13 | .input-group 14 | input#btn-input.form-control.input-sm(type='text', placeholder='Type your message here...') 15 | span.input-group-btn 16 | button#btn-chat.btn.btn-primary.btn-sm 17 | | Send 18 | .col-md-6 19 | include ../includes/_gambitform.jade 20 | 21 | 22 | script. 23 | var socket = io(); 24 | var lastMessage = ""; 25 | var lastReplyId, lastTopicId, lastGambitId; 26 | var oldscrollHeight = $(".panel-body")[0].scrollHeight; 27 | // Send message 28 | $('#btn-input').bind('keyup', function(e) { 29 | var code = e.keyCode || e.which; 30 | if(code == 13) { 31 | $('#btn-chat').trigger('click'); 32 | } 33 | }); 34 | 35 | $('#btn-chat').click(function(){ 36 | var message = $('#btn-input').val(); 37 | socket.emit('chat message', message); 38 | $('#btn-input').val(""); 39 | lastMessage = message; 40 | var userChat = ich.userChat({string:message}); 41 | $('.chat').append(userChat); 42 | return false; 43 | }); 44 | 45 | // Recieve Message 46 | socket.on('chat message', function(msgObj){ 47 | 48 | if (msgObj.replyId && msgObj.gambitId) { 49 | lastReplyId = msgObj.replyId; 50 | lastGambitId = msgObj.gambitId; 51 | lastTopicId = msgObj.topicId; 52 | } 53 | var botChat = ich.botChat(msgObj); 54 | $('.chat').append(botChat); 55 | 56 | var newscrollHeight = $(".panel-body")[0].scrollHeight; 57 | if(newscrollHeight > oldscrollHeight){ 58 | $(".panel-body").scrollTop($(".panel-body")[0].scrollHeight); 59 | } 60 | }); 61 | 62 | // providing we have a edit widget near by 63 | $("body").on({ 64 | click:function(e){ 65 | e.preventDefault(); 66 | $("#input").val($(e.target).text()) 67 | } 68 | }, ".js-edit"); 69 | 70 | $("body").on({ 71 | click:function(e){ 72 | e.preventDefault(); 73 | $('#replyId').val(''); 74 | $('#topicGroup').removeClass('hidden'); 75 | $('#topicId option[value="'+lastTopicId+'"]').prop('selected', true); 76 | $("#input").val($(e.target).parents("li").prev().find('.js-edit').text()); 77 | } 78 | }, ".js-editTopic"); 79 | 80 | $("body").on({ 81 | click:function(e){ 82 | e.preventDefault(); 83 | $("#input").val(lastMessage); 84 | $('#topicGroup').addClass('hidden'); 85 | $("#replyId").val(lastReplyId); 86 | // Reset the list 87 | $('#topicId option[value=""]').prop('selected', true); 88 | } 89 | }, ".js-editDirect"); 90 | 91 | script(id="botChat" type="text/html"). 92 |
  • 93 | 94 | User Avatar 95 | 96 |
    97 |
    98 | #{locals.projectName} 99 | {{createdAt}} 100 |
    101 | {{#topicName}} 102 |

    Topic: {{topicName}}

    103 | {{/topicName}} 104 | 105 | {{#gambitId}} 106 |

    {{string}}

    107 | {{/gambitId}} 108 | 109 | {{^gambitId}} 110 | {{#string}} 111 |

    {{string}}

    112 | {{/string}} 113 | 114 | {{^string}} 115 |

    Create Topic Reply or Direct Reply

    116 | {{/string}} 117 | 118 | {{/gambitId}} 119 |
    120 |
  • 121 | 122 | script(id="userChat" type="text/html"). 123 |
  • 124 | 125 | User Avatar 126 | 127 |
    128 |
    129 | moments ago 130 | Bot Author 131 |
    132 |

    {{string}}

    133 |
    134 |
  • 135 | -------------------------------------------------------------------------------- /views/includes/_gambitform.jade: -------------------------------------------------------------------------------- 1 | form.form-horizontal.js-submit(action="/gambits", method="post") 2 | .form-group 3 | label.col-sm-2.control-label(for='input') Input 4 | .col-sm-10 5 | input#input.form-control.input-lg(type='text', name="input", placeholder='Input') 6 | .form-group 7 | label.col-sm-2.control-label(for='reply') Quick Reply 8 | .col-sm-10 9 | input#reply.form-control(type='text', name="reply", placeholder='Quick Reply') 10 | input#replyId.form-control(type='hidden', name="replyId") 11 | 12 | .form-group#topicGroup 13 | label.col-sm-2.control-label(for='reply') Topic 14 | .col-sm-5 15 | select#topicId.form-control(name="topicId") 16 | option(value="") None 17 | -for(var i = 0; i < topics.length; i++) 18 | option(value="#{topics[i]._id}")= topics[i].name 19 | 20 | 21 | .form-group 22 | .col-sm-offset-2.col-sm-10 23 | .checkbox 24 | label 25 | input#js-isQuestion(type='checkbox', name="isQuestion") 26 | | Is Question 27 | 28 | .form-group.js-questionType(class="hidden") 29 | label.col-sm-2.control-label(for='reply') Question Type 30 | .col-xs-3 31 | select#qtype.form-control(name="qType") 32 | option(value="") None 33 | option(value="WH") Question word 34 | option(value="YN") Yes/No 35 | option(value="TG") Tag Question 36 | option(value="CH") Choice Question 37 | 38 | .form-group 39 | .col-sm-offset-2.col-sm-10 40 | button.btn.btn-default(type='submit') Create Gambit 41 | 42 | script. 43 | 44 | $('.js-submit').submit(function(e){ 45 | e.preventDefault(); 46 | $.ajax({ 47 | type: 'POST', 48 | url: '/gambits/quick', 49 | data: $( this ).serialize(), 50 | success: function(result) { 51 | if (result.success) { 52 | $("#topicId").val(""); 53 | $("#replyId").val(""); 54 | $("#reply").val(""); 55 | $("#input").val(""); 56 | 57 | swal("Sweet", "Gambit Added", "success"); 58 | } else { 59 | swal("Sweet", result.error, "error"); 60 | } 61 | 62 | } 63 | }); 64 | }); 65 | 66 | $("#js-isQuestion").click(function(e){ 67 | $('.js-questionType').toggleClass('hidden'); 68 | }) 69 | -------------------------------------------------------------------------------- /views/includes/_head.jade: -------------------------------------------------------------------------------- 1 | title Editor 2 | meta(charset="utf-8") 3 | meta(http-equiv="X-UA-Compatible" content="IE=edge") 4 | meta(name="viewport" content="width=device-width, initial-scale=1") 5 | 6 | link(rel="stylesheet" href="/css/bootstrap.min.css") 7 | link(rel="stylesheet" href="/css/bootstrap-theme.min.css") 8 | link(rel="stylesheet" href="/css/typeahead.css") 9 | 10 | script(src='/js/sweet-alert.min.js') 11 | link(rel='stylesheet', type='text/css', href='/css/sweet-alert.css') 12 | 13 | script(src="//code.jquery.com/jquery-2.1.3.min.js") 14 | script(src="/js/bootstrap.min.js") 15 | script(src="/js/typeahead.js") 16 | script(src="/js/sortable.min.js") 17 | script(src="/js/cytoscape.min.js") 18 | script(src="/js/vivagraph.js") 19 | 20 | script(src="/js/icanhas.min.js") 21 | script(src="/socket.io/socket.io.js") 22 | 23 | link(rel='stylesheet' type='text/css' href='/css/chat.css') 24 | link(rel='stylesheet' type='text/css' href='/css/callout.css') 25 | link(rel="stylesheet" type='text/css' href="/css/app.css") 26 | -------------------------------------------------------------------------------- /views/includes/_knoledgemenu.jade: -------------------------------------------------------------------------------- 1 | mixin subNav(tab) 2 | ul.nav.nav-tabs 3 | if tab == 'concepts' 4 | +tab('Concepts', '/knowledge')(class='active') 5 | else 6 | +tab('Concepts', '/knowledge') 7 | 8 | if tab == 'world' 9 | +tab('World knowledge', '/knowledge/world')(class='active') 10 | else 11 | +tab('World knowledge', '/knowledge/world') 12 | 13 | if (tab == 'bot') 14 | +tab('Bot knowledge', '/knowledge/bot')(class='active') 15 | else 16 | +tab('Bot knowledge', '/knowledge/bot') 17 | 18 | if (tab == 'user') 19 | +tab('User knowledge', '/knowledge/user')(class='active') 20 | else 21 | +tab('User knowledge', '/knowledge/user') 22 | 23 | //- if (tab == 'graph') 24 | //- +tab('Graph knowledge', '/knowledge/graph')(class='active') 25 | //- else 26 | //- +tab('Graph knowledge', '/knowledge/graph') 27 | 28 | 29 | mixin tab(name, href) 30 | li(class!=attributes.class) 31 | a(href=href)= name 32 | -------------------------------------------------------------------------------- /views/index.jade: -------------------------------------------------------------------------------- 1 | extends ./_layout.jade 2 | block content 3 | .container 4 | .row 5 | .page-header 6 | h1 Real time interface 7 | 8 | 9 | include ./includes/_chat.jade 10 | -------------------------------------------------------------------------------- /views/knowledge/bot.jade: -------------------------------------------------------------------------------- 1 | include ../includes/_knoledgemenu.jade 2 | extends ../_layout.jade 3 | block content 4 | .container 5 | .row 6 | 7 | +subNav('bot') 8 | 9 | .bs-callout.bs-callout-default 10 | h4 Bot Facts and Knowledge 11 | p This table represents things the bot knows about itself. 12 | 13 | .form-group.pull-right 14 | form#target(action="/knowledge/bot/import" method="post" enctype="multipart/form-data") 15 | span.btn.btn-default.btn-file 16 | | Load Facts 17 | input#datafile(type='file' name="file") 18 | 19 | - if (success_messages.length != 0) 20 | p.bg-success= success_messages 21 | 22 | - if (error_messages.length != 0) 23 | p.bg-warning= error_messages 24 | 25 | 26 | form.form-inline(action="/knowledge/bot" method="post") 27 | .form-group 28 | label.sr-only(for='subject') Subject 29 | input#subject.form-control(type='text', name="subject", placeholder='Subject') 30 | |   31 | .form-group 32 | label.sr-only(for='predicate') Predicate 33 | input#predicate.form-control(type='text', name="predicate", placeholder='Predicate') 34 | |   35 | .form-group 36 | label.sr-only(for='object') Object 37 | input#object.form-control(type='text', name="object", placeholder='Object') 38 | |   39 | 40 | button.btn.btn-default(type='submit') Create Fact 41 | 42 | 43 | table.table.table-bordered.table-condensed 44 | thead 45 | tr 46 | th Subject 47 | th Predicate 48 | th Object 49 | th Action 50 | 51 | - for (var i = 0; i < concepts.length; i++) 52 | tr 53 | td= concepts[i].subject 54 | td= concepts[i].predicate 55 | td= concepts[i].object 56 | td 57 | a(data-s="#{concepts[i].subject}", data-p="#{concepts[i].predicate}", data-o="#{concepts[i].object}").js-del.btn.btn-danger.btn-xs Delete 58 | 59 | script. 60 | $('#datafile').change(function() { 61 | $('#target').submit(); 62 | }); 63 | 64 | $(".js-del").click(function(e){ 65 | var s = $(e.target).data('s'); 66 | var p = $(e.target).data('p'); 67 | var o = $(e.target).data('o'); 68 | 69 | swal({ 70 | title: "Are you sure?", 71 | text: "You will not be able to recover this fact!", 72 | type: "warning", 73 | showCancelButton: true, 74 | confirmButtonColor: "#DD6B55", 75 | confirmButtonText: "Yes, delete it!", 76 | closeOnConfirm: true 77 | }, function(){ 78 | $.ajax({ 79 | url: '/knowledge/bot/', 80 | type: 'DELETE', 81 | data: { 82 | s: s, 83 | p: p, 84 | o: o 85 | }, 86 | success: function(result) { 87 | $(e.target).parents('tr').remove(); 88 | } 89 | }); 90 | }); 91 | }); 92 | 93 | 94 | -------------------------------------------------------------------------------- /views/knowledge/graph.jade: -------------------------------------------------------------------------------- 1 | style. 2 | 3 | #cy { 4 | height: 100%; 5 | width: 100%; 6 | position: absolute; 7 | left: 0; 8 | top: 0; 9 | } 10 | 11 | #info { 12 | color: #c88; 13 | font-size: 1em; 14 | position: absolute; 15 | z-index: -1; 16 | left: 1em; 17 | top: 1em; 18 | } 19 | 20 | include ../includes/_knoledgemenu.jade 21 | extends ../_layout.jade 22 | block content 23 | .container 24 | .row 25 | +subNav('graph') 26 | 27 | .form-group.pull-right 28 | form#target(action="/knowledge/concepts/import" method="post" enctype="multipart/form-data") 29 | span.btn.btn-default.btn-file 30 | | Load Concepts 31 | input#datafile(type='file' name="file") 32 | 33 | script. 34 | 35 | function main () { 36 | //- var graph = Viva.Graph.graph(); 37 | 38 | var fromJSON = Viva.Graph.serializer().loadFromJSON; 39 | 40 | var jsonString = '{"nodes":[{"id":"hello"},{"id":"world"}],"links":[{"fromId":"hello","toId":"world"}]}' 41 | var graph = fromJSON(jsonString); 42 | 43 | var graphics = Viva.Graph.View.svgGraphics(); 44 | var nodeSize = 0; 45 | 46 | graphics.node(function(node) { 47 | 48 | //- Viva.Graph.svg("g"); 49 | return Viva.Graph.svg("rect") 50 | .attr("width", 10) 51 | .attr("height", 10) 52 | .attr("fill", "#00a2e8"); 53 | }).placeNode(function(nodeUI, pos) { 54 | nodeUI.attr('x', pos.x - 5).attr('y', pos.y - 5); 55 | }); 56 | 57 | var createMarker = function(id) { 58 | return Viva.Graph.svg('marker') 59 | .attr('id', id) 60 | .attr('viewBox', "0 0 10 10") 61 | .attr('refX', "10") 62 | .attr('refY', "5") 63 | .attr('markerUnits', "strokeWidth") 64 | .attr('markerWidth', "10") 65 | .attr('markerHeight', "5") 66 | .attr('orient', "auto"); 67 | }; 68 | 69 | var marker = createMarker('Triangle'); 70 | marker.append('path').attr('d', 'M 0 0 L 10 5 L 0 10 z'); 71 | 72 | var defs = graphics.getSvgRoot().append('defs'); 73 | defs.append(marker); 74 | 75 | var geom = Viva.Graph.geom(); 76 | 77 | var renderer = Viva.Graph.View.renderer(graph, { graphics : graphics }); 78 | renderer.run(); 79 | 80 | //- graphics.link(function(link){ 81 | //- var label = Viva.Graph.svg('text').attr('id','label_'+link.data.id).text(link.data.id); 82 | //- graphics.getSvgRoot().childNodes[0].append(label); 83 | 84 | //- return Viva.Graph.svg('path') 85 | //- .attr('stroke', 'gray') 86 | //- .attr('marker-end', 'url(#Triangle)') 87 | //- .attr('id', link.data.id); 88 | //- }).placeLink(function(linkUI, fromPos, toPos) { 89 | //- var toNodeSize = nodeSize; 90 | //- var fromNodeSize = nodeSize; 91 | 92 | //- var from = geom.intersectRect( 93 | //- fromPos.x - fromNodeSize / 2, // left 94 | //- fromPos.y - fromNodeSize / 2, // top 95 | //- fromPos.x + fromNodeSize / 2, // right 96 | //- fromPos.y + fromNodeSize / 2, // bottom 97 | //- fromPos.x, fromPos.y, toPos.x, toPos.y) 98 | //- || fromPos; 99 | 100 | //- var to = geom.intersectRect( 101 | //- toPos.x - toNodeSize / 2, // left 102 | //- toPos.y - toNodeSize / 2, // top 103 | //- toPos.x + toNodeSize / 2, // right 104 | //- toPos.y + toNodeSize / 2, // bottom 105 | //- // segment: 106 | //- toPos.x, toPos.y, fromPos.x, fromPos.y) 107 | //- || toPos; 108 | 109 | //- var data = 'M' + from.x + ',' + from.y + 'L' + to.x + ',' + to.y; 110 | //- linkUI.attr("d", data); 111 | 112 | //- document.getElementById('label_'+linkUI.attr('id')) 113 | //- .attr("x", (from.x + to.x) / 2) 114 | //- .attr("y", (from.y + to.y) / 2); 115 | //- }); 116 | 117 | // Finally we add something to the graph: 118 | //- graph.addNode('anvaka', '91bad8ceeec43ae303790f8fe238164b'); 119 | //- graph.addNode('indexzero', 'd43e8ea63b61e7669ded5b9d3c2e980f'); 120 | //- graph.addNode('test', 'd43e8ea63b61e7669ded5b9d3c2e980f'); 121 | //- graph.addLink('anvaka', 'indexzero', {id : 'isa'}); 122 | //- graph.addLink('anvaka', 'test', {id : 'member'}); 123 | 124 | } 125 | 126 | $(document).ready(main()) 127 | 128 | 129 | -------------------------------------------------------------------------------- /views/knowledge/index.jade: -------------------------------------------------------------------------------- 1 | include ../includes/_knoledgemenu.jade 2 | extends ../_layout.jade 3 | block content 4 | .container 5 | .row 6 | +subNav('concepts') 7 | 8 | .bs-callout.bs-callout-default 9 | h4 Concept Hierarchy 10 | p Concept table 11 | 12 | .form-group.pull-right 13 | form#target(action="/knowledge/concepts/import" method="post" enctype="multipart/form-data") 14 | span.btn.btn-default.btn-file 15 | | Load Concepts 16 | input#datafile(type='file' name="file") 17 | 18 | if (concept && concept.length !== 0) 19 | h3 Exploring #{concept[0].subject} 20 | 21 | table.table.table-bordered.table-condensed 22 | thead 23 | tr 24 | th Subject 25 | th Predicate 26 | th Object 27 | td Action 28 | 29 | - for (var i = 0; i < concept.length; i++) 30 | tr 31 | td= concept[i].subject 32 | td= concept[i].predicate 33 | td 34 | a(href="/knowledge/concept/#{concept[i].object}")= concept[i].object 35 | td 36 | a(data-s="#{concept[i].subject}", data-p="#{concept[i].predicate}", data-o="#{concept[i].object}").js-del.btn.btn-danger.btn-xs Delete 37 | 38 | if (concepts) 39 | table.table.table-bordered.table-condensed 40 | - for (var i = 0; i < concepts.length; i += 5) 41 | tr 42 | - if (concepts[i]) 43 | td 44 | a(href="/knowledge/concept/#{concepts[i].subject}")= concepts[i].subject 45 | - if (concepts[i+1]) 46 | td 47 | a(href="/knowledge/concept/#{concepts[i+1].subject}")= concepts[i+1].subject 48 | - if (concepts[i+2]) 49 | td 50 | a(href="/knowledge/concept/#{concepts[i+2].subject}")= concepts[i+2].subject 51 | - if (concepts[i+3]) 52 | td 53 | a(href="/knowledge/concept/#{concepts[i+3].subject}")= concepts[i+3].subject 54 | - if (concepts[i+4]) 55 | td 56 | a(href="/knowledge/concept/#{concepts[i+4].subject}")= concepts[i+4].subject 57 | 58 | 59 | 60 | script. 61 | $('#datafile').change(function() { 62 | $('#target').submit(); 63 | }); 64 | 65 | 66 | $(".js-del").click(function(e){ 67 | var s = $(e.target).data('s'); 68 | var p = $(e.target).data('p'); 69 | var o = $(e.target).data('o'); 70 | 71 | swal({ 72 | title: "Are you sure?", 73 | text: "You will not be able to recover this fact!", 74 | type: "warning", 75 | showCancelButton: true, 76 | confirmButtonColor: "#DD6B55", 77 | confirmButtonText: "Yes, delete it!", 78 | closeOnConfirm: true 79 | }, function(){ 80 | $.ajax({ 81 | url: '/knowledge/world/', 82 | type: 'DELETE', 83 | data: { 84 | s: s, 85 | p: p, 86 | o: o 87 | }, 88 | success: function(result) { 89 | $(e.target).parents('tr').remove(); 90 | } 91 | }); 92 | }); 93 | }); -------------------------------------------------------------------------------- /views/knowledge/user.jade: -------------------------------------------------------------------------------- 1 | extends ../_layout.jade 2 | block content 3 | .container 4 | .row 5 | 6 | include ../includes/_knoledgemenu.jade 7 | +subNav('user') 8 | 9 | .bs-callout.bs-callout-default 10 | h4 User Facts and Knowledge 11 | p This table is filled by though conversation and using 12 | b ^save(key,value) 13 | | and 14 | b`^createUserFact(s,p,o)` 15 | 16 | 17 | table.table.table-bordered.table-condensed 18 | thead 19 | tr 20 | th Subject 21 | th Predicate 22 | th Object 23 | th Action 24 | 25 | - for (var i = 0; i < concepts.length; i++) 26 | tr 27 | td= concepts[i].subject 28 | td= concepts[i].predicate 29 | td= concepts[i].object 30 | td 31 | a(data-s="#{concepts[i].subject}", data-p="#{concepts[i].predicate}", data-o="#{concepts[i].object}").js-del.btn.btn-danger.btn-xs Delete 32 | 33 | script. 34 | 35 | $(".js-del").click(function(e){ 36 | var s = $(e.target).data('s'); 37 | var p = $(e.target).data('p'); 38 | var o = $(e.target).data('o'); 39 | 40 | swal({ 41 | title: "Are you sure?", 42 | text: "You will not be able to recover this fact!", 43 | type: "warning", 44 | showCancelButton: true, 45 | confirmButtonColor: "#DD6B55", 46 | confirmButtonText: "Yes, delete it!", 47 | closeOnConfirm: true 48 | }, function(){ 49 | $.ajax({ 50 | url: '/knowledge/user/', 51 | type: 'DELETE', 52 | data: { 53 | s: s, 54 | p: p, 55 | o: o 56 | }, 57 | success: function(result) { 58 | $(e.target).parents('tr').remove(); 59 | } 60 | }); 61 | }); 62 | }); 63 | 64 | 65 | -------------------------------------------------------------------------------- /views/knowledge/world.jade: -------------------------------------------------------------------------------- 1 | extends ../_layout.jade 2 | block content 3 | .container 4 | .row 5 | 6 | include ../includes/_knoledgemenu.jade 7 | +subNav('world') 8 | 9 | br 10 | 11 | .form-group.pull-right 12 | form#target(action="/knowledge/world/import" method="post" enctype="multipart/form-data") 13 | span.btn.btn-default.btn-file 14 | | Load Facts 15 | input#datafile(type='file' name="file") 16 | 17 | - if (success_messages.length != 0) 18 | p.bg-success= success_messages 19 | 20 | - if (error_messages.length != 0) 21 | p.bg-warning= error_messages 22 | 23 | 24 | form.form-inline(action="/knowledge/world" method="post") 25 | .form-group 26 | label.sr-only(for='subject') Subject 27 | input#subject.form-control(type='text', name="subject", placeholder='Subject') 28 | |   29 | .form-group 30 | label.sr-only(for='predicate') Predicate 31 | input#predicate.form-control(type='text', name="predicate", placeholder='Predicate') 32 | |   33 | .form-group 34 | label.sr-only(for='object') Object 35 | input#object.form-control(type='text', name="object", placeholder='Object') 36 | |   37 | 38 | button.btn.btn-default(type='submit') Create Fact 39 | 40 | .bs-callout.bs-callout-default 41 | h4 World Knowledge 42 | p This is general shared or common knowledge. 43 | 44 | form.form-inline(action="/knowledge/world/filter" method="get") 45 | .form-group 46 | label.sr-only(for='subject') Subject 47 | input#subject.form-control(type='text', name="subject", placeholder='Subject', value='#{params.subject||""}') 48 | |   49 | .form-group 50 | label.sr-only(for='predicate') Predicate 51 | input#predicate.form-control(type='text', name="predicate", placeholder='Predicate', value='#{params.predicate||""}') 52 | |   53 | .form-group 54 | label.sr-only(for='object') Object 55 | input#object.form-control(type='text', name="object", placeholder='Object', value='#{params.object||""}') 56 | |   57 | 58 | button.btn.btn-default(type='submit') Filter Facts 59 | 60 | table.table.table-bordered.table-condensed 61 | thead 62 | tr 63 | th Subject 64 | th Predicate 65 | th Object 66 | th Action 67 | 68 | - for (var i = 0; i < concepts.length; i++) 69 | tr 70 | td 71 | a(href="/knowledge/concept/#{concepts[i].subject}")= concepts[i].subject 72 | td= concepts[i].predicate 73 | td 74 | a(href="/knowledge/concept/#{concepts[i].object}")= concepts[i].object 75 | td 76 | a(data-s="#{concepts[i].subject}", data-p="#{concepts[i].predicate}", data-o="#{concepts[i].object}").js-del.btn.btn-danger.btn-xs Delete 77 | 78 | script. 79 | $('#datafile').change(function() { 80 | $('#target').submit(); 81 | }); 82 | 83 | $(".js-del").click(function(e){ 84 | var s = $(e.target).data('s'); 85 | var p = $(e.target).data('p'); 86 | var o = $(e.target).data('o'); 87 | 88 | swal({ 89 | title: "Are you sure?", 90 | text: "You will not be able to recover this fact!", 91 | type: "warning", 92 | showCancelButton: true, 93 | confirmButtonColor: "#DD6B55", 94 | confirmButtonText: "Yes, delete it!", 95 | closeOnConfirm: true 96 | }, function(){ 97 | $.ajax({ 98 | url: '/knowledge/world/', 99 | type: 'DELETE', 100 | data: { 101 | s: s, 102 | p: p, 103 | o: o 104 | }, 105 | success: function(result) { 106 | $(e.target).parents('tr').remove(); 107 | } 108 | }); 109 | }); 110 | }); 111 | -------------------------------------------------------------------------------- /views/replies/get.jade: -------------------------------------------------------------------------------- 1 | extends ../_layout.jade 2 | block content 3 | .container 4 | .row 5 | .page-header 6 | h1 Reply '#{replies.reply}' 7 | a.btn.btn-primary.pull-right(href="/gambits/new?replyId=#{replies._id}") Create New Gambit 8 | 9 | - if (success_messages.length != 0) 10 | p.bg-success= success_messages 11 | 12 | //- code 13 | //- pre= replies 14 | 15 | ul 16 | - for (var i = 0; i < replies.gambits.length; i++) 17 | li 18 | a(href="/gambits/#{replies.gambits[i]._id}")= replies.gambits[i].input 19 | ul 20 | - for (var j = 0; j < replies.gambits[i].replies.length; j++) 21 | li 22 | a(href="/replies/#{replies.gambits[i].replies[j]._id}")= replies.gambits[i].replies[j].reply -------------------------------------------------------------------------------- /views/replies/index.jade: -------------------------------------------------------------------------------- 1 | extends ../_layout.jade 2 | block content 3 | .container 4 | .row 5 | .page-header 6 | h1 Replies 7 | 8 | - if (success_messages.length != 0) 9 | p.bg-success= success_messages 10 | 11 | table.table.table-bordered.table-condensed 12 | thead 13 | tr 14 | th Reply 15 | th(style="width:25px") RC 16 | th Action 17 | 18 | - for (var i = 0; i < replies.length; i++) 19 | tr 20 | td 21 | a(href="/replies/#{replies[i]._id}")= replies[i].reply 22 | td= replies[i].gambits.length 23 | td 24 | a(data-id="#{replies[i]._id}").js-del.btn.btn-danger.btn-xs Delete 25 | script. 26 | $(".js-del").click(function(e){ 27 | var id = $(e.target).data('id'); 28 | swal({ 29 | title: "Are you sure?", 30 | text: "You will not be able to recover this Reply!", 31 | type: "warning", 32 | showCancelButton: true, 33 | confirmButtonColor: "#DD6B55", 34 | confirmButtonText: "Yes, delete it!", 35 | closeOnConfirm: true 36 | }, function(){ 37 | $.ajax({ 38 | url: '/replies/' + id, 39 | type: 'DELETE', 40 | success: function(result) { 41 | $(e.target).parents('tr').remove(); 42 | } 43 | }); 44 | 45 | }); 46 | }) -------------------------------------------------------------------------------- /views/settings.jade: -------------------------------------------------------------------------------- 1 | extends ./_layout.jade 2 | block content 3 | .container 4 | .row 5 | .page-header 6 | h1 Settings 7 | .row 8 | 9 | - if (success_messages.length != 0) 10 | p.bg-success= success_messages 11 | 12 | - if (error_messages.length != 0) 13 | p.bg-warning= error_messages 14 | 15 | 16 | h4 Enable Slack 17 | p Turn on intergrated Slack Support. By enabling this option and adding a skack token your bot will join the channel with the same gambits loaded here. 18 | form(action="/settings/slack" method="post" enctype="multipart/form-data") 19 | .form-group 20 | label.control-label(for='inputToken') Slack API Token 21 | input#inputToken.form-control(type='text', name="token", value="#{token}", placeholder='Slack Token') 22 | .form-group 23 | input.btn.btn-success(type='submit', name="start", value="Start") 24 | | 25 | input.btn.btn-danger(type='submit', name="stop", value="Stop") 26 | -------------------------------------------------------------------------------- /views/topics/get.jade: -------------------------------------------------------------------------------- 1 | extends ../_layout.jade 2 | block content 3 | .container 4 | 5 | form(action="/topics/#{topic._id}/sort" method="post") 6 | .form-group 7 | button.btn.btn-warning.pull-right(type='submit') Sort Topic 8 | |   9 | a.btn.btn-primary.pull-right(href="/gambits/new?topicId=#{topic._id}") Create New Gambit 10 | |   11 | 12 | 13 | - if (success_messages.length != 0) 14 | p.bg-success= success_messages 15 | 16 | .row 17 | .page-header 18 | h1 Edit Topic '#{topic.name}' 19 | 20 | - if (error_messages.length != 0) 21 | p.bg-warning= error_messages 22 | 23 | form.form-horizontal(action="/topics/#{topic._id}", method="post") 24 | input(type="hidden" name="_method", value="put") 25 | 26 | .form-group 27 | label.col-sm-2.control-label(for='formName') Topic Name 28 | .col-sm-10 29 | input#formName.form-control(type='text', name="name", placeholder='Topic Name', value='#{topic.name}') 30 | .form-group 31 | label.col-sm-2.control-label(for='keywords') Keywords 32 | .col-sm-10 33 | input#keywords.form-control(type='text', name="keywords", placeholder='Topic Keywords', value='#{topic.keywords}') 34 | .form-group 35 | .col-sm-offset-2.col-sm-10 36 | .checkbox 37 | label 38 | - if (topic.system) 39 | input(type='checkbox', name="system", checked="checked") 40 | - else 41 | input(type='checkbox', name="system" ) 42 | | System Topic 43 | .form-group 44 | .col-sm-offset-2.col-sm-10 45 | .checkbox 46 | label 47 | - if (topic.keep) 48 | input(type='checkbox', name="keep", checked="checked") 49 | - else 50 | input(type='checkbox', name="keep" ) 51 | | Keep Topic 52 | .form-group 53 | .col-sm-offset-2.col-sm-10 54 | button.btn.btn-default(type='submit') Update Topic 55 | 56 | 57 | 58 | form.form-horizontal.js-submitTest 59 | .form-group 60 | label.col-sm-2.control-label(for='testPhrase') Test phrase 61 | .col-sm-10 62 | input#testPhrase.form-control.input-lg(type='text', name="phrase", placeholder='Test Phrase') 63 | 64 | .form-group 65 | .col-sm-offset-2.col-sm-10 66 | a.js-testPhrase.btn.btn-default Test for Match 67 | 68 | 69 | //- code 70 | //- pre= topic 71 | 72 | ul.sortable 73 | - for (var i = 0; i < topic.gambits.length; i++) 74 | li(id="#{topic.gambits[i]._id}") 75 | a(href="/gambits/#{topic.gambits[i]._id}")= topic.gambits[i].input 76 | ul 77 | - for (var j = 0; j < topic.gambits[i].replies.length; j++) 78 | li 79 | a(href="/replies/#{topic.gambits[i].replies[j]._id}")= topic.gambits[i].replies[j].reply 80 | 81 | script. 82 | $('.sortable').sortable().bind('sortupdate', function(e, ui) { 83 | //Triggered when the user stopped sorting and the DOM position has changed. 84 | var elements = $('.sortable > li'); 85 | 86 | var ids = elements.map(function(i, ex){ return ex.id; }).toArray(); 87 | $.ajax({ 88 | url: '/topics/#{topic._id}/reorder', 89 | type: 'POST', 90 | data: { 91 | ids: ids 92 | }, 93 | success: function(result) { 94 | console.log("done", result); 95 | } 96 | }); 97 | 98 | }); 99 | 100 | $('.js-submitTest').submit(function(e){ 101 | e.preventDefault(); 102 | $(".hilite").removeClass("hilite"); 103 | $('.js-testPhrase').trigger('click'); 104 | }); 105 | 106 | // Check to see if we can match 107 | $('.js-testPhrase').click(function(e){ 108 | e.preventDefault; 109 | var string = $("#testPhrase").val(); 110 | 111 | $.ajax({ 112 | url: '/topics/#{topic._id}/test', 113 | type: 'POST', 114 | data: { 115 | phrase: string 116 | }, 117 | success: function(result) { 118 | var stars = []; 119 | 120 | if (result.length !== 0) { 121 | for (var i = 0; i < result.length; i++) { 122 | $("#" + result[i]._id).addClass("hilite"); 123 | } 124 | 125 | swal("Tested", "Yay found matches", "success"); 126 | } else { 127 | swal({ 128 | title:"Tested", 129 | text: "'" + string + "' does not match.\n Create Gambit", 130 | type: "error", 131 | html: true 132 | }); 133 | } 134 | } 135 | }); 136 | }); -------------------------------------------------------------------------------- /views/topics/index.jade: -------------------------------------------------------------------------------- 1 | extends ../_layout.jade 2 | block content 3 | .container 4 | .row 5 | .page-header 6 | h1 Topic List 7 | p Topics are a logical grouping of Gambits. 8 | 9 | - if (success_messages.length != 0) 10 | p.bg-success= success_messages 11 | 12 | table.table.table-bordered.table-condensed 13 | thead 14 | tr 15 | th Name 16 | th Keywords 17 | th System 18 | th keep 19 | th(style="width:25px") GC 20 | th Action 21 | 22 | - for (var i = 0; i < topics.length; i++) 23 | tr 24 | td 25 | a(href="/topics/#{topics[i].id}")= topics[i].name 26 | td= topics[i].keywords 27 | td= (topics[i].system) ? "Y": "N" 28 | td= (topics[i].keep) ? "Y": "N" 29 | td= topics[i].gambits.length 30 | td 31 | a(href="/gambits/new?topicId=#{topics[i]._id}").btn.btn-primary.btn-xs New Gambit 32 | |   33 | a(data-id="#{topics[i].id}").js-del.btn.btn-danger.btn-xs Delete 34 | 35 | - if (error_messages.length != 0) 36 | p.bg-warning= error_messages 37 | 38 | 39 | form.form-horizontal(action="/topics", method="post") 40 | .form-group 41 | label.col-sm-2.control-label(for='formName') Topic Name 42 | .col-sm-10 43 | input#formName.form-control(type='text', name="name", placeholder='Topic Name') 44 | .form-group 45 | label.col-sm-2.control-label(for='keywords') Keywords 46 | .col-sm-10 47 | input#keywords.form-control(type='text', name="keywords", placeholder='Topic Keywords') 48 | .form-group 49 | .col-sm-offset-2.col-sm-10 50 | .checkbox 51 | label 52 | input(type='checkbox', name="system") 53 | | System Topic 54 | .form-group 55 | .col-sm-offset-2.col-sm-10 56 | button.btn.btn-default(type='submit') Create Topic 57 | 58 | script. 59 | $(".js-del").click(function(e){ 60 | var id = $(e.target).data('id'); 61 | swal({ 62 | title: "Are you sure?", 63 | text: "You will not be able to recover this topic!", 64 | type: "warning", 65 | showCancelButton: true, 66 | confirmButtonColor: "#DD6B55", 67 | confirmButtonText: "Yes, delete it!", 68 | closeOnConfirm: true 69 | }, function(){ 70 | $.ajax({ 71 | url: '/topics/' + id, 72 | type: 'DELETE', 73 | success: function(result) { 74 | $(e.target).parents('tr').remove(); 75 | } 76 | }); 77 | 78 | }); 79 | }) 80 | 81 | 82 | --------------------------------------------------------------------------------