116 | 117 |
├── .gitignore
├── LICENSE
├── README.md
├── app.js
├── apps
├── bootstrap.js
├── journal_database.js
├── models
│ ├── admin_model.js
│ ├── graph_model.js
│ ├── journal_model.js
│ ├── linker.js
│ └── topic_model.js
├── slug.js
├── topic_database.js
└── user_database.js
├── bin
└── www
├── config
├── config.json
├── owner.json
└── vocab
│ ├── biomed
│ ├── defns.json
│ └── labels.json
│ ├── climate
│ ├── defns.json
│ └── labels.json
│ └── vocab.md
├── data
└── .keep
├── package.json
├── public
├── css
│ ├── bootstrap-theme.css
│ ├── bootstrap-theme.min.css
│ ├── bootstrap.css
│ ├── bootstrap.min.css
│ ├── default.css
│ ├── medium-editor.css
│ └── style.css
├── images
│ ├── TopicQuestsLogo_sm.png
│ ├── github-1.jpg
│ └── map.gif
└── js
│ ├── .DS_Store
│ ├── bootstrap.js
│ ├── bootstrap.min.js
│ ├── bootstrap3-typeahead.js
│ ├── jquery-3.2.1.min.js
│ ├── medium-editor.min.js
│ └── vis.js
├── routes
├── helper.js
├── index.js
└── users.js
└── views
├── editor.hbs
├── error.hbs
├── iframe.hbs
├── index.hbs
├── journal_edit_form.hbs
├── journalview.hbs
├── layout.hbs
├── login_form.hbs
├── partials
├── air_form.hbs
├── footer.hbs
├── triple_form.hbs
└── webmenubar.hbs
├── signup_form.hbs
├── topic_index.hbs
└── topicview.hbs
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # Next.js build output
79 | .next
80 |
81 | # Nuxt.js build / generate output
82 | .nuxt
83 | dist
84 |
85 | # Gatsby files
86 | .cache/
87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
88 | # https://nextjs.org/blog/next-9-1#public-directory-support
89 | # public
90 |
91 | # vuepress build output
92 | .vuepress/dist
93 |
94 | # Serverless directories
95 | .serverless/
96 |
97 | # FuseBox cache
98 | .fusebox/
99 |
100 | # DynamoDB Local files
101 | .dynamodb/
102 |
103 | # TernJS port file
104 | .tern-port
105 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # lite-net-3
2 | Simple triple journal
3 |
4 | Just:
5 | * npm install
6 | * Edit the file /config/owner.json to suit your own credentials
7 | * Edit the file /config/config.json to suit whether private or public
8 | * Remove all database files in /data to start fresh
9 | * npm start
10 | * visit http://localhost:4000/
11 |
12 |
13 | To clean the databases, just delete the two files in the /data folder.
14 |
15 | ## UX
16 | On the Journal landing page, there are three methods by which new journal entries are crafted:
17 | * Entering a URL for harvesting
18 | * Entering a text note
19 | * Creating a triple
20 |
21 | ### Entering a URL for harvesting
22 | Past a URL in the box and click Submit.
23 | A new page will be presented with the content of the selected URL visible, together with two modes of journal fabrication: a text note and a triple.
24 |
25 | This allows for harvesting information from the selected resource.
26 | ### Entering a text note
27 | A text note can be any text desired, including copying and pasting from other sources. This is not (yet) a _rich text_ feature.
28 | In any text entered, it is possible to create _Wikilinks_ by surrounding a word or phrase with "[[ ... ]]".
29 | ### Creating a statement as a triple structure
30 | A triple consists of three components to form a statement like _something causes something else_, which has the form {subject, predicate, object}. Type the subject word or phrase into its box, and do the same for the predicate and the object. In each case, _typeahead_ will help you find the term or predicate. There is an opportunity to add a URL and a comment.
31 |
32 | ### Viewing a Graph
33 | Each Topic, particularly those which are not Relations, may have a graph created by Statements. Viewing a Graph for a topic means clicking the Graph View Button on a Topic View. At this time, some small graphs tend to sit in the upper left corner, and do not center.
34 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | const createError = require('http-errors');
2 | const express = require('express');
3 | const path = require('path');
4 | const cookieParser = require('cookie-parser');
5 | const logger = require('morgan');
6 | const flash = require("connect-flash");
7 | const session = require("express-session");
8 | const uuid = require('uuid');
9 | const indexRouter = require('./routes/index');
10 | const usersRouter = require('./routes/users');
11 |
12 | const app = express();
13 | const hbs = require('hbs');
14 |
15 | // view engine setup
16 | app.set('views', path.join(__dirname, 'views'));
17 | app.set('view engine', 'hbs');
18 | hbs.registerPartials(`${__dirname}/views/partials`);
19 |
20 | app.use(logger('dev'));
21 | app.use(express.json({limit: '50mb'}));
22 | app.use(express.urlencoded({ extended: false, limit: '50mb' }));
23 | app.use(cookieParser());
24 | app.use(express.static(path.join(__dirname, 'public')));
25 |
26 |
27 | app.use(flash());
28 | app.use(session({
29 | genid(req) {
30 | return uuid.v4(); // use UUIDs for session IDs
31 | },
32 | secret: "collaborative really hot sauce", //TODO ChangeMe
33 | resave: true,
34 | saveUninitialized: true
35 | }));
36 |
37 | app.use('/', indexRouter);
38 | app.use('/users', usersRouter);
39 |
40 | // catch 404 and forward to error handler
41 | app.use((req, res, next) => {
42 | next(createError(404));
43 | });
44 |
45 | // error handler
46 | app.use((err, req, res, next) => {
47 | // set locals, only providing error in development
48 | res.locals.message = err.message;
49 | res.locals.error = req.app.get('env') === 'development' ? err : {};
50 |
51 | // render the error page
52 | res.status(err.status || 500);
53 | res.render('error');
54 | });
55 |
56 | module.exports = app;
57 |
--------------------------------------------------------------------------------
/apps/bootstrap.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const owner = require('../config/owner');
3 | const userDB = require('./user_database');
4 | const topicDB = require('./topic_database');
5 | const AdminModel = require('./models/admin_model');
6 |
7 | class Bootstrap {
8 | constructor() {
9 | console.info("Bootstrap-1", AdminModel);
10 | }
11 |
12 | async migrateTransclusions() {
13 | console.info('Migrating Transclusions');
14 | //Not using this
15 | //For every href entry in backlinks array
16 | // if it is an href. find the journal id and substitute that into
17 | // a new array journal/
18 |
19 | /* topicDB.find({}, function(err, data) {
20 | console.info('Migrate-1', err, data);
21 | var backlinks;
22 | var newLinks = [];
23 | var found = false;
24 | var where;
25 | var theLink;
26 | data.forEach(function(topic) {
27 | console.info('Migrate-2', topic);
28 | backlinks = topic.backlinks;
29 | found = false;
30 | backlinks.forEach(function(link) {
31 | theLink = link;
32 | if (theLink.startsWith('");
36 | theLink = theLink.substring(0, where);
37 | //theLink is now the journal Id
38 | console.info('Migrate-3', link, theLink);
39 | newLinks.push(theLink);
40 | found = true;
41 | }
42 | });
43 | if (found) {
44 | console.log('Migrate-4', topic);
45 | topicDB.replaceBacklinks(topic._id, newLinks, function(err, data){
46 | //
47 | });
48 | }
49 | });
50 |
51 | });*/
52 | return true;
53 | };
54 |
55 | /**
56 | * @param { done }
57 | */
58 | async validateUserDB() {
59 | console.debug('validateUserDB');
60 | const empty = await userDB.isEmpty();
61 | console.info('BootstrapCheck', empty);
62 | if (!empty) {
63 | await AdminModel.signup(
64 | owner.email,
65 | owner.handle,
66 | owner.fullName,
67 | owner.password);
68 | } else {
69 | console.log('Not Bootstrapped');
70 | }
71 | return true;
72 | };
73 |
74 | async bootstrap() {
75 | console.debug('bootstrap');
76 | const done = await this.validateUserDB();
77 | return await this.migrateTransclusions();
78 | };
79 |
80 | };
81 |
82 | const instance = new Bootstrap();
83 | async function bootstrap() {
84 | await instance.bootstrap();
85 | }
86 | module.exports = bootstrap;
87 |
--------------------------------------------------------------------------------
/apps/journal_database.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Datastore = require('nedb-promises')
3 |
4 | class Database {
5 | constructor() {
6 | this.db = new Datastore({ filename: './data/journal' , autoload: true });
7 | console.info(`Database ${this.db}`);
8 | }
9 |
10 | /**
11 | * Insert a journal entry
12 | * @param jsonDoc
13 | */
14 | async put(jsonDoc) {
15 | return await this.db.insert(jsonDoc);
16 | }
17 |
18 | /**
19 | * Return a journal entry identified by Testing [[Backlinks]]id
20 | * @param id
21 | */
22 | async get(id) {
23 | return await this.db.findOne({ id });
24 | }
25 |
26 | /**
27 | * Return a list of journal entries sorted on date latest on top
28 | * @param limit
29 | * @param skip
30 | */
31 | async list(limit, skip) {
32 | console.info('JnlList', limit, skip);
33 | const result = await Promise.all([this.db.find({}).sort({ date: -1 }).limit(limit).skip(skip), this.db.count({})]);
34 | //console.info('JnlList-1', result);
35 | return result;
36 | }
37 |
38 | /**
39 | * General find support
40 | * @param query json
41 | */
42 | async find(query) {
43 | console.info('JnlFind', query);
44 | return await this.db.find(query);
45 | }
46 |
47 | async updateJournalText(id, newText, isTriple) {
48 | console.info('JnlUpdateText', id, newText, isTriple);
49 | if (!isTriple) {
50 | return await this.db.update( { id}, { $set: { text: newText} },{});
51 | } else {
52 | return await this.db.update( { id}, { $set: { bodylist: newText} },{});
53 | }
54 | }
55 |
56 | /**
57 | * Find topics by URL
58 | * @param url
59 | */
60 | async findByURL(url) {
61 | return await this.find({ urllist: url });
62 | }
63 |
64 | };
65 |
66 | const instance = new Database();
67 | module.exports = instance;
68 |
--------------------------------------------------------------------------------
/apps/models/admin_model.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const userDB = require('../user_database');
3 | const uuid = require('uuid');
4 | const bcrypt = require('bcrypt-nodejs');
5 | const util = require('util');
6 |
7 | class AdminModel {
8 |
9 | constructor() {
10 | this.hash = util.promisify(bcrypt.hash);
11 | this.compare = util.promisify(bcrypt.compare);
12 | }
13 |
14 | /**
15 | * Register a new account
16 | * @param email
17 | * @param handle
18 | * @param fullName
19 | * @param password
20 | */
21 | async signup(email, handle, fullName, password) {
22 | console.info("Signup", email, handle, fullName);
23 | const hash = await this.hash(password, null, null);
24 | console.info("Signup 2", hash);
25 | const account = await userDB.saveAccount({
26 | id: uuid.v4(),
27 | pwd: hash,
28 | email,
29 | handle,
30 | fullName,
31 | });
32 | console.info("Signup 3", account);
33 | return account
34 | };
35 |
36 | /**
37 | * Authenticate a user
38 | * @param email
39 | * @param password
40 | */
41 | async authenticate(email, password) {
42 | console.log("AdminModel.authenticate", email, password);
43 | const json = await userDB.fetchAccount(email);
44 | console.log("AdminModel.authenticate-1", email, json);
45 | if (!json) {
46 | throw new Error(`No account for ${email}`);
47 | }
48 | const success = await this.compare(password, json.pwd);
49 | return {success, handle: json.handle, userId: json.id};
50 | }
51 | }
52 |
53 | const instance = new AdminModel();
54 | module.exports = instance;
55 |
--------------------------------------------------------------------------------
/apps/models/graph_model.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const topicDB = require('../topic_database');
4 |
5 | class GraphModel {
6 |
7 | ////////////////////
8 | // NodeStruct and EdgeStruct construct JSON objects
9 | // for each topic and relation, to be used by vis.js
10 | ////////////////////
11 |
12 | nodeStruct(topicId, topiclabel) {
13 | var result = {};
14 | result.id = topicId;
15 | result.label = topiclabel;
16 | result.shape = "oval";
17 | result.mass = 2;
18 | //console.info('NODE', result);
19 | return result;
20 | }
21 |
22 | edgeStruct(fromId, toId) {
23 | var result = {};
24 | result.from = fromId;
25 | result.to = toId;
26 | result.arrows = 'to';
27 | //console.info('EDGE', result);
28 | return result;
29 | }
30 |
31 | ///////////////////////
32 | // We must construct sets, not bags of nodes and edges
33 | ///////////////////////
34 | nodeArrayContains(json, array) {
35 | var len = array.length,
36 | jo;
37 | for (var i = 0; i< len; i++) {
38 | jo = array[i];
39 | if (jo.id === json.id) {
40 | return true;
41 | }
42 | }
43 | return false;
44 | };
45 |
46 | edgeArrayContains(json, array) {
47 | var len = array.length,
48 | jo;
49 | for (var i = 0; i< len; i++) {
50 | jo = array[i];
51 | if ((jo.from === json.from) &&
52 | (jo.to === json.to) ||
53 | (jo.to === json.from) &&
54 | (jo.from === json.to)) {
55 | return true;
56 | }
57 | }
58 | return false;
59 | };
60 | /////////////////////////
61 | extractLabel(url) {
62 | var result = url;
63 | var where = result.indexOf('>'); // get first > from
64 | where = result.indexOf('>', where); // get second >
65 | result = result.substring(where+1).trim();
66 | result = result.substring(0, (result.length - 4));
67 | //console.info('EXTRACT', url, result);
68 | return result;
69 | }
70 |
71 | /**
72 | * Fetch a graph for a given topic
73 | * Called from TopicModel getTopic after it fetches the topic
74 | * @param topic
75 | * @return
76 | */
77 | async fetchGraph(topic) {
78 | const topicId = topic.id;
79 | const topicLabel = topic.label;
80 | //Fetch this topics relations
81 | console.info('FetchGraph-1', topicId, topicLabel);
82 | const data = await topicDB.listRelations(topicId);
83 | console.info('FetchGraph-2', data);
84 | var result = {};
85 | var nodeListSet = [];
86 | var edgeListSet = [];
87 | //If any data, construct a graph from that
88 | if (data && data.length > 0) {
89 | var reln;
90 | var sourceId;
91 | var sourceLabel;
92 | var sourceUrl;
93 | var targetUrl;
94 | var targetId;
95 | var targetLabel;
96 | var relnType;
97 | var relnId;
98 | var node;
99 | var edge;
100 | for (var r in data) {
101 | reln = data[r];
102 | relnId = reln.id;
103 | relnType = reln.type;
104 | node = this.nodeStruct(relnId, relnType);
105 | if (!this.nodeArrayContains(node, nodeListSet)) {
106 | nodeListSet.push(node);
107 | }
108 | sourceId = reln.sourceId;
109 | targetId = reln.targetId;
110 | sourceUrl = reln.source;
111 | targetUrl = reln.target;
112 | sourceLabel = this.extractLabel(sourceUrl);
113 | targetLabel = this.extractLabel(targetUrl);
114 | node = this.nodeStruct(sourceId, sourceLabel);
115 | if (!this.nodeArrayContains(node, nodeListSet)) {
116 | nodeListSet.push(node);
117 | }
118 | node = this.nodeStruct(targetId, targetLabel);
119 | if (!this.nodeArrayContains(node, nodeListSet)) {
120 | nodeListSet.push(node);
121 | }
122 | edge = this.edgeStruct(sourceId, relnId);
123 | if (!this.edgeArrayContains(edge, edgeListSet)) {
124 | edgeListSet.push(edge);
125 | }
126 | edge = this.edgeStruct(relnId, targetId);
127 | if (!this.edgeArrayContains(edge, edgeListSet)) {
128 | edgeListSet.push(edge);
129 | }
130 | }
131 | }
132 | result.nodes = nodeListSet;
133 | result.edges = edgeListSet;
134 | return result;
135 | };
136 |
137 | }
138 |
139 | const instance = new GraphModel();
140 | module.exports = instance;
--------------------------------------------------------------------------------
/apps/models/journal_model.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | /*
3 | {"id":"foo","label":"Foo","date":{"$$date":1582755161331},"backlinks":["Foo causes Bar","Foo isA Blah"],"_id":"cxo7mAtA67NaTa06","bodylist":["Foo is a crucial topic in the domain of nonsense."]}
4 |
5 | Topic Structure
6 | {
7 | id: the node's id - can be a slug or a uuid
8 | date: date value
9 | userHandle:
10 | userId:
11 | label: the topic's label
12 | backlinks: list of backlink hrefs ** Changing to list of Journal IDs
13 | bodylist: list of text objects, each of which becomes an AIR journal entry
14 | - has been processed for wikilinks and other items
15 | - in a topic, bodylist is an array
16 | urllist: list of URLs associated with this topic
17 | sourceId: relations only
18 | targetId: relations only
19 | source: href to source (subject) relations only
20 | target: href to target (object) relations only
21 | }
22 | example topic
23 | {
24 | "id": "TOP_backlinks",
25 | "userId": "7563c26a-319b-4652-b9ea-0b8cfee34b0b",
26 | "userHandle": "Joe",
27 | "nodeType": "topic",
28 | "label": "Backlinks",
29 | "date": {
30 | "$$date": 1584130903110
31 | },
32 | "urllist": [],
33 | "backlinks": ["id
195 | * @param id
196 | */
197 | async getTopic(id) {
198 | const data = await TopicModel.getTopic(id);
199 | console.info('JnlModel.getTopic', data);
200 | return await this.populateBacklinks(data);
201 | };
202 |
203 | async ajaxFindLabel(q) {
204 | console.log('JournalAjax', q);
205 | return await TopicModel.ajaxFindLabel(q);
206 | };
207 | /**
208 | * Return a specific journal entry identified by id
209 | * @param id
210 | */
211 | async getJournalEntry(id) {
212 | console.info("NM-GJ", id);
213 | return await journalDB.get(id);
214 | };
215 |
216 | /**
217 | * Add another AIR (addressable information resource) or URL
218 | * to a topic identified by id
219 | * @param id
220 | * @param body the AIR
221 | * @param url optional
222 | */
223 | async updateTopic(id, url, body) {
224 | return await topicDB.updateTopic(id, url, body);
225 | };
226 |
227 | async updateJournalEntry(id, content, userId, userHandle, isTriple) {
228 | console.info("JournalModel.updateJournalEntry", isTriple);
229 | const {body, topiclist} = linker.resolveWikiLinks(content);
230 | const jnl = await journalDB.get(id);
231 | var notes = jnl.notes;
232 | if (notes) {
233 |
234 | }
235 | await journalDB.updateJournalText(id, body, isTriple);
236 | if (topiclist.length > 0) {
237 | console.info("updateJournal-1");
238 | await this.processTopics(topiclist, null, null, id, userId, userHandle);
239 | return;
240 | } else {
241 | return;
242 | }
243 | };
244 |
245 | async processTopics(topiclist, url, text, id, userId, userHandle) {
246 | console.info('ProcessTopics', topiclist, id, text);
247 | let json;
248 | let i;
249 | const promises = topiclist.map(
250 | (json)=> TopicModel.processTopic(
251 | json.label, json.slug, url, text,
252 | id, userId, userHandle)
253 | )
254 | await Promise.allSettled(promises);
255 | };
256 |
257 | /**
258 | * Create a new AIR - text topic
259 | * @param content which may have wikilinks
260 | * @param url optional
261 | * @param userId
262 | * @param userHandle
263 | */
264 | async newAIR(content, url, userId, userHandle) {
265 | const json = {};
266 | json.raw = content;
267 | console.info('NewAirJnl-1', content, url);
268 | const {body, topiclist} = linker.resolveWikiLinks(content);
269 | console.info('NewAirJnl-2', body, topiclist);
270 | const uid = `JNL_${uuid.v4()}`;
271 | json.id = uid;
272 | json.userId = userId;
273 | json.userHandle = userHandle;
274 | json.text = body;
275 | json.date = new Date();
276 | if (url) {
277 | const ul = [];
278 | ul.push(url);
279 | json.urllist = ul;
280 | }
281 | // we now have an AIR ready to persist
282 | // and possible a list of topics to process
283 | const dat = await journalDB.put(json);
284 | const len = topiclist.length;
285 | console.info("newAIR", dat, len, topiclist);
286 | if (len > 0) {
287 | console.info("newAir-1");
288 | await this.processTopics(topiclist, url, null, uid, userId, userHandle);
289 | return dat;
290 | } else {
291 | console.info('newAir-2');
292 | return dat;
293 | }
294 | }
295 | }
296 |
297 | const instance = new JournalModel();
298 | module.exports = instance;
299 |
--------------------------------------------------------------------------------
/apps/models/linker.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const toSlug = require('../slug');
3 |
4 | class Linker {
5 |
6 | /**
7 | * A term might be surrounded with html, e.g. ,
8 | * @param term
9 | * @returns cleaned up term
10 | */
11 | cleanTerm(term) {
12 | if (term.startsWith('<')) {
13 | let result = term;
14 | let where = result.indexOf('>');
15 | if (where > -1) {
16 | result = result.substring((where+1));
17 | where = result.indexOf('<');
18 | if (where > -1) {
19 | result = result.substring(0, where);
20 | }
21 | return result;
22 | }
23 | } else {
24 | return term;
25 | }
26 | }
27 |
28 | /**
29 | * Given a term --> topic,
30 | * create an href for it.
31 | * @param term
32 | * @return href
33 | */
34 | getHref(term, slug) {
35 |
36 | const result = `${term}`;
37 | return result;
38 | };
39 | //////////////////////////////////
40 | // This is complex:
41 | // We are walking through a block of text to be added to a
42 | // topic node. If we see a Wikilink, we must do the following:
43 | // a) convert that term to an href
44 | // b) fire up the backlink to this block of text
45 | // which technically means that the block of text must have
46 | // its own ID,
47 | // OR,it means that this entire topic represents the backlink
48 | //
49 | //////////////////////////////////
50 | /**
51 | * Given some text, look for Wikilinks.
52 | * Where found, convert those to hrefs and reconstruct the
53 | * text including the hrefs.
54 | * Returns the revised text (with hrefs, if any) and a list
55 | * of topics and their slugs found, if any
56 | * @param text
57 | */
58 | resolveWikiLinks(text) {
59 | console.info('LINKER', text);
60 | const topiclist = []; // topic is a json object with label and slug
61 | let result = "";
62 | let begin = text.indexOf("[[");
63 | if (begin > -1) {
64 | result = `${text.substring(0, begin)} `;
65 | }
66 | let end = 0;
67 | let term;
68 | let slug;
69 | let jsonT;
70 | //loop if there is any [[ found
71 | while (begin > -1) {
72 | begin += 2;
73 | //find the end
74 | end = text.indexOf("]]", begin);
75 | if (end > -1) { // end found
76 | term = text.substring(begin, end).trim();
77 | console.info('LINKER-1', begin, end, term, text);
78 | term = this.cleanTerm(term);
79 | //ALL wikilinks are to Topics
80 | slug = `TOP_${toSlug(term)}`;
81 | // add href to result
82 | result += `${this.getHref(term, slug)} `;
83 | jsonT = {};
84 | jsonT.label = term;
85 | jsonT.slug = slug;
86 | topiclist.push(jsonT);
87 | end += 2;
88 | begin = text.indexOf("[[", end);
89 | console.info('LINKER-2', begin, end, text.length, term, text);
90 | if (begin === -1 && (end) < text.length) {
91 | //add remainder, if any
92 | result += text.substring(end);
93 | } else if (begin > -1 && end < text.length) {
94 | //add gap from last end+2
95 | result += `${text.substring((end), begin)} `;
96 | }
97 | } else { // proper end not found - error condition
98 | throw new Error("Open Wikilink with improper or no Closing Wikilink-missing ]]");
99 | }
100 | }
101 | if (result === "") { // if nothing found, just return the text
102 | result = text;
103 | }
104 | return {body: result.trim(), topiclist};
105 | };
106 |
107 | /**
108 | * Craft a triple for viewing as 3 hrefs
109 | * @param subject
110 | * @param sSlug
111 | * @param object
112 | * @param oSlug
113 | * @param predicate
114 | * @param pSlug
115 | * @return
116 | */
117 | setHrefs(subject, sSlug, object, oSlug, predicate, pSlug) {
118 | let result = "";
119 | const sHref = `${subject}`;
120 | const oHref = `${object}`;
121 | const pHref = `${predicate}`;
122 | result += `${sHref} `;
123 | result += `${pHref} `;
124 | result += ` ${oHref}`;
125 | return result;
126 | };
127 |
128 | };
129 |
130 | const instance = new Linker();
131 | module.exports = instance;
132 |
--------------------------------------------------------------------------------
/apps/models/topic_model.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const topicDB = require('../topic_database');
3 | const graphModel = require('./graph_model');
4 |
5 | class TopicModel {
6 |
7 | /**
8 | * Add another AIR (addressable information resource) or URL
9 | * to a topic identified by id
10 | * @param id
11 | * @param body the AIR
12 | * @param url optional
13 | */
14 | async updateTopic(id, url, body) {
15 | console.info('UpdateTopic', id, url, body);
16 | //fetch the topic
17 | const data = await topicDB.get(id);
18 | console.info('UpdateTopic-1', data);
19 | //update the topic using treating arrays as sets (no duplicates)
20 | let somelist;
21 | let madeChanges = false;
22 | if (url) {
23 | somelist = data.urllist;
24 | if (!somelist) {
25 | somelist = [];
26 | somelist.push(url);
27 | madeChanges = true;
28 | } else if (!somelist.includes(url)) {
29 | madeChanges = true;
30 | somelist.push(url);
31 | }
32 | data.urllist = somelist;
33 | }
34 | if (body) {
35 | somelist = data.bodylist;
36 | if (!somelist) {
37 | somelist = [];
38 | somelist.push(body);
39 | madeChanges = true;
40 | }
41 | else if (!somelist.includes(body)) {
42 | somelist.push(body);
43 | madeChanges = true;
44 | }
45 | data.bodylist = somelist;
46 | }
47 | if (madeChanges) {
48 | console.info('UpdateTopic-3', data);
49 | //delete the old one
50 | const numRemoved = await topicDB.delete(data._id);
51 | console.info('RemTopic', id, numRemoved);
52 | // insert the new one
53 | const dat = await topicDB.put(data);
54 | console.info('UpdateTopic-4', dat);
55 | //await topicDB.compact();
56 | //NO: compacting messes up backlinks - not sure why
57 | return dat;
58 | }
59 | }
60 |
61 | /**
62 | * Process a term which is a topic
63 | * either make a new node from that term if not exists
64 | * else add backlink to it with the content and its id
65 | * @param term
66 | * @param slug
67 | * @param url
68 | * @param content of the journal entry
69 | * @param id of the journal entry
70 | * @param userId
71 | * @param userHandle
72 | */
73 | async processTopic(term, slug, url, content, id, userId, userHandle) {
74 | console.info("ProcessTopic", term, slug, '|', content, '|', url, id);
75 | const data = await topicDB.get(slug);
76 | console.info('ProcessTopic-1', data);
77 | if (data) {
78 | console.info('ProcessTopic-1a', content);
79 | if (content) {
80 | await this.updateTopic(slug, url, content);
81 | await topicDB.addBacklink(slug, id);
82 | } else {
83 | await topicDB.addBacklink(slug, id);
84 | }
85 | } else {
86 | const json = {};
87 | if (slug.startsWith('TOP')) {
88 | json.id = slug;
89 | } else {
90 | json.id = `TOP_${slug}`;
91 | }
92 | json.userId = userId;
93 | json.userHandle = userHandle;
94 | json.nodeType = 'topic';
95 | json.label = term;
96 | json.date = new Date();
97 | json.urllist = [];
98 | if (url) {
99 | json.urllist.push(url);
100 | }
101 | json.backlinks = [];
102 | json.backlinks.push(id);
103 | const dat = await topicDB.put(json);
104 | console.info('ProceessTopic-2', dat);
105 | }
106 | };
107 |
108 | /**
109 | * Process a term which is a predicate - also a topic
110 | * @param predicate
111 | * @param predicateSlug
112 | * @param type the predicate type - not its slug
113 | * @param subject
114 | * @param subjectSlug
115 | * @param object
116 | * @param objectSlug
117 | * @param url
118 | * @param content the journal entry itself
119 | * @param id the journal entry id
120 | * @param userId
121 | * @param userHandle
122 | */
123 | async processPredicate(predicate, predicateSlug, type,
124 | subject, subjectSlug,
125 | object, objectSlug,
126 | url,
127 | content, id,
128 | userId, userHandle) {
129 | console.info("ProcessPredicate", predicate, predicateSlug, url, id);
130 | const data = await topicDB.get(predicateSlug);
131 | console.info('ProcessPredicate-1', data);
132 | if (data) {
133 | await this.updateTopic(predicateSlug, url, content);
134 | await topicDB.addBacklink(predicateSlug, id);
135 | } else {
136 | const json = {};
137 | if (predicateSlug.startsWith('TOP')) {
138 | json.id = predicateSlug;
139 | } else {
140 | json.id = `TOP_${predicateSlug}`;
141 | }
142 | json.userId = userId;
143 | json.userHandle = userHandle;
144 | json.nodeType = 'relation';
145 | json.type = type;
146 | json.label = predicate;
147 | json.date = new Date();
148 | json.source = `${subject}`
149 | json.target = `${object}`
150 | json.sourceId = subjectSlug;
151 | json.targetId = objectSlug;
152 | json.backlinks = [];
153 | json.urllist = [];
154 | if (url) {
155 | json.urllist.push(url);
156 | }
157 |
158 | json.backlinks.push(id);
159 | const dat = await topicDB.put(json);
160 | console.info('ProcessPredicate-2', dat);
161 | }
162 | };
163 |
164 | async ajaxFindLabel(q) {
165 |
166 | const rx = new RegExp(q, 'i');
167 | console.info("TMajax", rx);
168 | const docs = await topicDB.find({ label: { $regex: rx } });
169 | console.info('AjaxFind', docs);
170 | return {
171 | options: docs.map((doc)=>doc.label)
172 | }
173 | };
174 |
175 | /**
176 | * List topics
177 | * @param limit
178 | * @param skip
179 | */
180 | async listTopics(limit, skip) {
181 | const docs = await topicDB.list(limit, skip);
182 | console.info("TopicModel.listTopics", docs);
183 | return docs;
184 | };
185 |
186 | async getTopic(topicId) {
187 | const data = await topicDB.get(topicId);
188 | const graph = await graphModel.fetchGraph(data);
189 | data.graph = JSON.stringify(graph);
190 | return data;
191 | }
192 |
193 |
194 | }
195 |
196 | const instance = new TopicModel();
197 | module.exports = instance;
198 |
--------------------------------------------------------------------------------
/apps/slug.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | /**A utility file to convert a term to a slug */
3 | const slugify = require('slugify');
4 |
5 | function toSlug(term) {
6 | // by making it lower case, we trap the same term
7 | // no matter whether caps are involved.
8 | var re = new RegExp("'", "g"); // remove apostrophies from text
9 | const tx = term.trim()
10 | .toLowerCase()
11 | .replace(re, "_");
12 | return slugify(tx, '_');
13 | };
14 |
15 | module.exports = toSlug;
16 |
--------------------------------------------------------------------------------
/apps/topic_database.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Datastore = require('nedb-promises');
3 |
4 | class Database {
5 | constructor() {
6 | this.db = new Datastore({ filename: './data/topics' , autoload: true });
7 | console.info(`Database ${this.db}`);
8 | }
9 |
10 | /**
11 | * Insert a topic
12 | * @param jsonDoc the topic
13 | */
14 | async put(jsonDoc) {
15 | return await this.db.insert(jsonDoc);
16 | };
17 |
18 | /**
19 | * @param id
20 | * @parm backlink
21 | */
22 | async addBacklink(id, backlink) {
23 | return await this.db.update({ id }, { $push: { backlinks: backlink } });
24 | };
25 |
26 | /**
27 | * For compacting as needed
28 | */
29 | async compact() {
30 | return await this.db.persistence.compactDatafile();
31 | };
32 |
33 | /**
34 | * Remove a topic identified by _id
35 | * @param _id
36 | */
37 | async delete(_id) {
38 | return await this.db.remove({ _id });
39 | };
40 |
41 | /**
42 | * @param id
43 | */
44 | async get(id) {
45 | return await this.db.findOne({ id });
46 | };
47 |
48 | /**
49 | * General find support
50 | * @param query json
51 | */
52 | async find(query) {
53 | console.info('TDB', query);
54 | return await this.db.find(query);
55 | };
56 |
57 | /**
58 | * Find topics by URL
59 | * @param url
60 | */
61 | async findByURL(url) {
62 | return await this.find({ url });
63 | };
64 |
65 | /**
66 | * Replace a topic
67 | * @param newTopic
68 | */
69 | async replaceBacklinks(_id, backlinks) {
70 | numRep = await this.db.update({ _id }, {$set:{ backlinks }});
71 | console.info('ReplaceBacklinks', _id, backlinks, numRep);
72 | await this.db.persistence.compactDatafile();
73 | return numRep;
74 | };
75 |
76 | /**
77 | * Add in either {@code url}, or {@code body} or both
78 | * @param url can be {@code null}
79 | * @param body can be {@code null}
80 | */
81 | async updateTopic(id, url, body) {
82 | //TODO rewrite this to avoid duplicates
83 | if (url) {
84 | await this.db.update({ id }, { $push: { urllist: url } }, {});
85 | await this.db.update({ id }, { $push: { bodylist: body } }, {});
86 | } else if (body) {
87 | await this.db.update({ id }, { $push: { bodylist: body } }, {});
88 | } else {
89 | throw new Error('TopicDatabas.updateTopic got nothing ', id);
90 | }
91 | };
92 |
93 | /**
94 | * Return a list of topics sorted on label
95 | * @param limit
96 | * @param skip
97 | */
98 | async list(limit, skip) {
99 | console.info('TopList', limit, skip);
100 |
101 | const result = await Promise.all([this.db.find({}).sort({ label: 1 }).limit(limit).skip(skip), this.db.count({})]);
102 |
103 | return result;
104 | };
105 |
106 | /**
107 | * Return a list of relations for a given topic
108 | * @param topicId
109 | */
110 | async listRelations(topicId) {
111 | return await this.db.find({ nodeType: 'relation', $or: [{ sourceId: topicId }, { targetId: topicId }]});
112 | };
113 |
114 | };
115 |
116 | const instance = new Database();
117 | module.exports = instance;
118 |
--------------------------------------------------------------------------------
/apps/user_database.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Datastore = require('nedb-promises');
3 |
4 | class UserDatabase {
5 | constructor() {
6 | this.db = new Datastore({ filename: './data/users' , autoload: true });
7 | console.info(`Database ${this.db}`);
8 | }
9 |
10 | /**
11 | * Persist an account
12 | * @param struct
13 | */
14 | async saveAccount(struct) {
15 | return await this.db.insert(struct);
16 | };
17 |
18 | /**
19 | * Report {@code true} if database is empty
20 | */
21 | async isEmpty() {
22 | const doc = await this.db.find({});
23 | console.info("UserEmpty", doc);
24 | return (doc && doc.length > 0);
25 | };
26 |
27 | /**
28 | * Return an account or {@code null}
29 | * @param email
30 | */
31 | async fetchAccount(email) {
32 | return await this.db.findOne({ email });
33 | }
34 | }
35 |
36 | const instance = new UserDatabase();
37 | module.exports = instance;
38 |
--------------------------------------------------------------------------------
/bin/www:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | "use strict"
3 |
4 | /**
5 | * Module dependencies.
6 | */
7 |
8 | const app = require('../app');
9 | const bootstrap = require('../apps/bootstrap');
10 | const debug = require('debug')('lite-net:server');
11 | const http = require('http');
12 |
13 | /**
14 | * Get port from environment and store in Express.
15 | */
16 |
17 | const port = normalizePort(process.env.PORT || '4000');
18 | app.set('port', port);
19 |
20 | /**
21 | * Create HTTP server.
22 | */
23 | let server;
24 | bootstrap().then(()=>{
25 | server = http.createServer(app);
26 |
27 | /**
28 | * Listen on provided port, on all network interfaces.
29 | */
30 |
31 | server.listen(port);
32 | server.on('error', onError);
33 | server.on('listening', onListening);
34 | })
35 |
36 | /**
37 | * Normalize a port into a number, string, or false.
38 | */
39 |
40 | function normalizePort(val) {
41 | const port = parseInt(val, 10);
42 |
43 | if (isNaN(port)) {
44 | // named pipe
45 | return val;
46 | }
47 |
48 | if (port >= 0) {
49 | // port number
50 | return port;
51 | }
52 |
53 | return false;
54 | }
55 |
56 | /**
57 | * Event listener for HTTP server "error" event.
58 | */
59 |
60 | function onError(error) {
61 | if (error.syscall !== 'listen') {
62 | throw error;
63 | }
64 |
65 | const bind = typeof port === 'string'
66 | ? `Pipe ${port}`
67 | : `Port ${port}`;
68 |
69 | // handle specific listen errors with friendly messages
70 | switch (error.code) {
71 | case 'EACCES':
72 | console.error(`${bind} requires elevated privileges`);
73 | process.exit(1);
74 | break;
75 | case 'EADDRINUSE':
76 | console.error(`${bind} is already in use`);
77 | process.exit(1);
78 | break;
79 | default:
80 | throw error;
81 | }
82 | }
83 |
84 | /**
85 | * Event listener for HTTP server "listening" event.
86 | */
87 |
88 | function onListening() {
89 | const addr = server.address();
90 | const bind = typeof addr === 'string'
91 | ? `pipe ${addr}`
92 | : `port ${addr.port}`;
93 | debug(`Listening on ${bind}`);
94 | }
95 |
--------------------------------------------------------------------------------
/config/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "banner": "LiteNet-3",
3 | "vocabulary": "biomed",
4 | "canSignup": false,
5 | "isPrivatePortal": false
6 | }
--------------------------------------------------------------------------------
/config/owner.json:
--------------------------------------------------------------------------------
1 | {
2 | "email": "joe@sixpack.com",
3 | "fullName": "Joe Sixpack",
4 | "handle": "Joe",
5 | "password": "joe"
6 | }
--------------------------------------------------------------------------------
/config/vocab/biomed/defns.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/config/vocab/biomed/labels.json:
--------------------------------------------------------------------------------
1 | { "terms": [
2 | "absorb",
3 | "acetylate",
4 | "act as",
5 | "activate",
6 | "affect",
7 | "affect positive",
8 | "affect negative",
9 | "alkalize",
10 | "alter",
11 | "anthropomorphize",
12 | "associated with",
13 | "bind to",
14 | "block",
15 | "can elevate",
16 | "catalyze",
17 | "not catalyze",
18 | "cause",
19 | "cleave",
20 | "conjugate",
21 | "conjugated by",
22 | "contain",
23 | "control",
24 | "deactivate",
25 | "decrease",
26 | "defined as",
27 | "destroy",
28 | "disrupt",
29 | "differentiate",
30 | "down regulate",
31 | "essential for",
32 | "express",
33 | "expressed in",
34 | "has acronym",
35 | "has part",
36 | "has synonym",
37 | "hydrolise",
38 | "improve",
39 | "inactivate",
40 | "incorporate",
41 | "increase",
42 | "induce",
43 | "inhibit",
44 | "is a",
45 | "linked to",
46 | "located at",
47 | "located in",
48 | "mediate",
49 | "move",
50 | "neutralize",
51 | "not absorb",
52 | "not affect",
53 | "not associated with",
54 | "not bind to",
55 | "not block",
56 | "not conjugated by",
57 | "not contain",
58 | "not decrease",
59 | "not hydrolise",
60 | "not increase",
61 | "not induce",
62 | "not inhibit",
63 | "not linked to",
64 | "not oxygenate",
65 | "not polymerize",
66 | "not prevent",
67 | "not promote",
68 | "not reduce",
69 | "not regulate",
70 | "not require",
71 | "not same as",
72 | "not suppress",
73 | "oxydize",
74 | "oxygenate",
75 | "phosphorylate",
76 | "polymerize",
77 | "prevent",
78 | "produce",
79 | "promote",
80 | "reduce",
81 | "regulate",
82 | "require",
83 | "same as",
84 | "suppress",
85 | "synthesize",
86 | "transport",
87 | "trigger",
88 | "up regulate",
89 | "use",
90 | "weaken",
91 | "what",
92 | "who",
93 | "where",
94 | "when",
95 | "inform",
96 | "determine",
97 | "address",
98 | "lead",
99 | "lag",
100 | "suggest",
101 | "contradict",
102 | "agree with",
103 | "question",
104 | "influence",
105 | "threaten",
106 | "predict",
107 | "mediate",
108 | "complement",
109 | "own",
110 | "use",
111 | "risk",
112 | "confound",
113 | "measure",
114 | "relevant",
115 | "equivalent",
116 | "consistent with",
117 | "inconsistent with",
118 | "support",
119 | "object",
120 | "challenge"
121 |
122 | ]
123 | }
124 |
--------------------------------------------------------------------------------
/config/vocab/climate/defns.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KnowledgeGarden/lite-net-3/92d9e8053fb95e06e2a325eb25a775435dc86c00/config/vocab/climate/defns.json
--------------------------------------------------------------------------------
/config/vocab/climate/labels.json:
--------------------------------------------------------------------------------
1 | {
2 | "terms": [
3 | "causes",
4 | "is a",
5 | "emits",
6 | "releases",
7 | "absorbs",
8 | "who",
9 | "where",
10 | "when",
11 | "inform",
12 | "determine",
13 | "address",
14 | "lead",
15 | "lag",
16 | "suggest",
17 | "contradict",
18 | "agree with",
19 | "question",
20 | "influence",
21 | "threaten",
22 | "predict",
23 | "mediate",
24 | "complement",
25 | "own",
26 | "use",
27 | "risk",
28 | "confound",
29 | "measure",
30 | "relevant",
31 | "equivalent",
32 | "consistent with",
33 | "inconsistent with",
34 | "support",
35 | "object",
36 | "challenge"
37 | ]
38 | }
--------------------------------------------------------------------------------
/config/vocab/vocab.md:
--------------------------------------------------------------------------------
1 | # Predicate Vocabulary Plugins
2 | This directory defines a space in which new predicate vocabularies can be defined. The _climate_ vocabulary is a shell for now.
3 |
4 | The _biomed_ vocabulary is currently being developed.
5 |
6 | The particular vocabulary to be used is defined in the /config/config.json file.
7 |
8 | Each vocabular directory contains two files:
9 | * defns.json in which _topics_ are defined as JSON objects to be imported into the topic database.
10 | * labels.json in which the labels for each predicate are made available to the _triple forms_ for selecting predicates.
11 |
--------------------------------------------------------------------------------
/data/.keep:
--------------------------------------------------------------------------------
1 | keep round if empty
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lite-net",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "node ./bin/www"
7 | },
8 | "dependencies": {
9 | "bcrypt-nodejs": "0.0.3",
10 | "connect-flash": "^0.1.1",
11 | "cookie-parser": "~1.4.4",
12 | "debug": "~2.6.9",
13 | "express": "~4.16.1",
14 | "express-session": "^1.17.0",
15 | "hbs": "^4.1.0",
16 | "http-errors": "~1.6.3",
17 | "jquery-typeahead": "^2.11.0",
18 | "morgan": "~1.9.1",
19 | "nedb-promises": "^4.0.1",
20 | "slugify": "^1.3.6",
21 | "uuid": "^7.0.1"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/public/css/bootstrap-theme.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.3.5 (http://getbootstrap.com)
3 | * Copyright 2011-2015 Twitter, Inc.
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5 | */
6 | .btn-default,
7 | .btn-primary,
8 | .btn-success,
9 | .btn-info,
10 | .btn-warning,
11 | .btn-danger {
12 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
13 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
14 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
15 | }
16 | .btn-default:active,
17 | .btn-primary:active,
18 | .btn-success:active,
19 | .btn-info:active,
20 | .btn-warning:active,
21 | .btn-danger:active,
22 | .btn-default.active,
23 | .btn-primary.active,
24 | .btn-success.active,
25 | .btn-info.active,
26 | .btn-warning.active,
27 | .btn-danger.active {
28 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
29 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
30 | }
31 | .btn-default.disabled,
32 | .btn-primary.disabled,
33 | .btn-success.disabled,
34 | .btn-info.disabled,
35 | .btn-warning.disabled,
36 | .btn-danger.disabled,
37 | .btn-default[disabled],
38 | .btn-primary[disabled],
39 | .btn-success[disabled],
40 | .btn-info[disabled],
41 | .btn-warning[disabled],
42 | .btn-danger[disabled],
43 | fieldset[disabled] .btn-default,
44 | fieldset[disabled] .btn-primary,
45 | fieldset[disabled] .btn-success,
46 | fieldset[disabled] .btn-info,
47 | fieldset[disabled] .btn-warning,
48 | fieldset[disabled] .btn-danger {
49 | -webkit-box-shadow: none;
50 | box-shadow: none;
51 | }
52 | .btn-default .badge,
53 | .btn-primary .badge,
54 | .btn-success .badge,
55 | .btn-info .badge,
56 | .btn-warning .badge,
57 | .btn-danger .badge {
58 | text-shadow: none;
59 | }
60 | .btn:active,
61 | .btn.active {
62 | background-image: none;
63 | }
64 | .btn-default {
65 | text-shadow: 0 1px 0 #fff;
66 | background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
67 | background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);
68 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));
69 | background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
70 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
71 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
72 | background-repeat: repeat-x;
73 | border-color: #dbdbdb;
74 | border-color: #ccc;
75 | }
76 | .btn-default:hover,
77 | .btn-default:focus {
78 | background-color: #e0e0e0;
79 | background-position: 0 -15px;
80 | }
81 | .btn-default:active,
82 | .btn-default.active {
83 | background-color: #e0e0e0;
84 | border-color: #dbdbdb;
85 | }
86 | .btn-default.disabled,
87 | .btn-default[disabled],
88 | fieldset[disabled] .btn-default,
89 | .btn-default.disabled:hover,
90 | .btn-default[disabled]:hover,
91 | fieldset[disabled] .btn-default:hover,
92 | .btn-default.disabled:focus,
93 | .btn-default[disabled]:focus,
94 | fieldset[disabled] .btn-default:focus,
95 | .btn-default.disabled.focus,
96 | .btn-default[disabled].focus,
97 | fieldset[disabled] .btn-default.focus,
98 | .btn-default.disabled:active,
99 | .btn-default[disabled]:active,
100 | fieldset[disabled] .btn-default:active,
101 | .btn-default.disabled.active,
102 | .btn-default[disabled].active,
103 | fieldset[disabled] .btn-default.active {
104 | background-color: #e0e0e0;
105 | background-image: none;
106 | }
107 | .btn-primary {
108 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
109 | background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
110 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
111 | background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
112 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
113 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
114 | background-repeat: repeat-x;
115 | border-color: #245580;
116 | }
117 | .btn-primary:hover,
118 | .btn-primary:focus {
119 | background-color: #265a88;
120 | background-position: 0 -15px;
121 | }
122 | .btn-primary:active,
123 | .btn-primary.active {
124 | background-color: #265a88;
125 | border-color: #245580;
126 | }
127 | .btn-primary.disabled,
128 | .btn-primary[disabled],
129 | fieldset[disabled] .btn-primary,
130 | .btn-primary.disabled:hover,
131 | .btn-primary[disabled]:hover,
132 | fieldset[disabled] .btn-primary:hover,
133 | .btn-primary.disabled:focus,
134 | .btn-primary[disabled]:focus,
135 | fieldset[disabled] .btn-primary:focus,
136 | .btn-primary.disabled.focus,
137 | .btn-primary[disabled].focus,
138 | fieldset[disabled] .btn-primary.focus,
139 | .btn-primary.disabled:active,
140 | .btn-primary[disabled]:active,
141 | fieldset[disabled] .btn-primary:active,
142 | .btn-primary.disabled.active,
143 | .btn-primary[disabled].active,
144 | fieldset[disabled] .btn-primary.active {
145 | background-color: #265a88;
146 | background-image: none;
147 | }
148 | .btn-success {
149 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
150 | background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
151 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
152 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
153 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
154 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
155 | background-repeat: repeat-x;
156 | border-color: #3e8f3e;
157 | }
158 | .btn-success:hover,
159 | .btn-success:focus {
160 | background-color: #419641;
161 | background-position: 0 -15px;
162 | }
163 | .btn-success:active,
164 | .btn-success.active {
165 | background-color: #419641;
166 | border-color: #3e8f3e;
167 | }
168 | .btn-success.disabled,
169 | .btn-success[disabled],
170 | fieldset[disabled] .btn-success,
171 | .btn-success.disabled:hover,
172 | .btn-success[disabled]:hover,
173 | fieldset[disabled] .btn-success:hover,
174 | .btn-success.disabled:focus,
175 | .btn-success[disabled]:focus,
176 | fieldset[disabled] .btn-success:focus,
177 | .btn-success.disabled.focus,
178 | .btn-success[disabled].focus,
179 | fieldset[disabled] .btn-success.focus,
180 | .btn-success.disabled:active,
181 | .btn-success[disabled]:active,
182 | fieldset[disabled] .btn-success:active,
183 | .btn-success.disabled.active,
184 | .btn-success[disabled].active,
185 | fieldset[disabled] .btn-success.active {
186 | background-color: #419641;
187 | background-image: none;
188 | }
189 | .btn-info {
190 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
191 | background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
192 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
193 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
194 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
195 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
196 | background-repeat: repeat-x;
197 | border-color: #28a4c9;
198 | }
199 | .btn-info:hover,
200 | .btn-info:focus {
201 | background-color: #2aabd2;
202 | background-position: 0 -15px;
203 | }
204 | .btn-info:active,
205 | .btn-info.active {
206 | background-color: #2aabd2;
207 | border-color: #28a4c9;
208 | }
209 | .btn-info.disabled,
210 | .btn-info[disabled],
211 | fieldset[disabled] .btn-info,
212 | .btn-info.disabled:hover,
213 | .btn-info[disabled]:hover,
214 | fieldset[disabled] .btn-info:hover,
215 | .btn-info.disabled:focus,
216 | .btn-info[disabled]:focus,
217 | fieldset[disabled] .btn-info:focus,
218 | .btn-info.disabled.focus,
219 | .btn-info[disabled].focus,
220 | fieldset[disabled] .btn-info.focus,
221 | .btn-info.disabled:active,
222 | .btn-info[disabled]:active,
223 | fieldset[disabled] .btn-info:active,
224 | .btn-info.disabled.active,
225 | .btn-info[disabled].active,
226 | fieldset[disabled] .btn-info.active {
227 | background-color: #2aabd2;
228 | background-image: none;
229 | }
230 | .btn-warning {
231 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
232 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
233 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
234 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
235 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
236 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
237 | background-repeat: repeat-x;
238 | border-color: #e38d13;
239 | }
240 | .btn-warning:hover,
241 | .btn-warning:focus {
242 | background-color: #eb9316;
243 | background-position: 0 -15px;
244 | }
245 | .btn-warning:active,
246 | .btn-warning.active {
247 | background-color: #eb9316;
248 | border-color: #e38d13;
249 | }
250 | .btn-warning.disabled,
251 | .btn-warning[disabled],
252 | fieldset[disabled] .btn-warning,
253 | .btn-warning.disabled:hover,
254 | .btn-warning[disabled]:hover,
255 | fieldset[disabled] .btn-warning:hover,
256 | .btn-warning.disabled:focus,
257 | .btn-warning[disabled]:focus,
258 | fieldset[disabled] .btn-warning:focus,
259 | .btn-warning.disabled.focus,
260 | .btn-warning[disabled].focus,
261 | fieldset[disabled] .btn-warning.focus,
262 | .btn-warning.disabled:active,
263 | .btn-warning[disabled]:active,
264 | fieldset[disabled] .btn-warning:active,
265 | .btn-warning.disabled.active,
266 | .btn-warning[disabled].active,
267 | fieldset[disabled] .btn-warning.active {
268 | background-color: #eb9316;
269 | background-image: none;
270 | }
271 | .btn-danger {
272 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
273 | background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
274 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
275 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
276 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
277 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
278 | background-repeat: repeat-x;
279 | border-color: #b92c28;
280 | }
281 | .btn-danger:hover,
282 | .btn-danger:focus {
283 | background-color: #c12e2a;
284 | background-position: 0 -15px;
285 | }
286 | .btn-danger:active,
287 | .btn-danger.active {
288 | background-color: #c12e2a;
289 | border-color: #b92c28;
290 | }
291 | .btn-danger.disabled,
292 | .btn-danger[disabled],
293 | fieldset[disabled] .btn-danger,
294 | .btn-danger.disabled:hover,
295 | .btn-danger[disabled]:hover,
296 | fieldset[disabled] .btn-danger:hover,
297 | .btn-danger.disabled:focus,
298 | .btn-danger[disabled]:focus,
299 | fieldset[disabled] .btn-danger:focus,
300 | .btn-danger.disabled.focus,
301 | .btn-danger[disabled].focus,
302 | fieldset[disabled] .btn-danger.focus,
303 | .btn-danger.disabled:active,
304 | .btn-danger[disabled]:active,
305 | fieldset[disabled] .btn-danger:active,
306 | .btn-danger.disabled.active,
307 | .btn-danger[disabled].active,
308 | fieldset[disabled] .btn-danger.active {
309 | background-color: #c12e2a;
310 | background-image: none;
311 | }
312 | .thumbnail,
313 | .img-thumbnail {
314 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
315 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
316 | }
317 | .dropdown-menu > li > a:hover,
318 | .dropdown-menu > li > a:focus {
319 | background-color: #e8e8e8;
320 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
321 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
322 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
323 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
324 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
325 | background-repeat: repeat-x;
326 | }
327 | .dropdown-menu > .active > a,
328 | .dropdown-menu > .active > a:hover,
329 | .dropdown-menu > .active > a:focus {
330 | background-color: #2e6da4;
331 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
332 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
333 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
334 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
335 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
336 | background-repeat: repeat-x;
337 | }
338 | .navbar-default {
339 | background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
340 | background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%);
341 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8));
342 | background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
343 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
344 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
345 | background-repeat: repeat-x;
346 | border-radius: 4px;
347 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
348 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
349 | }
350 | .navbar-default .navbar-nav > .open > a,
351 | .navbar-default .navbar-nav > .active > a {
352 | background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
353 | background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
354 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
355 | background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
356 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
357 | background-repeat: repeat-x;
358 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
359 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
360 | }
361 | .navbar-brand,
362 | .navbar-nav > li > a {
363 | text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
364 | }
365 | .navbar-inverse {
366 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
367 | background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);
368 | background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));
369 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
370 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
371 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
372 | background-repeat: repeat-x;
373 | border-radius: 4px;
374 | }
375 | .navbar-inverse .navbar-nav > .open > a,
376 | .navbar-inverse .navbar-nav > .active > a {
377 | background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
378 | background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
379 | background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
380 | background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
381 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
382 | background-repeat: repeat-x;
383 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
384 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
385 | }
386 | .navbar-inverse .navbar-brand,
387 | .navbar-inverse .navbar-nav > li > a {
388 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
389 | }
390 | .navbar-static-top,
391 | .navbar-fixed-top,
392 | .navbar-fixed-bottom {
393 | border-radius: 0;
394 | }
395 | @media (max-width: 767px) {
396 | .navbar .navbar-nav .open .dropdown-menu > .active > a,
397 | .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
398 | .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
399 | color: #fff;
400 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
401 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
402 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
403 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
404 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
405 | background-repeat: repeat-x;
406 | }
407 | }
408 | .alert {
409 | text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
410 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
411 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
412 | }
413 | .alert-success {
414 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
415 | background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
416 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
417 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
418 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
419 | background-repeat: repeat-x;
420 | border-color: #b2dba1;
421 | }
422 | .alert-info {
423 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
424 | background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
425 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
426 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
427 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
428 | background-repeat: repeat-x;
429 | border-color: #9acfea;
430 | }
431 | .alert-warning {
432 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
433 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
434 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
435 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
436 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
437 | background-repeat: repeat-x;
438 | border-color: #f5e79e;
439 | }
440 | .alert-danger {
441 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
442 | background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
443 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
444 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
445 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
446 | background-repeat: repeat-x;
447 | border-color: #dca7a7;
448 | }
449 | .progress {
450 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
451 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
452 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
453 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
454 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
455 | background-repeat: repeat-x;
456 | }
457 | .progress-bar {
458 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
459 | background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
460 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
461 | background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
462 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
463 | background-repeat: repeat-x;
464 | }
465 | .progress-bar-success {
466 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
467 | background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
468 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
469 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
470 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
471 | background-repeat: repeat-x;
472 | }
473 | .progress-bar-info {
474 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
475 | background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
476 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
477 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
478 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
479 | background-repeat: repeat-x;
480 | }
481 | .progress-bar-warning {
482 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
483 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
484 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
485 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
486 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
487 | background-repeat: repeat-x;
488 | }
489 | .progress-bar-danger {
490 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
491 | background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
492 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
493 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
494 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
495 | background-repeat: repeat-x;
496 | }
497 | .progress-bar-striped {
498 | 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);
499 | 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);
500 | 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);
501 | }
502 | .list-group {
503 | border-radius: 4px;
504 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
505 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
506 | }
507 | .list-group-item.active,
508 | .list-group-item.active:hover,
509 | .list-group-item.active:focus {
510 | text-shadow: 0 -1px 0 #286090;
511 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
512 | background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
513 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
514 | background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
515 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
516 | background-repeat: repeat-x;
517 | border-color: #2b669a;
518 | }
519 | .list-group-item.active .badge,
520 | .list-group-item.active:hover .badge,
521 | .list-group-item.active:focus .badge {
522 | text-shadow: none;
523 | }
524 | .panel {
525 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
526 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
527 | }
528 | .panel-default > .panel-heading {
529 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
530 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
531 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
532 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
533 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
534 | background-repeat: repeat-x;
535 | }
536 | .panel-primary > .panel-heading {
537 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
538 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
539 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
540 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
541 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
542 | background-repeat: repeat-x;
543 | }
544 | .panel-success > .panel-heading {
545 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
546 | background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
547 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
548 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
549 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
550 | background-repeat: repeat-x;
551 | }
552 | .panel-info > .panel-heading {
553 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
554 | background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
555 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
556 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
557 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
558 | background-repeat: repeat-x;
559 | }
560 | .panel-warning > .panel-heading {
561 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
562 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
563 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
564 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
565 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
566 | background-repeat: repeat-x;
567 | }
568 | .panel-danger > .panel-heading {
569 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
570 | background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
571 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
572 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
573 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
574 | background-repeat: repeat-x;
575 | }
576 | .well {
577 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
578 | background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
579 | background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
580 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
581 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
582 | background-repeat: repeat-x;
583 | border-color: #dcdcdc;
584 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
585 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
586 | }
587 | /*# sourceMappingURL=bootstrap-theme.css.map */
588 |
--------------------------------------------------------------------------------
/public/css/bootstrap-theme.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.3.5 (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.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.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.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{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.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{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.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{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.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{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.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{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.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{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;border-radius:4px}.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/default.css:
--------------------------------------------------------------------------------
1 | .medium-toolbar-arrow-under:after {
2 | border-color: #242424 transparent transparent transparent;
3 | top: 50px; }
4 |
5 | .medium-toolbar-arrow-over:before {
6 | border-color: transparent transparent #242424 transparent;
7 | top: -8px; }
8 |
9 | .medium-editor-toolbar {
10 | background-color: #242424;
11 | background: -webkit-linear-gradient(top, #242424, rgba(36, 36, 36, 0.75));
12 | background: linear-gradient(to bottom, #242424, rgba(36, 36, 36, 0.75));
13 | border: 1px solid #000;
14 | border-radius: 5px;
15 | box-shadow: 0 0 3px #000; }
16 | .medium-editor-toolbar li button {
17 | background-color: #242424;
18 | background: -webkit-linear-gradient(top, #242424, rgba(36, 36, 36, 0.89));
19 | background: linear-gradient(to bottom, #242424, rgba(36, 36, 36, 0.89));
20 | border: 0;
21 | border-right: 1px solid #000;
22 | border-left: 1px solid #333;
23 | border-left: 1px solid rgba(255, 255, 255, 0.1);
24 | box-shadow: 0 2px 2px rgba(0, 0, 0, 0.3);
25 | color: #fff;
26 | height: 50px;
27 | min-width: 50px;
28 | -webkit-transition: background-color .2s ease-in;
29 | transition: background-color .2s ease-in; }
30 | .medium-editor-toolbar li button:hover {
31 | background-color: #000;
32 | color: yellow; }
33 | .medium-editor-toolbar li .medium-editor-button-first {
34 | border-bottom-left-radius: 5px;
35 | border-top-left-radius: 5px; }
36 | .medium-editor-toolbar li .medium-editor-button-last {
37 | border-bottom-right-radius: 5px;
38 | border-top-right-radius: 5px; }
39 | .medium-editor-toolbar li .medium-editor-button-active {
40 | background-color: #000;
41 | background: -webkit-linear-gradient(top, #242424, rgba(0, 0, 0, 0.89));
42 | background: linear-gradient(to bottom, #242424, rgba(0, 0, 0, 0.89));
43 | color: #fff; }
44 |
45 | .medium-editor-toolbar-form {
46 | background: #242424;
47 | border-radius: 5px;
48 | color: #999; }
49 | .medium-editor-toolbar-form .medium-editor-toolbar-input {
50 | background: #242424;
51 | box-sizing: border-box;
52 | color: #ccc;
53 | height: 50px; }
54 | .medium-editor-toolbar-form a {
55 | color: #fff; }
56 |
57 | .medium-editor-toolbar-anchor-preview {
58 | background: #242424;
59 | border-radius: 5px;
60 | color: #fff; }
61 |
62 | .medium-editor-placeholder:after {
63 | color: #b3b3b1; }
64 |
--------------------------------------------------------------------------------
/public/css/medium-editor.css:
--------------------------------------------------------------------------------
1 | @-webkit-keyframes medium-editor-image-loading {
2 | 0% {
3 | -webkit-transform: scale(0);
4 | transform: scale(0); }
5 | 100% {
6 | -webkit-transform: scale(1);
7 | transform: scale(1); } }
8 |
9 | @keyframes medium-editor-image-loading {
10 | 0% {
11 | -webkit-transform: scale(0);
12 | transform: scale(0); }
13 | 100% {
14 | -webkit-transform: scale(1);
15 | transform: scale(1); } }
16 |
17 | @-webkit-keyframes medium-editor-pop-upwards {
18 | 0% {
19 | opacity: 0;
20 | -webkit-transform: matrix(0.97, 0, 0, 1, 0, 12);
21 | transform: matrix(0.97, 0, 0, 1, 0, 12); }
22 | 20% {
23 | opacity: .7;
24 | -webkit-transform: matrix(0.99, 0, 0, 1, 0, 2);
25 | transform: matrix(0.99, 0, 0, 1, 0, 2); }
26 | 40% {
27 | opacity: 1;
28 | -webkit-transform: matrix(1, 0, 0, 1, 0, -1);
29 | transform: matrix(1, 0, 0, 1, 0, -1); }
30 | 100% {
31 | -webkit-transform: matrix(1, 0, 0, 1, 0, 0);
32 | transform: matrix(1, 0, 0, 1, 0, 0); } }
33 |
34 | @keyframes medium-editor-pop-upwards {
35 | 0% {
36 | opacity: 0;
37 | -webkit-transform: matrix(0.97, 0, 0, 1, 0, 12);
38 | transform: matrix(0.97, 0, 0, 1, 0, 12); }
39 | 20% {
40 | opacity: .7;
41 | -webkit-transform: matrix(0.99, 0, 0, 1, 0, 2);
42 | transform: matrix(0.99, 0, 0, 1, 0, 2); }
43 | 40% {
44 | opacity: 1;
45 | -webkit-transform: matrix(1, 0, 0, 1, 0, -1);
46 | transform: matrix(1, 0, 0, 1, 0, -1); }
47 | 100% {
48 | -webkit-transform: matrix(1, 0, 0, 1, 0, 0);
49 | transform: matrix(1, 0, 0, 1, 0, 0); } }
50 |
51 | .medium-editor-anchor-preview {
52 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
53 | font-size: 16px;
54 | left: 0;
55 | line-height: 1.4;
56 | max-width: 280px;
57 | position: absolute;
58 | text-align: center;
59 | top: 0;
60 | word-break: break-all;
61 | word-wrap: break-word;
62 | visibility: hidden;
63 | z-index: 2000; }
64 | .medium-editor-anchor-preview a {
65 | color: #fff;
66 | display: inline-block;
67 | margin: 5px 5px 10px; }
68 |
69 | .medium-editor-anchor-preview-active {
70 | visibility: visible; }
71 |
72 | .medium-editor-dragover {
73 | background: #ddd; }
74 |
75 | .medium-editor-image-loading {
76 | -webkit-animation: medium-editor-image-loading 1s infinite ease-in-out;
77 | animation: medium-editor-image-loading 1s infinite ease-in-out;
78 | background-color: #333;
79 | border-radius: 100%;
80 | display: inline-block;
81 | height: 40px;
82 | width: 40px; }
83 |
84 | .medium-editor-placeholder {
85 | position: relative; }
86 | .medium-editor-placeholder:after {
87 | content: attr(data-placeholder) !important;
88 | font-style: italic;
89 | position: absolute;
90 | left: 0;
91 | top: 0;
92 | white-space: pre;
93 | padding: inherit;
94 | margin: inherit; }
95 |
96 | .medium-editor-placeholder-relative {
97 | position: relative; }
98 | .medium-editor-placeholder-relative:after {
99 | content: attr(data-placeholder) !important;
100 | font-style: italic;
101 | position: relative;
102 | white-space: pre;
103 | padding: inherit;
104 | margin: inherit; }
105 |
106 | .medium-toolbar-arrow-under:after, .medium-toolbar-arrow-over:before {
107 | border-style: solid;
108 | content: '';
109 | display: block;
110 | height: 0;
111 | left: 50%;
112 | margin-left: -8px;
113 | position: absolute;
114 | width: 0; }
115 |
116 | .medium-toolbar-arrow-under:after {
117 | border-width: 8px 8px 0 8px; }
118 |
119 | .medium-toolbar-arrow-over:before {
120 | border-width: 0 8px 8px 8px;
121 | top: -8px; }
122 |
123 | .medium-editor-toolbar {
124 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
125 | font-size: 16px;
126 | left: 0;
127 | position: absolute;
128 | top: 0;
129 | visibility: hidden;
130 | z-index: 2000; }
131 | .medium-editor-toolbar ul {
132 | margin: 0;
133 | padding: 0; }
134 | .medium-editor-toolbar li {
135 | float: left;
136 | list-style: none;
137 | margin: 0;
138 | padding: 0; }
139 | .medium-editor-toolbar li button {
140 | box-sizing: border-box;
141 | cursor: pointer;
142 | display: block;
143 | font-size: 14px;
144 | line-height: 1.33;
145 | margin: 0;
146 | padding: 15px;
147 | text-decoration: none; }
148 | .medium-editor-toolbar li button:focus {
149 | outline: none; }
150 | .medium-editor-toolbar li .medium-editor-action-underline {
151 | text-decoration: underline; }
152 | .medium-editor-toolbar li .medium-editor-action-pre {
153 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
154 | font-size: 12px;
155 | font-weight: 100;
156 | padding: 15px 0; }
157 |
158 | .medium-editor-toolbar-active {
159 | visibility: visible; }
160 |
161 | .medium-editor-sticky-toolbar {
162 | position: fixed;
163 | top: 1px; }
164 |
165 | .medium-editor-relative-toolbar {
166 | position: relative; }
167 |
168 | .medium-editor-toolbar-active.medium-editor-stalker-toolbar {
169 | -webkit-animation: medium-editor-pop-upwards 160ms forwards linear;
170 | animation: medium-editor-pop-upwards 160ms forwards linear; }
171 |
172 | .medium-editor-action-bold {
173 | font-weight: bolder; }
174 |
175 | .medium-editor-action-italic {
176 | font-style: italic; }
177 |
178 | .medium-editor-toolbar-form {
179 | display: none; }
180 | .medium-editor-toolbar-form input,
181 | .medium-editor-toolbar-form a {
182 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; }
183 | .medium-editor-toolbar-form .medium-editor-toolbar-form-row {
184 | line-height: 14px;
185 | margin-left: 5px;
186 | padding-bottom: 5px; }
187 | .medium-editor-toolbar-form .medium-editor-toolbar-input,
188 | .medium-editor-toolbar-form label {
189 | border: none;
190 | box-sizing: border-box;
191 | font-size: 14px;
192 | margin: 0;
193 | padding: 6px;
194 | width: 316px;
195 | display: inline-block; }
196 | .medium-editor-toolbar-form .medium-editor-toolbar-input:focus,
197 | .medium-editor-toolbar-form label:focus {
198 | -webkit-appearance: none;
199 | -moz-appearance: none;
200 | appearance: none;
201 | border: none;
202 | box-shadow: none;
203 | outline: 0; }
204 | .medium-editor-toolbar-form a {
205 | display: inline-block;
206 | font-size: 24px;
207 | font-weight: bolder;
208 | margin: 0 10px;
209 | text-decoration: none; }
210 |
211 | .medium-editor-toolbar-form-active {
212 | display: block; }
213 |
214 | .medium-editor-toolbar-actions:after {
215 | clear: both;
216 | content: "";
217 | display: table; }
218 |
219 | .medium-editor-element {
220 | word-wrap: break-word;
221 | min-height: 30px; }
222 | .medium-editor-element img {
223 | max-width: 100%; }
224 | .medium-editor-element sub {
225 | vertical-align: sub; }
226 | .medium-editor-element sup {
227 | vertical-align: super; }
228 |
229 | .medium-editor-hidden {
230 | display: none; }
231 |
--------------------------------------------------------------------------------
/public/css/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 50px;
3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
4 | }
5 |
6 | a {
7 | color: #00B7FF;
8 | }
9 |
10 | .editor {
11 |
12 | }
13 |
14 | .bullet {
15 | width: 8px;
16 | height: 8px;
17 | background-color: #ff0000;
18 | }
19 |
--------------------------------------------------------------------------------
/public/images/TopicQuestsLogo_sm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KnowledgeGarden/lite-net-3/92d9e8053fb95e06e2a325eb25a775435dc86c00/public/images/TopicQuestsLogo_sm.png
--------------------------------------------------------------------------------
/public/images/github-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KnowledgeGarden/lite-net-3/92d9e8053fb95e06e2a325eb25a775435dc86c00/public/images/github-1.jpg
--------------------------------------------------------------------------------
/public/images/map.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KnowledgeGarden/lite-net-3/92d9e8053fb95e06e2a325eb25a775435dc86c00/public/images/map.gif
--------------------------------------------------------------------------------
/public/js/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KnowledgeGarden/lite-net-3/92d9e8053fb95e06e2a325eb25a775435dc86c00/public/js/.DS_Store
--------------------------------------------------------------------------------
/public/js/bootstrap3-typeahead.js:
--------------------------------------------------------------------------------
1 | /* =============================================================
2 | * bootstrap3-typeahead.js v4.0.2
3 | * https://github.com/bassjobsen/Bootstrap-3-Typeahead
4 | * =============================================================
5 | * Original written by @mdo and @fat
6 | * =============================================================
7 | * Copyright 2014 Bass Jobsen @bassjobsen
8 | *
9 | * Licensed under the Apache License, Version 2.0 (the 'License');
10 | * you may not use this file except in compliance with the License.
11 | * You may obtain a copy of the License at
12 | *
13 | * http://www.apache.org/licenses/LICENSE-2.0
14 | *
15 | * Unless required by applicable law or agreed to in writing, software
16 | * distributed under the License is distributed on an 'AS IS' BASIS,
17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | * See the License for the specific language governing permissions and
19 | * limitations under the License.
20 | * ============================================================ */
21 |
22 |
23 | (function (root, factory) {
24 |
25 | 'use strict';
26 |
27 | // CommonJS module is defined
28 | if (typeof module !== 'undefined' && module.exports) {
29 | module.exports = factory(require('jquery'));
30 | }
31 | // AMD module is defined
32 | else if (typeof define === 'function' && define.amd) {
33 | define(['jquery'], function ($) {
34 | return factory($);
35 | });
36 | } else {
37 | factory(root.jQuery);
38 | }
39 |
40 | }(this, function ($) {
41 |
42 | 'use strict';
43 | // jshint laxcomma: true
44 |
45 |
46 | /* TYPEAHEAD PUBLIC CLASS DEFINITION
47 | * ================================= */
48 |
49 | var Typeahead = function (element, options) {
50 | this.$element = $(element);
51 | this.options = $.extend({}, Typeahead.defaults, options);
52 | this.matcher = this.options.matcher || this.matcher;
53 | this.sorter = this.options.sorter || this.sorter;
54 | this.select = this.options.select || this.select;
55 | this.autoSelect = typeof this.options.autoSelect == 'boolean' ? this.options.autoSelect : true;
56 | this.highlighter = this.options.highlighter || this.highlighter;
57 | this.render = this.options.render || this.render;
58 | this.updater = this.options.updater || this.updater;
59 | this.displayText = this.options.displayText || this.displayText;
60 | this.itemLink = this.options.itemLink || this.itemLink;
61 | this.itemTitle = this.options.itemTitle || this.itemTitle;
62 | this.followLinkOnSelect = this.options.followLinkOnSelect || this.followLinkOnSelect;
63 | this.source = this.options.source;
64 | this.delay = this.options.delay;
65 | this.theme = this.options.theme && this.options.themes && this.options.themes[this.options.theme] || Typeahead.defaults.themes[Typeahead.defaults.theme];
66 | this.$menu = $(this.options.menu || this.theme.menu);
67 | this.$appendTo = this.options.appendTo ? $(this.options.appendTo) : null;
68 | this.fitToElement = typeof this.options.fitToElement == 'boolean' ? this.options.fitToElement : false;
69 | this.shown = false;
70 | this.listen();
71 | this.showHintOnFocus = typeof this.options.showHintOnFocus == 'boolean' || this.options.showHintOnFocus === 'all' ? this.options.showHintOnFocus : false;
72 | this.afterSelect = this.options.afterSelect;
73 | this.afterEmptySelect = this.options.afterEmptySelect;
74 | this.addItem = false;
75 | this.value = this.$element.val() || this.$element.text();
76 | this.keyPressed = false;
77 | this.focused = this.$element.is(':focus');
78 | this.changeInputOnSelect = this.options.changeInputOnSelect || this.changeInputOnSelect;
79 | this.changeInputOnMove = this.options.changeInputOnMove || this.changeInputOnMove;
80 | this.openLinkInNewTab = this.options.openLinkInNewTab || this.openLinkInNewTab;
81 | this.selectOnBlur = this.options.selectOnBlur || this.selectOnBlur;
82 | this.showCategoryHeader = this.options.showCategoryHeader || this.showCategoryHeader;
83 | };
84 |
85 | Typeahead.prototype = {
86 |
87 | constructor: Typeahead,
88 |
89 |
90 | setDefault: function (val) {
91 | // var val = this.$menu.find('.active').data('value');
92 | this.$element.data('active', val);
93 | if (this.autoSelect || val) {
94 | var newVal = this.updater(val);
95 | // Updater can be set to any random functions via "options" parameter in constructor above.
96 | // Add null check for cases when updater returns void or undefined.
97 | if (!newVal) {
98 | newVal = '';
99 | }
100 | this.$element
101 | .val(this.displayText(newVal) || newVal)
102 | .text(this.displayText(newVal) || newVal)
103 | .change();
104 | this.afterSelect(newVal);
105 | }
106 | return this.hide();
107 | },
108 |
109 | select: function () {
110 | var val = this.$menu.find('.active').data('value');
111 |
112 | this.$element.data('active', val);
113 | if (this.autoSelect || val) {
114 | var newVal = this.updater(val);
115 | // Updater can be set to any random functions via "options" parameter in constructor above.
116 | // Add null check for cases when updater returns void or undefined.
117 | if (!newVal) {
118 | newVal = '';
119 | }
120 |
121 | if (this.changeInputOnSelect) {
122 | this.$element
123 | .val(this.displayText(newVal) || newVal)
124 | .text(this.displayText(newVal) || newVal)
125 | .change();
126 | }
127 |
128 | if (this.followLinkOnSelect && this.itemLink(val)) {
129 | if (this.openLinkInNewTab) {
130 | window.open(this.itemLink(val), '_blank');
131 | } else {
132 | document.location = this.itemLink(val);
133 | }
134 | this.afterSelect(newVal);
135 | } else if (this.followLinkOnSelect && !this.itemLink(val)) {
136 | this.afterEmptySelect(newVal);
137 | } else {
138 | this.afterSelect(newVal);
139 | }
140 | } else {
141 | this.afterEmptySelect();
142 | }
143 |
144 | return this.hide();
145 | },
146 |
147 | updater: function (item) {
148 | return item;
149 | },
150 |
151 | setSource: function (source) {
152 | this.source = source;
153 | },
154 |
155 | show: function () {
156 | var pos = $.extend({}, this.$element.position(), {
157 | height: this.$element[0].offsetHeight
158 | });
159 |
160 | var scrollHeight = typeof this.options.scrollHeight == 'function' ?
161 | this.options.scrollHeight.call() :
162 | this.options.scrollHeight;
163 |
164 | var element;
165 | if (this.shown) {
166 | element = this.$menu;
167 | } else if (this.$appendTo) {
168 | element = this.$menu.appendTo(this.$appendTo);
169 | this.hasSameParent = this.$appendTo.is(this.$element.parent());
170 | } else {
171 | element = this.$menu.insertAfter(this.$element);
172 | this.hasSameParent = true;
173 | }
174 |
175 | if (!this.hasSameParent) {
176 | // We cannot rely on the element position, need to position relative to the window
177 | element.css('position', 'fixed');
178 | var offset = this.$element.offset();
179 | pos.top = offset.top;
180 | pos.left = offset.left;
181 | }
182 | // The rules for bootstrap are: 'dropup' in the parent and 'dropdown-menu-right' in the element.
183 | // Note that to get right alignment, you'll need to specify `menu` in the options to be:
184 | // ' '
185 | var dropup = $(element).parent().hasClass('dropup');
186 | var newTop = dropup ? 'auto' : (pos.top + pos.height + scrollHeight);
187 | var right = $(element).hasClass('dropdown-menu-right');
188 | var newLeft = right ? 'auto' : pos.left;
189 | // it seems like setting the css is a bad idea (just let Bootstrap do it), but I'll keep the old
190 | // logic in place except for the dropup/right-align cases.
191 | element.css({ top: newTop, left: newLeft }).show();
192 |
193 | if (this.options.fitToElement === true) {
194 | element.css('width', this.$element.outerWidth() + 'px');
195 | }
196 |
197 | this.shown = true;
198 | return this;
199 | },
200 |
201 | hide: function () {
202 | this.$menu.hide();
203 | this.shown = false;
204 | return this;
205 | },
206 |
207 | lookup: function (query) {
208 | if (typeof(query) != 'undefined' && query !== null) {
209 | this.query = query;
210 | } else {
211 | this.query = this.$element.val();
212 | }
213 |
214 | if (this.query.length < this.options.minLength && !this.options.showHintOnFocus) {
215 | return this.shown ? this.hide() : this;
216 | }
217 |
218 | var worker = $.proxy(function () {
219 |
220 | // Bloodhound (since 0.11) needs three arguments.
221 | // Two of them are callback functions (sync and async) for local and remote data processing
222 | // see https://github.com/twitter/typeahead.js/blob/master/src/bloodhound/bloodhound.js#L132
223 | if ($.isFunction(this.source) && this.source.length === 3) {
224 | this.source(this.query, $.proxy(this.process, this), $.proxy(this.process, this));
225 | } else if ($.isFunction(this.source)) {
226 | this.source(this.query, $.proxy(this.process, this));
227 | } else if (this.source) {
228 | this.process(this.source);
229 | }
230 | }, this);
231 |
232 | clearTimeout(this.lookupWorker);
233 | this.lookupWorker = setTimeout(worker, this.delay);
234 | },
235 |
236 | process: function (items) {
237 | var that = this;
238 |
239 | items = $.grep(items, function (item) {
240 | return that.matcher(item);
241 | });
242 |
243 | items = this.sorter(items);
244 |
245 | if (!items.length && !this.options.addItem) {
246 | return this.shown ? this.hide() : this;
247 | }
248 |
249 | if (items.length > 0) {
250 | this.$element.data('active', items[0]);
251 | } else {
252 | this.$element.data('active', null);
253 | }
254 |
255 | if (this.options.items != 'all') {
256 | items = items.slice(0, this.options.items);
257 | }
258 |
259 | // Add item
260 | if (this.options.addItem) {
261 | items.push(this.options.addItem);
262 | }
263 |
264 | return this.render(items).show();
265 | },
266 |
267 | matcher: function (item) {
268 | var it = this.displayText(item);
269 | return ~it.toLowerCase().indexOf(this.query.toLowerCase());
270 | },
271 |
272 | sorter: function (items) {
273 | var beginswith = [];
274 | var caseSensitive = [];
275 | var caseInsensitive = [];
276 | var item;
277 |
278 | while ((item = items.shift())) {
279 | var it = this.displayText(item);
280 | if (!it.toLowerCase().indexOf(this.query.toLowerCase())) {
281 | beginswith.push(item);
282 | } else if (~it.indexOf(this.query)) {
283 | caseSensitive.push(item);
284 | } else {
285 | caseInsensitive.push(item);
286 | }
287 | }
288 |
289 | return beginswith.concat(caseSensitive, caseInsensitive);
290 | },
291 |
292 | highlighter: function (item) {
293 | var text = this.query;
294 | if (text === '') {
295 | return item;
296 | }
297 | var matches = item.match(/(>)([^<]*)(<)/g);
298 | var first = [];
299 | var second = [];
300 | var i;
301 | if (matches && matches.length) {
302 | // html
303 | for (i = 0; i < matches.length; ++i) {
304 | if (matches[i].length > 2) {// escape '><'
305 | first.push(matches[i]);
306 | }
307 | }
308 | } else {
309 | // text
310 | first = [];
311 | first.push(item);
312 | }
313 | text = text.replace((/[\(\)\/\.\*\+\?\[\]]/g), function (mat) {
314 | return '\\' + mat;
315 | });
316 | var reg = new RegExp(text, 'g');
317 | var m;
318 | for (i = 0; i < first.length; ++i) {
319 | m = first[i].match(reg);
320 | if (m && m.length > 0) {// find all text nodes matches
321 | second.push(first[i]);
322 | }
323 | }
324 | for (i = 0; i < second.length; ++i) {
325 | item = item.replace(second[i], second[i].replace(reg, '$&'));
326 | }
327 | return item;
328 | },
329 |
330 | render: function (items) {
331 | var that = this;
332 | var self = this;
333 | var activeFound = false;
334 | var data = [];
335 | var _category = that.options.separator;
336 |
337 | $.each(items, function (key, value) {
338 | // inject separator
339 | if (key > 0 && value[_category] !== items[key - 1][_category]) {
340 | data.push({
341 | __type: 'divider'
342 | });
343 | }
344 |
345 | if (this.showCategoryHeader) {
346 | // inject category header
347 | if (value[_category] && (key === 0 || value[_category] !== items[key - 1][_category])) {
348 | data.push({
349 | __type: 'category',
350 | name: value[_category]
351 | });
352 | }
353 | }
354 |
355 | data.push(value);
356 | });
357 |
358 | items = $(data).map(function (i, item) {
359 | if ((item.__type || false) == 'category'){
360 | return $(that.options.headerHtml || that.theme.headerHtml).text(item.name)[0];
361 | }
362 |
363 | if ((item.__type || false) == 'divider'){
364 | return $(that.options.headerDivider || that.theme.headerDivider)[0];
365 | }
366 |
367 | var text = self.displayText(item);
368 | i = $(that.options.item || that.theme.item).data('value', item);
369 | i.find(that.options.itemContentSelector || that.theme.itemContentSelector)
370 | .addBack(that.options.itemContentSelector || that.theme.itemContentSelector)
371 | .html(that.highlighter(text, item));
372 | if(that.options.followLinkOnSelect) {
373 | i.find('a').attr('href', self.itemLink(item));
374 | }
375 | i.find('a').attr('title', self.itemTitle(item));
376 | if (text == self.$element.val()) {
377 | i.addClass('active');
378 | self.$element.data('active', item);
379 | activeFound = true;
380 | }
381 | return i[0];
382 | });
383 |
384 | if (this.autoSelect && !activeFound) {
385 | items.filter(':not(.dropdown-header)').first().addClass('active');
386 | this.$element.data('active', items.first().data('value'));
387 | }
388 | this.$menu.html(items);
389 | return this;
390 | },
391 |
392 | displayText: function (item) {
393 | return typeof item !== 'undefined' && typeof item.name != 'undefined' ? item.name : item;
394 | },
395 |
396 | itemLink: function (item) {
397 | return null;
398 | },
399 |
400 | itemTitle: function (item) {
401 | return null;
402 | },
403 |
404 | next: function (event) {
405 | var active = this.$menu.find('.active').removeClass('active');
406 | var next = active.next();
407 |
408 | if (!next.length) {
409 | next = $(this.$menu.find($(this.options.item || this.theme.item).prop('tagName'))[0]);
410 | }
411 |
412 | while (next.hasClass('divider') || next.hasClass('dropdown-header')) {
413 | next = next.next();
414 | }
415 |
416 | next.addClass('active');
417 | // added for screen reader
418 | var newVal = this.updater(next.data('value'));
419 | if (this.changeInputOnMove) {
420 | this.$element.val(this.displayText(newVal) || newVal);
421 | }
422 | },
423 |
424 | prev: function (event) {
425 | var active = this.$menu.find('.active').removeClass('active');
426 | var prev = active.prev();
427 |
428 | if (!prev.length) {
429 | prev = this.$menu.find($(this.options.item || this.theme.item).prop('tagName')).last();
430 | }
431 |
432 | while (prev.hasClass('divider') || prev.hasClass('dropdown-header')) {
433 | prev = prev.prev();
434 | }
435 |
436 | prev.addClass('active');
437 | // added for screen reader
438 | var newVal = this.updater(prev.data('value'));
439 | if (this.changeInputOnMove) {
440 | this.$element.val(this.displayText(newVal) || newVal);
441 | }
442 | },
443 |
444 | listen: function () {
445 | this.$element
446 | .on('focus.bootstrap3Typeahead', $.proxy(this.focus, this))
447 | .on('blur.bootstrap3Typeahead', $.proxy(this.blur, this))
448 | .on('keypress.bootstrap3Typeahead', $.proxy(this.keypress, this))
449 | .on('propertychange.bootstrap3Typeahead input.bootstrap3Typeahead', $.proxy(this.input, this))
450 | .on('keyup.bootstrap3Typeahead', $.proxy(this.keyup, this));
451 |
452 | if (this.eventSupported('keydown')) {
453 | this.$element.on('keydown.bootstrap3Typeahead', $.proxy(this.keydown, this));
454 | }
455 |
456 | var itemTagName = $(this.options.item || this.theme.item).prop('tagName');
457 | if ('ontouchstart' in document.documentElement && 'onmousemove' in document.documentElement) {
458 | this.$menu
459 | .on('touchstart', itemTagName, $.proxy(this.touchstart, this))
460 | .on('touchend', itemTagName, $.proxy(this.click, this))
461 | .on('click', $.proxy(this.click, this))
462 | .on('mouseenter', itemTagName, $.proxy(this.mouseenter, this))
463 | .on('mouseleave', itemTagName, $.proxy(this.mouseleave, this))
464 | .on('mousedown', $.proxy(this.mousedown,this));
465 | } else if ('ontouchstart' in document.documentElement) {
466 | this.$menu
467 | .on('touchstart', itemTagName, $.proxy(this.touchstart, this))
468 | .on('touchend', itemTagName, $.proxy(this.click, this));
469 | } else {
470 | this.$menu
471 | .on('click', $.proxy(this.click, this))
472 | .on('mouseenter', itemTagName, $.proxy(this.mouseenter, this))
473 | .on('mouseleave', itemTagName, $.proxy(this.mouseleave, this))
474 | .on('mousedown', $.proxy(this.mousedown, this));
475 | }
476 | },
477 |
478 | destroy: function () {
479 | this.$element.data('typeahead', null);
480 | this.$element.data('active', null);
481 | this.$element
482 | .unbind('focus.bootstrap3Typeahead')
483 | .unbind('blur.bootstrap3Typeahead')
484 | .unbind('keypress.bootstrap3Typeahead')
485 | .unbind('propertychange.bootstrap3Typeahead input.bootstrap3Typeahead')
486 | .unbind('keyup.bootstrap3Typeahead');
487 |
488 | if (this.eventSupported('keydown')) {
489 | this.$element.unbind('keydown.bootstrap3-typeahead');
490 | }
491 |
492 | this.$menu.remove();
493 | this.destroyed = true;
494 | },
495 |
496 | eventSupported: function (eventName) {
497 | var isSupported = eventName in this.$element;
498 | if (!isSupported) {
499 | this.$element.setAttribute(eventName, 'return;');
500 | isSupported = typeof this.$element[eventName] === 'function';
501 | }
502 | return isSupported;
503 | },
504 |
505 | move: function (e) {
506 | if (!this.shown) {
507 | return;
508 | }
509 |
510 | switch (e.keyCode) {
511 | case 9: // tab
512 | case 13: // enter
513 | case 27: // escape
514 | e.preventDefault();
515 | break;
516 |
517 | case 38: // up arrow
518 | // with the shiftKey (this is actually the left parenthesis)
519 | if (e.shiftKey) {
520 | return;
521 | }
522 | e.preventDefault();
523 | this.prev();
524 | break;
525 |
526 | case 40: // down arrow
527 | // with the shiftKey (this is actually the right parenthesis)
528 | if (e.shiftKey) {
529 | return;
530 | }
531 | e.preventDefault();
532 | this.next();
533 | break;
534 | }
535 | },
536 |
537 | keydown: function (e) {
538 | /**
539 | * Prevent to make an ajax call while copying and pasting.
540 | *
541 | * @author Simone Sacchi
542 | * @version 2018/01/18
543 | */
544 | if (e.keyCode === 17) { // ctrl
545 | return;
546 | }
547 | this.keyPressed = true;
548 | this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40, 38, 9, 13, 27]);
549 | if (!this.shown && e.keyCode == 40) {
550 | this.lookup();
551 | } else {
552 | this.move(e);
553 | }
554 | },
555 |
556 | keypress: function (e) {
557 | if (this.suppressKeyPressRepeat) {
558 | return;
559 | }
560 | this.move(e);
561 | },
562 |
563 | input: function (e) {
564 | // This is a fixed for IE10/11 that fires the input event when a placehoder is changed
565 | // (https://connect.microsoft.com/IE/feedback/details/810538/ie-11-fires-input-event-on-focus)
566 | var currentValue = this.$element.val() || this.$element.text();
567 | if (this.value !== currentValue) {
568 | this.value = currentValue;
569 | this.lookup();
570 | }
571 | },
572 |
573 | keyup: function (e) {
574 | if (this.destroyed) {
575 | return;
576 | }
577 | switch (e.keyCode) {
578 | case 40: // down arrow
579 | case 38: // up arrow
580 | case 16: // shift
581 | case 17: // ctrl
582 | case 18: // alt
583 | break;
584 |
585 | case 9: // tab
586 | if (!this.shown || (this.showHintOnFocus && !this.keyPressed)) {
587 | return;
588 | }
589 | this.select();
590 | break;
591 | case 13: // enter
592 | if (!this.shown) {
593 | return;
594 | }
595 | this.select();
596 | break;
597 |
598 | case 27: // escape
599 | if (!this.shown) {
600 | return;
601 | }
602 | this.hide();
603 | break;
604 | }
605 |
606 | },
607 |
608 | focus: function (e) {
609 | if (!this.focused) {
610 | this.focused = true;
611 | this.keyPressed = false;
612 | if (this.options.showHintOnFocus && this.skipShowHintOnFocus !== true) {
613 | if (this.options.showHintOnFocus === 'all') {
614 | this.lookup('');
615 | } else {
616 | this.lookup();
617 | }
618 | }
619 | }
620 | if (this.skipShowHintOnFocus) {
621 | this.skipShowHintOnFocus = false;
622 | }
623 | },
624 |
625 | blur: function (e) {
626 | if (!this.mousedover && !this.mouseddown && this.shown) {
627 | if (this.selectOnBlur) {
628 | this.select();
629 | }
630 | this.hide();
631 | this.focused = false;
632 | this.keyPressed = false;
633 | } else if (this.mouseddown) {
634 | // This is for IE that blurs the input when user clicks on scroll.
635 | // We set the focus back on the input and prevent the lookup to occur again
636 | this.skipShowHintOnFocus = true;
637 | this.$element.focus();
638 | this.mouseddown = false;
639 | }
640 | },
641 |
642 | click: function (e) {
643 | e.preventDefault();
644 | this.skipShowHintOnFocus = true;
645 | this.select();
646 | this.$element.focus();
647 | this.hide();
648 | },
649 |
650 | mouseenter: function (e) {
651 | this.mousedover = true;
652 | this.$menu.find('.active').removeClass('active');
653 | $(e.currentTarget).addClass('active');
654 | },
655 |
656 | mouseleave: function (e) {
657 | this.mousedover = false;
658 | if (!this.focused && this.shown) {
659 | this.hide();
660 | }
661 | },
662 |
663 | /**
664 | * We track the mousedown for IE. When clicking on the menu scrollbar, IE makes the input blur thus hiding the menu.
665 | */
666 | mousedown: function (e) {
667 | this.mouseddown = true;
668 | this.$menu.one('mouseup', function (e) {
669 | // IE won't fire this, but FF and Chrome will so we reset our flag for them here
670 | this.mouseddown = false;
671 | }.bind(this));
672 | },
673 |
674 | touchstart: function (e) {
675 | e.preventDefault();
676 | this.$menu.find('.active').removeClass('active');
677 | $(e.currentTarget).addClass('active');
678 | },
679 |
680 | touchend: function (e) {
681 | e.preventDefault();
682 | this.select();
683 | this.$element.focus();
684 | }
685 |
686 | };
687 |
688 |
689 | /* TYPEAHEAD PLUGIN DEFINITION
690 | * =========================== */
691 |
692 | var old = $.fn.typeahead;
693 |
694 | $.fn.typeahead = function (option) {
695 | var arg = arguments;
696 | if (typeof option == 'string' && option == 'getActive') {
697 | return this.data('active');
698 | }
699 | return this.each(function () {
700 | var $this = $(this);
701 | var data = $this.data('typeahead');
702 | var options = typeof option == 'object' && option;
703 | if (!data) {
704 | $this.data('typeahead', (data = new Typeahead(this, options)));
705 | }
706 | if (typeof option == 'string' && data[option]) {
707 | if (arg.length > 1) {
708 | data[option].apply(data, Array.prototype.slice.call(arg, 1));
709 | } else {
710 | data[option]();
711 | }
712 | }
713 | });
714 | };
715 |
716 | Typeahead.defaults = {
717 | source: [],
718 | items: 8,
719 | minLength: 1,
720 | scrollHeight: 0,
721 | autoSelect: true,
722 | afterSelect: $.noop,
723 | afterEmptySelect: $.noop,
724 | addItem: false,
725 | followLinkOnSelect: false,
726 | delay: 0,
727 | separator: 'category',
728 | changeInputOnSelect: true,
729 | changeInputOnMove: true,
730 | openLinkInNewTab: false,
731 | selectOnBlur: true,
732 | showCategoryHeader: true,
733 | theme: "bootstrap3",
734 | themes: {
735 | bootstrap3: {
736 | menu: ' ',
737 | item: '{{message}}
2 | {{error.status}}
3 | {{error.stack}}
4 |
--------------------------------------------------------------------------------
/views/iframe.hbs:
--------------------------------------------------------------------------------
1 |
2 |
54 |
89 |
111 |
116 |
117 | Journal
77 | Topics Index
78 | {{#if isAuthenticated}}
79 |
80 |
81 |
82 |
96 | {{/if}}
97 | Journal Entries
98 | Number of Entries: {{itemCount}}
99 |
100 | {{#if a1}}
101 |
128 |
132 | {{{text}}}
133 |
134 | {{/each}}
135 | Journal Editor
9 |
17 | Journal Entry
2 |
3 | {{#if canEdit}}
4 | Edit
5 | {{/if}}
6 | {{date}}
7 | {{{text}}}
8 | {{#if urllist}}
9 |
{{{flashMsg}}}
81 | {{/if}} 82 | {{{body}}} 83 | {{> footer}} 84 |Already have an account? Login
63 |Or go home.
64 |